MDL-54680 enrol_lti: Offer cartridges in LTI provider
authorJohn Okely <john@moodle.com>
Mon, 23 May 2016 08:12:40 +0000 (16:12 +0800)
committerJohn Okely <john@moodle.com>
Tue, 16 Aug 2016 06:15:02 +0000 (14:15 +0800)
14 files changed:
enrol/lti/cartridge.php [new file with mode: 0644]
enrol/lti/classes/helper.php
enrol/lti/classes/manage_table.php
enrol/lti/lang/en/enrol_lti.php
enrol/lti/styles.css [new file with mode: 0644]
enrol/lti/tests/fixtures/input.xml [new file with mode: 0644]
enrol/lti/tests/fixtures/test_ambiguous_nodes-expected.xml [new file with mode: 0644]
enrol/lti/tests/fixtures/test_correct_xpath-expected.xml [new file with mode: 0644]
enrol/lti/tests/fixtures/test_missing_node-expected.xml [new file with mode: 0644]
enrol/lti/tests/fixtures/test_nodes_removed-expected.xml [new file with mode: 0644]
enrol/lti/tests/helper_test.php
enrol/lti/version.php
enrol/lti/xml/imslticc.xml [new file with mode: 0644]
lib/templates/copy_box.mustache [new file with mode: 0644]

diff --git a/enrol/lti/cartridge.php b/enrol/lti/cartridge.php
new file mode 100644 (file)
index 0000000..bf61743
--- /dev/null
@@ -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 <john@moodle.com>
+ * @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);
+}
index 83a9b13..aabd9a6 100644 (file)
@@ -380,4 +380,219 @@ class helper {
               </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();
+    }
 }
index d334483..6ebec61 100644 (file)
@@ -96,12 +96,7 @@ class manage_table extends \table_sql {
      * @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);
     }
@@ -113,8 +108,9 @@ class manage_table extends \table_sql {
      * @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);
     }
 
     /**
@@ -124,7 +120,7 @@ class manage_table extends \table_sql {
      * @return string
      */
     public function col_secret($tool) {
-        return $this->get_display_text($tool, $tool->secret);
+        return $this->get_copyable_text($tool, $tool->secret);
     }
 
 
@@ -215,4 +211,22 @@ class manage_table extends \table_sql {
 
         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;
+    }
 }
index 8b2cd1e..0579023 100644 (file)
@@ -37,6 +37,7 @@ $string['enrolstartdate_help'] = 'If enabled, users can access from this date on
 $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.';
diff --git a/enrol/lti/styles.css b/enrol/lti/styles.css
new file mode 100644 (file)
index 0000000..9128815
--- /dev/null
@@ -0,0 +1,4 @@
+.copy_box {
+    width: 100%;
+    max-width: 350px;
+}
diff --git a/enrol/lti/tests/fixtures/input.xml b/enrol/lti/tests/fixtures/input.xml
new file mode 100644 (file)
index 0000000..6202832
--- /dev/null
@@ -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>
diff --git a/enrol/lti/tests/fixtures/test_ambiguous_nodes-expected.xml b/enrol/lti/tests/fixtures/test_ambiguous_nodes-expected.xml
new file mode 100644 (file)
index 0000000..8ef4597
--- /dev/null
@@ -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>
diff --git a/enrol/lti/tests/fixtures/test_correct_xpath-expected.xml b/enrol/lti/tests/fixtures/test_correct_xpath-expected.xml
new file mode 100644 (file)
index 0000000..88c21eb
--- /dev/null
@@ -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>
diff --git a/enrol/lti/tests/fixtures/test_missing_node-expected.xml b/enrol/lti/tests/fixtures/test_missing_node-expected.xml
new file mode 100644 (file)
index 0000000..e4a5619
--- /dev/null
@@ -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>
diff --git a/enrol/lti/tests/fixtures/test_nodes_removed-expected.xml b/enrol/lti/tests/fixtures/test_nodes_removed-expected.xml
new file mode 100644 (file)
index 0000000..34eb129
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+    
+    <parentnode>
+        
+    </parentnode>
+    <ambiguous id="0"/>
+    <ambiguous id="1"/>
+</root>
index 971fa50..df502f9 100644 (file)
@@ -247,6 +247,233 @@ class enrol_lti_helper_testcase extends advanced_testcase {
         $this->assertTrue(isset($tools[$tool3->id]));
     }
 
