Add IMS/LTI plugin - refs #5333

remotes/angel/1.11.x
Angel Fernando Quiroz Campos 8 years ago
parent 8583e24d31
commit 779efaacd3
  1. 180
      plugin/ims_lti/ImsLtiPlugin.php
  2. 180
      plugin/ims_lti/ImsLtiTool.php
  3. 539
      plugin/ims_lti/OAuthSimple.php
  4. 60
      plugin/ims_lti/add.php
  5. 4
      plugin/ims_lti/assets/style.css
  6. 47
      plugin/ims_lti/create.php
  7. 5
      plugin/ims_lti/delete.php
  8. 72
      plugin/ims_lti/edit.php
  9. 77
      plugin/ims_lti/form.php
  10. 12
      plugin/ims_lti/install.php
  11. 16
      plugin/ims_lti/lang/english.php
  12. 19
      plugin/ims_lti/list.php
  13. 4
      plugin/ims_lti/plugin.php
  14. 29
      plugin/ims_lti/start.php
  15. 12
      plugin/ims_lti/uninstall.php
  16. 16
      plugin/ims_lti/view/add.tpl
  17. 33
      plugin/ims_lti/view/list.tpl
  18. 3
      plugin/ims_lti/view/start.tpl

@ -0,0 +1,180 @@
<?php
/* For license terms, see /license.txt */
/**
* Description of MsiLti
*
* @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
*/
class ImsLtiPlugin extends Plugin
{
const TABLE_TOOL = 'plugin_msi_lti_tool';
/**
* Class cronstructor
*/
protected function __construct()
{
$version = '1.0';
$author = 'Angel Fernando Quiroz Campos';
parent::__construct($version, $author, ['enabled' => 'boolean']);
$this->setCourseSettings();
}
/**
* Get the class instance
* @staticvar MsiLtiPlugin $result
* @return MsiLtiPlugin
*/
public static function create()
{
static $result = null;
return $result ?: $result = new self();
}
/**
* Get the plugin directory name
*/
public function get_name()
{
return 'ims_lti';
}
/**
* Install the plugin. Setup the database
*/
public function install()
{
$this->setupDatabase();
}
/**
* Unistall plugin. Clear the database
*/
public function uninstall()
{
$this->clearDatabase();
}
/**
* Creates the plugin tables on database
* @return boolean
*/
private function setupDatabase()
{
$entityManager = Database::getManager();
$connection = $entityManager->getConnection();
$chamiloSchema = $connection->getSchemaManager();
$pluginSchema = new \Doctrine\DBAL\Schema\Schema();
$platform = $connection->getDatabasePlatform();
if ($chamiloSchema->tablesExist([self::TABLE_TOOL])) {
return false;
}
$toolTable = $pluginSchema->createTable(self::TABLE_TOOL);
$toolTable->addColumn(
'id',
\Doctrine\DBAL\Types\Type::INTEGER,
['autoincrement' => true, 'unsigned' => true]
);
$toolTable->addColumn('name', \Doctrine\DBAL\Types\Type::STRING);
$toolTable->addColumn('description', \Doctrine\DBAL\Types\Type::TEXT, ['notnull' => false]);
$toolTable->addColumn('launch_url', \Doctrine\DBAL\Types\Type::TEXT);
$toolTable->addColumn('consumer_key', \Doctrine\DBAL\Types\Type::STRING);
$toolTable->addColumn('shared_secret', \Doctrine\DBAL\Types\Type::STRING);
$toolTable->addColumn('custom_params', \Doctrine\DBAL\Types\Type::TEXT);
$toolTable->setPrimaryKey(['id']);
$queries = $pluginSchema->toSql($platform);
foreach ($queries as $query) {
Database::query($query);
}
return true;
}
/**
* Drops the plugin tables on database
* @return boolean
*/
private function clearDatabase()
{
$entityManager = Database::getManager();
$connection = $entityManager->getConnection();
$chamiloSchema = $connection->getSchemaManager();
if (!$chamiloSchema->tablesExist([self::TABLE_TOOL])) {
return false;
}
$sql = 'DROP TABLE IF EXISTS ' . self::TABLE_TOOL;
Database::query($sql);
return true;
}
/**
* Set the course settings
*/
private function setCourseSettings()
{
$button = Display::toolbarButton(
$this->get_lang('AddExternalTool'),
api_get_path(WEB_PLUGIN_PATH) . 'ims_lti/add.php',
'cog',
'primary'
);
$this->course_settings = [
[
'name' => $this->get_lang('ImsLtiDescription') . $button . '<hr>',
'type' => 'html'
]
];
}
/**
* Add the course tool
* @param \Chamilo\CoreBundle\Entity\Course $course
* @param ImsLtiTool $tool
*/
public function addCourseTool(\Chamilo\CoreBundle\Entity\Course $course, ImsLtiTool $tool)
{
$em = Database::getManager();
$cToolId = AddCourse::generateToolId($course->getId());
$cTool = new \Chamilo\CourseBundle\Entity\CTool();
$cTool
->setId($cToolId)
->setCId($course->getId())
->setName($tool->getName())
->setLink($this->get_name() . '/start.php?' . http_build_query(['id' => $tool->getId()]))
->setImage($this->get_name() . '.png')
->setVisibility(1)
->setAdmin(0)
->setAddress('squaregray.gif')
->setAddedTool('NO')
->setTarget('_self')
->setCategory('plugin')
->setSessionId(0);
$em->persist($cTool);
$em->flush();
}
protected function getConfigExtraText()
{
$text = $this->get_lang('ImsLtiDescription');
$text .= sprintf(
$this->get_lang('ManageToolButton'),
api_get_path(WEB_PLUGIN_PATH) . 'ims_lti/list.php'
);
return $text;
}
}

