|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 // This file is part of Moodle - http://moodle.org/ 00003 // 00004 // Moodle is free software: you can redistribute it and/or modify 00005 // it under the terms of the GNU General Public License as published by 00006 // the Free Software Foundation, either version 3 of the License, or 00007 // (at your option) any later version. 00008 // 00009 // Moodle is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 // GNU General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License 00015 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00016 // 00017 // This file is part of BasicLTI4Moodle 00018 // 00019 // BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability) 00020 // consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web 00021 // based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI 00022 // specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS 00023 // are already supporting or going to support BasicLTI. This project Implements the consumer 00024 // for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas. 00025 // BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem 00026 // at the GESSI research group at UPC. 00027 // SimpleLTI consumer for Moodle is an implementation of the early specification of LTI 00028 // by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a 00029 // Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier. 00030 // 00031 // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis 00032 // of the Universitat Politecnica de Catalunya http://www.upc.edu 00033 // Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu 00034 00050 defined('MOODLE_INTERNAL') || die; 00051 00052 // TODO: Switch to core oauthlib once implemented - MDL-30149 00053 use moodle\mod\lti as lti; 00054 00055 require_once($CFG->dirroot.'/mod/lti/OAuth.php'); 00056 00057 define('LTI_URL_DOMAIN_REGEX', '/(?:https?:\/\/)?(?:www\.)?([^\/]+)(?:\/|$)/i'); 00058 00059 define('LTI_LAUNCH_CONTAINER_DEFAULT', 1); 00060 define('LTI_LAUNCH_CONTAINER_EMBED', 2); 00061 define('LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS', 3); 00062 define('LTI_LAUNCH_CONTAINER_WINDOW', 4); 00063 define('LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW', 5); 00064 00065 define('LTI_TOOL_STATE_ANY', 0); 00066 define('LTI_TOOL_STATE_CONFIGURED', 1); 00067 define('LTI_TOOL_STATE_PENDING', 2); 00068 define('LTI_TOOL_STATE_REJECTED', 3); 00069 00070 define('LTI_SETTING_NEVER', 0); 00071 define('LTI_SETTING_ALWAYS', 1); 00072 define('LTI_SETTING_DELEGATE', 2); 00073 00079 function lti_view($instance) { 00080 global $PAGE, $CFG; 00081 00082 if (empty($instance->typeid)) { 00083 $tool = lti_get_tool_by_url_match($instance->toolurl, $instance->course); 00084 if ($tool) { 00085 $typeid = $tool->id; 00086 } else { 00087 $typeid = null; 00088 } 00089 } else { 00090 $typeid = $instance->typeid; 00091 } 00092 00093 if ($typeid) { 00094 $typeconfig = lti_get_type_config($typeid); 00095 } else { 00096 //There is no admin configuration for this tool. Use configuration in the lti instance record plus some defaults. 00097 $typeconfig = (array)$instance; 00098 00099 $typeconfig['sendname'] = $instance->instructorchoicesendname; 00100 $typeconfig['sendemailaddr'] = $instance->instructorchoicesendemailaddr; 00101 $typeconfig['customparameters'] = $instance->instructorcustomparameters; 00102 $typeconfig['acceptgrades'] = $instance->instructorchoiceacceptgrades; 00103 $typeconfig['allowroster'] = $instance->instructorchoiceallowroster; 00104 $typeconfig['forcessl'] = '0'; 00105 } 00106 00107 //Default the organizationid if not specified 00108 if (empty($typeconfig['organizationid'])) { 00109 $urlparts = parse_url($CFG->wwwroot); 00110 00111 $typeconfig['organizationid'] = $urlparts['host']; 00112 } 00113 00114 if (!empty($instance->resourcekey)) { 00115 $key = $instance->resourcekey; 00116 } else if (!empty($typeconfig['resourcekey'])) { 00117 $key = $typeconfig['resourcekey']; 00118 } else { 00119 $key = ''; 00120 } 00121 00122 if (!empty($instance->password)) { 00123 $secret = $instance->password; 00124 } else if (!empty($typeconfig['password'])) { 00125 $secret = $typeconfig['password']; 00126 } else { 00127 $secret = ''; 00128 } 00129 00130 $endpoint = !empty($instance->toolurl) ? $instance->toolurl : $typeconfig['toolurl']; 00131 $endpoint = trim($endpoint); 00132 00133 //If the current request is using SSL and a secure tool URL is specified, use it 00134 if (lti_request_is_using_ssl() && !empty($instance->securetoolurl)) { 00135 $endpoint = trim($instance->securetoolurl); 00136 } 00137 00138 //If SSL is forced, use the secure tool url if specified. Otherwise, make sure https is on the normal launch URL. 00139 if ($typeconfig['forcessl'] == '1') { 00140 if (!empty($instance->securetoolurl)) { 00141 $endpoint = trim($instance->securetoolurl); 00142 } 00143 00144 $endpoint = lti_ensure_url_is_https($endpoint); 00145 } else { 00146 if (!strstr($endpoint, '://')) { 00147 $endpoint = 'http://' . $endpoint; 00148 } 00149 } 00150 00151 $orgid = $typeconfig['organizationid']; 00152 00153 $course = $PAGE->course; 00154 $requestparams = lti_build_request($instance, $typeconfig, $course); 00155 00156 $launchcontainer = lti_get_launch_container($instance, $typeconfig); 00157 $returnurlparams = array('course' => $course->id, 'launch_container' => $launchcontainer, 'instanceid' => $instance->id); 00158 00159 if ( $orgid ) { 00160 $requestparams["tool_consumer_instance_guid"] = $orgid; 00161 } 00162 00163 if (empty($key) || empty($secret)) { 00164 $returnurlparams['unsigned'] = '1'; 00165 00166 //Add the return URL. We send the launch container along to help us avoid frames-within-frames when the user returns 00167 $url = new moodle_url('/mod/lti/return.php', $returnurlparams); 00168 $returnurl = $url->out(false); 00169 00170 if ($typeconfig['forcessl'] == '1') { 00171 $returnurl = lti_ensure_url_is_https($returnurl); 00172 } 00173 00174 $requestparams['launch_presentation_return_url'] = $returnurl; 00175 } 00176 00177 if (!empty($key) && !empty($secret)) { 00178 $parms = lti_sign_parameters($requestparams, $endpoint, "POST", $key, $secret); 00179 } else { 00180 //If no key and secret, do the launch unsigned. 00181 $parms = $requestparams; 00182 } 00183 00184 $debuglaunch = ( $instance->debuglaunch == 1 ); 00185 00186 $content = lti_post_launch_html($parms, $endpoint, $debuglaunch); 00187 00188 echo $content; 00189 } 00190 00191 function lti_build_sourcedid($instanceid, $userid, $launchid = null, $servicesalt) { 00192 $data = new stdClass(); 00193 00194 $data->instanceid = $instanceid; 00195 $data->userid = $userid; 00196 if (!empty($launchid)) { 00197 $data->launchid = $launchid; 00198 } else { 00199 $data->launchid = mt_rand(); 00200 } 00201 00202 $json = json_encode($data); 00203 00204 $hash = hash('sha256', $json . $servicesalt, false); 00205 00206 $container = new stdClass(); 00207 $container->data = $data; 00208 $container->hash = $hash; 00209 00210 return $container; 00211 } 00212 00222 function lti_build_request($instance, $typeconfig, $course) { 00223 global $USER, $CFG; 00224 00225 if (empty($instance->cmid)) { 00226 $instance->cmid = 0; 00227 } 00228 00229 $role = lti_get_ims_role($USER, $instance->cmid, $instance->course); 00230 00231 $locale = $course->lang; 00232 if ( strlen($locale) < 1 ) { 00233 $locale = $CFG->lang; 00234 } 00235 00236 $requestparams = array( 00237 'resource_link_id' => $instance->id, 00238 'resource_link_title' => $instance->name, 00239 'resource_link_description' => $instance->intro, 00240 'user_id' => $USER->id, 00241 'roles' => $role, 00242 'context_id' => $course->id, 00243 'context_label' => $course->shortname, 00244 'context_title' => $course->fullname, 00245 'launch_presentation_locale' => $locale, 00246 ); 00247 00248 $placementsecret = $instance->servicesalt; 00249 00250 if ( isset($placementsecret) ) { 00251 $sourcedid = json_encode(lti_build_sourcedid($instance->id, $USER->id, null, $placementsecret)); 00252 } 00253 00254 if ( isset($placementsecret) && 00255 ( $typeconfig['acceptgrades'] == LTI_SETTING_ALWAYS || 00256 ( $typeconfig['acceptgrades'] == LTI_SETTING_DELEGATE && $instance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS ) ) ) { 00257 $requestparams['lis_result_sourcedid'] = $sourcedid; 00258 00259 //Add outcome service URL 00260 $serviceurl = new moodle_url('/mod/lti/service.php'); 00261 $serviceurl = $serviceurl->out(); 00262 00263 if ($typeconfig['forcessl'] == '1') { 00264 $serviceurl = lti_ensure_url_is_https($serviceurl); 00265 } 00266 00267 $requestparams['lis_outcome_service_url'] = $serviceurl; 00268 } 00269 00270 // Send user's name and email data if appropriate 00271 if ( $typeconfig['sendname'] == LTI_SETTING_ALWAYS || 00272 ( $typeconfig['sendname'] == LTI_SETTING_DELEGATE && $instance->instructorchoicesendname == LTI_SETTING_ALWAYS ) ) { 00273 $requestparams['lis_person_name_given'] = $USER->firstname; 00274 $requestparams['lis_person_name_family'] = $USER->lastname; 00275 $requestparams['lis_person_name_full'] = $USER->firstname." ".$USER->lastname; 00276 } 00277 00278 if ( $typeconfig['sendemailaddr'] == LTI_SETTING_ALWAYS || 00279 ( $typeconfig['sendemailaddr'] == LTI_SETTING_DELEGATE && $instance->instructorchoicesendemailaddr == LTI_SETTING_ALWAYS ) ) { 00280 $requestparams['lis_person_contact_email_primary'] = $USER->email; 00281 } 00282 00283 // Concatenate the custom parameters from the administrator and the instructor 00284 // Instructor parameters are only taken into consideration if the administrator 00285 // has giver permission 00286 $customstr = $typeconfig['customparameters']; 00287 $instructorcustomstr = $instance->instructorcustomparameters; 00288 $custom = array(); 00289 $instructorcustom = array(); 00290 if ($customstr) { 00291 $custom = lti_split_custom_parameters($customstr); 00292 } 00293 if (!isset($typeconfig['allowinstructorcustom']) || $typeconfig['allowinstructorcustom'] == LTI_SETTING_NEVER) { 00294 $requestparams = array_merge($custom, $requestparams); 00295 } else { 00296 if ($instructorcustomstr) { 00297 $instructorcustom = lti_split_custom_parameters($instructorcustomstr); 00298 } 00299 foreach ($instructorcustom as $key => $val) { 00300 // Ignore the instructor's parameter 00301 if (!array_key_exists($key, $custom)) { 00302 $custom[$key] = $val; 00303 } 00304 } 00305 $requestparams = array_merge($custom, $requestparams); 00306 } 00307 00308 // Make sure we let the tool know what LMS they are being called from 00309 $requestparams["ext_lms"] = "moodle-2"; 00310 $requestparams['tool_consumer_info_product_family_code'] = 'moodle'; 00311 $requestparams['tool_consumer_info_version'] = strval($CFG->version); 00312 00313 // Add oauth_callback to be compliant with the 1.0A spec 00314 $requestparams['oauth_callback'] = 'about:blank'; 00315 00316 //The submit button needs to be part of the signature as it gets posted with the form. 00317 //This needs to be here to support launching without javascript. 00318 $submittext = get_string('press_to_submit', 'lti'); 00319 $requestparams['ext_submit'] = $submittext; 00320 00321 $requestparams['lti_version'] = 'LTI-1p0'; 00322 $requestparams['lti_message_type'] = 'basic-lti-launch-request'; 00323 00324 return $requestparams; 00325 } 00326 00327 function lti_get_tool_table($tools, $id) { 00328 global $CFG, $USER; 00329 $html = ''; 00330 00331 $typename = get_string('typename', 'lti'); 00332 $baseurl = get_string('baseurl', 'lti'); 00333 $action = get_string('action', 'lti'); 00334 $createdon = get_string('createdon', 'lti'); 00335 00336 if ($id == 'lti_configured') { 00337 $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>'; 00338 } 00339 00340 if (!empty($tools)) { 00341 $html .= " 00342 <div id=\"{$id}_container\" style=\"margin-top:.5em;margin-bottom:.5em\"> 00343 <table id=\"{$id}_tools\"> 00344 <thead> 00345 <tr> 00346 <th>$typename</th> 00347 <th>$baseurl</th> 00348 <th>$createdon</th> 00349 <th>$action</th> 00350 </tr> 00351 </thead> 00352 "; 00353 00354 foreach ($tools as $type) { 00355 $date = userdate($type->timecreated); 00356 $accept = get_string('accept', 'lti'); 00357 $update = get_string('update', 'lti'); 00358 $delete = get_string('delete', 'lti'); 00359 00360 $accepthtml = " 00361 <a class=\"editing_accept\" href=\"{$CFG->wwwroot}/mod/lti/typessettings.php?action=accept&id={$type->id}&sesskey={$USER->sesskey}&tab={$id}\" title=\"{$accept}\"> 00362 <img class=\"iconsmall\" alt=\"{$accept}\" src=\"{$CFG->wwwroot}/pix/t/clear.gif\"/> 00363 </a> 00364 "; 00365 00366 $deleteaction = 'delete'; 00367 00368 if ($type->state == LTI_TOOL_STATE_CONFIGURED) { 00369 $accepthtml = ''; 00370 } 00371 00372 if ($type->state != LTI_TOOL_STATE_REJECTED) { 00373 $deleteaction = 'reject'; 00374 $delete = get_string('reject', 'lti'); 00375 } 00376 00377 $html .= " 00378 <tr> 00379 <td> 00380 {$type->name} 00381 </td> 00382 <td> 00383 {$type->baseurl} 00384 </td> 00385 <td> 00386 {$date} 00387 </td> 00388 <td align=\"center\"> 00389 {$accepthtml} 00390 <a class=\"editing_update\" href=\"{$CFG->wwwroot}/mod/lti/typessettings.php?action=update&id={$type->id}&sesskey={$USER->sesskey}&tab={$id}\" title=\"{$update}\"> 00391 <img class=\"iconsmall\" alt=\"{$update}\" src=\"{$CFG->wwwroot}/pix/t/edit.gif\"/> 00392 </a> 00393 <a class=\"editing_delete\" href=\"{$CFG->wwwroot}/mod/lti/typessettings.php?action={$deleteaction}&id={$type->id}&sesskey={$USER->sesskey}&tab={$id}\" title=\"{$delete}\"> 00394 <img class=\"iconsmall\" alt=\"{$delete}\" src=\"{$CFG->wwwroot}/pix/t/delete.gif\"/> 00395 </a> 00396 </td> 00397 </tr> 00398 "; 00399 } 00400 $html .= '</table></div>'; 00401 } else { 00402 $html .= get_string('no_' . $id, 'lti'); 00403 } 00404 00405 return $html; 00406 } 00407 00415 function lti_split_custom_parameters($customstr) { 00416 $textlib = textlib_get_instance(); 00417 00418 $lines = preg_split("/[\n;]/", $customstr); 00419 $retval = array(); 00420 foreach ($lines as $line) { 00421 $pos = strpos($line, "="); 00422 if ( $pos === false || $pos < 1 ) { 00423 continue; 00424 } 00425 $key = trim($textlib->substr($line, 0, $pos)); 00426 $val = trim($textlib->substr($line, $pos+1, strlen($line))); 00427 $key = lti_map_keyname($key); 00428 $retval['custom_'.$key] = $val; 00429 } 00430 return $retval; 00431 } 00432 00440 function lti_map_keyname($key) { 00441 $textlib = textlib_get_instance(); 00442 00443 $newkey = ""; 00444 $key = $textlib->strtolower(trim($key)); 00445 foreach (str_split($key) as $ch) { 00446 if ( ($ch >= 'a' && $ch <= 'z') || ($ch >= '0' && $ch <= '9') ) { 00447 $newkey .= $ch; 00448 } else { 00449 $newkey .= '_'; 00450 } 00451 } 00452 return $newkey; 00453 } 00454 00462 function lti_get_ims_role($user, $cmid, $courseid) { 00463 $roles = array(); 00464 00465 if (empty($cmid)) { 00466 //If no cmid is passed, check if the user is a teacher in the course 00467 //This allows other modules to programmatically "fake" a launch without 00468 //a real LTI instance 00469 $coursecontext = get_context_instance(CONTEXT_COURSE, $courseid); 00470 00471 if (has_capability('moodle/course:manageactivities', $coursecontext)) { 00472 array_push($roles, 'Instructor'); 00473 } else { 00474 array_push($roles, 'Learner'); 00475 } 00476 } else { 00477 $context = get_context_instance(CONTEXT_MODULE, $cmid); 00478 00479 if (has_capability('mod/lti:manage', $context)) { 00480 array_push($roles, 'Instructor'); 00481 } else { 00482 array_push($roles, 'Learner'); 00483 } 00484 } 00485 00486 if (is_siteadmin($user)) { 00487 array_push($roles, 'urn:lti:sysrole:ims/lis/Administrator'); 00488 } 00489 00490 return join(',', $roles); 00491 } 00492 00500 function lti_get_type_config($typeid) { 00501 global $DB; 00502 00503 $query = "SELECT name, value 00504 FROM {lti_types_config} 00505 WHERE typeid = :typeid1 00506 UNION ALL 00507 SELECT 'toolurl' AS name, baseurl AS value 00508 FROM {lti_types} 00509 WHERE id = :typeid2"; 00510 00511 $typeconfig = array(); 00512 $configs = $DB->get_records_sql($query, array('typeid1' => $typeid, 'typeid2' => $typeid)); 00513 00514 if (!empty($configs)) { 00515 foreach ($configs as $config) { 00516 $typeconfig[$config->name] = $config->value; 00517 } 00518 } 00519 00520 return $typeconfig; 00521 } 00522 00523 function lti_get_tools_by_url($url, $state, $courseid = null) { 00524 $domain = lti_get_domain_from_url($url); 00525 00526 return lti_get_tools_by_domain($domain, $state, $courseid); 00527 } 00528 00529 function lti_get_tools_by_domain($domain, $state = null, $courseid = null) { 00530 global $DB, $SITE; 00531 00532 $filters = array('tooldomain' => $domain); 00533 00534 $statefilter = ''; 00535 $coursefilter = ''; 00536 00537 if ($state) { 00538 $statefilter = 'AND state = :state'; 00539 } 00540 00541 if ($courseid && $courseid != $SITE->id) { 00542 $coursefilter = 'OR course = :courseid'; 00543 } 00544 00545 $query = "SELECT * 00546 FROM {lti_types} 00547 WHERE tooldomain = :tooldomain 00548 AND (course = :siteid $coursefilter) 00549 $statefilter"; 00550 00551 return $DB->get_records_sql($query, array( 00552 'courseid' => $courseid, 00553 'siteid' => $SITE->id, 00554 'tooldomain' => $domain, 00555 'state' => $state 00556 )); 00557 } 00558 00563 function lti_filter_get_types($course) { 00564 global $DB; 00565 00566 if (!empty($course)) { 00567 $filter = array('course' => $course); 00568 } else { 00569 $filter = array(); 00570 } 00571 00572 return $DB->get_records('lti_types', $filter); 00573 } 00574 00575 function lti_get_types_for_add_instance() { 00576 global $DB, $SITE, $COURSE; 00577 00578 $query = "SELECT * 00579 FROM {lti_types} 00580 WHERE coursevisible = 1 00581 AND (course = :siteid OR course = :courseid) 00582 AND state = :active"; 00583 00584 $admintypes = $DB->get_records_sql($query, array('siteid' => $SITE->id, 'courseid' => $COURSE->id, 'active' => LTI_TOOL_STATE_CONFIGURED)); 00585 00586 $types = array(); 00587 $types[0] = (object)array('name' => get_string('automatic', 'lti'), 'course' => $SITE->id); 00588 00589 foreach ($admintypes as $type) { 00590 $types[$type->id] = $type; 00591 } 00592 00593 return $types; 00594 } 00595 00596 function lti_get_domain_from_url($url) { 00597 $matches = array(); 00598 00599 if (preg_match(LTI_URL_DOMAIN_REGEX, $url, $matches)) { 00600 return $matches[1]; 00601 } 00602 } 00603 00604 function lti_get_tool_by_url_match($url, $courseid = null, $state = LTI_TOOL_STATE_CONFIGURED) { 00605 $possibletools = lti_get_tools_by_url($url, $state, $courseid); 00606 00607 return lti_get_best_tool_by_url($url, $possibletools, $courseid); 00608 } 00609 00610 function lti_get_url_thumbprint($url) { 00611 $urlparts = parse_url(strtolower($url)); 00612 if (!isset($urlparts['path'])) { 00613 $urlparts['path'] = ''; 00614 } 00615 00616 if (!isset($urlparts['host'])) { 00617 $urlparts['host'] = ''; 00618 } 00619 00620 if (substr($urlparts['host'], 0, 4) === 'www.') { 00621 $urlparts['host'] = substr($urlparts['host'], 4); 00622 } 00623 00624 return $urllower = $urlparts['host'] . '/' . $urlparts['path']; 00625 } 00626 00627 function lti_get_best_tool_by_url($url, $tools, $courseid = null) { 00628 if (count($tools) === 0) { 00629 return null; 00630 } 00631 00632 $urllower = lti_get_url_thumbprint($url); 00633 00634 foreach ($tools as $tool) { 00635 $tool->_matchscore = 0; 00636 00637 $toolbaseurllower = lti_get_url_thumbprint($tool->baseurl); 00638 00639 if ($urllower === $toolbaseurllower) { 00640 //100 points for exact thumbprint match 00641 $tool->_matchscore += 100; 00642 } else if (substr($urllower, 0, strlen($toolbaseurllower)) === $toolbaseurllower) { 00643 //50 points if tool thumbprint starts with the base URL thumbprint 00644 $tool->_matchscore += 50; 00645 } 00646 00647 //Prefer course tools over site tools 00648 if (!empty($courseid)) { 00649 //Minus 10 points for not matching the course id (global tools) 00650 if ($tool->course != $courseid) { 00651 $tool->_matchscore -= 10; 00652 } 00653 } 00654 } 00655 00656 $bestmatch = array_reduce($tools, function($value, $tool) { 00657 if ($tool->_matchscore > $value->_matchscore) { 00658 return $tool; 00659 } else { 00660 return $value; 00661 } 00662 00663 }, (object)array('_matchscore' => -1)); 00664 00665 //None of the tools are suitable for this URL 00666 if ($bestmatch->_matchscore <= 0) { 00667 return null; 00668 } 00669 00670 return $bestmatch; 00671 } 00672 00673 function lti_get_shared_secrets_by_key($key) { 00674 global $DB; 00675 00676 //Look up the shared secret for the specified key in both the types_config table (for configured tools) 00677 //And in the lti resource table for ad-hoc tools 00678 $query = "SELECT t2.value 00679 FROM {lti_types_config} t1 00680 JOIN {lti_types_config} t2 ON t1.typeid = t2.typeid 00681 JOIN {lti_types} type ON t2.typeid = type.id 00682 WHERE t1.name = 'resourcekey' 00683 AND t1.value = :key1 00684 AND t2.name = 'password' 00685 AND type.state = :configured 00686 UNION 00687 SELECT password AS value 00688 FROM {lti} 00689 WHERE resourcekey = :key2"; 00690 00691 $sharedsecrets = $DB->get_records_sql($query, array('configured' => LTI_TOOL_STATE_CONFIGURED, 'key1' => $key, 'key2' => $key)); 00692 00693 $values = array_map(function($item) { 00694 return $item->value; 00695 }, $sharedsecrets); 00696 00697 //There should really only be one shared secret per key. But, we can't prevent 00698 //more than one getting entered. For instance, if the same key is used for two tool providers. 00699 return $values; 00700 } 00701 00707 function lti_delete_type($id) { 00708 global $DB; 00709 00710 //We should probably just copy the launch URL to the tool instances in this case... using a single query 00711 /* 00712 $instances = $DB->get_records('lti', array('typeid' => $id)); 00713 foreach ($instances as $instance) { 00714 $instance->typeid = 0; 00715 $DB->update_record('lti', $instance); 00716 }*/ 00717 00718 $DB->delete_records('lti_types', array('id' => $id)); 00719 $DB->delete_records('lti_types_config', array('typeid' => $id)); 00720 } 00721 00722 function lti_set_state_for_type($id, $state) { 00723 global $DB; 00724 00725 $DB->update_record('lti_types', array('id' => $id, 'state' => $state)); 00726 } 00727 00735 function lti_get_config($ltiobject) { 00736 $typeconfig = array(); 00737 $typeconfig = (array)$ltiobject; 00738 $additionalconfig = lti_get_type_config($ltiobject->typeid); 00739 $typeconfig = array_merge($typeconfig, $additionalconfig); 00740 return $typeconfig; 00741 } 00742 00752 function lti_get_type_config_from_instance($id) { 00753 global $DB; 00754 00755 $instance = $DB->get_record('lti', array('id' => $id)); 00756 $config = lti_get_config($instance); 00757 00758 $type = new stdClass(); 00759 $type->lti_fix = $id; 00760 if (isset($config['toolurl'])) { 00761 $type->lti_toolurl = $config['toolurl']; 00762 } 00763 if (isset($config['instructorchoicesendname'])) { 00764 $type->lti_sendname = $config['instructorchoicesendname']; 00765 } 00766 if (isset($config['instructorchoicesendemailaddr'])) { 00767 $type->lti_sendemailaddr = $config['instructorchoicesendemailaddr']; 00768 } 00769 if (isset($config['instructorchoiceacceptgrades'])) { 00770 $type->lti_acceptgrades = $config['instructorchoiceacceptgrades']; 00771 } 00772 if (isset($config['instructorchoiceallowroster'])) { 00773 $type->lti_allowroster = $config['instructorchoiceallowroster']; 00774 } 00775 00776 if (isset($config['instructorcustomparameters'])) { 00777 $type->lti_allowsetting = $config['instructorcustomparameters']; 00778 } 00779 return $type; 00780 } 00781 00789 function lti_get_type_type_config($id) { 00790 global $DB; 00791 00792 $basicltitype = $DB->get_record('lti_types', array('id' => $id)); 00793 $config = lti_get_type_config($id); 00794 00795 $type = new stdClass(); 00796 00797 $type->lti_typename = $basicltitype->name; 00798 00799 $type->typeid = $basicltitype->id; 00800 00801 $type->lti_toolurl = $basicltitype->baseurl; 00802 00803 if (isset($config['resourcekey'])) { 00804 $type->lti_resourcekey = $config['resourcekey']; 00805 } 00806 if (isset($config['password'])) { 00807 $type->lti_password = $config['password']; 00808 } 00809 00810 if (isset($config['sendname'])) { 00811 $type->lti_sendname = $config['sendname']; 00812 } 00813 if (isset($config['instructorchoicesendname'])) { 00814 $type->lti_instructorchoicesendname = $config['instructorchoicesendname']; 00815 } 00816 if (isset($config['sendemailaddr'])) { 00817 $type->lti_sendemailaddr = $config['sendemailaddr']; 00818 } 00819 if (isset($config['instructorchoicesendemailaddr'])) { 00820 $type->lti_instructorchoicesendemailaddr = $config['instructorchoicesendemailaddr']; 00821 } 00822 if (isset($config['acceptgrades'])) { 00823 $type->lti_acceptgrades = $config['acceptgrades']; 00824 } 00825 if (isset($config['instructorchoiceacceptgrades'])) { 00826 $type->lti_instructorchoiceacceptgrades = $config['instructorchoiceacceptgrades']; 00827 } 00828 if (isset($config['allowroster'])) { 00829 $type->lti_allowroster = $config['allowroster']; 00830 } 00831 if (isset($config['instructorchoiceallowroster'])) { 00832 $type->lti_instructorchoiceallowroster = $config['instructorchoiceallowroster']; 00833 } 00834 00835 if (isset($config['customparameters'])) { 00836 $type->lti_customparameters = $config['customparameters']; 00837 } 00838 00839 if (isset($config['forcessl'])) { 00840 $type->lti_forcessl = $config['forcessl']; 00841 } 00842 00843 if (isset($config['organizationid'])) { 00844 $type->lti_organizationid = $config['organizationid']; 00845 } 00846 if (isset($config['organizationurl'])) { 00847 $type->lti_organizationurl = $config['organizationurl']; 00848 } 00849 if (isset($config['organizationdescr'])) { 00850 $type->lti_organizationdescr = $config['organizationdescr']; 00851 } 00852 if (isset($config['launchcontainer'])) { 00853 $type->lti_launchcontainer = $config['launchcontainer']; 00854 } 00855 00856 if (isset($config['coursevisible'])) { 00857 $type->lti_coursevisible = $config['coursevisible']; 00858 } 00859 00860 if (isset($config['debuglaunch'])) { 00861 $type->lti_debuglaunch = $config['debuglaunch']; 00862 } 00863 00864 if (isset($config['module_class_type'])) { 00865 $type->lti_module_class_type = $config['module_class_type']; 00866 } 00867 00868 return $type; 00869 } 00870 00871 function lti_prepare_type_for_save($type, $config) { 00872 $type->baseurl = $config->lti_toolurl; 00873 $type->tooldomain = lti_get_domain_from_url($config->lti_toolurl); 00874 $type->name = $config->lti_typename; 00875 00876 $type->coursevisible = !empty($config->lti_coursevisible) ? $config->lti_coursevisible : 0; 00877 $config->lti_coursevisible = $type->coursevisible; 00878 00879 $type->forcessl = !empty($config->lti_forcessl) ? $config->lti_forcessl : 0; 00880 $config->lti_forcessl = $type->forcessl; 00881 00882 $type->timemodified = time(); 00883 00884 unset ($config->lti_typename); 00885 unset ($config->lti_toolurl); 00886 } 00887 00888 function lti_update_type($type, $config) { 00889 global $DB; 00890 00891 lti_prepare_type_for_save($type, $config); 00892 00893 if ($DB->update_record('lti_types', $type)) { 00894 foreach ($config as $key => $value) { 00895 if (substr($key, 0, 4)=='lti_' && !is_null($value)) { 00896 $record = new StdClass(); 00897 $record->typeid = $type->id; 00898 $record->name = substr($key, 4); 00899 $record->value = $value; 00900 00901 lti_update_config($record); 00902 } 00903 } 00904 } 00905 } 00906 00907 function lti_add_type($type, $config) { 00908 global $USER, $SITE, $DB; 00909 00910 lti_prepare_type_for_save($type, $config); 00911 00912 if (!isset($type->state)) { 00913 $type->state = LTI_TOOL_STATE_PENDING; 00914 } 00915 00916 if (!isset($type->timecreated)) { 00917 $type->timecreated = time(); 00918 } 00919 00920 if (!isset($type->createdby)) { 00921 $type->createdby = $USER->id; 00922 } 00923 00924 if (!isset($type->course)) { 00925 $type->course = $SITE->id; 00926 } 00927 00928 //Create a salt value to be used for signing passed data to extension services 00929 //The outcome service uses the service salt on the instance. This can be used 00930 //for communication with services not related to a specific LTI instance. 00931 $config->lti_servicesalt = uniqid('', true); 00932 00933 $id = $DB->insert_record('lti_types', $type); 00934 00935 if ($id) { 00936 foreach ($config as $key => $value) { 00937 if (substr($key, 0, 4)=='lti_' && !is_null($value)) { 00938 $record = new StdClass(); 00939 $record->typeid = $id; 00940 $record->name = substr($key, 4); 00941 $record->value = $value; 00942 00943 lti_add_config($record); 00944 } 00945 } 00946 } 00947 00948 return $id; 00949 } 00950 00958 function lti_add_config($config) { 00959 global $DB; 00960 00961 return $DB->insert_record('lti_types_config', $config); 00962 } 00963 00971 function lti_update_config($config) { 00972 global $DB; 00973 00974 $return = true; 00975 $old = $DB->get_record('lti_types_config', array('typeid' => $config->typeid, 'name' => $config->name)); 00976 00977 if ($old) { 00978 $config->id = $old->id; 00979 $return = $DB->update_record('lti_types_config', $config); 00980 } else { 00981 $return = $DB->insert_record('lti_types_config', $config); 00982 } 00983 return $return; 00984 } 00985 00998 function lti_sign_parameters($oldparms, $endpoint, $method, $oauthconsumerkey, $oauthconsumersecret) { 00999 //global $lastbasestring; 01000 $parms = $oldparms; 01001 01002 $testtoken = ''; 01003 01004 // TODO: Switch to core oauthlib once implemented - MDL-30149 01005 $hmacmethod = new lti\OAuthSignatureMethod_HMAC_SHA1(); 01006 $testconsumer = new lti\OAuthConsumer($oauthconsumerkey, $oauthconsumersecret, null); 01007 $accreq = lti\OAuthRequest::from_consumer_and_token($testconsumer, $testtoken, $method, $endpoint, $parms); 01008 $accreq->sign_request($hmacmethod, $testconsumer, $testtoken); 01009 01010 // Pass this back up "out of band" for debugging 01011 //$lastbasestring = $accreq->get_signature_base_string(); 01012 01013 $newparms = $accreq->get_parameters(); 01014 01015 return $newparms; 01016 } 01017 01025 function lti_post_launch_html($newparms, $endpoint, $debug=false) { 01026 $r = "<form action=\"".$endpoint."\" name=\"ltiLaunchForm\" id=\"ltiLaunchForm\" method=\"post\" encType=\"application/x-www-form-urlencoded\">\n"; 01027 01028 $submittext = $newparms['ext_submit']; 01029 01030 // Contruct html for the launch parameters 01031 foreach ($newparms as $key => $value) { 01032 $key = htmlspecialchars($key); 01033 $value = htmlspecialchars($value); 01034 if ( $key == "ext_submit" ) { 01035 $r .= "<input type=\"submit\" name=\""; 01036 } else { 01037 $r .= "<input type=\"hidden\" name=\""; 01038 } 01039 $r .= $key; 01040 $r .= "\" value=\""; 01041 $r .= $value; 01042 $r .= "\"/>\n"; 01043 } 01044 01045 if ( $debug ) { 01046 $r .= "<script language=\"javascript\"> \n"; 01047 $r .= " //<![CDATA[ \n"; 01048 $r .= "function basicltiDebugToggle() {\n"; 01049 $r .= " var ele = document.getElementById(\"basicltiDebug\");\n"; 01050 $r .= " if (ele.style.display == \"block\") {\n"; 01051 $r .= " ele.style.display = \"none\";\n"; 01052 $r .= " }\n"; 01053 $r .= " else {\n"; 01054 $r .= " ele.style.display = \"block\";\n"; 01055 $r .= " }\n"; 01056 $r .= "} \n"; 01057 $r .= " //]]> \n"; 01058 $r .= "</script>\n"; 01059 $r .= "<a id=\"displayText\" href=\"javascript:basicltiDebugToggle();\">"; 01060 $r .= get_string("toggle_debug_data", "lti")."</a>\n"; 01061 $r .= "<div id=\"basicltiDebug\" style=\"display:none\">\n"; 01062 $r .= "<b>".get_string("basiclti_endpoint", "lti")."</b><br/>\n"; 01063 $r .= $endpoint . "<br/>\n <br/>\n"; 01064 $r .= "<b>".get_string("basiclti_parameters", "lti")."</b><br/>\n"; 01065 foreach ($newparms as $key => $value) { 01066 $key = htmlspecialchars($key); 01067 $value = htmlspecialchars($value); 01068 $r .= "$key = $value<br/>\n"; 01069 } 01070 $r .= " <br/>\n"; 01071 //$r .= "<p><b>".get_string("basiclti_base_string", "lti")."</b><br/>\n".$lastbasestring."</p>\n"; 01072 $r .= "</div>\n"; 01073 } 01074 $r .= "</form>\n"; 01075 01076 if ( ! $debug ) { 01077 $ext_submit = "ext_submit"; 01078 $ext_submit_text = $submittext; 01079 $r .= " <script type=\"text/javascript\"> \n" . 01080 " //<![CDATA[ \n" . 01081 " document.getElementById(\"ltiLaunchForm\").style.display = \"none\";\n" . 01082 " nei = document.createElement('input');\n" . 01083 " nei.setAttribute('type', 'hidden');\n" . 01084 " nei.setAttribute('name', '".$ext_submit."');\n" . 01085 " nei.setAttribute('value', '".$ext_submit_text."');\n" . 01086 " document.getElementById(\"ltiLaunchForm\").appendChild(nei);\n" . 01087 " document.ltiLaunchForm.submit(); \n" . 01088 " //]]> \n" . 01089 " </script> \n"; 01090 } 01091 return $r; 01092 } 01093 01094 function lti_get_type($typeid) { 01095 global $DB; 01096 01097 return $DB->get_record('lti_types', array('id' => $typeid)); 01098 } 01099 01100 function lti_get_launch_container($lti, $toolconfig) { 01101 if (empty($lti->launchcontainer)) { 01102 $lti->launchcontainer = LTI_LAUNCH_CONTAINER_DEFAULT; 01103 } 01104 01105 if ($lti->launchcontainer == LTI_LAUNCH_CONTAINER_DEFAULT) { 01106 if (isset($toolconfig['launchcontainer'])) { 01107 $launchcontainer = $toolconfig['launchcontainer']; 01108 } 01109 } else { 01110 $launchcontainer = $lti->launchcontainer; 01111 } 01112 01113 if (empty($launchcontainer) || $launchcontainer == LTI_LAUNCH_CONTAINER_DEFAULT) { 01114 $launchcontainer = LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS; 01115 } 01116 01117 $devicetype = get_device_type(); 01118 01119 //Scrolling within the object element doesn't work on iOS or Android 01120 //Opening the popup window also had some issues in testing 01121 //For mobile devices, always take up the entire screen to ensure the best experience 01122 if ($devicetype === 'mobile' || $devicetype === 'tablet' ) { 01123 $launchcontainer = LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW; 01124 } 01125 01126 return $launchcontainer; 01127 } 01128 01129 function lti_request_is_using_ssl() { 01130 global $CFG; 01131 return (stripos($CFG->httpswwwroot, 'https://') === 0); 01132 } 01133 01134 function lti_ensure_url_is_https($url) { 01135 if (!strstr($url, '://')) { 01136 $url = 'https://' . $url; 01137 } else { 01138 //If the URL starts with http, replace with https 01139 if (stripos($url, 'http://') === 0) { 01140 $url = 'https://' . substr($url, 8); 01141 } 01142 } 01143 01144 return $url; 01145 }