Fix and improve Ims/LIT plugin - refs BT#13469

pull/2458/head
Angel Fernando Quiroz Campos 8 years ago
parent 20482534a3
commit 002b907867
  1. 221
      plugin/ims_lti/Entity/ImsLtiTool.php
  2. 57
      plugin/ims_lti/ImsLtiPlugin.php
  3. 185
      plugin/ims_lti/ImsLtiTool.php
  4. 8
      plugin/ims_lti/README.md
  5. 77
      plugin/ims_lti/add.php
  6. 6
      plugin/ims_lti/admin.php
  7. 12
      plugin/ims_lti/create.php
  8. 23
      plugin/ims_lti/delete.php
  9. 27
      plugin/ims_lti/edit.php
  10. 13
      plugin/ims_lti/form.php
  11. 3
      plugin/ims_lti/lang/english.php
  12. 20
      plugin/ims_lti/start.php
  13. 16
      plugin/ims_lti/view/admin.tpl
  14. 3
      plugin/ims_lti/view/start.tpl

@ -0,0 +1,221 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Entity\ImsLti;
use Doctrine\ORM\Mapping as ORM;
/**
* Class ImsLtiTool
*
* @ORM\Table(name="plugin_ims_lti_tool")
* @ORM\Entity()
*/
class ImsLtiTool
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
protected $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string")
*/
private $name = '';
/**
* @var string|null
*
* @ORM\Column(name="description", type="text", nullable=true)
*/
private $description = null;
/**
* @var string
*
* @ORM\Column(name="launch_url", type="string")
*/
private $launchUrl = '';
/**
* @var string
*
* @ORM\Column(name="consumer_key", type="string")
*/
private $consumerKey = '';
/**
* @var string
*
* @ORM\Column(name="shared_secret", type="string")
*/
private $sharedSecret = '';
/**
* @var string|null
*
* @ORM\Column(name="custom_params", type="text", nullable=true)
*/
private $customParams = null;
/**
* @var bool
*
* @ORM\Column(name="is_global", type="boolean")
*/
private $isGlobal = false;
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
* @return ImsLtiTool
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return null|string
*/
public function getDescription()
{
return $this->description;
}
/**
* @param null|string $description
* @return ImsLtiTool
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* @return string
*/
public function getLaunchUrl()
{
return $this->launchUrl;
}
/**
* @param string $launchUrl
* @return ImsLtiTool
*/
public function setLaunchUrl($launchUrl)
{
$this->launchUrl = $launchUrl;
return $this;
}
/**
* @return string
*/
public function getConsumerKey()
{
return $this->consumerKey;
}
/**
* @param string $consumerKey
* @return ImsLtiTool
*/
public function setConsumerKey($consumerKey)
{
$this->consumerKey = $consumerKey;
return $this;
}
/**
* @return string
*/
public function getSharedSecret()
{
return $this->sharedSecret;
}
/**
* @param string $sharedSecret
* @return ImsLtiTool
*/
public function setSharedSecret($sharedSecret)
{
$this->sharedSecret = $sharedSecret;
return $this;
}
/**
* @return null|string
*/
public function getCustomParams()
{
return $this->customParams;
}
/**
* @param null|string $customParams
* @return ImsLtiTool
*/
public function setCustomParams($customParams)
{
$this->customParams = $customParams;
return $this;
}
/**
* @return bool
*/
public function isGlobal()
{
return $this->isGlobal;
}
/**
* @param bool $isGlobal
* @return ImsLtiTool
*/
public function setIsGlobal($isGlobal)
{
$this->isGlobal = $isGlobal;
return $this;
}
/**
* @return array
*/
public function parseCustomParams()
{
$strings = explode($this->customParams, "\n");
$pairs = explode('=', $strings);
return [
'key' => 'custom_'.$pairs[0],
'value' => $pairs[1]
];
}
}