@ -0,0 +1,180 @@
<?php
/* For license terms, see /license.txt */
/**
* ImsLtiTool
*
* @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
*/
class ImsLtiTool
{
private $id;
private $name;
private $description;
private $launchUrl;
private $consumerKey;
private $sharedSecret;
private $customParams;
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()
{
if (!empty($this->id)) {
Database::update(
ImsLtiPlugin::TABLE_TOOL,
[
'name' => $this->name,
'description' => $this->description,
'launch_url' => $this->launchUrl,
'consumer_key' => $this->consumerKey,
'shared_secret' => $this->sharedSecret,
'custom_params' => $this->customParams
],
['id' => $this->id]
);
return;
}
$this->id = Database::insert(
ImsLtiPlugin::TABLE_TOOL,
[
'name' => $this->name,
'description' => $this->description,
'launch_url' => $this->launchUrl,
'consumer_key' => $this->consumerKey,
'shared_secret' => $this->sharedSecret,
'custom_params' => $this->customParams
]
);
}
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'];
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]
];
}
}

@ -0,0 +1,539 @@
<?php
/**
* OAuthSimple - A simpler version of OAuth
*
* https://github.com/jrconlin/oauthsimple
*
* @author jr conlin <src@jrconlin.com>
* @copyright unitedHeroes.net 2011
* @version 1.3
* @license See license.txt
*
*/
class OAuthSimple {
private $_secrets;
private $_default_signature_method;
private $_action;
private $_nonce_chars;
/**
* Constructor
*
* @access public
* @param api_key (String) The API Key (sometimes referred to as the consumer key) This value is usually supplied by the site you wish to use.
* @param shared_secret (String) The shared secret. This value is also usually provided by the site you wish to use.
* @return OAuthSimple (Object)
*/
function __construct ($APIKey = "", $sharedSecret=""){
if (!empty($APIKey))
{
$this->_secrets['consumer_key'] = $APIKey;
}
if (!empty($sharedSecret))
{
$this->_secrets['shared_secret'] = $sharedSecret;
}
$this->_default_signature_method = "HMAC-SHA1";
$this->_action = "GET";
$this->_nonce_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
return $this;
}
/**
* Reset the parameters and URL
*
* @access public
* @return OAuthSimple (Object)
*/
public function reset() {
$this->_parameters = Array();
$this->path = NULL;
$this->sbs = NULL;
return $this;
}
/**
* Set the parameters either from a hash or a string
*
* @access public
* @param(string, object) List of parameters for the call, this can either be a URI string (e.g. "foo=bar&gorp=banana" or an object/hash)
* @return OAuthSimple (Object)
*/
public function setParameters ($parameters=Array()) {
if (is_string($parameters))
{
$parameters = $this->_parseParameterString($parameters);
}
if (empty($this->_parameters))
{
$this->_parameters = $parameters;
}
else if (!empty($parameters))
{
$this->_parameters = array_merge($this->_parameters,$parameters);
}
if (empty($this->_parameters['oauth_nonce']))
{
$this->_getNonce();
}
if (empty($this->_parameters['oauth_timestamp']))
{
$this->_getTimeStamp();
}
if (empty($this->_parameters['oauth_consumer_key']))
{
$this->_getApiKey();
}
if (empty($this->_parameters['oauth_token']))
{
$this->_getAccessToken();
}
if (empty($this->_parameters['oauth_signature_method']))
{
$this->setSignatureMethod();
}
if (empty($this->_parameters['oauth_version']))
{
$this->_parameters['oauth_version']="1.0";
}
return $this;
}
/**
* Convenience method for setParameters
*
* @access public
* @see setParameters
*/
public function setQueryString ($parameters)
{
return $this->setParameters($parameters);
}
/**
* Set the target URL (does not include the parameters)
*
* @param path (String) the fully qualified URI (excluding query arguments) (e.g "http://example.org/foo")
* @return OAuthSimple (Object)
*/
public function setURL ($path)
{
if (empty($path))
{
throw new OAuthSimpleException('No path specified for OAuthSimple.setURL');
}
$this->_path=$path;
return $this;
}
/**
* Convenience method for setURL
*
* @param path (String)
* @see setURL
*/
public function setPath ($path)
{
return $this->_path=$path;
}
/**
* Set the "action" for the url, (e.g. GET,POST, DELETE, etc.)
*
* @param action (String) HTTP Action word.
* @return OAuthSimple (Object)
*/
public function setAction ($action)
{
if (empty($action))
{
$action = 'GET';
}
$action = strtoupper($action);
if (preg_match('/[^A-Z]/',$action))
{
throw new OAuthSimpleException('Invalid action specified for OAuthSimple.setAction');
}
$this->_action = $action;
return $this;
}
/**
* Set the signatures (as well as validate the ones you have)
*
* @param signatures (object) object/hash of the token/signature pairs {api_key:, shared_secret:, oauth_token: oauth_secret:}
* @return OAuthSimple (Object)
*/
public function signatures ($signatures)
{
if (!empty($signatures) && !is_array($signatures))
{
throw new OAuthSimpleException('Must pass dictionary array to OAuthSimple.signatures');
}
if (!empty($signatures))
{
if (empty($this->_secrets))
{
$this->_secrets=Array();
}
$this->_secrets=array_merge($this->_secrets,$signatures);
}
if (isset($this->_secrets['api_key']))
{
$this->_secrets['consumer_key'] = $this->_secrets['api_key'];
}
if (isset($this->_secrets['access_token']))
{
$this->_secrets['oauth_token'] = $this->_secrets['access_token'];
}
if (isset($this->_secrets['access_secret']))
{
$this->_secrets['shared_secret'] = $this->_secrets['access_secret'];
}
if (isset($this->_secrets['oauth_token_secret']))
{
$this->_secrets['oauth_secret'] = $this->_secrets['oauth_token_secret'];
}
if (empty($this->_secrets['consumer_key']))
{
throw new OAuthSimpleException('Missing required consumer_key in OAuthSimple.signatures');
}
if (empty($this->_secrets['shared_secret']))
{
throw new OAuthSimpleException('Missing requires shared_secret in OAuthSimple.signatures');
}
if (!empty($this->_secrets['oauth_token']) && empty($this->_secrets['oauth_secret']))
{
throw new OAuthSimpleException('Missing oauth_secret for supplied oauth_token in OAuthSimple.signatures');
}
return $this;
}
public function setTokensAndSecrets($signatures)
{
return $this->signatures($signatures);
}
/**
* Set the signature method (currently only Plaintext or SHA-MAC1)
*
* @param method (String) Method of signing the transaction (only PLAINTEXT and SHA-MAC1 allowed for now)
* @return OAuthSimple (Object)
*/
public function setSignatureMethod ($method="")
{
if (empty($method))
{
$method = $this->_default_signature_method;
}
$method = strtoupper($method);
switch($method)
{
case 'PLAINTEXT':
case 'HMAC-SHA1':
$this->_parameters['oauth_signature_method']=$method;
break;
default:
throw new OAuthSimpleException ("Unknown signing method $method specified for OAuthSimple.setSignatureMethod");
break;
}
return $this;
}
/** sign the request
*
* note: all arguments are optional, provided you've set them using the
* other helper functions.
*
* @param args (Array) hash of arguments for the call {action, path, parameters (array), method, signatures (array)} all arguments are optional.
* @return (Array) signed values
*/
public function sign($args=array())
{
if (!empty($args['action']))
{
$this->setAction($args['action']);
}
if (!empty($args['path']))
{
$this->setPath($args['path']);
}
if (!empty($args['method']))
{
$this->setSignatureMethod($args['method']);
}
if (!empty($args['signatures']))
{
$this->signatures($args['signatures']);
}
if (empty($args['parameters']))
{
$args['parameters']=array();
}
$this->setParameters($args['parameters']);
$normParams = $this->_normalizedParameters();
return Array (
'parameters' => $this->_parameters,
'signature' => self::_oauthEscape($this->_parameters['oauth_signature']),
'signed_url' => $this->_path . '?' . $normParams,
'header' => $this->getHeaderString(),
'sbs'=> $this->sbs
);
}
/**
* Return a formatted "header" string
*
* NOTE: This doesn't set the "Authorization: " prefix, which is required.
* It's not set because various set header functions prefer different
* ways to do that.
*
* @param args (Array)
* @return $result (String)
*/
public function getHeaderString ($args=array())
{
if (empty($this->_parameters['oauth_signature']))
{
$this->sign($args);
}
$result = 'OAuth ';
foreach ($this->_parameters as $pName => $pValue)
{
if (strpos($pName,'oauth_') !== 0)
{
continue;
}
if (is_array($pValue))
{
foreach ($pValue as $val)
{
$result .= $pName .'="' . self::_oauthEscape($val) . '", ';
}
}
else
{
$result .= $pName . '="' . self::_oauthEscape($pValue) . '", ';
}
}
return preg_replace('/, $/','',$result);
}
private function _parseParameterString ($paramString)
{
$elements = explode('&',$paramString);
$result = array();
foreach ($elements as $element)
{
list ($key,$token) = explode('=',$element);
if ($token)
{
$token = urldecode($token);
}
if (!empty($result[$key]))
{
if (!is_array($result[$key]))
{
$result[$key] = array($result[$key],$token);
}
else
{
array_push($result[$key],$token);
}
}
else
$result[$key]=$token;
}
return $result;
}
private static function _oauthEscape($string)
{
if ($string === 0) { return 0; }
if ($string == '0') { return '0'; }
if (strlen($string) == 0) { return ''; }
if (is_array($string)) {
throw new OAuthSimpleException('Array passed to _oauthEscape');
}
$string = urlencode($string);
//FIX: urlencode of ~ and '+'
$string = str_replace(
Array('%7E','+' ), // Replace these
Array('~', '%20'), // with these
$string);
return $string;
}
private function _getNonce($length=5)
{
$result = '';
$cLength = strlen($this->_nonce_chars);
for ($i=0; $i < $length; $i++)
{
$rnum = rand(0,$cLength - 1);
$result .= substr($this->_nonce_chars,$rnum,1);
}
$this->_parameters['oauth_nonce'] = $result;
return $result;
}
private function _getApiKey()
{
if (empty($this->_secrets['consumer_key']))
{
throw new OAuthSimpleException('No consumer_key set for OAuthSimple');
}
$this->_parameters['oauth_consumer_key']=$this->_secrets['consumer_key'];
return $this->_parameters['oauth_consumer_key'];
}
private function _getAccessToken()
{
if (!isset($this->_secrets['oauth_secret']))
{
return '';
}
if (!isset($this->_secrets['oauth_token']))
{
throw new OAuthSimpleException('No access token (oauth_token) set for OAuthSimple.');
}
$this->_parameters['oauth_token'] = $this->_secrets['oauth_token'];
return $this->_parameters['oauth_token'];
}
private function _getTimeStamp()
{
return $this->_parameters['oauth_timestamp'] = time();
}
private function _normalizedParameters()
{
$normalized_keys = array();
$return_array = array();
foreach ( $this->_parameters as $paramName=>$paramValue) {
if (preg_match('/w+_secret/', $paramName) OR
$paramName == "oauth_signature") {
continue;
}
// Read parameters from a file. Hope you're practicing safe PHP.
//if (strpos($paramValue, '@') !== 0 && !file_exists(substr($paramValue, 1)))
//{
if (is_array($paramValue))
{
$normalized_keys[self::_oauthEscape($paramName)] = array();
foreach($paramValue as $item)
{
array_push($normalized_keys[self::_oauthEscape($paramName)], self::_oauthEscape($item));
}
}
else
{
$normalized_keys[self::_oauthEscape($paramName)] = self::_oauthEscape($paramValue);
}
//}
}
ksort($normalized_keys);
foreach($normalized_keys as $key=>$val)
{
if (is_array($val))
{
sort($val);
foreach($val as $element)
{
array_push($return_array, $key . "=" . $element);
}
}
else
{
array_push($return_array, $key .'='. $val);
}
}
$presig = join("&", $return_array);
$sig = $this->_generateSignature($presig);
$this->_parameters['oauth_signature']=$sig;
array_push($return_array, "oauth_signature=$sig");
return join("&", $return_array);
}
private function _generateSignature ($parameters="")
{
$secretKey = '';
if(isset($this->_secrets['shared_secret']))
{
$secretKey = self::_oauthEscape($this->_secrets['shared_secret']);
}
$secretKey .= '&';
if(isset($this->_secrets['oauth_secret']))
{
$secretKey .= self::_oauthEscape($this->_secrets['oauth_secret']);
}
if(!empty($parameters)){
$parameters = urlencode($parameters);
}
switch($this->_parameters['oauth_signature_method'])
{
case 'PLAINTEXT':
return urlencode($secretKey);;
case 'HMAC-SHA1':
$this->sbs = self::_oauthEscape($this->_action).'&'.self::_oauthEscape($this->_path).'&'.$parameters;
return base64_encode(hash_hmac('sha1',$this->sbs,$secretKey,TRUE));
default:
throw new OAuthSimpleException('Unknown signature method for OAuthSimple');
break;
}
}
}
class OAuthSimpleException extends Exception {
public function __construct($err, $isDebug = FALSE)
{
self::log_error($err);
if ($isDebug)
{
self::display_error($err, TRUE);
}
}
public static function log_error($err)
{
error_log($err, 0);
}
public static function display_error($err, $kill = FALSE)
{
print_r($err);
if ($kill === FALSE)
{
die();
}
}
}

