Add IMS/LTI plugin - refs #5333
parent
8583e24d31
commit
779efaacd3
@ -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…
Reference in new issue