+    /**
+     * Test getting the launch url of a tool
+     */
+    public function test_get_launch_url() {
+        $course1 = $this->getDataGenerator()->create_course();
+        $data = new stdClass();
+        $data->courseid = $course1->id;
+        $tool1 = $this->create_tool($data);
+
+        $id = $tool1->id;
+        $launchurl = \enrol_lti\helper::get_launch_url($id);
+        $this->assertEquals('http://www.example.com/moodle/enrol/lti/tool.php?id=' . $id, $launchurl->out());
+    }
+
+    /**
+     * Test getting the cartridge url of a tool
+     */
+    public function test_get_cartridge_url() {
+        global $CFG;
+
+        $slasharguments = $CFG->slasharguments;
+
+        $CFG->slasharguments = false;
+
+        $course1 = $this->getDataGenerator()->create_course();
+        $data = new stdClass();
+        $data->courseid = $course1->id;
+        $tool1 = $this->create_tool($data);
+
+        $id = $tool1->id;
+        $token = \enrol_lti\helper::generate_tool_token($id);
+        $launchurl = \enrol_lti\helper::get_cartridge_url($tool1);
+        $this->assertEquals('http://www.example.com/moodle/enrol/lti/cartridge.php?id=' . $id . '&amp;token=' . $token,
+                            $launchurl->out());
+
+        $CFG->slasharguments = true;
+
+        $launchurl = \enrol_lti\helper::get_cartridge_url($tool1);
+        $this->assertEquals('http://www.example.com/moodle/enrol/lti/cartridge.php/' . $id . '/' . $token . '/cartridge.xml',
+                            $launchurl->out());
+
+        $CFG->slasharguments = $slasharguments;
+    }
+
+    /**
+     * Test getting the name of a tool
+     */
+    public function test_get_name() {
+        $course1 = $this->getDataGenerator()->create_course();
+        $data = new stdClass();
+        $data->courseid = $course1->id;
+        $tool1 = $this->create_tool($data);
+
+        $name = \enrol_lti\helper::get_name($tool1);
+        $this->assertEquals('Course: Test course 1', $name);
+
+        $tool1->name = 'Shared course';
+        $name = \enrol_lti\helper::get_name($tool1);
+        $this->assertEquals('Shared course', $name);
+    }
+
+    /**
+     * Test getting the description of a tool
+     */
+    public function test_get_description() {
+        $course1 = $this->getDataGenerator()->create_course();
+        $data = new stdClass();
+        $data->courseid = $course1->id;
+        $tool1 = $this->create_tool($data);
+
+        $description = \enrol_lti\helper::get_description($tool1);
+        $this->assertContains('Test course 1 Lorem ipsum dolor sit amet', $description);
+
+        $module1 = $this->getDataGenerator()->create_module('assign', array(
+                'course' => $course1->id
+            ));
+        $data = new stdClass();
+        $data->cmid = $module1->cmid;
+        $tool2 = $this->create_tool($data);
+        $description = \enrol_lti\helper::get_description($tool2);
+        $this->assertContains('Test assign 1', $description);
+    }
+
+    /**
+     * Test verifying a tool token.
+     */
+    public function test_verify_tool_token() {
+        $course1 = $this->getDataGenerator()->create_course();
+        $data = new stdClass();
+        $data->courseid = $course1->id;
+        $tool1 = $this->create_tool($data);
+
+        $token = \enrol_lti\helper::generate_tool_token($tool1->id);
+        $this->assertTrue(\enrol_lti\helper::verify_tool_token($tool1->id, $token));
+        $this->assertFalse(\enrol_lti\helper::verify_tool_token($tool1->id, 'incorrect token!'));
+    }
+
+    /**
+     * Data provider for the set_xpath test
+     */
+    public function set_xpath_provider() {
+        return [
+            "Correct structure" => [
+                "parameters" => [
+                    "/root" => [
+                        "/firstnode" => "Content 1",
+                        "/parentnode" => [
+                            "/childnode" => "Content 2"
+                        ]
+                    ]
+                ],
+                "expected" => "test_correct_xpath-expected.xml"
+            ],
+            "A null value, but no node to remove" => [
+                "parameters" => [
+                    "/root" => [
+                        "/nonexistant" => null,
+                        "/firstnode" => "Content 1"
+                    ]
+                ],
+                "expected" => "test_missing_node-expected.xml"
+            ],
+            "A string value, but no node existing to set" => [
+                "parameters" => [
+                    "/root" => [
+                        "/nonexistant" => "This will not be set",
+                        "/firstnode" => "Content 1"
+                    ]
+                ],
+                "expected" => "test_missing_node-expected.xml"
+            ],
+            "Array but no children exist" => [
+                "parameters" => [
+                    "/root" => [
+                        "/nonexistant" => [
+                            "/alsononexistant" => "This will not be set"
+                        ],
+                        "/firstnode" => "Content 1"
+                    ]
+                ],
+                "expected" => "test_missing_node-expected.xml"
+            ],
+            "Remove nodes" => [
+                "parameters" => [
+                    "/root" => [
+                        "/parentnode" => [
+                            "/childnode" => null
+                        ],
+                        "/firstnode" => null
+                    ]
+                ],
+                "expected" => "test_nodes_removed-expected.xml"
+            ],
+            "Get by attribute" => [
+                "parameters" => [
+                    "/root" => [
+                        "/ambiguous[@id='1']" => 'Content 1'
+                    ]
+                ],
+                "expected" => "test_ambiguous_nodes-expected.xml"
+            ]
+        ];
+    }
+
+    /**
+     * Test set_xpath.
+     * @dataProvider set_xpath_provider
+     * @param array $parameters A hash of parameters represented by a heirarchy of xpath expressions
+     * @param string $expected The name of the fixture file containing the expected result.
+     */
+    public function test_set_xpath($parameters, $expected) {
+        $helper = new ReflectionClass('enrol_lti\\helper');
+        $function = $helper->getMethod('set_xpath');
+        $function->setAccessible(true);
+
+        $document = new \DOMDocument();
+        $document->load(realpath(__DIR__ . '/fixtures/input.xml'));
+        $xpath = new \DOMXpath($document);
+        $function->invokeArgs(null, [$xpath, $parameters]);
+        $result = $document->saveXML();
+        $expected = file_get_contents(realpath(__DIR__ . '/fixtures/' . $expected));
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Test set_xpath when an incorrect xpath expression is given.
+     * @expectedException coding_exception
+     */
+    public function test_set_xpath_incorrect_xpath() {
+        $parameters = [
+            "/root" => [
+                "/firstnode" => null,
+                "/parentnode*&#^*#(" => [
+                    "/childnode" => null
+                ],
+            ]
+        ];
+        $helper = new ReflectionClass('enrol_lti\\helper');
+        $function = $helper->getMethod('set_xpath');
+        $function->setAccessible(true);
+
+        $document = new \DOMDocument();
+        $document->load(realpath(__DIR__ . '/fixtures/input.xml'));
+        $xpath = new \DOMXpath($document);
+        $function->invokeArgs(null, [$xpath, $parameters]);
+        $result = $document->saveXML();
+        $expected = file_get_contents(realpath(__DIR__ . '/fixtures/' . $expected));
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Test create cartridge.
+     */
+    public function test_create_cartridge() {
+        global $CFG;
+
+        $course1 = $this->getDataGenerator()->create_course();
+        $data = new stdClass();
+        $data->courseid = $course1->id;
+        $tool1 = $this->create_tool($data);
+
+        $cartridge = \enrol_lti\helper::create_cartridge($tool1->id);
+        $this->assertContains('<blti:title>Test LTI</blti:title>', $cartridge);
+        $this->assertContains("<blti:icon>$CFG->wwwroot/theme/image.php/_s/clean/theme/1/favicon</blti:icon>", $cartridge);
+        $this->assertContains("<blti:launch_url>$CFG->wwwroot/enrol/lti/tool.php?id=$tool1->id</blti:launch_url>", $cartridge);
+    }
+
     /**
      * Helper function used to create a tool.
      *
@@ -267,6 +494,12 @@ class enrol_lti_helper_testcase extends advanced_testcase {
             $course = get_course($data->courseid);
         }
 
+        if (!empty($data->cmid)) {
+            $data->contextid = context_module::instance($data->cmid)->id;
+        } else {
+            $data->contextid = context_course::instance($data->courseid)->id;
+        }
+
         // Set it to enabled if no status was specified.
         if (!isset($data->status)) {
             $data->status = ENROL_INSTANCE_ENABLED;
@@ -274,7 +507,6 @@ class enrol_lti_helper_testcase extends advanced_testcase {
 
         // Add some extra necessary fields to the data.
         $data->name = 'Test LTI';
-        $data->contextid = context_course::instance($data->courseid)->id;
         $data->roleinstructor = $studentrole->id;
         $data->rolelearner = $teacherrole->id;
 
index 926e739..1468621 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2016052300; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2016052301; // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires = 2016051900; // Requires this Moodle version (3.1)
 $plugin->component = 'enrol_lti'; // Full name of the plugin (used for diagnostics).
diff --git a/enrol/lti/xml/imslticc.xml b/enrol/lti/xml/imslticc.xml
new file mode 100644 (file)
index 0000000..839c57f
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<cartridge_basiclti_link xmlns="http://www.imsglobal.org/xsd/imslticc_v1p0"
+    xmlns:blti = "http://www.imsglobal.org/xsd/imsbasiclti_v1p0"
+    xmlns:lticm ="http://www.imsglobal.org/xsd/imslticm_v1p0"
+    xmlns:lticp ="http://www.imsglobal.org/xsd/imslticp_v1p0"
+    xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation = "http://www.imsglobal.org/xsd/imslticc_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticc_v1p0.xsd
+http://www.imsglobal.org/xsd/imsbasiclti_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imsbasiclti_v1p0.xsd
+http://www.imsglobal.org/xsd/imslticm_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticm_v1p0.xsd
+http://www.imsglobal.org/xsd/imslticp_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticp_v1p0.xsd">
+    <blti:title></blti:title>
+    <blti:description></blti:description>
+    <blti:extensions platform="org.moodle.lms">
+        <lticm:property name="icon_url"></lticm:property>
+        <lticm:property name="secure_icon_url"></lticm:property>
+    </blti:extensions>
+    <blti:launch_url></blti:launch_url>
+    <blti:secure_launch_url></blti:secure_launch_url>
+    <blti:icon></blti:icon>
+    <blti:secure_icon></blti:secure_icon>
+    <blti:vendor>
+        <lticp:code></lticp:code>
+        <lticp:name></lticp:name>
+        <lticp:description></lticp:description>
+        <lticp:url></lticp:url>
+    </blti:vendor>
+    <test></test>
+</cartridge_basiclti_link>
diff --git a/lib/templates/copy_box.mustache b/lib/templates/copy_box.mustache
new file mode 100644 (file)
index 0000000..b0ba295
--- /dev/null
@@ -0,0 +1,41 @@
+{{!
+    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/>.
+}}
+{{!
+    @template core/copy_box
+
+    Interface element to contain text that the user should copy. Will automaticaly select when clicked.
+
+    Classes required for JS:
+    * none
+
+    Data attributes required for JS:
+    * none
+
+    Context variables required for this template:
+    * text The content to be displayed ready for copying
+
+    Example context (json):
+    { "text": "Copyable text"}
+}}
+<input type="text" class="copy_box" value="{{{ text }}}" readonly="readonly" id="copy_box-{{uniqid}}"/>
+{{# js }}
+require(['jquery', 'theme_bootstrapbase/bootstrap'], function($) {
+    $('#copy_box-{{uniqid}}').on('click', function() {
+        $(this).select();
+    });
+});
+{{/ js }}