@ -0,0 +1,60 @@
<?php
/* For license terms, see /license.txt */
require '../../main/inc/global.inc.php';
api_protect_course_script();
api_protect_teacher_script();
$plugin = ImsLtiPlugin::create();
$em = Database::getManager();
$course = $em->find('ChamiloCoreBundle:Course', api_get_course_int_id());
$tools = ImsLtiTool::fetchAll();
$types = [
'0' => get_lang('None')
];
foreach ($tools as $tool) {
$types[$tool['id']] = $tool['name'];
}
$form = new FormValidator('ims_lti_add_tool');
$form->addText('tool_name', $plugin->get_lang('ToolName'));
$form->addSelect('type', $plugin->get_lang('Type'), $types);
$form->addRule('type', get_lang('Required'), 'required');
$form->addHtml('<div id="show_advanced_options">');
$form->addElement('url', 'url', $plugin->get_lang('LaunchUrl'));
$form->addText('consumer_key', $plugin->get_lang('ConsumerKey'), false);
$form->addText('shared_secret', $plugin->get_lang('SharedSecret'), false);
$form->addTextarea('custom_params', $plugin->get_lang('CustomParams'));
$form->addHtml('</div>');
$form->addButtonCreate($plugin->get_lang('AddExternalTool'));
if ($form->validate()) {
$formValues = $form->getSubmitValues();
if (!empty($formValues['type'])) {
$tool = ImsLtiTool::fetch($formValues['type']);
if (!$tool) {
Display::addFlash(
Display::return_message($plugin->get_lang('NoTool'))
);
// redirect to course
exit;
}
$plugin->addCourseTool($course, $tool);
}
}
$template = new Template($plugin->get_lang('AddExternalTool'));
$template->assign('form', $form->returnForm());
$content = $template->fetch('ims_lti/view/add.tpl');
$template->assign('content', $content);
$template->display_one_col_template();

