Skip to content

Commit

Permalink
MDL-54680 enrol_lti: Offer cartridges in LTI provider
Browse files Browse the repository at this point in the history
  • Loading branch information
John Okely committed Aug 16, 2016
1 parent 6f302b1 commit 3e9ab40
Show file tree
Hide file tree
Showing 14 changed files with 652 additions and 11 deletions.
61 changes: 61 additions & 0 deletions enrol/lti/cartridge.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Generates an XML IMS Cartridge with the details for the given tool
*
* @package enrol_lti
* @copyright 2016 John Okely <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

require_once(dirname(__FILE__) . '/../../config.php');
require_once($CFG->dirroot . '/lib/weblib.php');

$toolid = null;
$token = null;

$filearguments = get_file_argument();
$arguments = explode('/', trim($filearguments, '/'));
if (count($arguments) >= 2) { // Can put cartridge.xml at the end, or anything really.
list($toolid, $token) = $arguments;
}

$toolid = optional_param('id', $toolid, PARAM_INT);
$token = optional_param('token', $token, PARAM_ALPHANUM);

// Only show the cartridge if the token parameter is correct.
// If we do not compare with a shared secret, someone could very easily
// guess an id for the enrolment.
if (!\enrol_lti\helper::verify_tool_token($toolid, $token)) {
throw new \moodle_exception('incorrecttoken', 'enrol_lti');
}

$tool = \enrol_lti\helper::get_lti_tool($toolid);

if (!is_enabled_auth('lti')) {
print_error('pluginnotenabled', 'auth', '', get_string('pluginname', 'auth_lti'));

} else if (!enrol_is_enabled('lti')) {
print_error('enrolisdisabled', 'enrol_lti');

} else if ($tool->status != ENROL_INSTANCE_ENABLED) {
print_error('enrolisdisabled', 'enrol_lti');

} else {
header('Content-Type: text/xml; charset=utf-8');
echo \enrol_lti\helper::create_cartridge($toolid);
}
215 changes: 215 additions & 0 deletions enrol/lti/classes/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -380,4 +380,219 @@ public static function create_service_body($source, $grade) {
</imsx_POXBody>
</imsx_POXEnvelopeRequest>';
}

/**
* Returns the url to launch the lti tool.
*
* @param int $toolid the id of the shared tool
* @return moodle_url the url to launch the tool
* @since Moodle 3.2
*/
public static function get_launch_url($toolid) {
return new \moodle_url('/enrol/lti/tool.php', array('id' => $toolid));
}

/**
* Returns the name of the lti enrolment instance, or the name of the course/module being shared.
*
* @param stdClass $tool The lti tool
* @return string The name of the tool
* @since Moodle 3.2
*/
public static function get_name($tool) {
$name = null;

if (empty($tool->name)) {
$toolcontext = \context::instance_by_id($tool->contextid);
$name = $toolcontext->get_context_name();
} else {
$name = $tool->name;
};

return $name;
}

/**
* Returns a description of the course or module that this lti instance points to.
*
* @param stdClass $tool The lti tool
* @return string A description of the tool
* @since Moodle 3.2
*/
public static function get_description($tool) {
global $DB;
$description = '';
$context = \context::instance_by_id($tool->contextid);
if ($context->contextlevel == CONTEXT_COURSE) {
$course = $DB->get_record('course', array('id' => $context->instanceid));
$description = $course->summary;
} else if ($context->contextlevel == CONTEXT_MODULE) {
$cmid = $context->instanceid;
$cm = get_coursemodule_from_id(false, $context->instanceid, 0, false, MUST_EXIST);
$module = $DB->get_record($cm->modname, array('id' => $cm->instance));
$description = $module->intro;
}
return trim(html_to_text($description));
}

/**
* Returns the url to the cartridge representing the tool.
*
* If you have slash arguments enabled, this will be a nice url ending in cartridge.xml.
* If not it will be a php page with some parameters passed.
*
* @param stdClass $tool The lti tool
* @return string The url to the cartridge representing the tool
* @since Moodle 3.2
*/
public static function get_cartridge_url($tool) {
global $CFG;
$url = null;

$id = $tool->id;
$token = self::generate_tool_token($tool->id);
if ($CFG->slasharguments) {
$url = new \moodle_url('/enrol/lti/cartridge.php/' . $id . '/' . $token . '/cartridge.xml');
} else {
$url = new \moodle_url('/enrol/lti/cartridge.php',
array(
'id' => $id,
'token' => $token
)
);
}
return $url;
}

