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