@ -0,0 +1,4 @@
/* For license terms, see /license.txt */
.plugin-ims-lti-iframe {
width: 100%;
}

@ -0,0 +1,47 @@
<?php
/* For license terms, see /license.txt */
$cidReset = true;
require '../../main/inc/global.inc.php';
api_protect_admin_script();
$plugin = ImsLtiPlugin::create();
$form = new FormValidator('ism_lti_create_tool');
$form->addText('name', get_lang('Name'));
$form->addText('base_url', get_lang('BaseUrl'));
$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'));
if ($form->validate()) {
$formValues = $form->exportValues();
$externalTool = new ImsLtiTool();
$externalTool
->setName($formValues['name'])
->setDescription($formValues['description'])
->setLaunchUrl($formValues['url'])
->setConsumerKey($formValues['consumer_key'])
->setSharedSecret($formValues['shared_secret'])
->setCustomParams($formValues['custom_params'])
->save();
Display::addFlash(
Display::return_message($plugin->get_lang('ToolAdded'), 'success')
);
header('Location: ' . api_get_path(WEB_PLUGIN_PATH) . 'ims_lti/list.php');
exit;
}
$template = new Template($plugin->get_lang('AddExternalTool'));
$template->assign('form', $form->returnForm());
$content = $template->fetch('ims_lti/view/add.tpl');
$template->assign('header', $plugin->get_title());
$template->assign('content', $content);
$template->display_one_col_template();

