[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/lti/ -> locallib.php (source)

   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&nbsp;<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 .= "&nbsp;<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  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1