@ -6,6 +6,8 @@ use Chamilo\CoreBundle\Entity\Course;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\DBALException;
use Symfony\Component\Filesystem\Filesystem;
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
/**
* Description of MsiLti
@ -14,7 +16,7 @@ use Doctrine\DBAL\DBALException;
*/
class ImsLtiPlugin extends Plugin
{
const TABLE_TOOL = 'plugin_msi_lti_tool';
const TABLE_TOOL = 'plugin_ims_lti_tool';
public $isAdminPlugin = true;
@ -23,7 +25,7 @@ class ImsLtiPlugin extends Plugin
*/
protected function __construct()
{
$version = '1.0';
$version = '1.0 (beta)';
$author = 'Angel Fernando Quiroz Campos';
parent::__construct($version, $author, ['enabled' => 'boolean']);
@ -56,11 +58,23 @@ class ImsLtiPlugin extends Plugin
*/
public function install()
{
try {
$this->setupDatabase();
} catch (DBALException $e) {
error_log('Error while installing IMS/LTI plugin: '.$e->getMessage());
$pluginEntityPath = $this->getEntityPath();
if (!is_dir($pluginEntityPath)) {
if (!is_writable(dirname($pluginEntityPath))) {
$message = get_lang('ErrorCreatingDir').': '.$pluginEntityPath;
Display::addFlash(Display::return_message($message, 'error'));
return false;
}
mkdir($pluginEntityPath, api_get_permissions_for_new_directories());
}
$fs = new Filesystem();
$fs->mirror(__DIR__.'/Entity/', $pluginEntityPath, null, ['override']);
$this->createPluginTables();
}
/**
@ -68,8 +82,15 @@ class ImsLtiPlugin extends Plugin
*/
public function uninstall()
{
$pluginEntityPath = $this->getEntityPath();
$fs = new Filesystem();
if ($fs->exists($pluginEntityPath)) {
$fs->remove($pluginEntityPath);
}
try {
$this->clearDatabase();
$this->dropPluginTables();
$this->removeTools();
} catch (DBALException $e) {
error_log('Error while uninstalling IMS/LTI plugin: '.$e->getMessage());
@ -81,7 +102,7 @@ class ImsLtiPlugin extends Plugin
* @return boolean
* @throws \Doctrine\DBAL\DBALException
*/
private function setupDatabase()
private function createPluginTables()
{
$entityManager = Database::getManager();
$connection = $entityManager->getConnection();
@ -99,7 +120,7 @@ class ImsLtiPlugin extends Plugin
$toolTable->addColumn('launch_url', Type::TEXT);
$toolTable->addColumn('consumer_key', Type::STRING);
$toolTable->addColumn('shared_secret', Type::STRING);
$toolTable->addColumn('custom_params', Type::TEXT);
$toolTable->addColumn('custom_params', Type::TEXT)->setNotnull(false);
$toolTable->addColumn('is_global', Type::BOOLEAN);
$toolTable->setPrimaryKey(['id']);
@ -117,7 +138,7 @@ class ImsLtiPlugin extends Plugin
* @return boolean
* @throws \Doctrine\DBAL\DBALException
*/
private function clearDatabase()
private function dropPluginTables()
{
$entityManager = Database::getManager();
$connection = $entityManager->getConnection();
@ -166,14 +187,13 @@ class ImsLtiPlugin extends Plugin
* Add the course tool
* @param Course $course
* @param ImsLtiTool $tool
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function addCourseTool(Course $course, ImsLtiTool $tool)
{
$em = Database::getManager();
$cToolId = AddCourse::generateToolId($course->getId());
$cTool = new CTool();
$cTool
->setId($cToolId)
->setCId($course->getId())
->setName($tool->getName())
->setLink($this->get_name().'/start.php?'.http_build_query(['id' => $tool->getId()]))
@ -188,6 +208,11 @@ class ImsLtiPlugin extends Plugin
$em->persist($cTool);
$em->flush();
$cTool->setId($cTool->getIid());
$em->persist($cTool);
$em->flush();
}
/**
@ -203,4 +228,12 @@ class ImsLtiPlugin extends Plugin
return $text;
}
/**
* @return string
*/
public function getEntityPath()
{
return api_get_path(SYS_PATH).'src/Chamilo/PluginBundle/Entity/'.$this->getCamelCaseName();
}
}

@ -1,185 +0,0 @@
<?php
/* For license terms, see /license.txt */
/**
* ImsLtiTool
*
* @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
*/
class ImsLtiTool
{
private $id = 0;
private $name = '';
private $description = null;
private $launchUrl = '';
private $consumerKey = '';
private $sharedSecret = '';
private $customParams = null;
private $isGlobal = false;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function getDescription()
{
return $this->description;
}
public function getLaunchUrl()
{
return $this->launchUrl;
}
public function getConsumerKey()
{
return $this->consumerKey;
}
public function getSharedSecret()
{
return $this->sharedSecret;
}
public function getCustomParams()
{
return $this->customParams;
}
public function setId($id)
{
$this->id = $id;
return $this;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function setDescription($description)
{
$this->description = $description;
return $this;
}
public function setLaunchUrl($launchUrl)
{
$this->launchUrl = $launchUrl;
return $this;
}
public function setConsumerKey($consumerKey)
{
$this->consumerKey = $consumerKey;
return $this;
}
public function setSharedSecret($sharedSecret)
{
$this->sharedSecret = $sharedSecret;
return $this;
}
public function setCustomParams($customParams)
{
$this->customParams = $customParams;
return $this;
}
public function save()
{
$parameters = [
'name' => $this->name,
'description' => $this->description,
'launch_url' => $this->launchUrl,
'consumer_key' => $this->consumerKey,
'shared_secret' => $this->sharedSecret,
'custom_params' => $this->customParams,
'is_global' => $this->isGlobal
];
if (!empty($this->id)) {
Database::update(
ImsLtiPlugin::TABLE_TOOL,
$parameters,
['id' => $this->id]
);
return;
}
$this->id = Database::insert(ImsLtiPlugin::TABLE_TOOL, $parameters);
}
public static function fetch($id)
{
$result = Database::select(
'*',
ImsLtiPlugin::TABLE_TOOL,
['where' => [
'id = ?' => intval($id)
]],
'first'
);
if (empty($result)) {
return null;
}
$tool = new self();
$tool->id = $result['id'];
$tool->name = $result['name'];
$tool->description = $result['description'];
$tool->launchUrl = $result['launch_url'];
$tool->consumerKey = $result['consumer_key'];
$tool->sharedSecret = $result['shared_secret'];
$tool->customParams = $result['custom_params'];
$tool->isGlobal = (boolean) $result['is_global'];
return $tool;
}
public static function fetchAll()
{
return Database::select(
'*',
ImsLtiPlugin::TABLE_TOOL
);
}
public function parseCustomParams()
{
$strings = $this->customParams;
$foo = explode('=', $strings);
return [
'key' => 'custom_'.$foo[0],
'value' => $foo[1]
];
}
public function setIsGlobal($isGlobal = true)
{
$this->isGlobal = $isGlobal;
}
public function isGlobal()
{
return $this->isGlobal;
}
}

@ -1,6 +1,14 @@
IMS/LTI plugin
===
Version 1.0 (beta)
Installation
------------
1. Install the plugin from Plugin page
2. Enable the plugin from Plugin Settings page
3. Assign to the Administrator region.
This plugin is meant to be later integrated into Chamilo (in a major version release).
IMS/LTI defines the possibility to integrate tools or content into Chamilo.

@ -1,6 +1,9 @@
<?php
/* For license terms, see /license.txt */
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_course_script();
@ -8,20 +11,16 @@ api_protect_teacher_script();
$plugin = ImsLtiPlugin::create();
$em = Database::getManager();
$toolsRepo = $em->getRepository('ChamiloPluginBundle:ImsLti\ImsLtiTool');
$type = isset($_GET['type']) ? intval($_GET['type']) : 0;
/** @var ImsLtiTool $baseTool */
$baseTool = isset($_REQUEST['type']) ? $toolsRepo->find(intval($_REQUEST['type'])) : null;
/** @var Course $course */
$course = $em->find('ChamiloCoreBundle:Course', api_get_course_int_id());
$tools = array_filter(
ImsLtiTool::fetchAll(),
function ($tool) {
return (boolean) $tool['is_global'];
}
);
$globalTools = $toolsRepo->findBy(['isGlobal' => true]);
$isGlobalTool = $type ? array_key_exists($type, $tools) : true;
if (!$isGlobalTool) {
if ($baseTool && !$baseTool->isGlobal()) {
Display::addFlash(
Display::return_message($plugin->get_lang('ToolNotAvailable'), 'warning')
);
@ -31,16 +30,22 @@ if (!$isGlobalTool) {
}
$form = new FormValidator('ims_lti_add_tool');
$form->addText('name', $plugin->get_lang('ToolName'));
if (!$type) {
$form->addHtml('<div id="show_advanced_options">');
if ($baseTool) {
$form->addHtml('<p class="lead">'.Security::remove_XSS($baseTool->getDescription()).'</p>');
}
$form->addText('name', get_lang('Title'));
$form->addTextarea('description', get_lang('Description'), ['rows' => 10]);
if (!$baseTool) {
$form->addElement('url', 'url', $plugin->get_lang('LaunchUrl'));
$form->addText('consumer_key', $plugin->get_lang('ConsumerKey'), true);
$form->addText('shared_secret', $plugin->get_lang('SharedSecret'), true);
$form->addTextarea('custom_params', $plugin->get_lang('CustomParams'));
$form->addHtml('</div>');
$form->addRule('url', get_lang('Required'), 'required');
} else {
$form->addHidden('type', $baseTool->getId());
}
$form->addButtonCreate($plugin->get_lang('AddExternalTool'));
@ -49,45 +54,41 @@ if ($form->validate()) {
$formValues = $form->getSubmitValues();
$tool = null;
if ($type) {
$baseTool = ImsLtiTool::fetch($type);
if ($baseTool) {
$baseTool->setName($formValues['name']);
}
$tool = $baseTool;
if ($baseTool) {
$tool = clone $baseTool;
} else {
$tool = new ImsLtiTool();
$tool
->setName($formValues['name'])
->setLaunchUrl($formValues['url'])
->setConsumerKey($formValues['consumer_key'])
->setSharedSecret($formValues['shared_secret'])
->setCustomParams($formValues['custom_params'])
->isGlobal(false);
$tool->save();
->setCustomParams(
empty($formValues['custom_params']) ? null : $formValues['custom_params']
);
}
if ($tool) {
$plugin->addCourseTool($course, $tool);
$tool
->setName($formValues['name'])
->setDescription(
empty($formValues['description']) ? null : $formValues['description']
)
->isGlobal(false);
$em->persist($tool);
$em->flush();
Display::addFlash(
Display::return_message($plugin->get_lang('ToolAdded'), 'success')
);
} else {
Display::addFlash(
Display::return_message($plugin->get_lang('NoTool'), 'error')
);
}
$plugin->addCourseTool($course, $tool);
Display::addFlash(
Display::return_message($plugin->get_lang('ToolAdded'), 'success')
);
header('Location: '.api_get_course_url());
exit;
}
$template = new Template($plugin->get_lang('AddExternalTool'));
$template->assign('type', $type);
$template->assign('tools', $tools);
$template->assign('type', $baseTool->getId());
$template->assign('tools', $globalTools);
$template->assign('form', $form->returnForm());
$content = $template->fetch('ims_lti/view/add.tpl');

@ -4,10 +4,12 @@ $cidReset = true;
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_admin_script();
$plugin = ImsLtiPlugin::create();
$em = Database::getManager();
$tools = Database::select('*', ImsLtiPlugin::TABLE_TOOL);
$em = Database::getManager();
$tools = $em->getRepository('ChamiloPluginBundle:ImsLti\ImsLtiTool')->findAll();
$template = new Template($plugin->get_title());
$template->assign('tools', $tools);

@ -1,5 +1,7 @@
<?php
/* For license terms, see /license.txt */
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
$cidReset = true;
require_once __DIR__.'/../../main/inc/global.inc.php';
@ -8,11 +10,14 @@ api_protect_admin_script();
$plugin = ImsLtiPlugin::create();
$em = Database::getManager();
$form = new FormValidator('ism_lti_create_tool');
$form->addText('name', get_lang('Name'));
$form->addText('base_url', get_lang('BaseUrl'));
$form->addText('base_url', $plugin->get_lang('LaunchUrl'));
$form->addText('consumer_key', $plugin->get_lang('ConsumerKey'));
$form->addText('shared_secret', $plugin->get_lang('SharedSecret'));
$form->addTextarea('description', get_lang('Description'), ['rows' => 10]);
$form->addTextarea('custom_params', $plugin->get_lang('CustomParams'));
$form->addButtonCreate($plugin->get_lang('AddExternalTool'));
@ -27,7 +32,10 @@ if ($form->validate()) {
->setConsumerKey($formValues['consumer_key'])
->setSharedSecret($formValues['shared_secret'])
->setCustomParams($formValues['custom_params'])
->save();
->setIsGlobal(true);
$em->persist($externalTool);
$em->flush();
Display::addFlash(
Display::return_message($plugin->get_lang('ToolAdded'), 'success')

@ -1,6 +1,29 @@
<?php
/* For license terms, see /license.txt */
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
require_once __DIR__.'/../../main/inc/global.inc.php';
$plugin = ImsLtiPlugin::create();
api_protect_admin_script();
$em = Database::getManager();
/** @var ImsLtiTool $tool */
$tool = isset($_GET['id']) ? $em->find('ChamiloPluginBundle:ImsLti\ImsLtiTool', intval($_GET['id'])) : 0;
if (!$tool) {
api_not_allowed(true);
}
$em->remove($tool);
$em->flush();
Display::addFlash(
Display::return_message($plugin->get_lang('ToolDeleted'), 'success')
);
header('Location: '.api_get_path(WEB_PLUGIN_PATH).'ims_lti/admin.php');
exit;

@ -1,21 +1,26 @@
<?php
/* For license terms, see /license.txt */
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
$cidReset = true;
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_admin_script();
$toolId = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0;
if (empty($toolId)) {
if (!isset($_REQUEST['id'])) {
api_not_allowed(true);
}
$toolId = intval($_REQUEST['id']);
$plugin = ImsLtiPlugin::create();
$tool = ImsLtiTool::fetch($toolId);
$em = Database::getManager();
if (empty($tool)) {
/** @var ImsLtiTool $tool */
$tool = $em->find('ChamiloPluginBundle:ImsLti\ImsLtiTool', $toolId);
if (!$tool) {
Display::addFlash(
Display::return_message($plugin->get_lang('NoTool'), 'error')
);
@ -26,12 +31,12 @@ if (empty($tool)) {
$form = new FormValidator('ims_lti_edit_tool');
$form->addText('name', get_lang('Name'));
$form->addTextarea('description', get_lang('Description'));
$form->addText('url', get_lang('Url'));
$form->addTextarea('description', get_lang('Description'), ['rows' => 10]);
$form->addText('url', $plugin->get_lang('LaunchUrl'));
$form->addText('consumer_key', $plugin->get_lang('ConsumerKey'));
$form->addText('shared_secret', $plugin->get_lang('SharedSecret'));
$form->addTextarea('custom_params', $plugin->get_lang('CustomParams'));
$form->addButtonCreate($plugin->get_lang('AddExternalTool'));
$form->addButtonSave(get_lang('Save'));
$form->addHidden('id', $tool->getId());
$form->setDefaults([
'name' => $tool->getName(),
@ -51,8 +56,10 @@ if ($form->validate()) {
->setLaunchUrl($formValues['url'])
->setConsumerKey($formValues['consumer_key'])
->setSharedSecret($formValues['shared_secret'])
->setCustomParams($formValues['custom_params'])
->save();
->setCustomParams($formValues['custom_params']);
$em->persist($tool);
$em->flush();
Display::addFlash(
Display::return_message($plugin->get_lang('ToolEdited'), 'success')

@ -3,20 +3,25 @@
use Chamilo\UserBundle\Entity\User;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
require_once __DIR__.'/../../main/inc/global.inc.php';
require './OAuthSimple.php';
api_protect_course_script();
$toolId = isset($_GET['id']) ? intval($_GET['id']) : 0;
$em = Database::getManager();
/** @var ImsLtiTool $tool */
$tool = isset($_GET['id']) ? $em->find('ChamiloPluginBundle:ImsLti\ImsLtiTool', intval($_GET['id'])) : 0;
if (!$tool) {
api_not_allowed(true);
}
/** @var ImsLtiPlugin $imsLtiPlugin */
$imsLtiPlugin = ImsLtiPlugin::create();
/** @var ImsLtiTool $tool */
$tool = ImsLtiTool::fetch($toolId);
/** @var Course $course */
$course = $em->find('ChamiloCoreBundle:Course', api_get_course_int_id());
/** @var User $user */

@ -18,3 +18,6 @@ $strings['ToolNotAdded'] = 'Tool not added';
$strings['AvailableTools'] = 'Available tools';
$strings['ToolSettings'] = 'Tool settings';
$strings['ToolNotAvailable'] = 'Tool not available';
$strings['IsGlobal'] = 'Is global';
$strings['EditExternalTool'] = 'Editar external tool';
$strings['ToolDeleted'] = 'Tool deleted';

@ -1,23 +1,25 @@
<?php
/* For license terms, see /license.txt */
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
require_once __DIR__.'/../../main/inc/global.inc.php';
$toolId = isset($_GET['id']) ? intval($_GET['id']) : 0;
api_protect_course_script();
$em = Database::getManager();
/** @var ImsLtiTool $tool */
$tool = isset($_GET['id']) ? $em->find('ChamiloPluginBundle:ImsLti\ImsLtiTool', intval($_GET['id'])) : null;
if (empty($toolId)) {
if (!$tool) {
api_not_allowed(true);
}
api_protect_course_script();
$imsLtiPlugin = ImsLtiPlugin::create();
$tool = ImsLtiTool::fetch($toolId);
$htmlHeadXtra[] = '<link rel="stylesheet" href="../assets/css/style.css" type="text/css">';
$template = new Template($imsLtiPlugin->get_title());
$template = new Template($tool->getName());
$template->assign('tool', $tool);
$template->assign(
'launch_url',
api_get_path(WEB_PLUGIN_PATH).'ims_lti/form.php?'.http_build_query(['id' => $tool->getId()])

@ -10,15 +10,25 @@
<tr>
<th>{{ 'Name'|get_lang }}</th>
<th>{{ 'LaunchUrl'|get_plugin_lang('ImsLtiPlugin') }}</th>
<th>{{ 'Actions'|get_lang }}</th>
<th class="text-center">{{ 'IsGlobal'|get_plugin_lang('ImsLtiPlugin') }}</th>
<th class="text-right">{{ 'Actions'|get_lang }}</th>
</tr>
</thead>
<tbody>
{% for tool in tools %}
<tr>
<td>{{ tool.name }}</td>
<td>{{ tool.launch_url }}</td>
<td>
<td>{{ tool.launchUrl }}</td>
<td class="text-center">
{% if tool.isGlobal %}
<span class="fa fa-check-square-o" aria-hidden="true"></span>
<span class="sr-only">{{ 'Yes'|get_lang }}</span>
{% else %}
<span class="fa fa-square-o" aria-hidden="true"></span>
<span class="sr-only">{{ 'No'|get_lang }}</span>
{% endif %}
</td>
<td class="text-right">
<a href="{{ _p.web_plugin }}ims_lti/edit.php?{{ {'id': tool.id}|url_encode() }}" class="btn btn-success">
<span class="fa fa-edit fa-fw" aria-hidden="true"></span> {{ 'Edit'|get_lang }}
</a>

@ -1,3 +1,6 @@
{% if tool.description %}
<p class="lead">{{ tool.description }}</p>
{% endif %}
<div class="embed-responsive embed-responsive-16by9">
<iframe src="{{ launch_url }}" class="plugin-ims-lti-iframe"></iframe>
</div>

Loading…
Cancel
Save