@ -0,0 +1,5 @@
<?php
/* For license terms, see /license.txt */
require '../../main/inc/global.inc.php';
$plugin = ImsLtiPlugin::create();

@ -0,0 +1,72 @@
<?php
/* For license terms, see /license.txt */
$cidReset = true;
require '../../main/inc/global.inc.php';
api_protect_admin_script();
$toolId = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0;
if (empty($toolId)) {
api_not_allowed(true);
}
$plugin = ImsLtiPlugin::create();
$tool = ImsLtiTool::fetch($toolId);
if (empty($tool)) {
Display::addFlash(
Display::return_message($plugin->get_lang('NoTool'), 'error')
);
header('Location: ' . api_get_path(WEB_PLUGIN_PATH) . 'ims_lti/list.php');
exit;
}
$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->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->addHidden('id', $tool->getId());
$form->setDefaults([
'name' => $tool->getName(),
'description' => $tool->getDescription(),
'url' => $tool->getLaunchUrl(),
'consumer_key' => $tool->getConsumerKey(),
'shared_secret' => $tool->getSharedSecret(),
'custom_params' => $tool->getCustomParams()
]);
if ($form->validate()) {
$formValues = $form->exportValues();
$tool
->setName($formValues['name'])
->setDescription($formValues['description'])
->setLaunchUrl($formValues['url'])
->setConsumerKey($formValues['consumer_key'])
->setSharedSecret($formValues['shared_secret'])
->setCustomParams($formValues['custom_params'])
->save();
Display::addFlash(
Display::return_message($plugin->get_lang('ToolEdited'), 'success')
);
header('Location: ' . api_get_path(WEB_PLUGIN_PATH) . 'ims_lti/list.php');
exit;
}
$template = new Template($plugin->get_lang('EditExternalTool'));
$template->assign('form', $form->returnForm());
$content = $template->fetch('ims_lti/view/add.tpl');
$template->assign('header', $plugin->get_title());
$template->assign('content', $content);
$template->display_one_col_template();

