From 3e9ab40361de4867dc260a8bc31edfc3927aea1c Mon Sep 17 00:00:00 2001 From: John Okely Date: Mon, 23 May 2016 16:12:40 +0800 Subject: [PATCH] MDL-54680 enrol_lti: Offer cartridges in LTI provider --- enrol/lti/cartridge.php | 61 +++++ enrol/lti/classes/helper.php | 215 ++++++++++++++++ enrol/lti/classes/manage_table.php | 32 ++- enrol/lti/lang/en/enrol_lti.php | 1 + enrol/lti/styles.css | 4 + enrol/lti/tests/fixtures/input.xml | 9 + .../test_ambiguous_nodes-expected.xml | 9 + .../fixtures/test_correct_xpath-expected.xml | 9 + .../fixtures/test_missing_node-expected.xml | 9 + .../fixtures/test_nodes_removed-expected.xml | 9 + enrol/lti/tests/helper_test.php | 234 +++++++++++++++++- enrol/lti/version.php | 2 +- enrol/lti/xml/imslticc.xml | 28 +++ lib/templates/copy_box.mustache | 41 +++ 14 files changed, 652 insertions(+), 11 deletions(-) create mode 100644 enrol/lti/cartridge.php create mode 100644 enrol/lti/styles.css create mode 100644 enrol/lti/tests/fixtures/input.xml create mode 100644 enrol/lti/tests/fixtures/test_ambiguous_nodes-expected.xml create mode 100644 enrol/lti/tests/fixtures/test_correct_xpath-expected.xml create mode 100644 enrol/lti/tests/fixtures/test_missing_node-expected.xml create mode 100644 enrol/lti/tests/fixtures/test_nodes_removed-expected.xml create mode 100644 enrol/lti/xml/imslticc.xml create mode 100644 lib/templates/copy_box.mustache diff --git a/enrol/lti/cartridge.php b/enrol/lti/cartridge.php new file mode 100644 index 00000000000..bf61743b17b --- /dev/null +++ b/enrol/lti/cartridge.php @@ -0,0 +1,61 @@ +. + +/** + * Generates an XML IMS Cartridge with the details for the given tool + * + * @package enrol_lti + * @copyright 2016 John Okely + * @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); +} diff --git a/enrol/lti/classes/helper.php b/enrol/lti/classes/helper.php index 83a9b135707..aabd9a61cf7 100644 --- a/enrol/lti/classes/helper.php +++ b/enrol/lti/classes/helper.php @@ -380,4 +380,219 @@ class helper { '; } + + /** + * 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(); + } } diff --git a/enrol/lti/classes/manage_table.php b/enrol/lti/classes/manage_table.php index d3344839226..6ebec617a05 100644 --- a/enrol/lti/classes/manage_table.php +++ b/enrol/lti/classes/manage_table.php @@ -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; + } } diff --git a/enrol/lti/lang/en/enrol_lti.php b/enrol/lti/lang/en/enrol_lti.php index 8b2cd1e4b80..0579023cbfb 100644 --- a/enrol/lti/lang/en/enrol_lti.php +++ b/enrol/lti/lang/en/enrol_lti.php @@ -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 index 00000000000..9128815b8c5 --- /dev/null +++ b/enrol/lti/styles.css @@ -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 index 00000000000..620283251cd --- /dev/null +++ b/enrol/lti/tests/fixtures/input.xml @@ -0,0 +1,9 @@ + + + + + + + + + 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 index 00000000000..8ef4597cf4d --- /dev/null +++ b/enrol/lti/tests/fixtures/test_ambiguous_nodes-expected.xml @@ -0,0 +1,9 @@ + + + + + + + + Content 1 + 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 index 00000000000..88c21ebd1a5 --- /dev/null +++ b/enrol/lti/tests/fixtures/test_correct_xpath-expected.xml @@ -0,0 +1,9 @@ + + + Content 1 + + Content 2 + + + + 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 index 00000000000..e4a5619be79 --- /dev/null +++ b/enrol/lti/tests/fixtures/test_missing_node-expected.xml @@ -0,0 +1,9 @@ + + + Content 1 + + + + + + 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 index 00000000000..34eb129b0e5 --- /dev/null +++ b/enrol/lti/tests/fixtures/test_nodes_removed-expected.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/enrol/lti/tests/helper_test.php b/enrol/lti/tests/helper_test.php index 971fa505bb5..df502f9815b 100644 --- a/enrol/lti/tests/helper_test.php +++ b/enrol/lti/tests/helper_test.php @@ -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 . '&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('Test LTI', $cartridge); + $this->assertContains("$CFG->wwwroot/theme/image.php/_s/clean/theme/1/favicon", $cartridge); + $this->assertContains("$CFG->wwwroot/enrol/lti/tool.php?id=$tool1->id", $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; diff --git a/enrol/lti/version.php b/enrol/lti/version.php index 926e739b421..1468621185a 100644 --- a/enrol/lti/version.php +++ b/enrol/lti/version.php @@ -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 index 00000000000..839c57f7536 --- /dev/null +++ b/enrol/lti/xml/imslticc.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/lib/templates/copy_box.mustache b/lib/templates/copy_box.mustache new file mode 100644 index 00000000000..b0ba295e078 --- /dev/null +++ b/lib/templates/copy_box.mustache @@ -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 . +}} +{{! + @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"} +}} + +{{# js }} +require(['jquery', 'theme_bootstrapbase/bootstrap'], function($) { + $('#copy_box-{{uniqid}}').on('click', function() { + $(this).select(); + }); +}); +{{/ js }}