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