@ -0,0 +1,77 @@
<?php
/* For license terms, see /license.txt */
require '../../main/inc/global.inc.php';
require './OAuthSimple.php';
api_protect_course_script();
$toolId = isset($_GET['id']) ? intval($_GET['id']) : 0;
$em = Database::getManager();
$imsLtiPlugin = ImsLtiPlugin::create();
$tool = ImsLtiTool::fetch($toolId);
$course = $em->find('ChamiloCoreBundle:Course', api_get_course_int_id());
$user = $em->find('ChamiloUserBundle:User', api_get_user_id());
$params = [
'lti_message_type' => 'basic-lti-launch-request',
'lti_version' => 'LTI-1p0',
'resource_link_id' => $tool->getId(),
'resource_link_title' => $tool->getName(),
'user_id' => 'chamilo110x-' . $user->getId(),
'roles' => api_is_teacher() ? 'Instructor' : 'Student',
'lis_person_name_given' => $user->getFirstname(),
'lis_person_name_family' => $user->getLastname(),
'lis_person_name_full' => $user->getCompleteName(),
'lis_person_contact_email_primary' => $user->getEmail(),
'context_id' => $course->getId(),
'context_label' => $course->getCode(),
'context_title' => $course->getTitle(),
'launch_presentation_locale' => api_get_language_isocode(),
'launch_presentation_document_target' => 'embed',
'tool_consumer_info_product_family_code' => 'canvas',
'tool_consumer_info_version' => '1.10.2',
'tool_consumer_instance_guid' => 'campus.chamilo.org',
'tool_consumer_instance_name' => api_get_setting('siteName'),
'tool_consumer_instance_url' => api_get_setting('InstitutionUrl'),
'tool_consumer_instance_contact_email' => api_get_setting('emailAdministrator'),
'resource_link_description' => 'A quick revision PowerPoint about the Water cycle. Make sure you\'re clear about it!',
];
$oauth = new OAuthSimple(
$tool->getConsumerKey(),
$tool->getSharedSecret()
);
$oauth->setAction('post');
$oauth->setSignatureMethod('HMAC-SHA1');
$oauth->setParameters($params);
$result = $oauth->sign(array(
'path' => $tool->getLaunchUrl(),
'parameters' => array(
'oauth_callback' => 'about:blank'
)
));
?>
<!DOCTYPE html>
<html>
<head>
<title>title</title>
</head>
<body>
<form action="<?php echo $result['signed_url'] ?>" name="ltiLaunchForm" method="post" encType="application/x-www-form-urlencoded">
<input type="submit" value="Press to continue to external tool"/>
</form>
<script language="javascript">
document.ltiLaunchForm.submit();
</script>
</body>
</html>

