MDL-54129 mod_lti: Show in course checkbox works properly.
[moodle.git] / mod / lti / locallib.php
CommitLineData
b9b2e7bb 1<?php
61eb12d4
CS
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16//
b9b2e7bb
CS
17// This file is part of BasicLTI4Moodle
18//
19// BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability)
20// consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web
21// based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI
22// specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS
23// are already supporting or going to support BasicLTI. This project Implements the consumer
24// for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas.
25// BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem
26// at the GESSI research group at UPC.
27// SimpleLTI consumer for Moodle is an implementation of the early specification of LTI
28// by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a
29// Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier.
30//
31// BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis
32// of the Universitat Politecnica de Catalunya http://www.upc.edu
e3f69b58 33// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu.
b9b2e7bb
CS
34
35/**
61eb12d4 36 * This file contains the library of functions and constants for the lti module
b9b2e7bb 37 *
2b17ec3d 38 * @package mod_lti
61eb12d4 39 * @copyright 2009 Marc Alier, Jordi Piguillem, Nikolas Galanis
b9b2e7bb 40 * marc.alier@upc.edu
61eb12d4
CS
41 * @copyright 2009 Universitat Politecnica de Catalunya http://www.upc.edu
42 * @author Marc Alier
43 * @author Jordi Piguillem
44 * @author Nikolas Galanis
8f45215d 45 * @author Chris Scribner
01f38dd0 46 * @copyright 2015 Vital Source Technologies http://vitalsource.com
47 * @author Stephen Vickers
61eb12d4 48 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
b9b2e7bb
CS
49 */
50
51defined('MOODLE_INTERNAL') || die;
52
e3f69b58 53// TODO: Switch to core oauthlib once implemented - MDL-30149.
795dff01
CS
54use moodle\mod\lti as lti;
55
b9b2e7bb 56require_once($CFG->dirroot.'/mod/lti/OAuth.php');
7204d77b 57require_once($CFG->libdir.'/weblib.php');
b9b2e7bb
CS
58
59define('LTI_URL_DOMAIN_REGEX', '/(?:https?:\/\/)?(?:www\.)?([^\/]+)(?:\/|$)/i');
60
61define('LTI_LAUNCH_CONTAINER_DEFAULT', 1);
62define('LTI_LAUNCH_CONTAINER_EMBED', 2);
63define('LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS', 3);
64define('LTI_LAUNCH_CONTAINER_WINDOW', 4);
cca9d3f7 65define('LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW', 5);
b9b2e7bb
CS
66
67define('LTI_TOOL_STATE_ANY', 0);
68define('LTI_TOOL_STATE_CONFIGURED', 1);
69define('LTI_TOOL_STATE_PENDING', 2);
70define('LTI_TOOL_STATE_REJECTED', 3);
e3f69b58 71define('LTI_TOOL_PROXY_TAB', 4);
72
73define('LTI_TOOL_PROXY_STATE_CONFIGURED', 1);
74define('LTI_TOOL_PROXY_STATE_PENDING', 2);
75define('LTI_TOOL_PROXY_STATE_ACCEPTED', 3);
76define('LTI_TOOL_PROXY_STATE_REJECTED', 4);
b9b2e7bb
CS
77
78define('LTI_SETTING_NEVER', 0);
79define('LTI_SETTING_ALWAYS', 1);
5e078d62 80define('LTI_SETTING_DELEGATE', 2);
b9b2e7bb
CS
81
82/**
ae67efa8 83 * Return the launch data required for opening the external tool.
b9b2e7bb 84 *
ae67efa8
JL
85 * @param stdClass $instance the external tool activity settings
86 * @return array the endpoint URL and parameters (including the signature)
87 * @since Moodle 3.0
b9b2e7bb 88 */
ae67efa8 89function lti_get_launch_data($instance) {
b9b2e7bb
CS
90 global $PAGE, $CFG;
91
ea04a9f9 92 if (empty($instance->typeid)) {
606ab1a1 93 $tool = lti_get_tool_by_url_match($instance->toolurl, $instance->course);
ea04a9f9 94 if ($tool) {
b9b2e7bb
CS
95 $typeid = $tool->id;
96 } else {
97 $typeid = null;
98 }
99 } else {
100 $typeid = $instance->typeid;
e3f69b58 101 $tool = lti_get_type($typeid);
b9b2e7bb 102 }
e27cb316 103
ea04a9f9 104 if ($typeid) {
b9b2e7bb
CS
105 $typeconfig = lti_get_type_config($typeid);
106 } else {
e3f69b58 107 // There is no admin configuration for this tool. Use configuration in the lti instance record plus some defaults.
b9b2e7bb 108 $typeconfig = (array)$instance;
e27cb316 109
b9b2e7bb
CS
110 $typeconfig['sendname'] = $instance->instructorchoicesendname;
111 $typeconfig['sendemailaddr'] = $instance->instructorchoicesendemailaddr;
112 $typeconfig['customparameters'] = $instance->instructorcustomparameters;
f4f711d7
CS
113 $typeconfig['acceptgrades'] = $instance->instructorchoiceacceptgrades;
114 $typeconfig['allowroster'] = $instance->instructorchoiceallowroster;
d8d04121 115 $typeconfig['forcessl'] = '0';
b9b2e7bb 116 }
e27cb316 117
e3f69b58 118 // Default the organizationid if not specified.
ea04a9f9 119 if (empty($typeconfig['organizationid'])) {
b9b2e7bb 120 $urlparts = parse_url($CFG->wwwroot);
e27cb316 121
b9b2e7bb
CS
122 $typeconfig['organizationid'] = $urlparts['host'];
123 }
e27cb316 124
e3f69b58 125 if (isset($tool->toolproxyid)) {
126 $toolproxy = lti_get_tool_proxy($tool->toolproxyid);
127 $key = $toolproxy->guid;
128 $secret = $toolproxy->secret;
3dd9ca24 129 } else {
e3f69b58 130 $toolproxy = null;
131 if (!empty($instance->resourcekey)) {
132 $key = $instance->resourcekey;
133 } else if (!empty($typeconfig['resourcekey'])) {
134 $key = $typeconfig['resourcekey'];
135 } else {
136 $key = '';
137 }
138 if (!empty($instance->password)) {
139 $secret = $instance->password;
140 } else if (!empty($typeconfig['password'])) {
141 $secret = $typeconfig['password'];
142 } else {
143 $secret = '';
144 }
3dd9ca24 145 }
e27cb316 146
b9b2e7bb 147 $endpoint = !empty($instance->toolurl) ? $instance->toolurl : $typeconfig['toolurl'];
d8d04121 148 $endpoint = trim($endpoint);
e27cb316 149
e3f69b58 150 // If the current request is using SSL and a secure tool URL is specified, use it.
ea04a9f9 151 if (lti_request_is_using_ssl() && !empty($instance->securetoolurl)) {
d8d04121
CS
152 $endpoint = trim($instance->securetoolurl);
153 }
e27cb316 154
e3f69b58 155 // If SSL is forced, use the secure tool url if specified. Otherwise, make sure https is on the normal launch URL.
156 if (isset($typeconfig['forcessl']) && ($typeconfig['forcessl'] == '1')) {
ea04a9f9 157 if (!empty($instance->securetoolurl)) {
d8d04121
CS
158 $endpoint = trim($instance->securetoolurl);
159 }
e27cb316 160
d8d04121
CS
161 $endpoint = lti_ensure_url_is_https($endpoint);
162 } else {
ea04a9f9 163 if (!strstr($endpoint, '://')) {
d8d04121
CS
164 $endpoint = 'http://' . $endpoint;
165 }
f17f4959 166 }
e27cb316 167
d8d04121
CS
168 $orgid = $typeconfig['organizationid'];
169
b9b2e7bb 170 $course = $PAGE->course;
e3f69b58 171 $islti2 = isset($tool->toolproxyid);
172 $allparams = lti_build_request($instance, $typeconfig, $course, $typeid, $islti2);
173 if ($islti2) {
174 $requestparams = lti_build_request_lti2($tool, $allparams);
8fa50fdd 175 } else {
e3f69b58 176 $requestparams = $allparams;
8fa50fdd 177 }
e3f69b58 178 $requestparams = array_merge($requestparams, lti_build_standard_request($instance, $orgid, $islti2));
179 $customstr = '';
180 if (isset($typeconfig['customparameters'])) {
181 $customstr = $typeconfig['customparameters'];
1706e83b 182 }
e3f69b58 183 $requestparams = array_merge($requestparams, lti_build_custom_parameters($toolproxy, $tool, $instance, $allparams, $customstr,
184 $instance->instructorcustomparameters, $islti2));
185
186 $launchcontainer = lti_get_launch_container($instance, $typeconfig);
edc89dfe
DW
187 $returnurlparams = array('course' => $course->id,
188 'launch_container' => $launchcontainer,
189 'instanceid' => $instance->id,
190 'sesskey' => sesskey());
6d6bd5f1
EL
191
192 // Add the return URL. We send the launch container along to help us avoid frames-within-frames when the user returns.
e3f69b58 193 $url = new \moodle_url('/mod/lti/return.php', $returnurlparams);
1706e83b 194 $returnurl = $url->out(false);
e27cb316 195
e3f69b58 196 if (isset($typeconfig['forcessl']) && ($typeconfig['forcessl'] == '1')) {
1706e83b 197 $returnurl = lti_ensure_url_is_https($returnurl);
9d57ad17 198 }
6d6bd5f1 199
e3f69b58 200 $target = '';
8fa50fdd
MN
201 switch($launchcontainer) {
202 case LTI_LAUNCH_CONTAINER_EMBED:
203 case LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS:
204 $target = 'iframe';
205 break;
206 case LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW:
207 $target = 'frame';
208 break;
209 case LTI_LAUNCH_CONTAINER_WINDOW:
210 $target = 'window';
211 break;
212 }
e3f69b58 213 if (!empty($target)) {
8fa50fdd
MN
214 $requestparams['launch_presentation_document_target'] = $target;
215 }
216
1706e83b 217 $requestparams['launch_presentation_return_url'] = $returnurl;
e27cb316 218
d3496e3f
MN
219 // Allow request params to be updated by sub-plugins.
220 $plugins = core_component::get_plugin_list('ltisource');
221 foreach (array_keys($plugins) as $plugin) {
222 $pluginparams = component_callback('ltisource_'.$plugin, 'before_launch',
223 array($instance, $endpoint, $requestparams), array());
224
225 if (!empty($pluginparams) && is_array($pluginparams)) {
226 $requestparams = array_merge($requestparams, $pluginparams);
227 }
228 }
8fa50fdd 229
ea04a9f9 230 if (!empty($key) && !empty($secret)) {
3dd9ca24 231 $parms = lti_sign_parameters($requestparams, $endpoint, "POST", $key, $secret);
533c42ea 232
e3f69b58 233 $endpointurl = new \moodle_url($endpoint);
533c42ea
MN
234 $endpointparams = $endpointurl->params();
235
236 // Strip querystring params in endpoint url from $parms to avoid duplication.
237 if (!empty($endpointparams) && !empty($parms)) {
238 foreach (array_keys($endpointparams) as $paramname) {
239 if (isset($parms[$paramname])) {
240 unset($parms[$paramname]);
241 }
242 }
243 }
244
3dd9ca24 245 } else {
e3f69b58 246 // If no key and secret, do the launch unsigned.
247 $returnurlparams['unsigned'] = '1';
3dd9ca24 248 $parms = $requestparams;
3dd9ca24 249 }
e27cb316 250
ae67efa8
JL
251 return array($endpoint, $parms);
252}
253
254/**
c4c838ae 255 * Launch an external tool activity.
ae67efa8
JL
256 *
257 * @param stdClass $instance the external tool activity settings
c4c838ae 258 * @return string The HTML code containing the javascript code for the launch
ae67efa8 259 */
c4c838ae 260function lti_launch_tool($instance) {
ae67efa8
JL
261
262 list($endpoint, $parms) = lti_get_launch_data($instance);
b9b2e7bb 263 $debuglaunch = ( $instance->debuglaunch == 1 );
e27cb316 264
dbb0fec9 265 $content = lti_post_launch_html($parms, $endpoint, $debuglaunch);
e27cb316 266
b9b2e7bb
CS
267 echo $content;
268}
269
e3f69b58 270/**
271 * Prepares an LTI registration request message
272 *
273 * $param object $instance Tool Proxy instance object
274 */
275function lti_register($toolproxy) {
cc193e0d
RW
276 $endpoint = $toolproxy->regurl;
277
278 // Change the status to pending.
279 $toolproxy->state = LTI_TOOL_PROXY_STATE_PENDING;
280 lti_update_tool_proxy($toolproxy);
281
af9d3a92 282 $requestparams = lti_build_registration_request($toolproxy);
cc193e0d
RW
283
284 $content = lti_post_launch_html($requestparams, $endpoint, false);
285
286 echo $content;
287}
e3f69b58 288
af9d3a92
JO
289
290/**
291 * Gets the parameters for the regirstration request
292 *
293 * @param object $toolproxy Tool Proxy instance object
294 * @return array Registration request parameters
295 */
296function lti_build_registration_request($toolproxy) {
e3f69b58 297 $key = $toolproxy->guid;
298 $secret = $toolproxy->secret;
e3f69b58 299
300 $requestparams = array();
301 $requestparams['lti_message_type'] = 'ToolProxyRegistrationRequest';
302 $requestparams['lti_version'] = 'LTI-2p0';
303 $requestparams['reg_key'] = $key;
304 $requestparams['reg_password'] = $secret;
305
e3f69b58 306 // Add the profile URL.
307 $profileservice = lti_get_service_by_name('profile');
308 $profileservice->set_tool_proxy($toolproxy);
309 $requestparams['tc_profile_url'] = $profileservice->parse_value('$ToolConsumerProfile.url');
310
311 // Add the return URL.
01f38dd0 312 $returnurlparams = array('id' => $toolproxy->id, 'sesskey' => sesskey());
cc193e0d 313 $url = new \moodle_url('/mod/lti/externalregistrationreturn.php', $returnurlparams);
e3f69b58 314 $returnurl = $url->out(false);
315
316 $requestparams['launch_presentation_return_url'] = $returnurl;
e3f69b58 317
cc193e0d 318 return $requestparams;
e3f69b58 319}
320
8fa50fdd
MN
321/**
322 * Build source ID
323 *
324 * @param int $instanceid
325 * @param int $userid
326 * @param string $servicesalt
327 * @param null|int $typeid
328 * @param null|int $launchid
329 * @return stdClass
330 */
331function lti_build_sourcedid($instanceid, $userid, $servicesalt, $typeid = null, $launchid = null) {
e3f69b58 332 $data = new \stdClass();
e27cb316 333
996b0fd9
CS
334 $data->instanceid = $instanceid;
335 $data->userid = $userid;
8fa50fdd 336 $data->typeid = $typeid;
ea04a9f9 337 if (!empty($launchid)) {
f4f711d7
CS
338 $data->launchid = $launchid;
339 } else {
340 $data->launchid = mt_rand();
341 }
996b0fd9
CS
342
343 $json = json_encode($data);
344
345 $hash = hash('sha256', $json . $servicesalt, false);
346
e3f69b58 347 $container = new \stdClass();
996b0fd9
CS
348 $container->data = $data;
349 $container->hash = $hash;
350
351 return $container;
352}
353
b9b2e7bb
CS
354/**
355 * This function builds the request that must be sent to the tool producer
356 *
357 * @param object $instance Basic LTI instance object
ff9d3d81 358 * @param array $typeconfig Basic LTI tool configuration
b9b2e7bb 359 * @param object $course Course object
8fa50fdd 360 * @param int|null $typeid Basic LTI tool ID
e3f69b58 361 * @param boolean $islti2 True if an LTI 2 tool is being launched
b9b2e7bb 362 *
e3f69b58 363 * @return array Request details
b9b2e7bb 364 */
e3f69b58 365function lti_build_request($instance, $typeconfig, $course, $typeid = null, $islti2 = false) {
b9b2e7bb
CS
366 global $USER, $CFG;
367
ea04a9f9 368 if (empty($instance->cmid)) {
16cac566
CS
369 $instance->cmid = 0;
370 }
e27cb316 371
e3f69b58 372 $role = lti_get_ims_role($USER, $instance->cmid, $instance->course, $islti2);
b9b2e7bb 373
ff9d3d81
MN
374 $intro = '';
375 if (!empty($instance->cmid)) {
376 $intro = format_module_intro('lti', $instance, $instance->cmid);
377 $intro = html_to_text($intro, 0, false);
378
379 // This may look weird, but this is required for new lines
380 // so we generate the same OAuth signature as the tool provider.
381 $intro = str_replace("\n", "\r\n", $intro);
382 }
b9b2e7bb 383 $requestparams = array(
34eb0501 384 'resource_link_title' => $instance->name,
ff9d3d81 385 'resource_link_description' => $intro,
34eb0501 386 'user_id' => $USER->id,
e3f69b58 387 'lis_person_sourcedid' => $USER->idnumber,
34eb0501
CS
388 'roles' => $role,
389 'context_id' => $course->id,
390 'context_label' => $course->shortname,
391 'context_title' => $course->fullname,
b9b2e7bb 392 );
219f956a
CB
393 if (!empty($instance->id)) {
394 $requestparams['resource_link_id'] = $instance->id;
395 }
396 if (!empty($instance->resource_link_id)) {
397 $requestparams['resource_link_id'] = $instance->resource_link_id;
398 }
e3f69b58 399 if ($course->format == 'site') {
400 $requestparams['context_type'] = 'Group';
401 } else {
402 $requestparams['context_type'] = 'CourseSection';
403 $requestparams['lis_course_section_sourcedid'] = $course->idnumber;
8fa50fdd 404 }
b9b2e7bb 405 $placementsecret = $instance->servicesalt;
e27cb316 406
219f956a 407 if ( !empty($instance->id) && isset($placementsecret) && ($islti2 ||
e3f69b58 408 $typeconfig['acceptgrades'] == LTI_SETTING_ALWAYS ||
409 ($typeconfig['acceptgrades'] == LTI_SETTING_DELEGATE && $instance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS))) {
e27cb316 410
058cd1c1 411 $sourcedid = json_encode(lti_build_sourcedid($instance->id, $USER->id, $placementsecret, $typeid));
412 $requestparams['lis_result_sourcedid'] = $sourcedid;
413
e3f69b58 414 // Add outcome service URL.
415 $serviceurl = new \moodle_url('/mod/lti/service.php');
34eb0501 416 $serviceurl = $serviceurl->out();
feeb5294 417
8fa50fdd
MN
418 $forcessl = false;
419 if (!empty($CFG->mod_lti_forcessl)) {
420 $forcessl = true;
421 }
422
e3f69b58 423 if ((isset($typeconfig['forcessl']) && ($typeconfig['forcessl'] == '1')) or $forcessl) {
d8d04121
CS
424 $serviceurl = lti_ensure_url_is_https($serviceurl);
425 }
feeb5294 426
34eb0501 427 $requestparams['lis_outcome_service_url'] = $serviceurl;
b9b2e7bb
CS
428 }
429
e3f69b58 430 // Send user's name and email data if appropriate.
431 if ($islti2 || $typeconfig['sendname'] == LTI_SETTING_ALWAYS ||
5e078d62 432 ( $typeconfig['sendname'] == LTI_SETTING_DELEGATE && $instance->instructorchoicesendname == LTI_SETTING_ALWAYS ) ) {
e3f69b58 433 $requestparams['lis_person_name_given'] = $USER->firstname;
434 $requestparams['lis_person_name_family'] = $USER->lastname;
435 $requestparams['lis_person_name_full'] = $USER->firstname . ' ' . $USER->lastname;
435c709c 436 $requestparams['ext_user_username'] = $USER->username;
b9b2e7bb
CS
437 }
438
e3f69b58 439 if ($islti2 || $typeconfig['sendemailaddr'] == LTI_SETTING_ALWAYS ||
440 ($typeconfig['sendemailaddr'] == LTI_SETTING_DELEGATE && $instance->instructorchoicesendemailaddr == LTI_SETTING_ALWAYS)) {
34eb0501 441 $requestparams['lis_person_contact_email_primary'] = $USER->email;
b9b2e7bb
CS
442 }
443
e3f69b58 444 return $requestparams;
445}
446
447/**
448 * This function builds the request that must be sent to an LTI 2 tool provider
449 *
450 * @param object $tool Basic LTI tool object
451 * @param array $params Custom launch parameters
452 *
453 * @return array Request details
454 */
455function lti_build_request_lti2($tool, $params) {
456
457 $requestparams = array();
458
459 $capabilities = lti_get_capabilities();
460 $enabledcapabilities = explode("\n", $tool->enabledcapability);
461 foreach ($enabledcapabilities as $capability) {
462 if (array_key_exists($capability, $capabilities)) {
463 $val = $capabilities[$capability];
464 if ($val && (substr($val, 0, 1) != '$')) {
465 if (isset($params[$val])) {
466 $requestparams[$capabilities[$capability]] = $params[$capabilities[$capability]];
467 }
b9b2e7bb
CS
468 }
469 }
b9b2e7bb
CS
470 }
471
e3f69b58 472 return $requestparams;
473
474}
475
476/**
477 * This function builds the standard parameters for an LTI 1 or 2 request that must be sent to the tool producer
478 *
479 * @param object $instance Basic LTI instance object
480 * @param string $orgid Organisation ID
481 * @param boolean $islti2 True if an LTI 2 tool is being launched
482 *
483 * @return array Request details
484 */
485function lti_build_standard_request($instance, $orgid, $islti2) {
486 global $CFG;
487
488 $requestparams = array();
489
490 $requestparams['resource_link_id'] = $instance->id;
491 if (property_exists($instance, 'resource_link_id') and !empty($instance->resource_link_id)) {
492 $requestparams['resource_link_id'] = $instance->resource_link_id;
493 }
494
495 $requestparams['launch_presentation_locale'] = current_language();
496
497 // Make sure we let the tool know what LMS they are being called from.
498 $requestparams['ext_lms'] = 'moodle-2';
34eb0501
CS
499 $requestparams['tool_consumer_info_product_family_code'] = 'moodle';
500 $requestparams['tool_consumer_info_version'] = strval($CFG->version);
3dd9ca24 501
e3f69b58 502 // Add oauth_callback to be compliant with the 1.0A spec.
34eb0501 503 $requestparams['oauth_callback'] = 'about:blank';
3dd9ca24 504
e3f69b58 505 if (!$islti2) {
506 $requestparams['lti_version'] = 'LTI-1p0';
507 } else {
508 $requestparams['lti_version'] = 'LTI-2p0';
509 }
34eb0501 510 $requestparams['lti_message_type'] = 'basic-lti-launch-request';
e27cb316 511
e3f69b58 512 if ($orgid) {
513 $requestparams["tool_consumer_instance_guid"] = $orgid;
514 }
515 if (!empty($CFG->mod_lti_institution_name)) {
516 $requestparams['tool_consumer_instance_name'] = $CFG->mod_lti_institution_name;
517 } else {
2a5bb939 518 $requestparams['tool_consumer_instance_name'] = get_site()->shortname;
e3f69b58 519 }
2a5bb939 520 $requestparams['tool_consumer_instance_description'] = get_site()->fullname;
e3f69b58 521
b9b2e7bb
CS
522 return $requestparams;
523}
524
e3f69b58 525/**
526 * This function builds the custom parameters
527 *
528 * @param object $toolproxy Tool proxy instance object
529 * @param object $tool Tool instance object
530 * @param object $instance Tool placement instance object
531 * @param array $params LTI launch parameters
532 * @param string $customstr Custom parameters defined for tool
533 * @param string $instructorcustomstr Custom parameters defined for this placement
534 * @param boolean $islti2 True if an LTI 2 tool is being launched
535 *
536 * @return array Custom parameters
537 */
538function lti_build_custom_parameters($toolproxy, $tool, $instance, $params, $customstr, $instructorcustomstr, $islti2) {
539
540 // Concatenate the custom parameters from the administrator and the instructor
541 // Instructor parameters are only taken into consideration if the administrator
542 // has given permission.
543 $custom = array();
544 if ($customstr) {
545 $custom = lti_split_custom_parameters($toolproxy, $tool, $params, $customstr, $islti2);
546 }
547 if (!isset($typeconfig['allowinstructorcustom']) || $typeconfig['allowinstructorcustom'] != LTI_SETTING_NEVER) {
548 if ($instructorcustomstr) {
01f38dd0 549 $custom = array_merge(lti_split_custom_parameters($toolproxy, $tool, $params,
550 $instructorcustomstr, $islti2), $custom);
e3f69b58 551 }
552 }
553 if ($islti2) {
01f38dd0 554 $custom = array_merge(lti_split_custom_parameters($toolproxy, $tool, $params,
555 $tool->parameter, true), $custom);
e3f69b58 556 $settings = lti_get_tool_settings($tool->toolproxyid);
557 $custom = array_merge($custom, lti_get_custom_parameters($toolproxy, $tool, $params, $settings));
558 $settings = lti_get_tool_settings($tool->toolproxyid, $instance->course);
559 $custom = array_merge($custom, lti_get_custom_parameters($toolproxy, $tool, $params, $settings));
560 $settings = lti_get_tool_settings($tool->toolproxyid, $instance->course, $instance->id);
561 $custom = array_merge($custom, lti_get_custom_parameters($toolproxy, $tool, $params, $settings));
562 }
563
564 return $custom;
565}
566
ea04a9f9 567function lti_get_tool_table($tools, $id) {
99938034 568 global $CFG, $OUTPUT, $USER;
795dff01 569 $html = '';
e27cb316 570
795dff01
CS
571 $typename = get_string('typename', 'lti');
572 $baseurl = get_string('baseurl', 'lti');
573 $action = get_string('action', 'lti');
574 $createdon = get_string('createdon', 'lti');
e27cb316 575
795dff01 576 if (!empty($tools)) {
5de15b83 577 $html .= "
e3f69b58 578 <div id=\"{$id}_tools_container\" style=\"margin-top:.5em;margin-bottom:.5em\">
5de15b83 579 <table id=\"{$id}_tools\">
795dff01
CS
580 <thead>
581 <tr>
582 <th>$typename</th>
583 <th>$baseurl</th>
584 <th>$createdon</th>
585 <th>$action</th>
586 </tr>
587 </thead>
5de15b83 588 ";
e27cb316 589
795dff01 590 foreach ($tools as $type) {
101ec5fd 591 $date = userdate($type->timecreated, get_string('strftimedatefullshort', 'core_langconfig'));
795dff01
CS
592 $accept = get_string('accept', 'lti');
593 $update = get_string('update', 'lti');
594 $delete = get_string('delete', 'lti');
e27cb316 595
e3f69b58 596 if (empty($type->toolproxyid)) {
597 $baseurl = new \moodle_url('/mod/lti/typessettings.php', array(
598 'action' => 'accept',
599 'id' => $type->id,
600 'sesskey' => sesskey(),
601 'tab' => $id
602 ));
603 $ref = $type->baseurl;
604 } else {
605 $baseurl = new \moodle_url('/mod/lti/toolssettings.php', array(
606 'action' => 'accept',
607 'id' => $type->id,
608 'sesskey' => sesskey(),
609 'tab' => $id
610 ));
611 $ref = $type->tpname;
612 }
baf41e4b
FM
613
614 $accepthtml = $OUTPUT->action_icon($baseurl,
e3f69b58 615 new \pix_icon('t/check', $accept, '', array('class' => 'iconsmall')), null,
baf41e4b 616 array('title' => $accept, 'class' => 'editing_accept'));
795dff01
CS
617
618 $deleteaction = 'delete';
e27cb316 619
ea04a9f9 620 if ($type->state == LTI_TOOL_STATE_CONFIGURED) {
795dff01
CS
621 $accepthtml = '';
622 }
e27cb316 623
ea04a9f9 624 if ($type->state != LTI_TOOL_STATE_REJECTED) {
795dff01
CS
625 $deleteaction = 'reject';
626 $delete = get_string('reject', 'lti');
627 }
e27cb316 628
baf41e4b
FM
629 $updateurl = clone($baseurl);
630 $updateurl->param('action', 'update');
631 $updatehtml = $OUTPUT->action_icon($updateurl,
e3f69b58 632 new \pix_icon('t/edit', $update, '', array('class' => 'iconsmall')), null,
baf41e4b
FM
633 array('title' => $update, 'class' => 'editing_update'));
634
e3f69b58 635 if (($type->state != LTI_TOOL_STATE_REJECTED) || empty($type->toolproxyid)) {
636 $deleteurl = clone($baseurl);
637 $deleteurl->param('action', $deleteaction);
638 $deletehtml = $OUTPUT->action_icon($deleteurl,
639 new \pix_icon('t/delete', $delete, '', array('class' => 'iconsmall')), null,
640 array('title' => $delete, 'class' => 'editing_delete'));
641 } else {
642 $deletehtml = '';
643 }
5de15b83 644 $html .= "
795dff01
CS
645 <tr>
646 <td>
647 {$type->name}
648 </td>
649 <td>
e3f69b58 650 {$ref}
795dff01
CS
651 </td>
652 <td>
653 {$date}
654 </td>
5de15b83 655 <td align=\"center\">
baf41e4b 656 {$accepthtml}{$updatehtml}{$deletehtml}
795dff01
CS
657 </td>
658 </tr>
5de15b83 659 ";
795dff01
CS
660 }
661 $html .= '</table></div>';
662 } else {
663 $html .= get_string('no_' . $id, 'lti');
664 }
e27cb316 665
795dff01
CS
666 return $html;
667}
668
e3f69b58 669/**
670 * This function builds the tab for a category of tool proxies
671 *
672 * @param object $toolproxies Tool proxy instance objects
673 * @param string $id Category ID
674 *
675 * @return string HTML for tab
676 */
677function lti_get_tool_proxy_table($toolproxies, $id) {
678 global $OUTPUT;
679
680 if (!empty($toolproxies)) {
681 $typename = get_string('typename', 'lti');
682 $url = get_string('registrationurl', 'lti');
683 $action = get_string('action', 'lti');
684 $createdon = get_string('createdon', 'lti');
685
686 $html = <<< EOD
687 <div id="{$id}_tool_proxies_container" style="margin-top: 0.5em; margin-bottom: 0.5em">
688 <table id="{$id}_tool_proxies">
689 <thead>
690 <tr>
691 <th>{$typename}</th>
692 <th>{$url}</th>
693 <th>{$createdon}</th>
694 <th>{$action}</th>
695 </tr>
696 </thead>
697EOD;
698 foreach ($toolproxies as $toolproxy) {
699 $date = userdate($toolproxy->timecreated, get_string('strftimedatefullshort', 'core_langconfig'));
700 $accept = get_string('register', 'lti');
701 $update = get_string('update', 'lti');
702 $delete = get_string('delete', 'lti');
703
704 $baseurl = new \moodle_url('/mod/lti/registersettings.php', array(
705 'action' => 'accept',
706 'id' => $toolproxy->id,
707 'sesskey' => sesskey(),
708 'tab' => $id
709 ));
710
711 $registerurl = new \moodle_url('/mod/lti/register.php', array(
712 'id' => $toolproxy->id,
713 'sesskey' => sesskey(),
714 'tab' => 'tool_proxy'
715 ));
716
717 $accepthtml = $OUTPUT->action_icon($registerurl,
718 new \pix_icon('t/check', $accept, '', array('class' => 'iconsmall')), null,
719 array('title' => $accept, 'class' => 'editing_accept'));
720
721 $deleteaction = 'delete';
722
723 if ($toolproxy->state != LTI_TOOL_PROXY_STATE_CONFIGURED) {
724 $accepthtml = '';
725 }
726
727 if (($toolproxy->state == LTI_TOOL_PROXY_STATE_CONFIGURED) || ($toolproxy->state == LTI_TOOL_PROXY_STATE_PENDING)) {
728 $delete = get_string('cancel', 'lti');
729 }
730
731 $updateurl = clone($baseurl);
732 $updateurl->param('action', 'update');
733 $updatehtml = $OUTPUT->action_icon($updateurl,
734 new \pix_icon('t/edit', $update, '', array('class' => 'iconsmall')), null,
735 array('title' => $update, 'class' => 'editing_update'));
736
737 $deleteurl = clone($baseurl);
738 $deleteurl->param('action', $deleteaction);
739 $deletehtml = $OUTPUT->action_icon($deleteurl,
740 new \pix_icon('t/delete', $delete, '', array('class' => 'iconsmall')), null,
741 array('title' => $delete, 'class' => 'editing_delete'));
742 $html .= <<< EOD
743 <tr>
744 <td>
745 {$toolproxy->name}
746 </td>
747 <td>
748 {$toolproxy->regurl}
749 </td>
750 <td>
751 {$date}
752 </td>
753 <td align="center">
754 {$accepthtml}{$updatehtml}{$deletehtml}
755 </td>
756 </tr>
757EOD;
758 }
759 $html .= '</table></div>';
760 } else {
761 $html = get_string('no_' . $id, 'lti');
762 }
763
764 return $html;
765}
766
01f38dd0 767/**
768 * Extracts the enabled capabilities into an array, including those implicitly declared in a parameter
769 *
770 * @param object $tool Tool instance object
771 *
772 * @return Array of enabled capabilities
773 */
774function lti_get_enabled_capabilities($tool) {
775 if (!empty($tool->enabledcapability)) {
776 $enabledcapabilities = explode("\n", $tool->enabledcapability);
777 } else {
778 $enabledcapabilities = array();
779 }
780 $paramstr = str_replace("\r\n", "\n", $tool->parameter);
781 $paramstr = str_replace("\n\r", "\n", $paramstr);
782 $paramstr = str_replace("\r", "\n", $paramstr);
783 $params = explode("\n", $paramstr);
784 foreach ($params as $param) {
785 $pos = strpos($param, '=');
786 if (($pos === false) || ($pos < 1)) {
787 continue;
788 }
789 $value = trim(core_text::substr($param, $pos + 1, strlen($param)));
790 if (substr($value, 0, 1) == '$') {
791 $value = substr($value, 1);
792 if (!in_array($value, $enabledcapabilities)) {
793 $enabledcapabilities[] = $value;
794 }
795 }
796 }
797 return $enabledcapabilities;
798}
799
b9b2e7bb
CS
800/**
801 * Splits the custom parameters field to the various parameters
802 *
e3f69b58 803 * @param object $toolproxy Tool proxy instance object
804 * @param object $tool Tool instance object
805 * @param array $params LTI launch parameters
806 * @param string $customstr String containing the parameters
807 * @param boolean $islti2 True if an LTI 2 tool is being launched
b9b2e7bb
CS
808 *
809 * @return Array of custom parameters
810 */
e3f69b58 811function lti_split_custom_parameters($toolproxy, $tool, $params, $customstr, $islti2 = false) {
812 $customstr = str_replace("\r\n", "\n", $customstr);
813 $customstr = str_replace("\n\r", "\n", $customstr);
814 $customstr = str_replace("\r", "\n", $customstr);
815 $lines = explode("\n", $customstr); // Or should this split on "/[\n;]/"?
b9b2e7bb
CS
816 $retval = array();
817 foreach ($lines as $line) {
01f38dd0 818 $pos = strpos($line, '=');
b9b2e7bb
CS
819 if ( $pos === false || $pos < 1 ) {
820 continue;
821 }
2f1e464a 822 $key = trim(core_text::substr($line, 0, $pos));
e3f69b58 823 $val = trim(core_text::substr($line, $pos + 1, strlen($line)));
824 $val = lti_parse_custom_parameter($toolproxy, $tool, $params, $val, $islti2);
825 $key2 = lti_map_keyname($key);
826 $retval['custom_'.$key2] = $val;
827 if ($islti2 && ($key != $key2)) {
828 $retval['custom_'.$key] = $val;
829 }
830 }
831 return $retval;
832}
833
834/**
835 * Adds the custom parameters to an array
836 *
837 * @param object $toolproxy Tool proxy instance object
838 * @param object $tool Tool instance object
839 * @param array $params LTI launch parameters
840 * @param array $parameters Array containing the parameters
841 *
842 * @return array Array of custom parameters
843 */
844function lti_get_custom_parameters($toolproxy, $tool, $params, $parameters) {
845 $retval = array();
846 foreach ($parameters as $key => $val) {
847 $key2 = lti_map_keyname($key);
848 $val = lti_parse_custom_parameter($toolproxy, $tool, $params, $val, true);
849 $retval['custom_'.$key2] = $val;
850 if ($key != $key2) {
851 $retval['custom_'.$key] = $val;
852 }
b9b2e7bb
CS
853 }
854 return $retval;
855}
856
e3f69b58 857/**
858 * Parse a custom parameter to replace any substitution variables
859 *
860 * @param object $toolproxy Tool proxy instance object
861 * @param object $tool Tool instance object
862 * @param array $params LTI launch parameters
863 * @param string $value Custom parameter value
864 * @param boolean $islti2 True if an LTI 2 tool is being launched
865 *
866 * @return Parsed value of custom parameter
867 */
868function lti_parse_custom_parameter($toolproxy, $tool, $params, $value, $islti2) {
869 global $USER, $COURSE;
870
871 if ($value) {
872 if (substr($value, 0, 1) == '\\') {
873 $value = substr($value, 1);
874 } else if (substr($value, 0, 1) == '$') {
875 $value1 = substr($value, 1);
01f38dd0 876 $enabledcapabilities = lti_get_enabled_capabilities($tool);
e3f69b58 877 if (!$islti2 || in_array($value1, $enabledcapabilities)) {
878 $capabilities = lti_get_capabilities();
879 if (array_key_exists($value1, $capabilities)) {
880 $val = $capabilities[$value1];
881 if ($val) {
882 if (substr($val, 0, 1) != '$') {
883 $value = $params[$val];
884 } else {
885 $valarr = explode('->', substr($val, 1), 2);
ccfd168a 886 $value = "{${$valarr[0]}->{$valarr[1]}}";
e3f69b58 887 $value = str_replace('<br />' , ' ', $value);
888 $value = str_replace('<br>' , ' ', $value);
889 $value = format_string($value);
890 }
891 }
892 } else if ($islti2) {
893 $val = $value;
894 $services = lti_get_services();
895 foreach ($services as $service) {
896 $service->set_tool_proxy($toolproxy);
897 $value = $service->parse_value($val);
898 if ($val != $value) {
899 break;
900 }
901 }
902 }
903 }
904 }
905 }
906 return $value;
907}
908
b9b2e7bb
CS
909/**
910 * Used for building the names of the different custom parameters
911 *
912 * @param string $key Parameter name
913 *
914 * @return string Processed name
915 */
dbb0fec9 916function lti_map_keyname($key) {
b9b2e7bb 917 $newkey = "";
2f1e464a 918 $key = core_text::strtolower(trim($key));
b9b2e7bb
CS
919 foreach (str_split($key) as $ch) {
920 if ( ($ch >= 'a' && $ch <= 'z') || ($ch >= '0' && $ch <= '9') ) {
921 $newkey .= $ch;
922 } else {
923 $newkey .= '_';
924 }
925 }
926 return $newkey;
927}
928
929/**
16cac566 930 * Gets the IMS role string for the specified user and LTI course module.
e27cb316 931 *
e3f69b58 932 * @param mixed $user User object or user id
933 * @param int $cmid The course module id of the LTI activity
934 * @param int $courseid The course id of the LTI activity
935 * @param boolean $islti2 True if an LTI 2 tool is being launched
936 *
16cac566 937 * @return string A role string suitable for passing with an LTI launch
b9b2e7bb 938 */
e3f69b58 939function lti_get_ims_role($user, $cmid, $courseid, $islti2) {
16cac566 940 $roles = array();
e27cb316 941
ea04a9f9 942 if (empty($cmid)) {
e3f69b58 943 // If no cmid is passed, check if the user is a teacher in the course
944 // This allows other modules to programmatically "fake" a launch without
945 // a real LTI instance.
c288a3db 946 $coursecontext = context_course::instance($courseid);
e27cb316 947
591b3128 948 if (has_capability('moodle/course:manageactivities', $coursecontext, $user)) {
1d4f052e
CS
949 array_push($roles, 'Instructor');
950 } else {
951 array_push($roles, 'Learner');
952 }
16cac566 953 } else {
c288a3db 954 $context = context_module::instance($cmid);
1d4f052e 955
ea04a9f9 956 if (has_capability('mod/lti:manage', $context)) {
1d4f052e
CS
957 array_push($roles, 'Instructor');
958 } else {
959 array_push($roles, 'Learner');
960 }
b9b2e7bb 961 }
1d4f052e 962
ea04a9f9 963 if (is_siteadmin($user)) {
e3f69b58 964 if (!$islti2) {
965 array_push($roles, 'urn:lti:sysrole:ims/lis/Administrator', 'urn:lti:instrole:ims/lis/Administrator');
966 } else {
967 array_push($roles, 'http://purl.imsglobal.org/vocab/lis/v2/person#Administrator');
968 }
b9b2e7bb 969 }
e27cb316 970
16cac566 971 return join(',', $roles);
b9b2e7bb
CS
972}
973
974/**
975 * Returns configuration details for the tool
976 *
977 * @param int $typeid Basic LTI tool typeid
978 *
979 * @return array Tool Configuration
980 */
981function lti_get_type_config($typeid) {
982 global $DB;
983
5f136255
EL
984 $query = "SELECT name, value
985 FROM {lti_types_config}
986 WHERE typeid = :typeid1
987 UNION ALL
cc12ae6c 988 SELECT 'toolurl' AS name, " . $DB->sql_compare_text('baseurl', 1333) . " AS value
5f136255 989 FROM {lti_types}
3f358828 990 WHERE id = :typeid2
991 UNION ALL
992 SELECT 'icon' AS name, " . $DB->sql_compare_text('icon', 1333) . " AS value
993 FROM {lti_types}
994 WHERE id = :typeid3
995 UNION ALL
996 SELECT 'secureicon' AS name, " . $DB->sql_compare_text('secureicon', 1333) . " AS value
997 FROM {lti_types}
998 WHERE id = :typeid4";
e27cb316 999
b9b2e7bb 1000 $typeconfig = array();
3f358828 1001 $configs = $DB->get_records_sql($query,
1002 array('typeid1' => $typeid, 'typeid2' => $typeid, 'typeid3' => $typeid, 'typeid4' => $typeid));
e27cb316 1003
b9b2e7bb
CS
1004 if (!empty($configs)) {
1005 foreach ($configs as $config) {
1006 $typeconfig[$config->name] = $config->value;
1007 }
1008 }
e27cb316 1009
b9b2e7bb
CS
1010 return $typeconfig;
1011}
1012
ea04a9f9 1013function lti_get_tools_by_url($url, $state, $courseid = null) {
b9b2e7bb 1014 $domain = lti_get_domain_from_url($url);
e27cb316 1015
996b0fd9 1016 return lti_get_tools_by_domain($domain, $state, $courseid);
b9b2e7bb
CS
1017}
1018
ea04a9f9 1019function lti_get_tools_by_domain($domain, $state = null, $courseid = null) {
b9b2e7bb 1020 global $DB, $SITE;
e27cb316 1021
b9b2e7bb 1022 $filters = array('tooldomain' => $domain);
e27cb316 1023
b9b2e7bb
CS
1024 $statefilter = '';
1025 $coursefilter = '';
e27cb316 1026
ea04a9f9 1027 if ($state) {
b9b2e7bb
CS
1028 $statefilter = 'AND state = :state';
1029 }
e27cb316 1030
ea04a9f9 1031 if ($courseid && $courseid != $SITE->id) {
b9b2e7bb
CS
1032 $coursefilter = 'OR course = :courseid';
1033 }
e27cb316 1034
5f136255
EL
1035 $query = "SELECT *
1036 FROM {lti_types}
1037 WHERE tooldomain = :tooldomain
1038 AND (course = :siteid $coursefilter)
1039 $statefilter";
e27cb316 1040
b9b2e7bb 1041 return $DB->get_records_sql($query, array(
e27cb316
CS
1042 'courseid' => $courseid,
1043 'siteid' => $SITE->id,
1044 'tooldomain' => $domain,
b9b2e7bb
CS
1045 'state' => $state
1046 ));
1047}
1048
1049/**
1050 * Returns all basicLTI tools configured by the administrator
1051 *
1052 */
6831c7cd 1053function lti_filter_get_types($course) {
b9b2e7bb
CS
1054 global $DB;
1055
ea04a9f9 1056 if (!empty($course)) {
e3f69b58 1057 $where = "WHERE t.course = :course";
1058 $params = array('course' => $course);
6831c7cd 1059 } else {
e3f69b58 1060 $where = '';
1061 $params = array();
6831c7cd 1062 }
e3f69b58 1063 $query = "SELECT t.id, t.name, t.baseurl, t.state, t.toolproxyid, t.timecreated, tp.name tpname
1064 FROM {lti_types} t LEFT OUTER JOIN {lti_tool_proxies} tp ON t.toolproxyid = tp.id
1065 {$where}";
1066 return $DB->get_records_sql($query, $params);
b9b2e7bb
CS
1067}
1068
d81a603e
MN
1069/**
1070 * Given an array of tools, filter them based on their state
1071 *
1072 * @param array $tools An array of lti_types records
1073 * @param int $state One of the LTI_TOOL_STATE_* constants
1074 * @return array
1075 */
1076function lti_filter_tool_types(array $tools, $state) {
1077 $return = array();
1078 foreach ($tools as $key => $tool) {
1079 if ($tool->state == $state) {
1080 $return[$key] = $tool;
1081 }
1082 }
1083 return $return;
1084}
1085
01e8bfd7
JO
1086/**
1087 * Returns all lti types visible in this course
1088 *
1089 * @param int $courseid The id of the course to retieve types for
2348c137 1090 * @return stdClass[] All the lti types visible in the given course
01e8bfd7
JO
1091 */
1092function lti_get_lti_types_by_course($courseid) {
1093 global $DB, $SITE;
e27cb316 1094
5f136255
EL
1095 $query = "SELECT *
1096 FROM {lti_types}
1097 WHERE coursevisible = 1
1098 AND (course = :siteid OR course = :courseid)
1099 AND state = :active";
e27cb316 1100
01e8bfd7
JO
1101 return $DB->get_records_sql($query,
1102 array('siteid' => $SITE->id, 'courseid' => $courseid, 'active' => LTI_TOOL_STATE_CONFIGURED));
1103}
1104
1105/**
1106 * Returns tool types for lti add instance and edit page
1107 *
1108 * @return array Array of lti types
1109 */
1110function lti_get_types_for_add_instance() {
1111 global $COURSE;
1112 $admintypes = lti_get_lti_types_by_course($COURSE->id);
e27cb316 1113
b9b2e7bb 1114 $types = array();
e3f69b58 1115 $types[0] = (object)array('name' => get_string('automatic', 'lti'), 'course' => 0, 'toolproxyid' => null);
e27cb316 1116
ea04a9f9 1117 foreach ($admintypes as $type) {
16e8f130 1118 $types[$type->id] = $type;
b9b2e7bb 1119 }
e27cb316 1120
b9b2e7bb
CS
1121 return $types;
1122}
1123
01e8bfd7
JO
1124/**
1125 * Returns a list of configured types in the given course
1126 *
1127 * @param int $courseid The id of the course to retieve types for
2348c137
MG
1128 * @param int $sectionreturn section to return to for forming the URLs
1129 * @return array Array of lti types. Each element is object with properties: name, title, icon, help, link
01e8bfd7 1130 */
2348c137 1131function lti_get_configured_types($courseid, $sectionreturn = 0) {
01e8bfd7
JO
1132 global $OUTPUT;
1133 $types = array();
1134 $admintypes = lti_get_lti_types_by_course($courseid);
1135
1136 foreach ($admintypes as $ltitype) {
1137 $type = new stdClass();
1138 $type->modclass = MOD_CLASS_ACTIVITY;
1139 $type->name = 'lti_type_' . $ltitype->id;
1140 $type->title = $ltitype->name;
1141 if (empty($ltitype->icon)) {
1142 $type->icon = $OUTPUT->pix_icon('icon', '', 'lti', array('class' => 'icon'));
1143 } else {
1144 $type->icon = html_writer::empty_tag('img', array('src' => $ltitype->icon, 'alt' => $ltitype->name, 'class' => 'icon'));
1145 }
2348c137
MG
1146 $type->link = new moodle_url('/course/modedit.php', array('add' => 'lti', 'return' => 0, 'course' => $courseid,
1147 'sr' => $sectionreturn, 'typeid' => $ltitype->id));
01e8bfd7
JO
1148 $types[] = $type;
1149 }
1150 return $types;
1151}
1152
ea04a9f9 1153function lti_get_domain_from_url($url) {
b9b2e7bb 1154 $matches = array();
e27cb316 1155
ea04a9f9 1156 if (preg_match(LTI_URL_DOMAIN_REGEX, $url, $matches)) {
b9b2e7bb
CS
1157 return $matches[1];
1158 }
1159}
1160
ea04a9f9 1161function lti_get_tool_by_url_match($url, $courseid = null, $state = LTI_TOOL_STATE_CONFIGURED) {
b69dc429 1162 $possibletools = lti_get_tools_by_url($url, $state, $courseid);
e27cb316 1163
7023b65e 1164 return lti_get_best_tool_by_url($url, $possibletools, $courseid);
b9b2e7bb
CS
1165}
1166
ea04a9f9 1167function lti_get_url_thumbprint($url) {
8fa50fdd
MN
1168 // Parse URL requires a schema otherwise everything goes into 'path'. Fixed 5.4.7 or later.
1169 if (preg_match('/https?:\/\//', $url) !== 1) {
1170 $url = 'http://'.$url;
1171 }
b9b2e7bb 1172 $urlparts = parse_url(strtolower($url));
ea04a9f9 1173 if (!isset($urlparts['path'])) {
b9b2e7bb
CS
1174 $urlparts['path'] = '';
1175 }
e27cb316 1176
ea04a9f9 1177 if (!isset($urlparts['host'])) {
d8d04121
CS
1178 $urlparts['host'] = '';
1179 }
e27cb316 1180
ea04a9f9 1181 if (substr($urlparts['host'], 0, 4) === 'www.') {
d8d04121 1182 $urlparts['host'] = substr($urlparts['host'], 4);
b9b2e7bb 1183 }
e27cb316 1184
b9b2e7bb
CS
1185 return $urllower = $urlparts['host'] . '/' . $urlparts['path'];
1186}
1187
ea04a9f9
EL
1188function lti_get_best_tool_by_url($url, $tools, $courseid = null) {
1189 if (count($tools) === 0) {
b9b2e7bb
CS
1190 return null;
1191 }
e27cb316 1192
b9b2e7bb 1193 $urllower = lti_get_url_thumbprint($url);
e27cb316 1194
ea04a9f9 1195 foreach ($tools as $tool) {
b9b2e7bb 1196 $tool->_matchscore = 0;
e27cb316 1197
b9b2e7bb 1198 $toolbaseurllower = lti_get_url_thumbprint($tool->baseurl);
e27cb316 1199
ea04a9f9 1200 if ($urllower === $toolbaseurllower) {
e3f69b58 1201 // 100 points for exact thumbprint match.
b9b2e7bb 1202 $tool->_matchscore += 100;
ea04a9f9 1203 } else if (substr($urllower, 0, strlen($toolbaseurllower)) === $toolbaseurllower) {
e3f69b58 1204 // 50 points if tool thumbprint starts with the base URL thumbprint.
b9b2e7bb
CS
1205 $tool->_matchscore += 50;
1206 }
e27cb316 1207
e3f69b58 1208 // Prefer course tools over site tools.
ea04a9f9 1209 if (!empty($courseid)) {
e3f69b58 1210 // Minus 10 points for not matching the course id (global tools).
ea04a9f9 1211 if ($tool->course != $courseid) {
7023b65e
CS
1212 $tool->_matchscore -= 10;
1213 }
1214 }
b9b2e7bb 1215 }
e27cb316 1216
ea04a9f9
EL
1217 $bestmatch = array_reduce($tools, function($value, $tool) {
1218 if ($tool->_matchscore > $value->_matchscore) {
b9b2e7bb
CS
1219 return $tool;
1220 } else {
1221 return $value;
1222 }
e27cb316 1223
b9b2e7bb 1224 }, (object)array('_matchscore' => -1));
e27cb316 1225
e3f69b58 1226 // None of the tools are suitable for this URL.
ea04a9f9 1227 if ($bestmatch->_matchscore <= 0) {
b9b2e7bb
CS
1228 return null;
1229 }
e27cb316 1230
b9b2e7bb
CS
1231 return $bestmatch;
1232}
1233
ea04a9f9 1234function lti_get_shared_secrets_by_key($key) {
020eea1b 1235 global $DB;
e27cb316 1236
e3f69b58 1237 // Look up the shared secret for the specified key in both the types_config table (for configured tools)
1238 // And in the lti resource table for ad-hoc tools.
5f136255
EL
1239 $query = "SELECT t2.value
1240 FROM {lti_types_config} t1
1241 JOIN {lti_types_config} t2 ON t1.typeid = t2.typeid
1242 JOIN {lti_types} type ON t2.typeid = type.id
1243 WHERE t1.name = 'resourcekey'
1244 AND t1.value = :key1
1245 AND t2.name = 'password'
e3f69b58 1246 AND type.state = :configured1
1247 UNION
1248 SELECT tp.secret AS value
1249 FROM {lti_tool_proxies} tp
1250 JOIN {lti_types} t ON tp.id = t.toolproxyid
1251 WHERE tp.guid = :key2
1252 AND t.state = :configured2
5f136255
EL
1253 UNION
1254 SELECT password AS value
1255 FROM {lti}
e3f69b58 1256 WHERE resourcekey = :key3";
e27cb316 1257
e3f69b58 1258 $sharedsecrets = $DB->get_records_sql($query, array('configured1' => LTI_TOOL_STATE_CONFIGURED,
1259 'configured2' => LTI_TOOL_STATE_CONFIGURED, 'key1' => $key, 'key2' => $key, 'key3' => $key));
e27cb316 1260
ea04a9f9 1261 $values = array_map(function($item) {
020eea1b
CS
1262 return $item->value;
1263 }, $sharedsecrets);
e27cb316 1264
e3f69b58 1265 // There should really only be one shared secret per key. But, we can't prevent
1266 // more than one getting entered. For instance, if the same key is used for two tool providers.
020eea1b
CS
1267 return $values;
1268}
1269
b9b2e7bb
CS
1270/**
1271 * Delete a Basic LTI configuration
1272 *
1273 * @param int $id Configuration id
1274 */
1275function lti_delete_type($id) {
1276 global $DB;
1277
e3f69b58 1278 // We should probably just copy the launch URL to the tool instances in this case... using a single query.
b9b2e7bb
CS
1279 /*
1280 $instances = $DB->get_records('lti', array('typeid' => $id));
1281 foreach ($instances as $instance) {
1282 $instance->typeid = 0;
1283 $DB->update_record('lti', $instance);
1284 }*/
1285
1286 $DB->delete_records('lti_types', array('id' => $id));
1287 $DB->delete_records('lti_types_config', array('typeid' => $id));
1288}
1289
ea04a9f9 1290function lti_set_state_for_type($id, $state) {
b9b2e7bb 1291 global $DB;
e27cb316 1292
b9b2e7bb
CS
1293 $DB->update_record('lti_types', array('id' => $id, 'state' => $state));
1294}
1295
1296/**
1297 * Transforms a basic LTI object to an array
1298 *
1299 * @param object $ltiobject Basic LTI object
1300 *
1301 * @return array Basic LTI configuration details
1302 */
1303function lti_get_config($ltiobject) {
1304 $typeconfig = array();
1305 $typeconfig = (array)$ltiobject;
1306 $additionalconfig = lti_get_type_config($ltiobject->typeid);
1307 $typeconfig = array_merge($typeconfig, $additionalconfig);
1308 return $typeconfig;
1309}
1310
1311/**
1312 *
1313 * Generates some of the tool configuration based on the instance details
1314 *
1315 * @param int $id
1316 *
1317 * @return Instance configuration
1318 *
1319 */
1320function lti_get_type_config_from_instance($id) {
1321 global $DB;
1322
1323 $instance = $DB->get_record('lti', array('id' => $id));
1324 $config = lti_get_config($instance);
1325
e3f69b58 1326 $type = new \stdClass();
b9b2e7bb
CS
1327 $type->lti_fix = $id;
1328 if (isset($config['toolurl'])) {
1329 $type->lti_toolurl = $config['toolurl'];
1330 }
1331 if (isset($config['instructorchoicesendname'])) {
1332 $type->lti_sendname = $config['instructorchoicesendname'];
1333 }
1334 if (isset($config['instructorchoicesendemailaddr'])) {
1335 $type->lti_sendemailaddr = $config['instructorchoicesendemailaddr'];
1336 }
1337 if (isset($config['instructorchoiceacceptgrades'])) {
1338 $type->lti_acceptgrades = $config['instructorchoiceacceptgrades'];
1339 }
1340 if (isset($config['instructorchoiceallowroster'])) {
1341 $type->lti_allowroster = $config['instructorchoiceallowroster'];
1342 }
1343
1344 if (isset($config['instructorcustomparameters'])) {
1345 $type->lti_allowsetting = $config['instructorcustomparameters'];
1346 }
1347 return $type;
1348}
1349
1350/**
1351 * Generates some of the tool configuration based on the admin configuration details
1352 *
1353 * @param int $id
1354 *
1355 * @return Configuration details
1356 */
1357function lti_get_type_type_config($id) {
1358 global $DB;
1359
1360 $basicltitype = $DB->get_record('lti_types', array('id' => $id));
1361 $config = lti_get_type_config($id);
1362
e3f69b58 1363 $type = new \stdClass();
5f72d102 1364
b9b2e7bb 1365 $type->lti_typename = $basicltitype->name;
e27cb316 1366
6831c7cd 1367 $type->typeid = $basicltitype->id;
e27cb316 1368
e3f69b58 1369 $type->toolproxyid = $basicltitype->toolproxyid;
1370
b9b2e7bb 1371 $type->lti_toolurl = $basicltitype->baseurl;
e27cb316 1372
cc193e0d
RW
1373 $type->lti_description = $basicltitype->description;
1374
e3f69b58 1375 $type->lti_parameters = $basicltitype->parameter;
1376
3f358828 1377 $type->lti_icon = $basicltitype->icon;
1378
1379 $type->lti_secureicon = $basicltitype->secureicon;
1380
b9b2e7bb
CS
1381 if (isset($config['resourcekey'])) {
1382 $type->lti_resourcekey = $config['resourcekey'];
1383 }
1384 if (isset($config['password'])) {
1385 $type->lti_password = $config['password'];
1386 }
1387
1388 if (isset($config['sendname'])) {
1389 $type->lti_sendname = $config['sendname'];
1390 }
ea04a9f9 1391 if (isset($config['instructorchoicesendname'])) {
b9b2e7bb
CS
1392 $type->lti_instructorchoicesendname = $config['instructorchoicesendname'];
1393 }
ea04a9f9 1394 if (isset($config['sendemailaddr'])) {
b9b2e7bb
CS
1395 $type->lti_sendemailaddr = $config['sendemailaddr'];
1396 }
ea04a9f9 1397 if (isset($config['instructorchoicesendemailaddr'])) {
b9b2e7bb
CS
1398 $type->lti_instructorchoicesendemailaddr = $config['instructorchoicesendemailaddr'];
1399 }
ea04a9f9 1400 if (isset($config['acceptgrades'])) {
b9b2e7bb
CS
1401 $type->lti_acceptgrades = $config['acceptgrades'];
1402 }
ea04a9f9 1403 if (isset($config['instructorchoiceacceptgrades'])) {
b9b2e7bb
CS
1404 $type->lti_instructorchoiceacceptgrades = $config['instructorchoiceacceptgrades'];
1405 }
ea04a9f9 1406 if (isset($config['allowroster'])) {
b9b2e7bb
CS
1407 $type->lti_allowroster = $config['allowroster'];
1408 }
ea04a9f9 1409 if (isset($config['instructorchoiceallowroster'])) {
b9b2e7bb
CS
1410 $type->lti_instructorchoiceallowroster = $config['instructorchoiceallowroster'];
1411 }
1412
1413 if (isset($config['customparameters'])) {
1414 $type->lti_customparameters = $config['customparameters'];
1415 }
1416
ea04a9f9 1417 if (isset($config['forcessl'])) {
d8d04121
CS
1418 $type->lti_forcessl = $config['forcessl'];
1419 }
e27cb316 1420
b9b2e7bb
CS
1421 if (isset($config['organizationid'])) {
1422 $type->lti_organizationid = $config['organizationid'];
1423 }
1424 if (isset($config['organizationurl'])) {
1425 $type->lti_organizationurl = $config['organizationurl'];
1426 }
1427 if (isset($config['organizationdescr'])) {
1428 $type->lti_organizationdescr = $config['organizationdescr'];
1429 }
1430 if (isset($config['launchcontainer'])) {
1431 $type->lti_launchcontainer = $config['launchcontainer'];
1432 }
e27cb316 1433
b9b2e7bb
CS
1434 if (isset($config['coursevisible'])) {
1435 $type->lti_coursevisible = $config['coursevisible'];
1436 }
e27cb316 1437
b9b2e7bb
CS
1438 if (isset($config['debuglaunch'])) {
1439 $type->lti_debuglaunch = $config['debuglaunch'];
1440 }
e27cb316 1441
b9b2e7bb 1442 if (isset($config['module_class_type'])) {
036e84c3 1443 $type->lti_module_class_type = $config['module_class_type'];
b9b2e7bb
CS
1444 }
1445
1446 return $type;
1447}
1448
ea04a9f9 1449function lti_prepare_type_for_save($type, $config) {
e3f69b58 1450 if (isset($config->lti_toolurl)) {
1451 $type->baseurl = $config->lti_toolurl;
1452 $type->tooldomain = lti_get_domain_from_url($config->lti_toolurl);
1453 }
cc193e0d
RW
1454 if (isset($config->lti_description)) {
1455 $type->description = $config->lti_description;
1456 }
e3f69b58 1457 if (isset($config->lti_typename)) {
1458 $type->name = $config->lti_typename;
1459 }
af9d3a92 1460 if (isset($config->lti_coursevisible)) {
692b5348 1461 $type->coursevisible = $config->lti_coursevisible;
af9d3a92 1462 }
e27cb316 1463
3f358828 1464 if (isset($config->lti_icon)) {
1465 $type->icon = $config->lti_icon;
1466 }
1467 if (isset($config->lti_secureicon)) {
1468 $type->secureicon = $config->lti_secureicon;
1469 }
1470
0c9c4669
SC
1471 $type->forcessl = !empty($config->lti_forcessl) ? $config->lti_forcessl : 0;
1472 $config->lti_forcessl = $type->forcessl;
e27cb316 1473
b9b2e7bb 1474 $type->timemodified = time();
e27cb316 1475
b9b2e7bb
CS
1476 unset ($config->lti_typename);
1477 unset ($config->lti_toolurl);
cc193e0d 1478 unset ($config->lti_description);
3f358828 1479 unset ($config->lti_icon);
1480 unset ($config->lti_secureicon);
b9b2e7bb
CS
1481}
1482
ea04a9f9 1483function lti_update_type($type, $config) {
3f358828 1484 global $DB, $CFG;
e27cb316 1485
b9b2e7bb 1486 lti_prepare_type_for_save($type, $config);
e27cb316 1487
3f358828 1488 $clearcache = false;
1489 if (lti_request_is_using_ssl() && !empty($type->secureicon)) {
1490 $clearcache = !isset($config->oldicon) || ($config->oldicon !== $type->secureicon);
1491 } else {
1492 $clearcache = isset($type->icon) && (!isset($config->oldicon) || ($config->oldicon !== $type->icon));
1493 }
1494 unset($config->oldicon);
1495
b9b2e7bb
CS
1496 if ($DB->update_record('lti_types', $type)) {
1497 foreach ($config as $key => $value) {
e3f69b58 1498 if (substr($key, 0, 4) == 'lti_' && !is_null($value)) {
1499 $record = new \StdClass();
b9b2e7bb
CS
1500 $record->typeid = $type->id;
1501 $record->name = substr($key, 4);
1502 $record->value = $value;
b9b2e7bb
CS
1503 lti_update_config($record);
1504 }
1505 }
3f358828 1506 require_once($CFG->libdir.'/modinfolib.php');
1507 if ($clearcache) {
1508 rebuild_course_cache();
1509 }
b9b2e7bb
CS
1510 }
1511}
1512
ea04a9f9 1513function lti_add_type($type, $config) {
b9b2e7bb 1514 global $USER, $SITE, $DB;
e27cb316 1515
b9b2e7bb 1516 lti_prepare_type_for_save($type, $config);
e27cb316 1517
ea04a9f9 1518 if (!isset($type->state)) {
b9b2e7bb
CS
1519 $type->state = LTI_TOOL_STATE_PENDING;
1520 }
e27cb316 1521
ea04a9f9 1522 if (!isset($type->timecreated)) {
b9b2e7bb
CS
1523 $type->timecreated = time();
1524 }
e27cb316 1525
ea04a9f9 1526 if (!isset($type->createdby)) {
b9b2e7bb
CS
1527 $type->createdby = $USER->id;
1528 }
e27cb316 1529
ea04a9f9 1530 if (!isset($type->course)) {
b9b2e7bb
CS
1531 $type->course = $SITE->id;
1532 }
e27cb316 1533
e3f69b58 1534 // Create a salt value to be used for signing passed data to extension services
1535 // The outcome service uses the service salt on the instance. This can be used
1536 // for communication with services not related to a specific LTI instance.
b9b2e7bb
CS
1537 $config->lti_servicesalt = uniqid('', true);
1538
1539 $id = $DB->insert_record('lti_types', $type);
1540
1541 if ($id) {
1542 foreach ($config as $key => $value) {
e3f69b58 1543 if (substr($key, 0, 4) == 'lti_' && !is_null($value)) {
1544 $record = new \StdClass();
b9b2e7bb
CS
1545 $record->typeid = $id;
1546 $record->name = substr($key, 4);
1547 $record->value = $value;
1548
1549 lti_add_config($record);
1550 }
1551 }
1552 }
e27cb316 1553
996b0fd9 1554 return $id;
b9b2e7bb
CS
1555}
1556
e3f69b58 1557/**
1558 * Given an array of tool proxies, filter them based on their state
1559 *
1560 * @param array $toolproxies An array of lti_tool_proxies records
1561 * @param int $state One of the LTI_TOOL_PROXY_STATE_* constants
1562 *
1563 * @return array
1564 */
1565function lti_filter_tool_proxy_types(array $toolproxies, $state) {
1566 $return = array();
1567 foreach ($toolproxies as $key => $toolproxy) {
1568 if ($toolproxy->state == $state) {
1569 $return[$key] = $toolproxy;
1570 }
1571 }
1572 return $return;
1573}
1574
1575/**
1576 * Get the tool proxy instance given its GUID
1577 *
1578 * @param string $toolproxyguid Tool proxy GUID value
1579 *
1580 * @return object
1581 */
1582function lti_get_tool_proxy_from_guid($toolproxyguid) {
1583 global $DB;
1584
1585 $toolproxy = $DB->get_record('lti_tool_proxies', array('guid' => $toolproxyguid));
1586
1587 return $toolproxy;
1588}
1589
cc193e0d
RW
1590/**
1591 * Get the tool proxy instance given its registration URL
1592 *
1593 * @param string $regurl Tool proxy registration URL
1594 *
af9d3a92 1595 * @return array The record of the tool proxy with this url
cc193e0d
RW
1596 */
1597function lti_get_tool_proxies_from_registration_url($regurl) {
1598 global $DB;
1599
1600 return $DB->get_records_sql(
1601 'SELECT * FROM {lti_tool_proxies}
1602 WHERE '.$DB->sql_compare_text('regurl', 256).' = :regurl',
1603 array('regurl' => $regurl)
1604 );
1605}
1606
e3f69b58 1607/**
1608 * Generates some of the tool proxy configuration based on the admin configuration details
1609 *
1610 * @param int $id
1611 *
1612 * @return Tool Proxy details
1613 */
1614function lti_get_tool_proxy($id) {
1615 global $DB;
1616
1617 $toolproxy = $DB->get_record('lti_tool_proxies', array('id' => $id));
1618 return $toolproxy;
1619}
1620
1621/**
1622 * Generates some of the tool proxy configuration based on the admin configuration details
1623 *
1624 * @param int $id
1625 *
1626 * @return Tool Proxy details
1627 */
1628function lti_get_tool_proxy_config($id) {
1629 $toolproxy = lti_get_tool_proxy($id);
1630
1631 $tp = new \stdClass();
1632 $tp->lti_registrationname = $toolproxy->name;
1633 $tp->toolproxyid = $toolproxy->id;
1634 $tp->state = $toolproxy->state;
1635 $tp->lti_registrationurl = $toolproxy->regurl;
1636 $tp->lti_capabilities = explode("\n", $toolproxy->capabilityoffered);
1637 $tp->lti_services = explode("\n", $toolproxy->serviceoffered);
1638
1639 return $tp;
1640}
1641
1642/**
1643 * Update the database with a tool proxy instance
1644 *
1645 * @param object $config Tool proxy definition
1646 *
1647 * @return int Record id number
1648 */
1649function lti_add_tool_proxy($config) {
1650 global $USER, $DB;
1651
1652 $toolproxy = new \stdClass();
1653 if (isset($config->lti_registrationname)) {
1654 $toolproxy->name = trim($config->lti_registrationname);
1655 }
1656 if (isset($config->lti_registrationurl)) {
1657 $toolproxy->regurl = trim($config->lti_registrationurl);
1658 }
1659 if (isset($config->lti_capabilities)) {
1660 $toolproxy->capabilityoffered = implode("\n", $config->lti_capabilities);
cc193e0d
RW
1661 } else {
1662 $toolproxy->capabilityoffered = implode("\n", array_keys(lti_get_capabilities()));
e3f69b58 1663 }
1664 if (isset($config->lti_services)) {
1665 $toolproxy->serviceoffered = implode("\n", $config->lti_services);
cc193e0d 1666 } else {
af9d3a92
JO
1667 $func = function($s) {
1668 return $s->get_id();
1669 };
cc193e0d
RW
1670 $servicenames = array_map($func, lti_get_services());
1671 $toolproxy->serviceoffered = implode("\n", $servicenames);
e3f69b58 1672 }
1673 if (isset($config->toolproxyid) && !empty($config->toolproxyid)) {
1674 $toolproxy->id = $config->toolproxyid;
1675 if (!isset($toolproxy->state) || ($toolproxy->state != LTI_TOOL_PROXY_STATE_ACCEPTED)) {
1676 $toolproxy->state = LTI_TOOL_PROXY_STATE_CONFIGURED;
1677 $toolproxy->guid = random_string();
1678 $toolproxy->secret = random_string();
1679 }
1680 $id = lti_update_tool_proxy($toolproxy);
1681 } else {
1682 $toolproxy->state = LTI_TOOL_PROXY_STATE_CONFIGURED;
1683 $toolproxy->timemodified = time();
1684 $toolproxy->timecreated = $toolproxy->timemodified;
1685 if (!isset($toolproxy->createdby)) {
1686 $toolproxy->createdby = $USER->id;
1687 }
1688 $toolproxy->guid = random_string();
1689 $toolproxy->secret = random_string();
1690 $id = $DB->insert_record('lti_tool_proxies', $toolproxy);
1691 }
1692
1693 return $id;
1694}
1695
1696/**
1697 * Updates a tool proxy in the database
1698 *
1699 * @param object $toolproxy Tool proxy
1700 *
1701 * @return int Record id number
1702 */
1703function lti_update_tool_proxy($toolproxy) {
1704 global $DB;
1705
1706 $toolproxy->timemodified = time();
1707 $id = $DB->update_record('lti_tool_proxies', $toolproxy);
1708
1709 return $id;
1710}
1711
1712/**
1713 * Delete a Tool Proxy
1714 *
1715 * @param int $id Tool Proxy id
1716 */
1717function lti_delete_tool_proxy($id) {
1718 global $DB;
1719 $DB->delete_records('lti_tool_settings', array('toolproxyid' => $id));
1720 $tools = $DB->get_records('lti_types', array('toolproxyid' => $id));
1721 foreach ($tools as $tool) {
1722 lti_delete_type($tool->id);
1723 }
1724 $DB->delete_records('lti_tool_proxies', array('id' => $id));
1725}
1726
b9b2e7bb
CS
1727/**
1728 * Add a tool configuration in the database
1729 *
e3f69b58 1730 * @param object $config Tool configuration
b9b2e7bb
CS
1731 *
1732 * @return int Record id number
1733 */
1734function lti_add_config($config) {
1735 global $DB;
1736
1737 return $DB->insert_record('lti_types_config', $config);
1738}
1739
1740/**
1741 * Updates a tool configuration in the database
1742 *
e3f69b58 1743 * @param object $config Tool configuration
b9b2e7bb
CS
1744 *
1745 * @return Record id number
1746 */
1747function lti_update_config($config) {
1748 global $DB;
1749
1750 $return = true;
1751 $old = $DB->get_record('lti_types_config', array('typeid' => $config->typeid, 'name' => $config->name));
e27cb316 1752
b9b2e7bb
CS
1753 if ($old) {
1754 $config->id = $old->id;
1755 $return = $DB->update_record('lti_types_config', $config);
1756 } else {
1757 $return = $DB->insert_record('lti_types_config', $config);
1758 }
1759 return $return;
1760}
1761
e3f69b58 1762/**
1763 * Gets the tool settings
1764 *
1765 * @param int $toolproxyid Id of tool proxy record
1766 * @param int $courseid Id of course (null if system settings)
1767 * @param int $instanceid Id of course module (null if system or context settings)
1768 *
1769 * @return array Array settings
1770 */
1771function lti_get_tool_settings($toolproxyid, $courseid = null, $instanceid = null) {
1772 global $DB;
1773
1774 $settings = array();
1775 $settingsstr = $DB->get_field('lti_tool_settings', 'settings', array('toolproxyid' => $toolproxyid,
1776 'course' => $courseid, 'coursemoduleid' => $instanceid));
1777 if ($settingsstr !== false) {
1778 $settings = json_decode($settingsstr, true);
1779 }
1780 return $settings;
1781}
1782
1783/**
1784 * Sets the tool settings (
1785 *
1786 * @param array $settings Array of settings
1787 * @param int $toolproxyid Id of tool proxy record
1788 * @param int $courseid Id of course (null if system settings)
1789 * @param int $instanceid Id of course module (null if system or context settings)
1790 */
1791function lti_set_tool_settings($settings, $toolproxyid, $courseid = null, $instanceid = null) {
1792 global $DB;
1793
1794 $json = json_encode($settings);
1795 $record = $DB->get_record('lti_tool_settings', array('toolproxyid' => $toolproxyid,
1796 'course' => $courseid, 'coursemoduleid' => $instanceid));
1797 if ($record !== false) {
1798 $DB->update_record('lti_tool_settings', array('id' => $record->id, 'settings' => $json, 'timemodified' => time()));
1799 } else {
1800 $record = new \stdClass();
1801 $record->toolproxyid = $toolproxyid;
1802 $record->course = $courseid;
1803 $record->coursemoduleid = $instanceid;
1804 $record->settings = $json;
1805 $record->timecreated = time();
1806 $record->timemodified = $record->timecreated;
1807 $DB->insert_record('lti_tool_settings', $record);
1808 }
1809}
1810
b9b2e7bb
CS
1811/**
1812 * Signs the petition to launch the external tool using OAuth
1813 *
1814 * @param $oldparms Parameters to be passed for signing
1815 * @param $endpoint url of the external tool
1816 * @param $method Method for sending the parameters (e.g. POST)
1817 * @param $oauth_consumoer_key Key
1818 * @param $oauth_consumoer_secret Secret
b9b2e7bb 1819 */
3dd9ca24 1820function lti_sign_parameters($oldparms, $endpoint, $method, $oauthconsumerkey, $oauthconsumersecret) {
e3f69b58 1821
b9b2e7bb 1822 $parms = $oldparms;
b9b2e7bb
CS
1823
1824 $testtoken = '';
e27cb316 1825
e3f69b58 1826 // TODO: Switch to core oauthlib once implemented - MDL-30149.
795dff01
CS
1827 $hmacmethod = new lti\OAuthSignatureMethod_HMAC_SHA1();
1828 $testconsumer = new lti\OAuthConsumer($oauthconsumerkey, $oauthconsumersecret, null);
795dff01 1829 $accreq = lti\OAuthRequest::from_consumer_and_token($testconsumer, $testtoken, $method, $endpoint, $parms);
b9b2e7bb
CS
1830 $accreq->sign_request($hmacmethod, $testconsumer, $testtoken);
1831
b9b2e7bb
CS
1832 $newparms = $accreq->get_parameters();
1833
1834 return $newparms;
1835}
1836
1837/**
1838 * Posts the launch petition HTML
1839 *
1840 * @param $newparms Signed parameters
1841 * @param $endpoint URL of the external tool
1842 * @param $debug Debug (true/false)
1843 */
dbb0fec9 1844function lti_post_launch_html($newparms, $endpoint, $debug=false) {
e3f69b58 1845 $r = "<form action=\"" . $endpoint .
1846 "\" name=\"ltiLaunchForm\" id=\"ltiLaunchForm\" method=\"post\" encType=\"application/x-www-form-urlencoded\">\n";
e27cb316 1847
e3f69b58 1848 // Contruct html for the launch parameters.
b9b2e7bb
CS
1849 foreach ($newparms as $key => $value) {
1850 $key = htmlspecialchars($key);
1851 $value = htmlspecialchars($value);
1852 if ( $key == "ext_submit" ) {
e3f69b58 1853 $r .= "<input type=\"submit\"";
b9b2e7bb 1854 } else {
e3f69b58 1855 $r .= "<input type=\"hidden\" name=\"{$key}\"";
b9b2e7bb 1856 }
e3f69b58 1857 $r .= " value=\"";
b9b2e7bb
CS
1858 $r .= $value;
1859 $r .= "\"/>\n";
1860 }
1861
1862 if ( $debug ) {
1863 $r .= "<script language=\"javascript\"> \n";
1864 $r .= " //<![CDATA[ \n";
1865 $r .= "function basicltiDebugToggle() {\n";
1866 $r .= " var ele = document.getElementById(\"basicltiDebug\");\n";
ea04a9f9 1867 $r .= " if (ele.style.display == \"block\") {\n";
b9b2e7bb
CS
1868 $r .= " ele.style.display = \"none\";\n";
1869 $r .= " }\n";
1870 $r .= " else {\n";
1871 $r .= " ele.style.display = \"block\";\n";
1872 $r .= " }\n";
1873 $r .= "} \n";
1874 $r .= " //]]> \n";
1875 $r .= "</script>\n";
1876 $r .= "<a id=\"displayText\" href=\"javascript:basicltiDebugToggle();\">";
1877 $r .= get_string("toggle_debug_data", "lti")."</a>\n";
1878 $r .= "<div id=\"basicltiDebug\" style=\"display:none\">\n";
e3f69b58 1879 $r .= "<b>".get_string("basiclti_endpoint", "lti")."</b><br/>\n";
b9b2e7bb 1880 $r .= $endpoint . "<br/>\n&nbsp;<br/>\n";
e3f69b58 1881 $r .= "<b>".get_string("basiclti_parameters", "lti")."</b><br/>\n";
b9b2e7bb
CS
1882 foreach ($newparms as $key => $value) {
1883 $key = htmlspecialchars($key);
1884 $value = htmlspecialchars($value);
1885 $r .= "$key = $value<br/>\n";
1886 }
1887 $r .= "&nbsp;<br/>\n";
b9b2e7bb
CS
1888 $r .= "</div>\n";
1889 }
1890 $r .= "</form>\n";
1891
1892 if ( ! $debug ) {
b9b2e7bb
CS
1893 $r .= " <script type=\"text/javascript\"> \n" .
1894 " //<![CDATA[ \n" .
b9b2e7bb
CS
1895 " document.ltiLaunchForm.submit(); \n" .
1896 " //]]> \n" .
1897 " </script> \n";
1898 }
1899 return $r;
1900}
1901
ea04a9f9 1902function lti_get_type($typeid) {
996b0fd9 1903 global $DB;
e27cb316 1904
996b0fd9 1905 return $DB->get_record('lti_types', array('id' => $typeid));
57d1dffd
CS
1906}
1907
ea04a9f9
EL
1908function lti_get_launch_container($lti, $toolconfig) {
1909 if (empty($lti->launchcontainer)) {
e27cb316
CS
1910 $lti->launchcontainer = LTI_LAUNCH_CONTAINER_DEFAULT;
1911 }
1912
ea04a9f9
EL
1913 if ($lti->launchcontainer == LTI_LAUNCH_CONTAINER_DEFAULT) {
1914 if (isset($toolconfig['launchcontainer'])) {
c4d80efe
CS
1915 $launchcontainer = $toolconfig['launchcontainer'];
1916 }
1917 } else {
1918 $launchcontainer = $lti->launchcontainer;
1919 }
e27cb316 1920
ea04a9f9 1921 if (empty($launchcontainer) || $launchcontainer == LTI_LAUNCH_CONTAINER_DEFAULT) {
c4d80efe
CS
1922 $launchcontainer = LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS;
1923 }
57d1dffd 1924
c3d2fbf9 1925 $devicetype = core_useragent::get_device_type();
57d1dffd 1926
e3f69b58 1927 // Scrolling within the object element doesn't work on iOS or Android
1928 // Opening the popup window also had some issues in testing
1929 // For mobile devices, always take up the entire screen to ensure the best experience.
c3d2fbf9 1930 if ($devicetype === core_useragent::DEVICETYPE_MOBILE || $devicetype === core_useragent::DEVICETYPE_TABLET ) {
57d1dffd
CS
1931 $launchcontainer = LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW;
1932 }
e27cb316 1933
57d1dffd 1934 return $launchcontainer;
d8d04121
CS
1935}
1936
1937function lti_request_is_using_ssl() {
33dca156
PS
1938 global $CFG;
1939 return (stripos($CFG->httpswwwroot, 'https://') === 0);
d8d04121
CS
1940}
1941
ea04a9f9
EL
1942function lti_ensure_url_is_https($url) {
1943 if (!strstr($url, '://')) {
d8d04121
CS
1944 $url = 'https://' . $url;
1945 } else {
e3f69b58 1946 // If the URL starts with http, replace with https.
ea04a9f9 1947 if (stripos($url, 'http://') === 0) {
adc23cc9 1948 $url = 'https://' . substr($url, 7);
d8d04121
CS
1949 }
1950 }
e27cb316 1951
d8d04121 1952 return $url;
61eb12d4 1953}
8fa50fdd
MN
1954
1955/**
1956 * Determines if we should try to log the request
1957 *
1958 * @param string $rawbody
1959 * @return bool
1960 */
1961function lti_should_log_request($rawbody) {
1962 global $CFG;
1963
1964 if (empty($CFG->mod_lti_log_users)) {
1965 return false;
1966 }
1967
1968 $logusers = explode(',', $CFG->mod_lti_log_users);
1969 if (empty($logusers)) {
1970 return false;
1971 }
1972
1973 try {
e3f69b58 1974 $xml = new \SimpleXMLElement($rawbody);
8fa50fdd
MN
1975 $ns = $xml->getNamespaces();
1976 $ns = array_shift($ns);
1977 $xml->registerXPathNamespace('lti', $ns);
1978 $requestuserid = '';
1979 if ($node = $xml->xpath('//lti:userId')) {
1980 $node = $node[0];
1981 $requestuserid = clean_param((string) $node, PARAM_INT);
1982 } else if ($node = $xml->xpath('//lti:sourcedId')) {
1983 $node = $node[0];
1984 $resultjson = json_decode((string) $node);
1985 $requestuserid = clean_param($resultjson->data->userid, PARAM_INT);
1986 }
1987 } catch (Exception $e) {
1988 return false;
1989 }
1990
1991 if (empty($requestuserid) or !in_array($requestuserid, $logusers)) {
1992 return false;
1993 }
1994
1995 return true;
1996}
1997
1998/**
233b677b 1999 * Logs the request to a file in temp dir.
8fa50fdd
MN
2000 *
2001 * @param string $rawbody
2002 */
2003function lti_log_request($rawbody) {
2004 if ($tempdir = make_temp_directory('mod_lti', false)) {
2005 if ($tempfile = tempnam($tempdir, 'mod_lti_request'.date('YmdHis'))) {
00e27060
MN
2006 $content = "Request Headers:\n";
2007 foreach (moodle\mod\lti\OAuthUtil::get_headers() as $header => $value) {
2008 $content .= "$header: $value\n";
2009 }
2010 $content .= "Request Body:\n";
2011 $content .= $rawbody;
2012
2013 file_put_contents($tempfile, $content);
2014 chmod($tempfile, 0644);
2015 }
2016 }
2017}
2018
2019/**
233b677b 2020 * Log an LTI response.
00e27060
MN
2021 *
2022 * @param string $responsexml The response XML
2023 * @param Exception $e If there was an exception, pass that too
2024 */
2025function lti_log_response($responsexml, $e = null) {
2026 if ($tempdir = make_temp_directory('mod_lti', false)) {
2027 if ($tempfile = tempnam($tempdir, 'mod_lti_response'.date('YmdHis'))) {
2028 $content = '';
2029 if ($e instanceof Exception) {
2030 $info = get_exception_info($e);
2031
2032 $content .= "Exception:\n";
2033 $content .= "Message: $info->message\n";
2034 $content .= "Debug info: $info->debuginfo\n";
2035 $content .= "Backtrace:\n";
2036 $content .= format_backtrace($info->backtrace, true);
2037 $content .= "\n";
2038 }
2039 $content .= "Response XML:\n";
2040 $content .= $responsexml;
2041
2042 file_put_contents($tempfile, $content);
8fa50fdd
MN
2043 chmod($tempfile, 0644);
2044 }
2045 }
2046}
2047
2048/**
2049 * Fetches LTI type configuration for an LTI instance
2050 *
2051 * @param stdClass $instance
2052 * @return array Can be empty if no type is found
2053 */
2054function lti_get_type_config_by_instance($instance) {
2055 $typeid = null;
2056 if (empty($instance->typeid)) {
2057 $tool = lti_get_tool_by_url_match($instance->toolurl, $instance->course);
2058 if ($tool) {
2059 $typeid = $tool->id;
2060 }
2061 } else {
2062 $typeid = $instance->typeid;
2063 }
2064 if (!empty($typeid)) {
2065 return lti_get_type_config($typeid);
2066 }
2067 return array();
2068}
2069
2070/**
2071 * Enforce type config settings onto the LTI instance
2072 *
2073 * @param stdClass $instance
2074 * @param array $typeconfig
2075 */
2076function lti_force_type_config_settings($instance, array $typeconfig) {
2077 $forced = array(
2078 'instructorchoicesendname' => 'sendname',
2079 'instructorchoicesendemailaddr' => 'sendemailaddr',
2080 'instructorchoiceacceptgrades' => 'acceptgrades',
2081 );
2082
2083 foreach ($forced as $instanceparam => $typeconfigparam) {
2084 if (array_key_exists($typeconfigparam, $typeconfig) && $typeconfig[$typeconfigparam] != LTI_SETTING_DELEGATE) {
2085 $instance->$instanceparam = $typeconfig[$typeconfigparam];
2086 }
2087 }
2088}
2089
e3f69b58 2090/**
2091 * Initializes an array with the capabilities supported by the LTI module
2092 *
2093 * @return array List of capability names (without a dollar sign prefix)
2094 */
2095function lti_get_capabilities() {
2096
2097 $capabilities = array(
2098 'basic-lti-launch-request' => '',
2099 'Context.id' => 'context_id',
2100 'CourseSection.title' => 'context_title',
2101 'CourseSection.label' => 'context_label',
2102 'CourseSection.sourcedId' => 'lis_course_section_sourcedid',
2103 'CourseSection.longDescription' => '$COURSE->summary',
2104 'CourseSection.timeFrame.begin' => '$COURSE->startdate',
2105 'ResourceLink.id' => 'resource_link_id',
2106 'ResourceLink.title' => 'resource_link_title',
2107 'ResourceLink.description' => 'resource_link_description',
2108 'User.id' => 'user_id',
2109 'User.username' => '$USER->username',
2110 'Person.name.full' => 'lis_person_name_full',
2111 'Person.name.given' => 'lis_person_name_given',
2112 'Person.name.family' => 'lis_person_name_family',
2113 'Person.email.primary' => 'lis_person_contact_email_primary',
2114 'Person.sourcedId' => 'lis_person_sourcedid',
2115 'Person.name.middle' => '$USER->middlename',
2116 'Person.address.street1' => '$USER->address',
2117 'Person.address.locality' => '$USER->city',
2118 'Person.address.country' => '$USER->country',
2119 'Person.address.timezone' => '$USER->timezone',
2120 'Person.phone.primary' => '$USER->phone1',
2121 'Person.phone.mobile' => '$USER->phone2',
2122 'Person.webaddress' => '$USER->url',
2123 'Membership.role' => 'roles',
2124 'Result.sourcedId' => 'lis_result_sourcedid',
2125 'Result.autocreate' => 'lis_outcome_service_url');
2126
2127 return $capabilities;
2128
2129}
2130
2131/**
2132 * Initializes an array with the services supported by the LTI module
2133 *
2134 * @return array List of services
2135 */
2136function lti_get_services() {
2137
2138 $services = array();
2139 $definedservices = core_component::get_plugin_list('ltiservice');
2140 foreach ($definedservices as $name => $location) {
2141 $classname = "\\ltiservice_{$name}\\local\\service\\{$name}";
2142 $services[] = new $classname();
2143 }
2144
2145 return $services;
2146
2147}
2148
2149/**
2150 * Initializes an instance of the named service
2151 *
2152 * @param string $servicename Name of service
2153 *
2154 * @return mod_lti\local\ltiservice\service_base Service
2155 */
2156function lti_get_service_by_name($servicename) {
2157
2158 $service = false;
2159 $classname = "\\ltiservice_{$servicename}\\local\\service\\{$servicename}";
2160 if (class_exists($classname)) {
2161 $service = new $classname();
2162 }
2163
2164 return $service;
2165
2166}
2167
2168/**
2169 * Finds a service by id
2170 *
2171 * @param array $services Array of services
2172 * @param string $resourceid ID of resource
2173 *
2174 * @return mod_lti\local\ltiservice\service_base Service
2175 */
2176function lti_get_service_by_resource_id($services, $resourceid) {
2177
2178 $service = false;
2179 foreach ($services as $aservice) {
2180 foreach ($aservice->get_resources() as $resource) {
2181 if ($resource->get_id() === $resourceid) {
2182 $service = $aservice;
2183 break 2;
2184 }
2185 }
2186 }
2187
2188 return $service;
2189
2190}
2191
2192/**
2193 * Extracts the named contexts from a tool proxy
2194 *
2195 * @param object $json
2196 *
2197 * @return array Contexts
2198 */
2199function lti_get_contexts($json) {
2200
2201 $contexts = array();
2202 if (isset($json->{'@context'})) {
2203 foreach ($json->{'@context'} as $context) {
2204 if (is_object($context)) {
2205 $contexts = array_merge(get_object_vars($context), $contexts);
2206 }
2207 }
2208 }
2209
2210 return $contexts;
2211
2212}
2213
2214/**
2215 * Converts an ID to a fully-qualified ID
2216 *
2217 * @param array $contexts
2218 * @param string $id
2219 *
2220 * @return string Fully-qualified ID
2221 */
2222function lti_get_fqid($contexts, $id) {
2223
2224 $parts = explode(':', $id, 2);
2225 if (count($parts) > 1) {
2226 if (array_key_exists($parts[0], $contexts)) {
2227 $id = $contexts[$parts[0]] . $parts[1];
2228 }
2229 }
2230
2231 return $id;
2232
2233}
cc193e0d 2234
af9d3a92
JO
2235/**
2236 * Returns the icon for the given tool type
2237 *
2238 * @param stdClass $type The tool type
2239 *
2240 * @return string The url to the tool type's corresponding icon
2241 */
cc193e0d
RW
2242function get_tool_type_icon_url(stdClass $type) {
2243 global $OUTPUT;
2244
2245 $iconurl = $type->secureicon;
2246
2247 if (empty($iconurl)) {
2248 $iconurl = $type->icon;
2249 }
2250
2251 if (empty($iconurl)) {
cc193e0d
RW
2252 $iconurl = $OUTPUT->pix_url('icon', 'lti')->out();
2253 }
2254
2255 return $iconurl;
2256}
2257
af9d3a92
JO
2258/**
2259 * Returns the edit url for the given tool type
2260 *
2261 * @param stdClass $type The tool type
2262 *
2263 * @return string The url to edit the tool type
2264 */
cc193e0d 2265function get_tool_type_edit_url(stdClass $type) {
af9d3a92
JO
2266 $url = new moodle_url('/mod/lti/typessettings.php',
2267 array('action' => 'update', 'id' => $type->id, 'sesskey' => sesskey(), 'returnto' => 'toolconfigure'));
cc193e0d
RW
2268 return $url->out();
2269}
2270
af9d3a92
JO
2271/**
2272 * Returns the course url for the given tool type
2273 *
2274 * @param stdClass $type The tool type
2275 *
2276 * @return string|void The url to the course of the tool type, void if it is a site wide type
2277 */
cc193e0d
RW
2278function get_tool_type_course_url(stdClass $type) {
2279 if ($type->course == 1) {
2280 return;
2281 } else {
2282 $url = new moodle_url('/course/view.php', array('id' => $type->course));
2283 return $url->out();
2284 }
2285}
2286
af9d3a92
JO
2287/**
2288 * Returns the icon and edit urls for the tool type and the course url if it is a course type.
2289 *
2290 * @param stdClass $type The tool type
2291 *
2292 * @return string The url to the course of the tool type
2293 */
cc193e0d
RW
2294function get_tool_type_urls(stdClass $type) {
2295 $courseurl = get_tool_type_course_url($type);
2296
2297 $urls = array(
2298 'icon' => get_tool_type_icon_url($type),
2299 'edit' => get_tool_type_edit_url($type),
2300 );
2301
2302 if ($courseurl) {
2303 $urls['course'] = $courseurl;
2304 }
2305
2306 return $urls;
2307}
2308
af9d3a92
JO
2309/**
2310 * Returns information on the current state of the tool type
2311 *
2312 * @param stdClass $type The tool type
2313 *
2314 * @return array An array with a text description of the state, and boolean for whether it is in each state:
2315 * pending, configured, rejected, unknown
2316 */
cc193e0d 2317function get_tool_type_state_info(stdClass $type) {
cc193e0d
RW
2318 $state = '';
2319 $isconfigured = false;
2320 $ispending = false;
cc193e0d
RW
2321 $isrejected = false;
2322 $isunknown = false;
2323 switch ($type->state) {
cc193e0d 2324 case LTI_TOOL_STATE_CONFIGURED:
af9d3a92 2325 $state = get_string('active', 'mod_lti');
cc193e0d
RW
2326 $isconfigured = true;
2327 break;
2328 case LTI_TOOL_STATE_PENDING:
af9d3a92 2329 $state = get_string('pending', 'mod_lti');
cc193e0d
RW
2330 $ispending = true;
2331 break;
2332 case LTI_TOOL_STATE_REJECTED:
af9d3a92 2333 $state = get_string('rejected', 'mod_lti');
cc193e0d
RW
2334 $isrejected = true;
2335 break;
2336 default:
af9d3a92 2337 $state = get_string('unknownstate', 'mod_lti');
cc193e0d
RW
2338 $isunknown = true;
2339 break;
2340 }
2341
2342 return array(
2343 'text' => $state,
2344 'pending' => $ispending,
2345 'configured' => $isconfigured,
2346 'rejected' => $isrejected,
cc193e0d
RW
2347 'unknown' => $isunknown
2348 );
2349}
2350
af9d3a92
JO
2351/**
2352 * Returns a summary of each LTI capability this tool type requires in plain language
2353 *
2354 * @param stdClass $type The tool type
2355 *
2356 * @return array An array of text descriptions of each of the capabilities this tool type requires
2357 */
cc193e0d
RW
2358function get_tool_type_capability_groups($type) {
2359 $capabilities = lti_get_enabled_capabilities($type);
2360 $groups = array();
af9d3a92
JO
2361 $hascourse = false;
2362 $hasactivities = false;
2363 $hasuseraccount = false;
2364 $hasuserpersonal = false;
cc193e0d 2365
cc193e0d
RW
2366 foreach ($capabilities as $capability) {
2367 // Bail out early if we've already found all groups.
2368 if (count($groups) >= 4) {
2369 continue;
2370 }
2371
af9d3a92
JO
2372 if (!$hascourse && preg_match('/^CourseSection/', $capability)) {
2373 $hascourse = true;
2374 $groups[] = get_string('courseinformation', 'mod_lti');
2375 } else if (!$hasactivities && preg_match('/^ResourceLink/', $capability)) {
2376 $hasactivities = true;
2377 $groups[] = get_string('courseactivitiesorresources', 'mod_lti');
2378 } else if (!$hasuseraccount && preg_match('/^User/', $capability) || preg_match('/^Membership/', $capability)) {
2379 $hasuseraccount = true;
2380 $groups[] = get_string('useraccountinformation', 'mod_lti');
2381 } else if (!$hasuserpersonal && preg_match('/^Person/', $capability)) {
2382 $hasuserpersonal = true;
2383 $groups[] = get_string('userpersonalinformation', 'mod_lti');
cc193e0d
RW
2384 }
2385 }
2386
2387 return $groups;
2388}
2389
af9d3a92
JO
2390
2391/**
2392 * Returns the ids of each instance of this tool type
2393 *
2394 * @param stdClass $type The tool type
2395 *
2396 * @return array An array of ids of the instances of this tool type
2397 */
cc193e0d
RW
2398function get_tool_type_instance_ids($type) {
2399 global $DB;
2400
af9d3a92 2401 return array_keys($DB->get_fieldset_select('lti', 'id', 'typeid = ?', array($type->id)));
cc193e0d
RW
2402}
2403
af9d3a92
JO
2404/**
2405 * Serialises this tool type
2406 *
2407 * @param stdClass $type The tool type
2408 *
2409 * @return array An array of values representing this type
2410 */
cc193e0d
RW
2411function serialise_tool_type(stdClass $type) {
2412 $capabilitygroups = get_tool_type_capability_groups($type);
2413 $instanceids = get_tool_type_instance_ids($type);
2414
cc193e0d
RW
2415 return array(
2416 'id' => $type->id,
2417 'name' => $type->name,
af9d3a92 2418 'description' => isset($type->description) ? $type->description : get_string('editdescription', 'mod_lti'),
cc193e0d
RW
2419 'urls' => get_tool_type_urls($type),
2420 'state' => get_tool_type_state_info($type),
2421 'hascapabilitygroups' => !empty($capabilitygroups),
2422 'capabilitygroups' => $capabilitygroups,
2423 // Course ID of 1 means it's not linked to a course.
2424 'courseid' => $type->course == 1 ? 0 : $type->course,
2425 'instanceids' => $instanceids,
2426 'instancecount' => count($instanceids)
2427 );
2428}
af9d3a92
JO
2429
2430/**
2431 * Loads the cartridge information into the tool type, if the launch url is for a cartridge file
2432 *
2433 * @param stdClass $type The tool type object to be filled in
2434 * @since Moodle 3.1
2435 */
2436function lti_load_type_if_cartridge($type) {
2437 if (!empty($type->lti_toolurl) && lti_is_cartridge($type->lti_toolurl)) {
2438 lti_load_type_from_cartridge($type->lti_toolurl, $type);
2439 }
2440}
2441
2442/**
2443 * Loads the cartridge information into the new tool, if the launch url is for a cartridge file
2444 *
2445 * @param stdClass $lti The tools config
2446 * @since Moodle 3.1
2447 */
2448function lti_load_tool_if_cartridge($lti) {
2449 if (!empty($lti->toolurl) && lti_is_cartridge($lti->toolurl)) {
2450 lti_load_tool_from_cartridge($lti->toolurl, $lti);
2451 }
2452}
2453
2454/**
2455 * Determines if the given url is for a IMS basic cartridge
2456 *
2457 * @param string $url The url to be checked
2458 * @return True if the url is for a cartridge
2459 * @since Moodle 3.1
2460 */
2461function lti_is_cartridge($url) {
2462 // If it is empty, it's not a cartridge.
2463 if (empty($url)) {
2464 return false;
2465 }
2466 // If it has xml at the end of the url, it's a cartridge.
2467 if (preg_match('/\.xml$/', $url)) {
2468 return true;
2469 }
2470 // Even if it doesn't have .xml, load the url to check if it's a cartridge..
2471 try {
2472 $toolinfo = lti_load_cartridge($url,
2473 array(
2474 "launch_url" => "launchurl"
2475 )
2476 );
2477 if (!empty($toolinfo['launchurl'])) {
2478 return true;
2479 }
2480 } catch (moodle_exception $e) {
2481 return false; // Error loading the xml, so it's not a cartridge.
2482 }
2483 return false;
2484}
2485
2486/**
2487 * Allows you to load settings for an external tool type from an IMS cartridge.
2488 *
2489 * @param string $url The URL to the cartridge
2490 * @param stdClass $type The tool type object to be filled in
2491 * @throws moodle_exception if the cartridge could not be loaded correctly
2492 * @since Moodle 3.1
2493 */
2494function lti_load_type_from_cartridge($url, $type) {
2495 $toolinfo = lti_load_cartridge($url,
2496 array(
2497 "title" => "lti_typename",
2498 "launch_url" => "lti_toolurl",
2499 "description" => "lti_description"
2500 ),
2501 array(
2502 "icon_url" => "lti_icon",
2503 "secure_icon_url" => "lti_secureicon"
2504 )
2505 );
2506 foreach ($toolinfo as $property => $value) {
2507 $type->$property = $value;
2508 }
2509}
2510
2511/**
2512 * Allows you to load in the configuration for an external tool from an IMS cartridge.
2513 *
2514 * @param string $url The URL to the cartridge
2515 * @param stdClass $lti LTI object
2516 * @throws moodle_exception if the cartridge could not be loaded correctly
2517 * @since Moodle 3.1
2518 */
2519function lti_load_tool_from_cartridge($url, $lti) {
2520 $toolinfo = lti_load_cartridge($url,
2521 array(
2522 "title" => "name",
2523 "launch_url" => "toolurl",
2524 "secure_launch_url" => "securetoolurl",
2525 "description" => "intro"
2526 ),
2527 array(
2528 "icon_url" => "icon",
2529 "secure_icon_url" => "secureicon"
2530 )
2531 );
2532 foreach ($toolinfo as $property => $value) {
2533 $lti->$property = $value;
2534 }
2535}
2536
2537/**
2538 * Search for a tag within an XML DOMDocument
2539 *
2540 * @param string $url The url of the cartridge to be loaded
2541 * @param array $map The map of tags to keys in the return array
2542 * @param array $propertiesmap The map of properties to keys in the return array
2543 * @return array An associative array with the given keys and their values from the cartridge
2544 * @throws moodle_exception if the cartridge could not be loaded correctly
2545 * @since Moodle 3.1
2546 */
2547function lti_load_cartridge($url, $map, $propertiesmap = array()) {
2548 global $CFG;
2549 require_once($CFG->libdir. "/filelib.php");
2550 // TODO MDL-46023 Replace this code with a call to the new library.
2551 $origentity = libxml_disable_entity_loader(true);
2552
2553 $curl = new curl();
2554 $response = $curl->get($url);
2555
2556 $document = new DOMDocument();
2557 @$document->loadXML($response, LIBXML_DTDLOAD | LIBXML_DTDATTR);
2558
2559 $cartridge = new DomXpath($document);
2560
2561 $errors = libxml_get_errors();
2562 if (count($errors) > 0) {
2563 $message = 'Failed to load cartridge.';
2564 foreach ($errors as $error) {
2565 $message .= "\n" . trim($error->message, "\n\r\t .") . " at line " . $error->line;
2566 }
2567 throw new moodle_exception($message);
2568 }
2569
2570 $toolinfo = array();
2571 foreach ($map as $tag => $key) {
2572 $value = get_tag($tag, $cartridge);
2573 if ($value) {
2574 $toolinfo[$key] = $value;
2575 }
2576 }
2577 if (!empty($propertiesmap)) {
2578 foreach ($propertiesmap as $property => $key) {
2579 $value = get_tag("property", $cartridge, $property);
2580 if ($value) {
2581 $toolinfo[$key] = $value;
2582 }
2583 }
2584 }
2585 libxml_disable_entity_loader($origentity);
2586 return $toolinfo;
2587}
2588
2589/**
2590 * Search for a tag within an XML DOMDocument
2591 *
2592 * @param stdClass $tagname The name of the tag to search for
2593 * @param XPath $xpath The XML to find the tag in
2594 * @param XPath $attribute The attribute to search for (if we should search for a child node with the given
2595 * value for the name attribute
2596 * @since Moodle 3.1
2597 */
2598function get_tag($tagname, $xpath, $attribute = null) {
2599 if ($attribute) {
2600 $result = $xpath->query('//*[local-name() = \'' . $tagname . '\'][@name="' . $attribute . '"]');
2601 } else {
2602 $result = $xpath->query('//*[local-name() = \'' . $tagname . '\']');
2603 }
2604 if ($result->length > 0) {
2605 return $result->item(0)->nodeValue;
2606 }
2607 return null;
2608}