/**
* Returns a unique hash for this site and this enrolment instance.
*
* Used to verify that the link to the cartridge has not just been guessed.
*
* @param int $toolid The id of the shared tool
* @return string MD5 hash of combined site ID and enrolment instance ID.
* @since Moodle 3.2
*/
public static function generate_tool_token($toolid) {
$siteidentifier = get_site_identifier();
$checkhash = md5($siteidentifier . '_enrol_lti_' . $toolid);
return $checkhash;
}

/**
* Verifies that the given token matches the token of the given shared tool.
*
* @param int $toolid The id of the shared tool
* @param string $token hash for this site and this enrolment instance
* @return boolean True if the token matches, false if it does not
* @since Moodle 3.2
*/
public static function verify_tool_token($toolid, $token) {
return $token == self::generate_tool_token($toolid);
}

/**
* Returns the parameters of the cartridge as an associative array of partial xpath.
*
* @param int $toolid The id of the shared tool
* @return array Recursive associative array with partial xpath to be concatenated into an xpath expression
* before setting the value.
* @since Moodle 3.2
*/
protected static function get_cartridge_parameters($toolid) {
global $OUTPUT, $PAGE, $SITE;
$PAGE->set_context(\context_system::instance());

// Get the tool.
$tool = self::get_lti_tool($toolid);

// Work out the name of the tool.
$title = self::get_name($tool);
$launchurl = self::get_launch_url($toolid);
$launchurl = $launchurl->out();
$icon = $OUTPUT->favicon();
$icon = $icon->out();
$securelaunchurl = null;
$secureicon = null;
$vendorurl = new \moodle_url('/');
$vendorurl = $vendorurl->out();
$description = self::get_description($tool);

// If we are a https site, we can add the launch url and icon urls as secure equivalents.
if (\is_https()) {
$securelaunchurl = $launchurl;
$secureicon = $icon;
}

return array(
"/cc:cartridge_basiclti_link" => array(
"/blti:title" => $title,
"/blti:description" => $description,
"/blti:extensions" => array(
"/lticm:property[@name='icon_url']" => $icon,
"/lticm:property[@name='secure_icon_url']" => $secureicon
),
"/blti:launch_url" => $launchurl,
"/blti:secure_launch_url" => $securelaunchurl,
"/blti:icon" => $icon,
"/blti:secure_icon" => $secureicon,
"/blti:vendor" => array(
"/lticp:code" => $SITE->shortname,
"/lticp:name" => $SITE->fullname,
"/lticp:description" => trim(html_to_text($SITE->summary)),
"/lticp:url" => $vendorurl
)
)
);
}

/**
* Traverses a recursive associative array, setting the properties of the corresponding
* xpath element.
*
* @param DOMXPath $xpath The xpath with the xml to modify
* @param array $parameters The array of xpaths to search through
* @param string $prefix The current xpath prefix (gets longer the deeper into the array you go)
* @return void
* @since Moodle 3.2
*/
protected static function set_xpath($xpath, $parameters, $prefix = '') {
foreach ($parameters as $key => $value) {
if (is_array($value)) {
self::set_xpath($xpath, $value, $prefix . $key);
} else {
$result = @$xpath->query($prefix . $key);
if ($result) {
$node = $result->item(0);
if ($node) {
if (is_null($value)) {
$node->parentNode->removeChild($node);
} else {
$node->nodeValue = $value;
}
}
} else {
throw new \coding_exception('Please check your XPATH and try again.');
}
}
}
}