@ -0,0 +1,12 @@
<?php
/* For license terms, see /license.txt */
/**
* Install the MSI/LTI Plugin
* @package chamilo.plugin.ims_lti
*/
if (!api_is_platform_admin()) {
die ('You must have admin permissions to install plugins');
}
ImsLtiPlugin::create()->install();

@ -0,0 +1,16 @@
<?php
/* For license terms, see /license.txt */
$strings['plugin_title'] = 'IMS/LTI';
$strings['plugin_comment'] = 'IMS/LTI';
$strings['enabled'] = 'Enabled';
$strings['ImsLtiDescription'] = '<p>Learning Tools Interoperability® (LTI®) is a specification developed by IMS Global Learning Consortium. The principal concept of LTI is to establish a standard way of integrating rich learning applications (often remotely hosted and provided through third-party services) with platforms like learning management systems, portals, learning object repositories, or other educational environments.</p>';
$strings['ManageToolButton'] = '<p>To manage the tools go to <a href="%s">Tool list</a></p>';
$strings['AddExternalTool'] = 'Add external tool';
$strings['ProviderName'] = 'Provider tool';
$strings['LaunchUrl'] = 'Launch URL';
$strings['ConsumerKey'] = 'Consumer key';
$strings['SharedSecret'] = 'Shared secret';
$strings['CustomParams'] = 'Custom params';
$strings['ToolName'] = 'Tool name';

