Commit | Line | Data |
---|---|---|
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 | |
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 | * |
61eb12d4 CS |
38 | * @package mod |
39 | * @subpackage lti | |
40 | * @copyright 2009 Marc Alier, Jordi Piguillem, Nikolas Galanis | |
b9b2e7bb | 41 | * marc.alier@upc.edu |
61eb12d4 CS |
42 | * @copyright 2009 Universitat Politecnica de Catalunya http://www.upc.edu |
43 | * @author Marc Alier | |
44 | * @author Jordi Piguillem | |
45 | * @author Nikolas Galanis | |
8f45215d | 46 | * @author Chris Scribner |
61eb12d4 | 47 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
b9b2e7bb CS |
48 | */ |
49 | ||
50 | defined('MOODLE_INTERNAL') || die; | |
51 | ||
fabd4fcf | 52 | // TODO: Switch to core oauthlib once implemented - MDL-30149 |
795dff01 CS |
53 | use moodle\mod\lti as lti; |
54 | ||
b9b2e7bb CS |
55 | require_once($CFG->dirroot.'/mod/lti/OAuth.php'); |
56 | ||
57 | define('LTI_URL_DOMAIN_REGEX', '/(?:https?:\/\/)?(?:www\.)?([^\/]+)(?:\/|$)/i'); | |
58 | ||
59 | define('LTI_LAUNCH_CONTAINER_DEFAULT', 1); | |
60 | define('LTI_LAUNCH_CONTAINER_EMBED', 2); | |
61 | define('LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS', 3); | |
62 | define('LTI_LAUNCH_CONTAINER_WINDOW', 4); | |
cca9d3f7 | 63 | define('LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW', 5); |
b9b2e7bb CS |
64 | |
65 | define('LTI_TOOL_STATE_ANY', 0); | |
66 | define('LTI_TOOL_STATE_CONFIGURED', 1); | |
67 | define('LTI_TOOL_STATE_PENDING', 2); | |
68 | define('LTI_TOOL_STATE_REJECTED', 3); | |
69 | ||
70 | define('LTI_SETTING_NEVER', 0); | |
71 | define('LTI_SETTING_ALWAYS', 1); | |
5e078d62 | 72 | define('LTI_SETTING_DELEGATE', 2); |
b9b2e7bb CS |
73 | |
74 | /** | |
75 | * Prints a Basic LTI activity | |
76 | * | |
77 | * $param int $basicltiid Basic LTI activity id | |
78 | */ | |
67ddf847 | 79 | function lti_view($instance) { |
b9b2e7bb CS |
80 | global $PAGE, $CFG; |
81 | ||
ea04a9f9 | 82 | if (empty($instance->typeid)) { |
606ab1a1 | 83 | $tool = lti_get_tool_by_url_match($instance->toolurl, $instance->course); |
ea04a9f9 | 84 | if ($tool) { |
b9b2e7bb CS |
85 | $typeid = $tool->id; |
86 | } else { | |
87 | $typeid = null; | |
88 | } | |
89 | } else { | |
90 | $typeid = $instance->typeid; | |
91 | } | |
e27cb316 | 92 | |
ea04a9f9 | 93 | if ($typeid) { |
b9b2e7bb CS |
94 | $typeconfig = lti_get_type_config($typeid); |
95 | } else { | |
96 | //There is no admin configuration for this tool. Use configuration in the lti instance record plus some defaults. | |
97 | $typeconfig = (array)$instance; | |
e27cb316 | 98 | |
b9b2e7bb CS |
99 | $typeconfig['sendname'] = $instance->instructorchoicesendname; |
100 | $typeconfig['sendemailaddr'] = $instance->instructorchoicesendemailaddr; | |
101 | $typeconfig['customparameters'] = $instance->instructorcustomparameters; | |
f4f711d7 CS |
102 | $typeconfig['acceptgrades'] = $instance->instructorchoiceacceptgrades; |
103 | $typeconfig['allowroster'] = $instance->instructorchoiceallowroster; | |
d8d04121 | 104 | $typeconfig['forcessl'] = '0'; |
b9b2e7bb | 105 | } |
e27cb316 | 106 | |
b9b2e7bb | 107 | //Default the organizationid if not specified |
ea04a9f9 | 108 | if (empty($typeconfig['organizationid'])) { |
b9b2e7bb | 109 | $urlparts = parse_url($CFG->wwwroot); |
e27cb316 | 110 | |
b9b2e7bb CS |
111 | $typeconfig['organizationid'] = $urlparts['host']; |
112 | } | |
e27cb316 | 113 | |
ea04a9f9 | 114 | if (!empty($instance->resourcekey)) { |
3dd9ca24 | 115 | $key = $instance->resourcekey; |
ea04a9f9 | 116 | } else if (!empty($typeconfig['resourcekey'])) { |
3dd9ca24 CS |
117 | $key = $typeconfig['resourcekey']; |
118 | } else { | |
119 | $key = ''; | |
120 | } | |
121 | ||
ea04a9f9 | 122 | if (!empty($instance->password)) { |
3dd9ca24 | 123 | $secret = $instance->password; |
ea04a9f9 | 124 | } else if (!empty($typeconfig['password'])) { |
3dd9ca24 CS |
125 | $secret = $typeconfig['password']; |
126 | } else { | |
127 | $secret = ''; | |
128 | } | |
e27cb316 | 129 | |
b9b2e7bb | 130 | $endpoint = !empty($instance->toolurl) ? $instance->toolurl : $typeconfig['toolurl']; |
d8d04121 | 131 | $endpoint = trim($endpoint); |
e27cb316 | 132 | |
d8d04121 | 133 | //If the current request is using SSL and a secure tool URL is specified, use it |
ea04a9f9 | 134 | if (lti_request_is_using_ssl() && !empty($instance->securetoolurl)) { |
d8d04121 CS |
135 | $endpoint = trim($instance->securetoolurl); |
136 | } | |
e27cb316 | 137 | |
d8d04121 | 138 | //If SSL is forced, use the secure tool url if specified. Otherwise, make sure https is on the normal launch URL. |
ea04a9f9 EL |
139 | if ($typeconfig['forcessl'] == '1') { |
140 | if (!empty($instance->securetoolurl)) { | |
d8d04121 CS |
141 | $endpoint = trim($instance->securetoolurl); |
142 | } | |
e27cb316 | 143 | |
d8d04121 CS |
144 | $endpoint = lti_ensure_url_is_https($endpoint); |
145 | } else { | |
ea04a9f9 | 146 | if (!strstr($endpoint, '://')) { |
d8d04121 CS |
147 | $endpoint = 'http://' . $endpoint; |
148 | } | |
f17f4959 | 149 | } |
e27cb316 | 150 | |
d8d04121 CS |
151 | $orgid = $typeconfig['organizationid']; |
152 | ||
b9b2e7bb CS |
153 | $course = $PAGE->course; |
154 | $requestparams = lti_build_request($instance, $typeconfig, $course); | |
155 | ||
3dd9ca24 | 156 | $launchcontainer = lti_get_launch_container($instance, $typeconfig); |
c4d80efe | 157 | $returnurlparams = array('course' => $course->id, 'launch_container' => $launchcontainer, 'instanceid' => $instance->id); |
e27cb316 | 158 | |
3dd9ca24 CS |
159 | if ( $orgid ) { |
160 | $requestparams["tool_consumer_instance_guid"] = $orgid; | |
161 | } | |
e27cb316 | 162 | |
ea04a9f9 | 163 | if (empty($key) || empty($secret)) { |
9d57ad17 | 164 | $returnurlparams['unsigned'] = '1'; |
1706e83b | 165 | } |
6d6bd5f1 EL |
166 | |
167 | // Add the return URL. We send the launch container along to help us avoid frames-within-frames when the user returns. | |
1706e83b CS |
168 | $url = new moodle_url('/mod/lti/return.php', $returnurlparams); |
169 | $returnurl = $url->out(false); | |
e27cb316 | 170 | |
1706e83b CS |
171 | if ($typeconfig['forcessl'] == '1') { |
172 | $returnurl = lti_ensure_url_is_https($returnurl); | |
9d57ad17 | 173 | } |
6d6bd5f1 | 174 | |
1706e83b | 175 | $requestparams['launch_presentation_return_url'] = $returnurl; |
e27cb316 | 176 | |
ea04a9f9 | 177 | if (!empty($key) && !empty($secret)) { |
3dd9ca24 CS |
178 | $parms = lti_sign_parameters($requestparams, $endpoint, "POST", $key, $secret); |
179 | } else { | |
180 | //If no key and secret, do the launch unsigned. | |
181 | $parms = $requestparams; | |
3dd9ca24 | 182 | } |
e27cb316 | 183 | |
b9b2e7bb | 184 | $debuglaunch = ( $instance->debuglaunch == 1 ); |
e27cb316 | 185 | |
dbb0fec9 | 186 | $content = lti_post_launch_html($parms, $endpoint, $debuglaunch); |
e27cb316 | 187 | |
b9b2e7bb CS |
188 | echo $content; |
189 | } | |
190 | ||
ea04a9f9 | 191 | function lti_build_sourcedid($instanceid, $userid, $launchid = null, $servicesalt) { |
996b0fd9 | 192 | $data = new stdClass(); |
e27cb316 | 193 | |
996b0fd9 CS |
194 | $data->instanceid = $instanceid; |
195 | $data->userid = $userid; | |
ea04a9f9 | 196 | if (!empty($launchid)) { |
f4f711d7 CS |
197 | $data->launchid = $launchid; |
198 | } else { | |
199 | $data->launchid = mt_rand(); | |
200 | } | |
996b0fd9 CS |
201 | |
202 | $json = json_encode($data); | |
203 | ||
204 | $hash = hash('sha256', $json . $servicesalt, false); | |
205 | ||
206 | $container = new stdClass(); | |
207 | $container->data = $data; | |
208 | $container->hash = $hash; | |
209 | ||
210 | return $container; | |
211 | } | |
212 | ||
b9b2e7bb CS |
213 | /** |
214 | * This function builds the request that must be sent to the tool producer | |
215 | * | |
216 | * @param object $instance Basic LTI instance object | |
217 | * @param object $typeconfig Basic LTI tool configuration | |
218 | * @param object $course Course object | |
219 | * | |
220 | * @return array $request Request details | |
221 | */ | |
222 | function lti_build_request($instance, $typeconfig, $course) { | |
223 | global $USER, $CFG; | |
224 | ||
ea04a9f9 | 225 | if (empty($instance->cmid)) { |
16cac566 CS |
226 | $instance->cmid = 0; |
227 | } | |
e27cb316 | 228 | |
1d4f052e | 229 | $role = lti_get_ims_role($USER, $instance->cmid, $instance->course); |
b9b2e7bb | 230 | |
b9b2e7bb | 231 | $requestparams = array( |
34eb0501 CS |
232 | 'resource_link_id' => $instance->id, |
233 | 'resource_link_title' => $instance->name, | |
234 | 'resource_link_description' => $instance->intro, | |
235 | 'user_id' => $USER->id, | |
236 | 'roles' => $role, | |
237 | 'context_id' => $course->id, | |
238 | 'context_label' => $course->shortname, | |
239 | 'context_title' => $course->fullname, | |
2a457227 | 240 | 'launch_presentation_locale' => current_language() |
b9b2e7bb CS |
241 | ); |
242 | ||
b9b2e7bb | 243 | $placementsecret = $instance->servicesalt; |
e27cb316 | 244 | |
b9b2e7bb | 245 | if ( isset($placementsecret) ) { |
f4f711d7 | 246 | $sourcedid = json_encode(lti_build_sourcedid($instance->id, $USER->id, null, $placementsecret)); |
b9b2e7bb CS |
247 | } |
248 | ||
249 | if ( isset($placementsecret) && | |
5e078d62 CS |
250 | ( $typeconfig['acceptgrades'] == LTI_SETTING_ALWAYS || |
251 | ( $typeconfig['acceptgrades'] == LTI_SETTING_DELEGATE && $instance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS ) ) ) { | |
34eb0501 | 252 | $requestparams['lis_result_sourcedid'] = $sourcedid; |
e27cb316 | 253 | |
34eb0501 CS |
254 | //Add outcome service URL |
255 | $serviceurl = new moodle_url('/mod/lti/service.php'); | |
256 | $serviceurl = $serviceurl->out(); | |
feeb5294 | 257 | |
ea04a9f9 | 258 | if ($typeconfig['forcessl'] == '1') { |
d8d04121 CS |
259 | $serviceurl = lti_ensure_url_is_https($serviceurl); |
260 | } | |
feeb5294 | 261 | |
34eb0501 | 262 | $requestparams['lis_outcome_service_url'] = $serviceurl; |
b9b2e7bb CS |
263 | } |
264 | ||
b9b2e7bb | 265 | // Send user's name and email data if appropriate |
5e078d62 CS |
266 | if ( $typeconfig['sendname'] == LTI_SETTING_ALWAYS || |
267 | ( $typeconfig['sendname'] == LTI_SETTING_DELEGATE && $instance->instructorchoicesendname == LTI_SETTING_ALWAYS ) ) { | |
34eb0501 CS |
268 | $requestparams['lis_person_name_given'] = $USER->firstname; |
269 | $requestparams['lis_person_name_family'] = $USER->lastname; | |
270 | $requestparams['lis_person_name_full'] = $USER->firstname." ".$USER->lastname; | |
b9b2e7bb CS |
271 | } |
272 | ||
5e078d62 CS |
273 | if ( $typeconfig['sendemailaddr'] == LTI_SETTING_ALWAYS || |
274 | ( $typeconfig['sendemailaddr'] == LTI_SETTING_DELEGATE && $instance->instructorchoicesendemailaddr == LTI_SETTING_ALWAYS ) ) { | |
34eb0501 | 275 | $requestparams['lis_person_contact_email_primary'] = $USER->email; |
b9b2e7bb CS |
276 | } |
277 | ||
278 | // Concatenate the custom parameters from the administrator and the instructor | |
279 | // Instructor parameters are only taken into consideration if the administrator | |
280 | // has giver permission | |
281 | $customstr = $typeconfig['customparameters']; | |
282 | $instructorcustomstr = $instance->instructorcustomparameters; | |
283 | $custom = array(); | |
284 | $instructorcustom = array(); | |
285 | if ($customstr) { | |
dbb0fec9 | 286 | $custom = lti_split_custom_parameters($customstr); |
b9b2e7bb | 287 | } |
5e078d62 | 288 | if (!isset($typeconfig['allowinstructorcustom']) || $typeconfig['allowinstructorcustom'] == LTI_SETTING_NEVER) { |
b9b2e7bb CS |
289 | $requestparams = array_merge($custom, $requestparams); |
290 | } else { | |
291 | if ($instructorcustomstr) { | |
dbb0fec9 | 292 | $instructorcustom = lti_split_custom_parameters($instructorcustomstr); |
b9b2e7bb CS |
293 | } |
294 | foreach ($instructorcustom as $key => $val) { | |
ea04a9f9 EL |
295 | // Ignore the instructor's parameter |
296 | if (!array_key_exists($key, $custom)) { | |
b9b2e7bb CS |
297 | $custom[$key] = $val; |
298 | } | |
299 | } | |
300 | $requestparams = array_merge($custom, $requestparams); | |
301 | } | |
302 | ||
3dd9ca24 CS |
303 | // Make sure we let the tool know what LMS they are being called from |
304 | $requestparams["ext_lms"] = "moodle-2"; | |
34eb0501 CS |
305 | $requestparams['tool_consumer_info_product_family_code'] = 'moodle'; |
306 | $requestparams['tool_consumer_info_version'] = strval($CFG->version); | |
3dd9ca24 CS |
307 | |
308 | // Add oauth_callback to be compliant with the 1.0A spec | |
34eb0501 | 309 | $requestparams['oauth_callback'] = 'about:blank'; |
3dd9ca24 | 310 | |
558be8fc CS |
311 | //The submit button needs to be part of the signature as it gets posted with the form. |
312 | //This needs to be here to support launching without javascript. | |
3dd9ca24 | 313 | $submittext = get_string('press_to_submit', 'lti'); |
34eb0501 | 314 | $requestparams['ext_submit'] = $submittext; |
e27cb316 | 315 | |
34eb0501 CS |
316 | $requestparams['lti_version'] = 'LTI-1p0'; |
317 | $requestparams['lti_message_type'] = 'basic-lti-launch-request'; | |
e27cb316 | 318 | |
b9b2e7bb CS |
319 | return $requestparams; |
320 | } | |
321 | ||
ea04a9f9 | 322 | function lti_get_tool_table($tools, $id) { |
99938034 | 323 | global $CFG, $OUTPUT, $USER; |
795dff01 | 324 | $html = ''; |
e27cb316 | 325 | |
795dff01 CS |
326 | $typename = get_string('typename', 'lti'); |
327 | $baseurl = get_string('baseurl', 'lti'); | |
328 | $action = get_string('action', 'lti'); | |
329 | $createdon = get_string('createdon', 'lti'); | |
e27cb316 | 330 | |
ea04a9f9 | 331 | if ($id == 'lti_configured') { |
795dff01 CS |
332 | $html .= '<div><a style="margin-top:.25em" href="'.$CFG->wwwroot.'/mod/lti/typessettings.php?action=add&sesskey='.$USER->sesskey.'">'.get_string('addtype', 'lti').'</a></div>'; |
333 | } | |
e27cb316 | 334 | |
795dff01 | 335 | if (!empty($tools)) { |
5de15b83 CS |
336 | $html .= " |
337 | <div id=\"{$id}_container\" style=\"margin-top:.5em;margin-bottom:.5em\"> | |
338 | <table id=\"{$id}_tools\"> | |
795dff01 CS |
339 | <thead> |
340 | <tr> | |
341 | <th>$typename</th> | |
342 | <th>$baseurl</th> | |
343 | <th>$createdon</th> | |
344 | <th>$action</th> | |
345 | </tr> | |
346 | </thead> | |
5de15b83 | 347 | "; |
e27cb316 | 348 | |
795dff01 CS |
349 | foreach ($tools as $type) { |
350 | $date = userdate($type->timecreated); | |
351 | $accept = get_string('accept', 'lti'); | |
352 | $update = get_string('update', 'lti'); | |
353 | $delete = get_string('delete', 'lti'); | |
e27cb316 | 354 | |
baf41e4b FM |
355 | $baseurl = new moodle_url('/mod/lti/typessettings.php', array( |
356 | 'action' => 'accept', | |
357 | 'id' => $type->id, | |
358 | 'sesskey' => sesskey(), | |
359 | 'tab' => $id | |
360 | )); | |
361 | ||
362 | $accepthtml = $OUTPUT->action_icon($baseurl, | |
363 | new pix_icon('t/check', $accept, '', array('class' => 'iconsmall')), null, | |
364 | array('title' => $accept, 'class' => 'editing_accept')); | |
795dff01 CS |
365 | |
366 | $deleteaction = 'delete'; | |
e27cb316 | 367 | |
ea04a9f9 | 368 | if ($type->state == LTI_TOOL_STATE_CONFIGURED) { |
795dff01 CS |
369 | $accepthtml = ''; |
370 | } | |
e27cb316 | 371 | |
ea04a9f9 | 372 | if ($type->state != LTI_TOOL_STATE_REJECTED) { |
795dff01 CS |
373 | $deleteaction = 'reject'; |
374 | $delete = get_string('reject', 'lti'); | |
375 | } | |
e27cb316 | 376 | |
baf41e4b FM |
377 | $updateurl = clone($baseurl); |
378 | $updateurl->param('action', 'update'); | |
379 | $updatehtml = $OUTPUT->action_icon($updateurl, | |
380 | new pix_icon('t/edit', $accept, '', array('class' => 'iconsmall')), null, | |
381 | array('title' => $update, 'class' => 'editing_update')); | |
382 | ||
383 | $deleteurl = clone($baseurl); | |
384 | $deleteurl->param('action', $deleteaction); | |
385 | $deletehtml = $OUTPUT->action_icon($deleteurl, | |
386 | new pix_icon('t/delete', $accept, '', array('class' => 'iconsmall')), null, | |
387 | array('title' => $delete, 'class' => 'editing_delete')); | |
5de15b83 | 388 | $html .= " |
795dff01 CS |
389 | <tr> |
390 | <td> | |
391 | {$type->name} | |
392 | </td> | |
393 | <td> | |
394 | {$type->baseurl} | |
395 | </td> | |
396 | <td> | |
397 | {$date} | |
398 | </td> | |
5de15b83 | 399 | <td align=\"center\"> |
baf41e4b | 400 | {$accepthtml}{$updatehtml}{$deletehtml} |
795dff01 CS |
401 | </td> |
402 | </tr> | |
5de15b83 | 403 | "; |
795dff01 CS |
404 | } |
405 | $html .= '</table></div>'; | |
406 | } else { | |
407 | $html .= get_string('no_' . $id, 'lti'); | |
408 | } | |
e27cb316 | 409 | |
795dff01 CS |
410 | return $html; |
411 | } | |
412 | ||
b9b2e7bb CS |
413 | /** |
414 | * Splits the custom parameters field to the various parameters | |
415 | * | |
416 | * @param string $customstr String containing the parameters | |
417 | * | |
418 | * @return Array of custom parameters | |
419 | */ | |
dbb0fec9 | 420 | function lti_split_custom_parameters($customstr) { |
b9b2e7bb CS |
421 | $lines = preg_split("/[\n;]/", $customstr); |
422 | $retval = array(); | |
423 | foreach ($lines as $line) { | |
424 | $pos = strpos($line, "="); | |
425 | if ( $pos === false || $pos < 1 ) { | |
426 | continue; | |
427 | } | |
f8311def PS |
428 | $key = trim(textlib::substr($line, 0, $pos)); |
429 | $val = trim(textlib::substr($line, $pos+1, strlen($line))); | |
dbb0fec9 | 430 | $key = lti_map_keyname($key); |
b9b2e7bb CS |
431 | $retval['custom_'.$key] = $val; |
432 | } | |
433 | return $retval; | |
434 | } | |
435 | ||
436 | /** | |
437 | * Used for building the names of the different custom parameters | |
438 | * | |
439 | * @param string $key Parameter name | |
440 | * | |
441 | * @return string Processed name | |
442 | */ | |
dbb0fec9 | 443 | function lti_map_keyname($key) { |
b9b2e7bb | 444 | $newkey = ""; |
f8311def | 445 | $key = textlib::strtolower(trim($key)); |
b9b2e7bb CS |
446 | foreach (str_split($key) as $ch) { |
447 | if ( ($ch >= 'a' && $ch <= 'z') || ($ch >= '0' && $ch <= '9') ) { | |
448 | $newkey .= $ch; | |
449 | } else { | |
450 | $newkey .= '_'; | |
451 | } | |
452 | } | |
453 | return $newkey; | |
454 | } | |
455 | ||
456 | /** | |
16cac566 | 457 | * Gets the IMS role string for the specified user and LTI course module. |
e27cb316 | 458 | * |
16cac566 CS |
459 | * @param mixed $user User object or user id |
460 | * @param int $cmid The course module id of the LTI activity | |
461 | * @return string A role string suitable for passing with an LTI launch | |
b9b2e7bb | 462 | */ |
1d4f052e | 463 | function lti_get_ims_role($user, $cmid, $courseid) { |
16cac566 | 464 | $roles = array(); |
e27cb316 | 465 | |
ea04a9f9 | 466 | if (empty($cmid)) { |
1d4f052e CS |
467 | //If no cmid is passed, check if the user is a teacher in the course |
468 | //This allows other modules to programmatically "fake" a launch without | |
469 | //a real LTI instance | |
c288a3db | 470 | $coursecontext = context_course::instance($courseid); |
e27cb316 | 471 | |
ea04a9f9 | 472 | if (has_capability('moodle/course:manageactivities', $coursecontext)) { |
1d4f052e CS |
473 | array_push($roles, 'Instructor'); |
474 | } else { | |
475 | array_push($roles, 'Learner'); | |
476 | } | |
16cac566 | 477 | } else { |
c288a3db | 478 | $context = context_module::instance($cmid); |
1d4f052e | 479 | |
ea04a9f9 | 480 | if (has_capability('mod/lti:manage', $context)) { |
1d4f052e CS |
481 | array_push($roles, 'Instructor'); |
482 | } else { | |
483 | array_push($roles, 'Learner'); | |
484 | } | |
b9b2e7bb | 485 | } |
1d4f052e | 486 | |
ea04a9f9 | 487 | if (is_siteadmin($user)) { |
16cac566 | 488 | array_push($roles, 'urn:lti:sysrole:ims/lis/Administrator'); |
b9b2e7bb | 489 | } |
e27cb316 | 490 | |
16cac566 | 491 | return join(',', $roles); |
b9b2e7bb CS |
492 | } |
493 | ||
494 | /** | |
495 | * Returns configuration details for the tool | |
496 | * | |
497 | * @param int $typeid Basic LTI tool typeid | |
498 | * | |
499 | * @return array Tool Configuration | |
500 | */ | |
501 | function lti_get_type_config($typeid) { | |
502 | global $DB; | |
503 | ||
5f136255 EL |
504 | $query = "SELECT name, value |
505 | FROM {lti_types_config} | |
506 | WHERE typeid = :typeid1 | |
507 | UNION ALL | |
508 | SELECT 'toolurl' AS name, baseurl AS value | |
509 | FROM {lti_types} | |
510 | WHERE id = :typeid2"; | |
e27cb316 | 511 | |
b9b2e7bb | 512 | $typeconfig = array(); |
f17f4959 | 513 | $configs = $DB->get_records_sql($query, array('typeid1' => $typeid, 'typeid2' => $typeid)); |
e27cb316 | 514 | |
b9b2e7bb CS |
515 | if (!empty($configs)) { |
516 | foreach ($configs as $config) { | |
517 | $typeconfig[$config->name] = $config->value; | |
518 | } | |
519 | } | |
e27cb316 | 520 | |
b9b2e7bb CS |
521 | return $typeconfig; |
522 | } | |
523 | ||
ea04a9f9 | 524 | function lti_get_tools_by_url($url, $state, $courseid = null) { |
b9b2e7bb | 525 | $domain = lti_get_domain_from_url($url); |
e27cb316 | 526 | |
996b0fd9 | 527 | return lti_get_tools_by_domain($domain, $state, $courseid); |
b9b2e7bb CS |
528 | } |
529 | ||
ea04a9f9 | 530 | function lti_get_tools_by_domain($domain, $state = null, $courseid = null) { |
b9b2e7bb | 531 | global $DB, $SITE; |
e27cb316 | 532 | |
b9b2e7bb | 533 | $filters = array('tooldomain' => $domain); |
e27cb316 | 534 | |
b9b2e7bb CS |
535 | $statefilter = ''; |
536 | $coursefilter = ''; | |
e27cb316 | 537 | |
ea04a9f9 | 538 | if ($state) { |
b9b2e7bb CS |
539 | $statefilter = 'AND state = :state'; |
540 | } | |
e27cb316 | 541 | |
ea04a9f9 | 542 | if ($courseid && $courseid != $SITE->id) { |
b9b2e7bb CS |
543 | $coursefilter = 'OR course = :courseid'; |
544 | } | |
e27cb316 | 545 | |
5f136255 EL |
546 | $query = "SELECT * |
547 | FROM {lti_types} | |
548 | WHERE tooldomain = :tooldomain | |
549 | AND (course = :siteid $coursefilter) | |
550 | $statefilter"; | |
e27cb316 | 551 | |
b9b2e7bb | 552 | return $DB->get_records_sql($query, array( |
e27cb316 CS |
553 | 'courseid' => $courseid, |
554 | 'siteid' => $SITE->id, | |
555 | 'tooldomain' => $domain, | |
b9b2e7bb CS |
556 | 'state' => $state |
557 | )); | |
558 | } | |
559 | ||
560 | /** | |
561 | * Returns all basicLTI tools configured by the administrator | |
562 | * | |
563 | */ | |
6831c7cd | 564 | function lti_filter_get_types($course) { |
b9b2e7bb CS |
565 | global $DB; |
566 | ||
ea04a9f9 | 567 | if (!empty($course)) { |
6831c7cd CS |
568 | $filter = array('course' => $course); |
569 | } else { | |
570 | $filter = array(); | |
571 | } | |
e27cb316 | 572 | |
6831c7cd | 573 | return $DB->get_records('lti_types', $filter); |
b9b2e7bb CS |
574 | } |
575 | ||
ea04a9f9 | 576 | function lti_get_types_for_add_instance() { |
996b0fd9 | 577 | global $DB, $SITE, $COURSE; |
e27cb316 | 578 | |
5f136255 EL |
579 | $query = "SELECT * |
580 | FROM {lti_types} | |
581 | WHERE coursevisible = 1 | |
582 | AND (course = :siteid OR course = :courseid) | |
583 | AND state = :active"; | |
e27cb316 | 584 | |
606ab1a1 | 585 | $admintypes = $DB->get_records_sql($query, array('siteid' => $SITE->id, 'courseid' => $COURSE->id, 'active' => LTI_TOOL_STATE_CONFIGURED)); |
e27cb316 | 586 | |
b9b2e7bb | 587 | $types = array(); |
996b0fd9 | 588 | $types[0] = (object)array('name' => get_string('automatic', 'lti'), 'course' => $SITE->id); |
e27cb316 | 589 | |
ea04a9f9 | 590 | foreach ($admintypes as $type) { |
16e8f130 | 591 | $types[$type->id] = $type; |
b9b2e7bb | 592 | } |
e27cb316 | 593 | |
b9b2e7bb CS |
594 | return $types; |
595 | } | |
596 | ||
ea04a9f9 | 597 | function lti_get_domain_from_url($url) { |
b9b2e7bb | 598 | $matches = array(); |
e27cb316 | 599 | |
ea04a9f9 | 600 | if (preg_match(LTI_URL_DOMAIN_REGEX, $url, $matches)) { |
b9b2e7bb CS |
601 | return $matches[1]; |
602 | } | |
603 | } | |
604 | ||
ea04a9f9 | 605 | function lti_get_tool_by_url_match($url, $courseid = null, $state = LTI_TOOL_STATE_CONFIGURED) { |
b69dc429 | 606 | $possibletools = lti_get_tools_by_url($url, $state, $courseid); |
e27cb316 | 607 | |
7023b65e | 608 | return lti_get_best_tool_by_url($url, $possibletools, $courseid); |
b9b2e7bb CS |
609 | } |
610 | ||
ea04a9f9 | 611 | function lti_get_url_thumbprint($url) { |
b9b2e7bb | 612 | $urlparts = parse_url(strtolower($url)); |
ea04a9f9 | 613 | if (!isset($urlparts['path'])) { |
b9b2e7bb CS |
614 | $urlparts['path'] = ''; |
615 | } | |
e27cb316 | 616 | |
ea04a9f9 | 617 | if (!isset($urlparts['host'])) { |
d8d04121 CS |
618 | $urlparts['host'] = ''; |
619 | } | |
e27cb316 | 620 | |
ea04a9f9 | 621 | if (substr($urlparts['host'], 0, 4) === 'www.') { |
d8d04121 | 622 | $urlparts['host'] = substr($urlparts['host'], 4); |
b9b2e7bb | 623 | } |
e27cb316 | 624 | |
b9b2e7bb CS |
625 | return $urllower = $urlparts['host'] . '/' . $urlparts['path']; |
626 | } | |
627 | ||
ea04a9f9 EL |
628 | function lti_get_best_tool_by_url($url, $tools, $courseid = null) { |
629 | if (count($tools) === 0) { | |
b9b2e7bb CS |
630 | return null; |
631 | } | |
e27cb316 | 632 | |
b9b2e7bb | 633 | $urllower = lti_get_url_thumbprint($url); |
e27cb316 | 634 | |
ea04a9f9 | 635 | foreach ($tools as $tool) { |
b9b2e7bb | 636 | $tool->_matchscore = 0; |
e27cb316 | 637 | |
b9b2e7bb | 638 | $toolbaseurllower = lti_get_url_thumbprint($tool->baseurl); |
e27cb316 | 639 | |
ea04a9f9 | 640 | if ($urllower === $toolbaseurllower) { |
7023b65e | 641 | //100 points for exact thumbprint match |
b9b2e7bb | 642 | $tool->_matchscore += 100; |
ea04a9f9 | 643 | } else if (substr($urllower, 0, strlen($toolbaseurllower)) === $toolbaseurllower) { |
7023b65e | 644 | //50 points if tool thumbprint starts with the base URL thumbprint |
b9b2e7bb CS |
645 | $tool->_matchscore += 50; |
646 | } | |
e27cb316 | 647 | |
7023b65e | 648 | //Prefer course tools over site tools |
ea04a9f9 | 649 | if (!empty($courseid)) { |
4c598b13 | 650 | //Minus 10 points for not matching the course id (global tools) |
ea04a9f9 | 651 | if ($tool->course != $courseid) { |
7023b65e CS |
652 | $tool->_matchscore -= 10; |
653 | } | |
654 | } | |
b9b2e7bb | 655 | } |
e27cb316 | 656 | |
ea04a9f9 EL |
657 | $bestmatch = array_reduce($tools, function($value, $tool) { |
658 | if ($tool->_matchscore > $value->_matchscore) { | |
b9b2e7bb CS |
659 | return $tool; |
660 | } else { | |
661 | return $value; | |
662 | } | |
e27cb316 | 663 | |
b9b2e7bb | 664 | }, (object)array('_matchscore' => -1)); |
e27cb316 | 665 | |
b9b2e7bb | 666 | //None of the tools are suitable for this URL |
ea04a9f9 | 667 | if ($bestmatch->_matchscore <= 0) { |
b9b2e7bb CS |
668 | return null; |
669 | } | |
e27cb316 | 670 | |
b9b2e7bb CS |
671 | return $bestmatch; |
672 | } | |
673 | ||
ea04a9f9 | 674 | function lti_get_shared_secrets_by_key($key) { |
020eea1b | 675 | global $DB; |
e27cb316 | 676 | |
020eea1b CS |
677 | //Look up the shared secret for the specified key in both the types_config table (for configured tools) |
678 | //And in the lti resource table for ad-hoc tools | |
5f136255 EL |
679 | $query = "SELECT t2.value |
680 | FROM {lti_types_config} t1 | |
681 | JOIN {lti_types_config} t2 ON t1.typeid = t2.typeid | |
682 | JOIN {lti_types} type ON t2.typeid = type.id | |
683 | WHERE t1.name = 'resourcekey' | |
684 | AND t1.value = :key1 | |
685 | AND t2.name = 'password' | |
686 | AND type.state = :configured | |
687 | UNION | |
688 | SELECT password AS value | |
689 | FROM {lti} | |
690 | WHERE resourcekey = :key2"; | |
e27cb316 | 691 | |
feb30fd8 | 692 | $sharedsecrets = $DB->get_records_sql($query, array('configured' => LTI_TOOL_STATE_CONFIGURED, 'key1' => $key, 'key2' => $key)); |
e27cb316 | 693 | |
ea04a9f9 | 694 | $values = array_map(function($item) { |
020eea1b CS |
695 | return $item->value; |
696 | }, $sharedsecrets); | |
e27cb316 | 697 | |
020eea1b CS |
698 | //There should really only be one shared secret per key. But, we can't prevent |
699 | //more than one getting entered. For instance, if the same key is used for two tool providers. | |
700 | return $values; | |
701 | } | |
702 | ||
b9b2e7bb CS |
703 | /** |
704 | * Delete a Basic LTI configuration | |
705 | * | |
706 | * @param int $id Configuration id | |
707 | */ | |
708 | function lti_delete_type($id) { | |
709 | global $DB; | |
710 | ||
711 | //We should probably just copy the launch URL to the tool instances in this case... using a single query | |
712 | /* | |
713 | $instances = $DB->get_records('lti', array('typeid' => $id)); | |
714 | foreach ($instances as $instance) { | |
715 | $instance->typeid = 0; | |
716 | $DB->update_record('lti', $instance); | |
717 | }*/ | |
718 | ||
719 | $DB->delete_records('lti_types', array('id' => $id)); | |
720 | $DB->delete_records('lti_types_config', array('typeid' => $id)); | |
721 | } | |
722 | ||
ea04a9f9 | 723 | function lti_set_state_for_type($id, $state) { |
b9b2e7bb | 724 | global $DB; |
e27cb316 | 725 | |
b9b2e7bb CS |
726 | $DB->update_record('lti_types', array('id' => $id, 'state' => $state)); |
727 | } | |
728 | ||
729 | /** | |
730 | * Transforms a basic LTI object to an array | |
731 | * | |
732 | * @param object $ltiobject Basic LTI object | |
733 | * | |
734 | * @return array Basic LTI configuration details | |
735 | */ | |
736 | function lti_get_config($ltiobject) { | |
737 | $typeconfig = array(); | |
738 | $typeconfig = (array)$ltiobject; | |
739 | $additionalconfig = lti_get_type_config($ltiobject->typeid); | |
740 | $typeconfig = array_merge($typeconfig, $additionalconfig); | |
741 | return $typeconfig; | |
742 | } | |
743 | ||
744 | /** | |
745 | * | |
746 | * Generates some of the tool configuration based on the instance details | |
747 | * | |
748 | * @param int $id | |
749 | * | |
750 | * @return Instance configuration | |
751 | * | |
752 | */ | |
753 | function lti_get_type_config_from_instance($id) { | |
754 | global $DB; | |
755 | ||
756 | $instance = $DB->get_record('lti', array('id' => $id)); | |
757 | $config = lti_get_config($instance); | |
758 | ||
759 | $type = new stdClass(); | |
760 | $type->lti_fix = $id; | |
761 | if (isset($config['toolurl'])) { | |
762 | $type->lti_toolurl = $config['toolurl']; | |
763 | } | |
764 | if (isset($config['instructorchoicesendname'])) { | |
765 | $type->lti_sendname = $config['instructorchoicesendname']; | |
766 | } | |
767 | if (isset($config['instructorchoicesendemailaddr'])) { | |
768 | $type->lti_sendemailaddr = $config['instructorchoicesendemailaddr']; | |
769 | } | |
770 | if (isset($config['instructorchoiceacceptgrades'])) { | |
771 | $type->lti_acceptgrades = $config['instructorchoiceacceptgrades']; | |
772 | } | |
773 | if (isset($config['instructorchoiceallowroster'])) { | |
774 | $type->lti_allowroster = $config['instructorchoiceallowroster']; | |
775 | } | |
776 | ||
777 | if (isset($config['instructorcustomparameters'])) { | |
778 | $type->lti_allowsetting = $config['instructorcustomparameters']; | |
779 | } | |
780 | return $type; | |
781 | } | |
782 | ||
783 | /** | |
784 | * Generates some of the tool configuration based on the admin configuration details | |
785 | * | |
786 | * @param int $id | |
787 | * | |
788 | * @return Configuration details | |
789 | */ | |
790 | function lti_get_type_type_config($id) { | |
791 | global $DB; | |
792 | ||
793 | $basicltitype = $DB->get_record('lti_types', array('id' => $id)); | |
794 | $config = lti_get_type_config($id); | |
795 | ||
036e84c3 | 796 | $type = new stdClass(); |
5f72d102 | 797 | |
b9b2e7bb | 798 | $type->lti_typename = $basicltitype->name; |
e27cb316 | 799 | |
6831c7cd | 800 | $type->typeid = $basicltitype->id; |
e27cb316 | 801 | |
b9b2e7bb | 802 | $type->lti_toolurl = $basicltitype->baseurl; |
e27cb316 | 803 | |
b9b2e7bb CS |
804 | if (isset($config['resourcekey'])) { |
805 | $type->lti_resourcekey = $config['resourcekey']; | |
806 | } | |
807 | if (isset($config['password'])) { | |
808 | $type->lti_password = $config['password']; | |
809 | } | |
810 | ||
811 | if (isset($config['sendname'])) { | |
812 | $type->lti_sendname = $config['sendname']; | |
813 | } | |
ea04a9f9 | 814 | if (isset($config['instructorchoicesendname'])) { |
b9b2e7bb CS |
815 | $type->lti_instructorchoicesendname = $config['instructorchoicesendname']; |
816 | } | |
ea04a9f9 | 817 | if (isset($config['sendemailaddr'])) { |
b9b2e7bb CS |
818 | $type->lti_sendemailaddr = $config['sendemailaddr']; |
819 | } | |
ea04a9f9 | 820 | if (isset($config['instructorchoicesendemailaddr'])) { |
b9b2e7bb CS |
821 | $type->lti_instructorchoicesendemailaddr = $config['instructorchoicesendemailaddr']; |
822 | } | |
ea04a9f9 | 823 | if (isset($config['acceptgrades'])) { |
b9b2e7bb CS |
824 | $type->lti_acceptgrades = $config['acceptgrades']; |
825 | } | |
ea04a9f9 | 826 | if (isset($config['instructorchoiceacceptgrades'])) { |
b9b2e7bb CS |
827 | $type->lti_instructorchoiceacceptgrades = $config['instructorchoiceacceptgrades']; |
828 | } | |
ea04a9f9 | 829 | if (isset($config['allowroster'])) { |
b9b2e7bb CS |
830 | $type->lti_allowroster = $config['allowroster']; |
831 | } | |
ea04a9f9 | 832 | if (isset($config['instructorchoiceallowroster'])) { |
b9b2e7bb CS |
833 | $type->lti_instructorchoiceallowroster = $config['instructorchoiceallowroster']; |
834 | } | |
835 | ||
836 | if (isset($config['customparameters'])) { | |
837 | $type->lti_customparameters = $config['customparameters']; | |
838 | } | |
839 | ||
ea04a9f9 | 840 | if (isset($config['forcessl'])) { |
d8d04121 CS |
841 | $type->lti_forcessl = $config['forcessl']; |
842 | } | |
e27cb316 | 843 | |
b9b2e7bb CS |
844 | if (isset($config['organizationid'])) { |
845 | $type->lti_organizationid = $config['organizationid']; | |
846 | } | |
847 | if (isset($config['organizationurl'])) { | |
848 | $type->lti_organizationurl = $config['organizationurl']; | |
849 | } | |
850 | if (isset($config['organizationdescr'])) { | |
851 | $type->lti_organizationdescr = $config['organizationdescr']; | |
852 | } | |
853 | if (isset($config['launchcontainer'])) { | |
854 | $type->lti_launchcontainer = $config['launchcontainer']; | |
855 | } | |
e27cb316 | 856 | |
b9b2e7bb CS |
857 | if (isset($config['coursevisible'])) { |
858 | $type->lti_coursevisible = $config['coursevisible']; | |
859 | } | |
e27cb316 | 860 | |
b9b2e7bb CS |
861 | if (isset($config['debuglaunch'])) { |
862 | $type->lti_debuglaunch = $config['debuglaunch']; | |
863 | } | |
e27cb316 | 864 | |
b9b2e7bb | 865 | if (isset($config['module_class_type'])) { |
036e84c3 | 866 | $type->lti_module_class_type = $config['module_class_type']; |
b9b2e7bb CS |
867 | } |
868 | ||
869 | return $type; | |
870 | } | |
871 | ||
ea04a9f9 | 872 | function lti_prepare_type_for_save($type, $config) { |
b9b2e7bb CS |
873 | $type->baseurl = $config->lti_toolurl; |
874 | $type->tooldomain = lti_get_domain_from_url($config->lti_toolurl); | |
875 | $type->name = $config->lti_typename; | |
e27cb316 | 876 | |
b9b2e7bb CS |
877 | $type->coursevisible = !empty($config->lti_coursevisible) ? $config->lti_coursevisible : 0; |
878 | $config->lti_coursevisible = $type->coursevisible; | |
e27cb316 | 879 | |
d8d04121 CS |
880 | $type->forcessl = !empty($config->lti_forcessl) ? $config->lti_forcessl : 0; |
881 | $config->lti_forcessl = $type->forcessl; | |
e27cb316 | 882 | |
b9b2e7bb | 883 | $type->timemodified = time(); |
e27cb316 | 884 | |
b9b2e7bb CS |
885 | unset ($config->lti_typename); |
886 | unset ($config->lti_toolurl); | |
887 | } | |
888 | ||
ea04a9f9 | 889 | function lti_update_type($type, $config) { |
b9b2e7bb | 890 | global $DB; |
e27cb316 | 891 | |
b9b2e7bb | 892 | lti_prepare_type_for_save($type, $config); |
e27cb316 | 893 | |
b9b2e7bb CS |
894 | if ($DB->update_record('lti_types', $type)) { |
895 | foreach ($config as $key => $value) { | |
896 | if (substr($key, 0, 4)=='lti_' && !is_null($value)) { | |
897 | $record = new StdClass(); | |
898 | $record->typeid = $type->id; | |
899 | $record->name = substr($key, 4); | |
900 | $record->value = $value; | |
e27cb316 | 901 | |
b9b2e7bb CS |
902 | lti_update_config($record); |
903 | } | |
904 | } | |
905 | } | |
906 | } | |
907 | ||
ea04a9f9 | 908 | function lti_add_type($type, $config) { |
b9b2e7bb | 909 | global $USER, $SITE, $DB; |
e27cb316 | 910 | |
b9b2e7bb | 911 | lti_prepare_type_for_save($type, $config); |
e27cb316 | 912 | |
ea04a9f9 | 913 | if (!isset($type->state)) { |
b9b2e7bb CS |
914 | $type->state = LTI_TOOL_STATE_PENDING; |
915 | } | |
e27cb316 | 916 | |
ea04a9f9 | 917 | if (!isset($type->timecreated)) { |
b9b2e7bb CS |
918 | $type->timecreated = time(); |
919 | } | |
e27cb316 | 920 | |
ea04a9f9 | 921 | if (!isset($type->createdby)) { |
b9b2e7bb CS |
922 | $type->createdby = $USER->id; |
923 | } | |
e27cb316 | 924 | |
ea04a9f9 | 925 | if (!isset($type->course)) { |
b9b2e7bb CS |
926 | $type->course = $SITE->id; |
927 | } | |
e27cb316 | 928 | |
b9b2e7bb | 929 | //Create a salt value to be used for signing passed data to extension services |
6831c7cd CS |
930 | //The outcome service uses the service salt on the instance. This can be used |
931 | //for communication with services not related to a specific LTI instance. | |
b9b2e7bb CS |
932 | $config->lti_servicesalt = uniqid('', true); |
933 | ||
934 | $id = $DB->insert_record('lti_types', $type); | |
935 | ||
936 | if ($id) { | |
937 | foreach ($config as $key => $value) { | |
938 | if (substr($key, 0, 4)=='lti_' && !is_null($value)) { | |
939 | $record = new StdClass(); | |
940 | $record->typeid = $id; | |
941 | $record->name = substr($key, 4); | |
942 | $record->value = $value; | |
943 | ||
944 | lti_add_config($record); | |
945 | } | |
946 | } | |
947 | } | |
e27cb316 | 948 | |
996b0fd9 | 949 | return $id; |
b9b2e7bb CS |
950 | } |
951 | ||
952 | /** | |
953 | * Add a tool configuration in the database | |
954 | * | |
955 | * @param $config Tool configuration | |
956 | * | |
957 | * @return int Record id number | |
958 | */ | |
959 | function lti_add_config($config) { | |
960 | global $DB; | |
961 | ||
962 | return $DB->insert_record('lti_types_config', $config); | |
963 | } | |
964 | ||
965 | /** | |
966 | * Updates a tool configuration in the database | |
967 | * | |
968 | * @param $config Tool configuration | |
969 | * | |
970 | * @return Record id number | |
971 | */ | |
972 | function lti_update_config($config) { | |
973 | global $DB; | |
974 | ||
975 | $return = true; | |
976 | $old = $DB->get_record('lti_types_config', array('typeid' => $config->typeid, 'name' => $config->name)); | |
e27cb316 | 977 | |
b9b2e7bb CS |
978 | if ($old) { |
979 | $config->id = $old->id; | |
980 | $return = $DB->update_record('lti_types_config', $config); | |
981 | } else { | |
982 | $return = $DB->insert_record('lti_types_config', $config); | |
983 | } | |
984 | return $return; | |
985 | } | |
986 | ||
987 | /** | |
988 | * Signs the petition to launch the external tool using OAuth | |
989 | * | |
990 | * @param $oldparms Parameters to be passed for signing | |
991 | * @param $endpoint url of the external tool | |
992 | * @param $method Method for sending the parameters (e.g. POST) | |
993 | * @param $oauth_consumoer_key Key | |
994 | * @param $oauth_consumoer_secret Secret | |
995 | * @param $submittext The text for the submit button | |
996 | * @param $orgid LMS name | |
997 | * @param $orgdesc LMS key | |
998 | */ | |
3dd9ca24 CS |
999 | function lti_sign_parameters($oldparms, $endpoint, $method, $oauthconsumerkey, $oauthconsumersecret) { |
1000 | //global $lastbasestring; | |
b9b2e7bb | 1001 | $parms = $oldparms; |
b9b2e7bb CS |
1002 | |
1003 | $testtoken = ''; | |
e27cb316 | 1004 | |
fabd4fcf | 1005 | // TODO: Switch to core oauthlib once implemented - MDL-30149 |
795dff01 CS |
1006 | $hmacmethod = new lti\OAuthSignatureMethod_HMAC_SHA1(); |
1007 | $testconsumer = new lti\OAuthConsumer($oauthconsumerkey, $oauthconsumersecret, null); | |
795dff01 | 1008 | $accreq = lti\OAuthRequest::from_consumer_and_token($testconsumer, $testtoken, $method, $endpoint, $parms); |
b9b2e7bb CS |
1009 | $accreq->sign_request($hmacmethod, $testconsumer, $testtoken); |
1010 | ||
1011 | // Pass this back up "out of band" for debugging | |
3dd9ca24 | 1012 | //$lastbasestring = $accreq->get_signature_base_string(); |
b9b2e7bb CS |
1013 | |
1014 | $newparms = $accreq->get_parameters(); | |
1015 | ||
1016 | return $newparms; | |
1017 | } | |
1018 | ||
1019 | /** | |
1020 | * Posts the launch petition HTML | |
1021 | * | |
1022 | * @param $newparms Signed parameters | |
1023 | * @param $endpoint URL of the external tool | |
1024 | * @param $debug Debug (true/false) | |
1025 | */ | |
dbb0fec9 | 1026 | function lti_post_launch_html($newparms, $endpoint, $debug=false) { |
b9b2e7bb | 1027 | $r = "<form action=\"".$endpoint."\" name=\"ltiLaunchForm\" id=\"ltiLaunchForm\" method=\"post\" encType=\"application/x-www-form-urlencoded\">\n"; |
e27cb316 | 1028 | |
b9b2e7bb CS |
1029 | $submittext = $newparms['ext_submit']; |
1030 | ||
1031 | // Contruct html for the launch parameters | |
1032 | foreach ($newparms as $key => $value) { | |
1033 | $key = htmlspecialchars($key); | |
1034 | $value = htmlspecialchars($value); | |
1035 | if ( $key == "ext_submit" ) { | |
1036 | $r .= "<input type=\"submit\" name=\""; | |
1037 | } else { | |
1038 | $r .= "<input type=\"hidden\" name=\""; | |
1039 | } | |
1040 | $r .= $key; | |
1041 | $r .= "\" value=\""; | |
1042 | $r .= $value; | |
1043 | $r .= "\"/>\n"; | |
1044 | } | |
1045 | ||
1046 | if ( $debug ) { | |
1047 | $r .= "<script language=\"javascript\"> \n"; | |
1048 | $r .= " //<![CDATA[ \n"; | |
1049 | $r .= "function basicltiDebugToggle() {\n"; | |
1050 | $r .= " var ele = document.getElementById(\"basicltiDebug\");\n"; | |
ea04a9f9 | 1051 | $r .= " if (ele.style.display == \"block\") {\n"; |
b9b2e7bb CS |
1052 | $r .= " ele.style.display = \"none\";\n"; |
1053 | $r .= " }\n"; | |
1054 | $r .= " else {\n"; | |
1055 | $r .= " ele.style.display = \"block\";\n"; | |
1056 | $r .= " }\n"; | |
1057 | $r .= "} \n"; | |
1058 | $r .= " //]]> \n"; | |
1059 | $r .= "</script>\n"; | |
1060 | $r .= "<a id=\"displayText\" href=\"javascript:basicltiDebugToggle();\">"; | |
1061 | $r .= get_string("toggle_debug_data", "lti")."</a>\n"; | |
1062 | $r .= "<div id=\"basicltiDebug\" style=\"display:none\">\n"; | |
1063 | $r .= "<b>".get_string("basiclti_endpoint", "lti")."</b><br/>\n"; | |
1064 | $r .= $endpoint . "<br/>\n <br/>\n"; | |
1065 | $r .= "<b>".get_string("basiclti_parameters", "lti")."</b><br/>\n"; | |
1066 | foreach ($newparms as $key => $value) { | |
1067 | $key = htmlspecialchars($key); | |
1068 | $value = htmlspecialchars($value); | |
1069 | $r .= "$key = $value<br/>\n"; | |
1070 | } | |
1071 | $r .= " <br/>\n"; | |
3dd9ca24 | 1072 | //$r .= "<p><b>".get_string("basiclti_base_string", "lti")."</b><br/>\n".$lastbasestring."</p>\n"; |
b9b2e7bb CS |
1073 | $r .= "</div>\n"; |
1074 | } | |
1075 | $r .= "</form>\n"; | |
1076 | ||
1077 | if ( ! $debug ) { | |
1078 | $ext_submit = "ext_submit"; | |
1079 | $ext_submit_text = $submittext; | |
1080 | $r .= " <script type=\"text/javascript\"> \n" . | |
1081 | " //<![CDATA[ \n" . | |
1082 | " document.getElementById(\"ltiLaunchForm\").style.display = \"none\";\n" . | |
1083 | " nei = document.createElement('input');\n" . | |
1084 | " nei.setAttribute('type', 'hidden');\n" . | |
1085 | " nei.setAttribute('name', '".$ext_submit."');\n" . | |
1086 | " nei.setAttribute('value', '".$ext_submit_text."');\n" . | |
1087 | " document.getElementById(\"ltiLaunchForm\").appendChild(nei);\n" . | |
1088 | " document.ltiLaunchForm.submit(); \n" . | |
1089 | " //]]> \n" . | |
1090 | " </script> \n"; | |
1091 | } | |
1092 | return $r; | |
1093 | } | |
1094 | ||
ea04a9f9 | 1095 | function lti_get_type($typeid) { |
996b0fd9 | 1096 | global $DB; |
e27cb316 | 1097 | |
996b0fd9 | 1098 | return $DB->get_record('lti_types', array('id' => $typeid)); |
57d1dffd CS |
1099 | } |
1100 | ||
ea04a9f9 EL |
1101 | function lti_get_launch_container($lti, $toolconfig) { |
1102 | if (empty($lti->launchcontainer)) { | |
e27cb316 CS |
1103 | $lti->launchcontainer = LTI_LAUNCH_CONTAINER_DEFAULT; |
1104 | } | |
1105 | ||
ea04a9f9 EL |
1106 | if ($lti->launchcontainer == LTI_LAUNCH_CONTAINER_DEFAULT) { |
1107 | if (isset($toolconfig['launchcontainer'])) { | |
c4d80efe CS |
1108 | $launchcontainer = $toolconfig['launchcontainer']; |
1109 | } | |
1110 | } else { | |
1111 | $launchcontainer = $lti->launchcontainer; | |
1112 | } | |
e27cb316 | 1113 | |
ea04a9f9 | 1114 | if (empty($launchcontainer) || $launchcontainer == LTI_LAUNCH_CONTAINER_DEFAULT) { |
c4d80efe CS |
1115 | $launchcontainer = LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS; |
1116 | } | |
57d1dffd CS |
1117 | |
1118 | $devicetype = get_device_type(); | |
1119 | ||
1120 | //Scrolling within the object element doesn't work on iOS or Android | |
1121 | //Opening the popup window also had some issues in testing | |
1122 | //For mobile devices, always take up the entire screen to ensure the best experience | |
ea04a9f9 | 1123 | if ($devicetype === 'mobile' || $devicetype === 'tablet' ) { |
57d1dffd CS |
1124 | $launchcontainer = LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW; |
1125 | } | |
e27cb316 | 1126 | |
57d1dffd | 1127 | return $launchcontainer; |
d8d04121 CS |
1128 | } |
1129 | ||
1130 | function lti_request_is_using_ssl() { | |
33dca156 PS |
1131 | global $CFG; |
1132 | return (stripos($CFG->httpswwwroot, 'https://') === 0); | |
d8d04121 CS |
1133 | } |
1134 | ||
ea04a9f9 EL |
1135 | function lti_ensure_url_is_https($url) { |
1136 | if (!strstr($url, '://')) { | |
d8d04121 CS |
1137 | $url = 'https://' . $url; |
1138 | } else { | |
1139 | //If the URL starts with http, replace with https | |
ea04a9f9 | 1140 | if (stripos($url, 'http://') === 0) { |
d8d04121 CS |
1141 | $url = 'https://' . substr($url, 8); |
1142 | } | |
1143 | } | |
e27cb316 | 1144 | |
d8d04121 | 1145 | return $url; |
61eb12d4 | 1146 | } |