/**
* Create an IMS cartridge for the tool.
*
* @param int $toolid The id of the shared tool
* @return string representing the generated cartridge
* @since Moodle 3.2
*/
public static function create_cartridge($toolid) {
$cartridge = new \DOMDocument();
$cartridge->load(realpath(__DIR__ . '/../xml/imslticc.xml'));
$xpath = new \DOMXpath($cartridge);
$xpath->registerNamespace('cc', 'http://www.imsglobal.org/xsd/imslticc_v1p0');
$parameters = self::get_cartridge_parameters($toolid);
self::set_xpath($xpath, $parameters);

return $cartridge->saveXML();
}
}
32 changes: 23 additions & 9 deletions enrol/lti/classes/manage_table.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,7 @@ public function __construct($courseid) {
* @return string
*/
public function col_name($tool) {
if (empty($tool->name)) {
$toolcontext = \context::instance_by_id($tool->contextid);
$name = $toolcontext->get_context_name();
} else {
$name = $tool->name;
};
$name = helper::get_name($tool);

return $this->get_display_text($tool, $name);
}
Expand All @@ -113,8 +108,9 @@ public function col_name($tool) {
* @return string
*/
public function col_url($tool) {
$url = new \moodle_url('/enrol/lti/tool.php', array('id' => $tool->id));
return $this->get_display_text($tool, $url);
$url = helper::get_cartridge_url($tool);

return $this->get_copyable_text($tool, $url);
}

/**
Expand All @@ -124,7 +120,7 @@ public function col_url($tool) {
* @return string
*/
public function col_secret($tool) {
return $this->get_display_text($tool, $tool->secret);
return $this->get_copyable_text($tool, $tool->secret);
}


Expand Down Expand Up @@ -215,4 +211,22 @@ protected function get_display_text($tool, $text) {

return $text;
}

/**
* Returns text to display in the columns.
*
* @param \stdClass $tool the tool
* @param string $text the text to alter
* @return string
* @since Moodle 3.2
*/
protected function get_copyable_text($tool, $text) {
global $OUTPUT;
$copyable = $OUTPUT->render_from_template('core/copy_box', array('text' => $text));
if ($tool->status != ENROL_INSTANCE_ENABLED) {
return \html_writer::tag('span', $copyable, array('class' => 'dimmed_text', 'style' => 'overflow: scroll'));
}

return $copyable;
}
}
1 change: 1 addition & 0 deletions enrol/lti/lang/en/enrol_lti.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
$string['frameembeddingnotenabled'] = 'To access the tool, please follow the link below.';
$string['gradesync'] = 'Grade synchronisation';
$string['gradesync_help'] = 'Whether grades from the tool are sent to the remote system (LTI consumer).';
$string['incorrecttoken'] = 'Token was incorrect please check the URL and try again, or contact the administrator of this tool.';
$string['maxenrolled'] = 'Maximum enrolled users';
$string['maxenrolled_help'] = 'The maximum number of remote users who can access the tool. If set to zero, the number of enrolled users is unlimited.';
$string['maxenrolledreached'] = 'The maximum number of remote users allowed to access the tool has been reached.';
Expand Down
4 changes: 4 additions & 0 deletions enrol/lti/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.copy_box {
width: 100%;
max-width: 350px;
}
9 changes: 9 additions & 0 deletions enrol/lti/tests/fixtures/input.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<firstnode></firstnode>
<parentnode>
<childnode></childnode>
</parentnode>
<ambiguous id="0"></ambiguous>
<ambiguous id="1"></ambiguous>
</root>
9 changes: 9 additions & 0 deletions enrol/lti/tests/fixtures/test_ambiguous_nodes-expected.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<firstnode/>
<parentnode>
<childnode/>
</parentnode>
<ambiguous id="0"/>
<ambiguous id="1">Content 1</ambiguous>
</root>
9 changes: 9 additions & 0 deletions enrol/lti/tests/fixtures/test_correct_xpath-expected.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<firstnode>Content 1</firstnode>
<parentnode>
<childnode>Content 2</childnode>
</parentnode>
<ambiguous id="0"/>
<ambiguous id="1"/>
</root>
9 changes: 9 additions & 0 deletions enrol/lti/tests/fixtures/test_missing_node-expected.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<firstnode>Content 1</firstnode>
<parentnode>
<childnode/>
</parentnode>
<ambiguous id="0"/>
<ambiguous id="1"/>
</root>
Loading

0 comments on commit 3e9ab40

Please sign in to comment.