@ -0,0 +1,19 @@
<?php
/* For license terms, see /license.txt */
$cidReset = true;
require '../../main/inc/global.inc.php';
$plugin = ImsLtiPlugin::create();
$em = Database::getManager();
$tools = Database::select('*', ImsLtiPlugin::TABLE_TOOL);
$template = new Template($plugin->get_title());
$template->assign('tools', $tools);
$content = $template->fetch('ims_lti/view/list.tpl');
$template->assign('header', $plugin->get_title());
$template->assign('content', $content);
$template->display_one_col_template();

@ -0,0 +1,4 @@
<?php
/* For license terms, see /license.txt */
$plugin_info = ImsLtiPlugin::create()->get_info();

@ -0,0 +1,29 @@
<?php
/* For license terms, see /license.txt */
require '../../main/inc/global.inc.php';
api_protect_course_script();
$toolId = isset($_GET['id']) ? intval($_GET['id']) : 0;
if (empty($toolId)) {
api_not_allowed();
}
$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->assign(
'launch_url',
api_get_path(WEB_PLUGIN_PATH) . 'ims_lti/form.php?' . http_build_query(['id' => $tool->getId()])
);
$content = $template->fetch('ims_lti/view/start.tpl');
$template->assign('header', $tool->getName());
$template->assign('content', $content);
$template->display_one_col_template();

@ -0,0 +1,12 @@
<?php
/* For license terms, see /license.txt */
/**
* Uninstall the MSI/LTI Plugin
* @package chamilo.plugin.ims_lti
*/
if (!api_is_platform_admin()) {
die ('You must have admin permissions to uninstall plugins');
}
ImsLtiPlugin::create()->uninstall();

@ -0,0 +1,16 @@
{{ form }}
<script>
$(document).on('ready', function () {
$('select[name="type"]').on('change', function () {
var advancedOptionsEl = $('#show_advanced_options');
var type = parseInt($(this).val());
if (type > 0) {
advancedOptionsEl.hide();
} else {
advancedOptionsEl.show();
}
});
});
</script>

@ -0,0 +1,33 @@
{{ 'ImsLtiDescription'|get_plugin_lang('ImsLtiPlugin') }}
<div class="btn-toolbar">
<a href="{{ _p.web_plugin }}ims_lti/create.php" class="btn btn-primary">
<span class="fa fa-plus fa-fw" aria-hidden="true"></span> {{ 'AddExternalTool'|get_plugin_lang('ImsLtiPlugin') }}
</a>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>{{ 'Name'|get_lang }}</th>
<th>{{ 'LaunchUrl'|get_plugin_lang('ImsLtiPlugin') }}</th>
<th>{{ 'Actions'|get_lang }}</th>
</tr>
</thead>
<tbody>
{% for tool in tools %}
<tr>
<td>{{ tool.name }}</td>
<td>{{ tool.launch_url }}</td>
<td>
<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>
<a href="{{ _p.web_plugin }}ims_lti/delete.php?{{ {'id': tool.id}|url_encode() }}" class="btn btn-danger">
<span class="fa fa-times fa-fw" aria-hidden="true"></span> {{ 'Delete'|get_lang }}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>

@ -0,0 +1,3 @@
<div class="embed-responsive embed-responsive-16by9">
<iframe src="{{ launch_url }}" class="plugin-ims-lti-iframe"></iframe>
</div>
Loading…
Cancel
Save