Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]

   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 defines the main lti configuration form
  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   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  47   */
  48  
  49  defined('MOODLE_INTERNAL') || die;
  50  
  51  require_once($CFG->dirroot.'/course/moodleform_mod.php');
  52  require_once($CFG->dirroot.'/mod/lti/locallib.php');
  53  
  54  class mod_lti_mod_form extends moodleform_mod {
  55  
  56      /** @var int the tool typeid, or 0 if the instance form is being created for a manually configured tool instance.*/
  57      protected int $typeid;
  58  
  59      /** @var string|null type */
  60      protected ?string $type;
  61  
  62      /**
  63       * Constructor.
  64       *
  65       * Throws an exception if trying to init the form for a new manual instance of a tool, which is not supported in 4.3 onward.
  66       *
  67       * @param \stdClass $current the current form data.
  68       * @param string $section the section number.
  69       * @param \stdClass $cm the course module object.
  70       * @param \stdClass $course the course object.
  71       * @throws moodle_exception if trying to init the form for the creation of a manual instance, which is no longer supported.
  72       */
  73      public function __construct($current, $section, $cm, $course) {
  74  
  75          // Setup some of the pieces used to control display in the form definition() method.
  76          // Type ID parameter being passed when adding an preconfigured tool from activity chooser.
  77          $this->typeid = optional_param('typeid', 0, PARAM_INT);
  78          $this->type = optional_param('type', null, PARAM_ALPHA);
  79  
  80          // Only permit construction if the form deals with editing an existing instance (current->id not empty), or creating an
  81          // instance from a preconfigured tool type ($this->typeid not empty). Make an exception for callers, such as core_completion
  82          // which aren't instantiating the form with the expected data, by checking whether the page has been set up, which is the
  83          // case for normal uses.
  84          global $PAGE;
  85          if ($PAGE->has_set_url() && str_contains($PAGE->url, '/course/modedit.php')) {
  86              if (empty($this->typeid) && empty($current->id)) {
  87                  throw new moodle_exception('lti:addmanualinstanceprohibitederror', 'mod_lti');
  88              }
  89          }
  90  
  91          parent::__construct($current, $section, $cm, $course);
  92      }
  93  
  94      /**
  95       * Defines the form for legacy instances. Here tool config is frozen because the manual configuration method is deprecated.
  96       *
  97       * @param array $instancetypes the array of options for the legacy 'preconfigured tools' select.
  98       * @return void
  99       */
 100      protected function legacy_instance_form_definition(array $instancetypes): void {
 101          global $OUTPUT;
 102  
 103          // The legacy form handles instances which are either entirely manually configured (current->typeid = 0), or which are
 104          // manually configured and have been domain-matched to a preconfigured tool (current->typeid != 0).
 105          $manualinstance = empty($this->current->typeid);
 106          $matchestoolnotavailabletocourse = !$manualinstance;
 107          $typeid = $manualinstance ? '0' : $this->current->typeid;
 108  
 109          // Since 'mod/lti:addmanualinstance' capability is deprecated, determining which users may have had access to the certain
 110          // form fields (the manual config fields) isn't straightforward. Users without 'mod/lti:addmanualinstance' would have only
 111          // been permitted to edit the basic instance fields (name, etc.), so care must be taken not to display the config fields to
 112          // these users. Users who can add/edit course tools (mod/lti:addcoursetool) are able to view tool information anyway, via
 113          // the tool definitions, so this capability is used as a replacement, to control access to these tool config fields.
 114          $canviewmanualconfig = has_capability('mod/lti:addcoursetool', $this->context);
 115          $showtypes = has_capability('mod/lti:addpreconfiguredinstance', $this->context);
 116  
 117          if ($manualinstance && !$canviewmanualconfig) {
 118              // If you cannot add a manual instance and this is already a manual instance, then remove the 'types' selector.
 119              $showtypes = false;
 120          }
 121  
 122          $mform =& $this->_form;
 123  
 124          // Show the deprecation notice, regardless of whether the user can view the tool configuration details or not.
 125          // They will still see locked privacy fields and should be informed as to why that is.
 126          $mform->addElement('html', $OUTPUT->notification(
 127              get_string('editmanualinstancedeprecationwarning', 'mod_lti', get_docs_url('External_tool')),
 128              \core\output\notification::NOTIFY_WARNING, false)
 129          );
 130  
 131          // Adding the "general" fieldset, where all the common settings are shown.
 132          $mform->addElement('html', "<div data-attribute='dynamic-import' hidden aria-hidden='true' role='alert'></div>");
 133          $mform->addElement('header', 'general', get_string('general', 'form'));
 134  
 135          // Adding the standard "name" field.
 136          $mform->addElement('text', 'name', get_string('basicltiname', 'lti'), ['size' => '64']);
 137          $mform->setType('name', PARAM_TEXT);
 138          $mform->addRule('name', null, 'required', null, 'client');
 139          $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
 140  
 141          // Adding the optional "intro" and "introformat" pair of fields.
 142          $this->standard_intro_elements(get_string('basicltiintro', 'lti'));
 143          $mform->setAdvanced('introeditor');
 144  
 145          // Display the label to the right of the checkbox so it looks better & matches rest of the form.
 146          if ($mform->elementExists('showdescription')) {
 147              $coursedesc = $mform->getElement('showdescription');
 148              if (!empty($coursedesc)) {
 149                  $coursedesc->setText(' ' . $coursedesc->getLabel());
 150                  $coursedesc->setLabel('&nbsp');
 151              }
 152          }
 153  
 154          $mform->setAdvanced('showdescription');
 155  
 156          $mform->addElement('checkbox', 'showtitlelaunch', get_string('display_name', 'lti'));
 157          $mform->setAdvanced('showtitlelaunch');
 158          $mform->addHelpButton('showtitlelaunch', 'display_name', 'lti');
 159  
 160          $mform->addElement('checkbox', 'showdescriptionlaunch', get_string('display_description', 'lti'));
 161          $mform->setAdvanced('showdescriptionlaunch');
 162          $mform->addHelpButton('showdescriptionlaunch', 'display_description', 'lti');
 163  
 164          if ($showtypes) {
 165              if ($manualinstance) {
 166                  // Legacy, manually configured instances: only freeze the element (not hardFreeze) so that disabledIf() still works.
 167                  // The data in the select is restricted so that only the current value is deemed valid, preventing DOM-edit changes,
 168                  // which are possible with frozen elements.
 169                  $tooltypes = $mform->addElement('select', 'typeid', get_string('external_tool_type', 'lti'));
 170                  $mform->addHelpButton('typeid', 'external_tool_type', 'lti');
 171                  $manualinstanceoption = $instancetypes[0]; // The 'Automatic, based on tool URL' option.
 172                  $tooltypes->addOption($manualinstanceoption->name, 0, []);
 173                  $mform->freeze('typeid');
 174              } else if ($matchestoolnotavailabletocourse) {
 175                  // Legacy instances domain-matched to site tools: use a hidden field for typeid and a static visual element when
 176                  // displaying these instances so that the string value of typeid is still visible when the element is frozen.
 177                  // This gets around the fact that a frozen select without a selected option will display nothing.
 178                  $mform->addElement('hidden', 'typeid', $typeid);
 179                  $mform->setType('typeid', PARAM_INT);
 180  
 181                  $manualinstanceoption = $instancetypes[0]; // The 'Automatic, based on tool URL' option.
 182                  $mform->addElement('static', 'typeiddisplayonly', get_string('external_tool_type', 'lti'),
 183                      $manualinstanceoption->name);
 184              }
 185          } else {
 186              // Need to submit these still, but hidden to avoid instructor modification.
 187              $mform->addElement('hidden', 'typeid', $typeid);
 188              $mform->setType('typeid', PARAM_INT);
 189          }
 190  
 191          // Disable the content selection button unconditionally. Freeze/hardFreeze is unsuitable for buttons.
 192          $mform->addElement('button', 'selectcontent', get_string('selectcontent', 'lti'), ['disabled' => 'disabled']);
 193          $mform->disabledIf('selectcontent', 'typeid', 'eq', $typeid);
 194  
 195          if ($canviewmanualconfig) {
 196              $mform->addElement('text', 'toolurl', get_string('launch_url', 'lti'), ['size' => '64']);
 197              $mform->setType('toolurl', PARAM_URL);
 198              $mform->addHelpButton('toolurl', 'launch_url', 'lti');
 199  
 200              $mform->addElement('text', 'securetoolurl', get_string('secure_launch_url', 'lti'), ['size' => '64']);
 201              $mform->setType('securetoolurl', PARAM_URL);
 202              $mform->setAdvanced('securetoolurl');
 203              $mform->addHelpButton('securetoolurl', 'secure_launch_url', 'lti');
 204          } else {
 205              // Need to submit these still, but hidden to avoid instructor modification.
 206              $mform->addElement('hidden', 'toolurl', '', ['id' => 'id_toolurl']);
 207              $mform->setType('toolurl', PARAM_URL);
 208              $mform->addElement('hidden', 'securetoolurl', '', ['id' => 'id_securetoolurl']);
 209              $mform->setType('securetoolurl', PARAM_URL);
 210          }
 211  
 212          $mform->addElement('hidden', 'lineitemresourceid', '', ['id' => 'id_lineitemresourceid']);
 213          $mform->setType('lineitemresourceid', PARAM_TEXT);
 214  
 215          $mform->addElement('hidden', 'lineitemtag', '', ['id' => 'id_lineitemtag']);
 216          $mform->setType('lineitemtag', PARAM_TEXT);
 217  
 218          $mform->addElement('hidden', 'lineitemsubreviewurl', '', ['id' => 'id_lineitemsubreviewurl']);
 219          $mform->setType('lineitemsubreviewurl', PARAM_URL);
 220  
 221          $mform->addElement('hidden', 'lineitemsubreviewparams', '', ['id' => 'id_lineitemsubreviewparams']);
 222          $mform->setType('lineitemsubreviewparams', PARAM_TEXT);
 223  
 224          $launchoptions = [
 225              LTI_LAUNCH_CONTAINER_DEFAULT => get_string('default', 'lti'),
 226              LTI_LAUNCH_CONTAINER_EMBED => get_string('embed', 'lti'),
 227              LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS => get_string('embed_no_blocks', 'lti'),
 228              LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW => get_string('existing_window', 'lti'),
 229              LTI_LAUNCH_CONTAINER_WINDOW => get_string('new_window', 'lti')
 230          ];
 231          $mform->addElement('select', 'launchcontainer', get_string('launchinpopup', 'lti'), $launchoptions);
 232          $mform->addHelpButton('launchcontainer', 'launchinpopup', 'lti');
 233          $mform->setAdvanced('launchcontainer');
 234  
 235          if ($canviewmanualconfig) {
 236              $mform->addElement('text', 'resourcekey', get_string('resourcekey', 'lti'));
 237              $mform->setType('resourcekey', PARAM_TEXT);
 238              $mform->setAdvanced('resourcekey');
 239              $mform->addHelpButton('resourcekey', 'resourcekey', 'lti');
 240              $mform->setForceLtr('resourcekey');
 241  
 242              $mform->addElement('passwordunmask', 'password', get_string('password', 'lti'));
 243              $mform->setType('password', PARAM_TEXT);
 244              $mform->setAdvanced('password');
 245              $mform->addHelpButton('password', 'password', 'lti');
 246  
 247              $mform->addElement('textarea', 'instructorcustomparameters', get_string('custom', 'lti'), ['rows' => 4, 'cols' => 60]);
 248              $mform->setType('instructorcustomparameters', PARAM_TEXT);
 249              $mform->setAdvanced('instructorcustomparameters');
 250              $mform->addHelpButton('instructorcustomparameters', 'custom', 'lti');
 251              $mform->setForceLtr('instructorcustomparameters');
 252  
 253              $mform->addElement('text', 'icon', get_string('icon_url', 'lti'), ['size' => '64']);
 254              $mform->setType('icon', PARAM_URL);
 255              $mform->setAdvanced('icon');
 256              $mform->addHelpButton('icon', 'icon_url', 'lti');
 257  
 258              $mform->addElement('text', 'secureicon', get_string('secure_icon_url', 'lti'), ['size' => '64']);
 259              $mform->setType('secureicon', PARAM_URL);
 260              $mform->setAdvanced('secureicon');
 261              $mform->addHelpButton('secureicon', 'secure_icon_url', 'lti');
 262          } else {
 263              // Need to submit these still, but hidden to avoid instructor modification.
 264              $mform->addElement('hidden', 'resourcekey', '', ['id' => 'id_resourcekey']);
 265              $mform->setType('resourcekey', PARAM_TEXT);
 266              $mform->addElement('hidden', 'password', '', ['id' => 'id_password']);
 267              $mform->setType('password', PARAM_TEXT);
 268              $mform->addElement('hidden', 'instructorcustomparameters', '', ['id' => 'id_instructorcustomparameters']);
 269              $mform->setType('instructorcustomparameters', PARAM_TEXT);
 270              $mform->addElement('hidden', 'icon', '', ['id' => 'id_icon']);
 271              $mform->setType('icon', PARAM_URL);
 272              $mform->addElement('hidden', 'secureicon', '', ['id' => 'id_secureicon']);
 273              $mform->setType('secureicon', PARAM_URL);
 274          }
 275  
 276          // Add privacy preferences fieldset where users choose whether to send their data.
 277          $mform->addElement('header', 'privacy', get_string('privacy', 'lti'));
 278  
 279          $mform->addElement('advcheckbox', 'instructorchoicesendname', get_string('share_name', 'lti'));
 280          $mform->addHelpButton('instructorchoicesendname', 'share_name', 'lti');
 281  
 282          $mform->addElement('advcheckbox', 'instructorchoicesendemailaddr', get_string('share_email', 'lti'));
 283          $mform->addHelpButton('instructorchoicesendemailaddr', 'share_email', 'lti');
 284  
 285          $mform->addElement('advcheckbox', 'instructorchoiceacceptgrades', get_string('accept_grades', 'lti'));
 286          $mform->addHelpButton('instructorchoiceacceptgrades', 'accept_grades', 'lti');
 287  
 288          // Add standard course module grading elements.
 289          $this->standard_grading_coursemodule_elements();
 290  
 291          // Add standard elements, common to all modules.
 292          $this->standard_coursemodule_elements();
 293          $mform->setAdvanced('cmidnumber');
 294  
 295          // Add standard buttons, common to all modules.
 296          $this->add_action_buttons();
 297  
 298          $mform->hardFreeze([
 299              'toolurl',
 300              'securetoolurl',
 301              'launchcontainer',
 302              'resourcekey',
 303              'password',
 304              'instructorcustomparameters',
 305              'icon',
 306              'secureicon',
 307              'instructorchoicesendname',
 308              'instructorchoicesendemailaddr',
 309              'instructorchoiceacceptgrades'
 310          ]);
 311      }
 312  
 313      public function definition() {
 314          global $PAGE, $OUTPUT, $COURSE;
 315  
 316          if ($this->type) {
 317              component_callback("ltisource_$this->type", 'add_instance_hook');
 318          }
 319  
 320          // Determine whether this tool instance is a manually configure instance (now deprecated).
 321          $manualinstance = empty($this->current->typeid) && empty($this->typeid);
 322  
 323          // Determine whether this tool instance is using a domain-matched site tool which is not visible at the course level.
 324          // In such a case, the instance has a typeid (the site tool) and toolurl (the url used to domain match the site tool) set,
 325          // and the type still exists (is not deleted).
 326          $instancetypes = lti_get_types_for_add_instance();
 327          $matchestoolnotavailabletocourse = false;
 328          if (!$manualinstance && !empty($this->current->toolurl)) {
 329              if (lti_get_type_config($this->current->typeid)) {
 330                  $matchestoolnotavailabletocourse = !in_array($this->current->typeid, array_keys($instancetypes));
 331              }
 332          }
 333  
 334          // Display the legacy form, presenting a read-only view of the configuration for unsupported (since 4.3) instances, which:
 335          // - Are manually configured instances (no longer supported. course tools should be configured and used instead).
 336          // - Are domain-matched to a hidden site level tool (no longer supported. to be replaced by URL-based course tool creation)
 337          // Instances based on preconfigured tools and which are not domain matched as above, are still valid and will be shown using
 338          // the non-legacy form.
 339          if ($manualinstance || $matchestoolnotavailabletocourse) {
 340              $this->legacy_instance_form_definition($instancetypes);
 341              return;
 342          }
 343  
 344          $tooltypeid = $this->current->typeid ?? $this->typeid;
 345          $tooltype = lti_get_type($tooltypeid);
 346  
 347          // Store the id of the tool type should it be linked to a tool proxy, to aid in disabling certain form elements.
 348          $toolproxytypeid = $tooltype->toolproxyid ? $tooltypeid : '';
 349  
 350          $issitetooltype = $tooltype->course == get_site()->id;
 351  
 352          $mform =& $this->_form;
 353  
 354          // Adding the "general" fieldset, where all the common settings are shown.
 355          $mform->addElement('html', "<div data-attribute='dynamic-import' hidden aria-hidden='true' role='alert'></div>");
 356          $mform->addElement('header', 'general', get_string('general', 'form'));
 357  
 358          // For tools supporting content selection, add the 'Select content button'.
 359          $config = lti_get_type_config($tooltypeid);
 360          $supportscontentitemselection = !empty($config['contentitem']);
 361  
 362          if ($supportscontentitemselection) {
 363              $contentitemurl = new moodle_url('/mod/lti/contentitem.php');
 364              $contentbuttonattributes = [
 365                  'data-contentitemurl' => $contentitemurl->out(false),
 366              ];
 367  
 368              // If this is an instance, was it created based on content selection in a prior-edit (need to infer since not stored).
 369              $iscontentitem = !empty($this->current->id)
 370                  && (!empty($this->current->toolurl) || !empty($this->current->instructorcustomparameters)
 371                  || !empty($this->current->secureicon) || !empty($this->current->icon));
 372  
 373              $selectcontentindicatorinner = $iscontentitem ?
 374                  $OUTPUT->pix_icon('i/valid', get_string('contentselected', 'mod_lti'), 'moodle', ['class' => 'mr-1'])
 375                  . get_string('contentselected', 'mod_lti') : '';
 376              $selectcontentindicator = html_writer::div($selectcontentindicatorinner, '',
 377                  ['aria-role' => 'status', 'id' => 'id_selectcontentindicator']);
 378              $selectcontentstatus = $iscontentitem ? 'true' : 'false';
 379              $selectcontentgrp = [
 380                  $mform->createElement('button', 'selectcontent', get_string('selectcontent', 'mod_lti'), $contentbuttonattributes,
 381                      ['customclassoverride' => 'btn-primary']),
 382                  $mform->createElement('html', $selectcontentindicator),
 383                  $mform->createElement('hidden', 'selectcontentstatus', $selectcontentstatus),
 384              ];
 385              $mform->setType('selectcontentstatus', PARAM_TEXT);
 386              $mform->addGroup($selectcontentgrp, 'selectcontentgroup', get_string('content'), ' ', false);
 387              $mform->addRule('selectcontentgroup', get_string('selectcontentvalidationerror', 'mod_lti'), 'required');
 388          }
 389  
 390          // Adding the standard "name" field.
 391          $mform->addElement('text', 'name', get_string('basicltiname', 'lti'), ['size' => '64']);
 392          $mform->setType('name', PARAM_TEXT);
 393          $mform->addRule('name', null, 'required', null, 'server');
 394          $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'server');
 395  
 396          // Show activity name when launched only applies to embedded type launches.
 397          if (in_array($config['launchcontainer'], [LTI_LAUNCH_CONTAINER_EMBED, LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS])) {
 398              $mform->addElement('checkbox', 'showtitlelaunch', get_string('display_name', 'lti'));
 399              $mform->setDefault('showtitlelaunch', true);
 400              $mform->addHelpButton('showtitlelaunch', 'display_name', 'lti');
 401          } else {
 402              // Include in the form anyway, so we retain the setting value in case the tool launch container is changed back.
 403              $mform->addElement('hidden', 'showtitlelaunch');
 404              $mform->setType('showtitlelaunch', PARAM_BOOL);
 405          }
 406  
 407          // Adding the optional "intro" and "introformat" pair of fields.
 408          $this->standard_intro_elements(get_string('basicltiintro', 'lti'));
 409  
 410          // Display the label to the right of the checkbox so it looks better & matches rest of the form.
 411          if ($mform->elementExists('showdescription')) {
 412              $coursedesc = $mform->getElement('showdescription');
 413              if (!empty($coursedesc)) {
 414                  $coursedesc->setText(' ' . $coursedesc->getLabel());
 415                  $coursedesc->setLabel('&nbsp');
 416              }
 417          }
 418  
 419          // Show activity description when launched only applies to embedded type launches.
 420          if (in_array($config['launchcontainer'], [LTI_LAUNCH_CONTAINER_EMBED, LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS])) {
 421              $mform->addElement('checkbox', 'showdescriptionlaunch', get_string('display_description', 'lti'));
 422              $mform->addHelpButton('showdescriptionlaunch', 'display_description', 'lti');
 423          } else {
 424              // Include in the form anyway, so we retain the setting value in case the tool launch container is changed back.
 425              $mform->addElement('hidden', 'showdescriptionlaunch');
 426              $mform->setType('showdescriptionlaunch', PARAM_BOOL);
 427          }
 428  
 429          // Tool settings.
 430          $mform->addElement('hidden', 'typeid', $tooltypeid, ['id' => 'hidden_typeid']);
 431          $mform->setType('typeid', PARAM_INT);
 432          if (!empty($config['contentitem'])) {
 433              $mform->addElement('hidden', 'contentitem', 1);
 434              $mform->setType('contentitem', PARAM_INT);
 435          }
 436  
 437          // Included to support deep linking return, but hidden to avoid instructor modification.
 438          $mform->addElement('hidden', 'toolurl', '', ['id' => 'id_toolurl']);
 439          $mform->setType('toolurl', PARAM_URL);
 440          $mform->addElement('hidden', 'securetoolurl', '', ['id' => 'id_securetoolurl']);
 441          $mform->setType('securetoolurl', PARAM_URL);
 442  
 443          $mform->addElement('hidden', 'urlmatchedtypeid', '', ['id' => 'id_urlmatchedtypeid']);
 444          $mform->setType('urlmatchedtypeid', PARAM_INT);
 445  
 446          $mform->addElement('hidden', 'lineitemresourceid', '', ['id' => 'id_lineitemresourceid']);
 447          $mform->setType('lineitemresourceid', PARAM_TEXT);
 448  
 449          $mform->addElement('hidden', 'lineitemtag', '', ['id' => 'id_lineitemtag']);
 450          $mform->setType('lineitemtag', PARAM_TEXT);
 451  
 452          $mform->addElement('hidden', 'lineitemsubreviewurl', '', ['id' => 'id_lineitemsubreviewurl']);
 453          $mform->setType('lineitemsubreviewurl', PARAM_URL);
 454  
 455          $mform->addElement('hidden', 'lineitemsubreviewparams', '', ['id' => 'id_lineitemsubreviewparams']);
 456          $mform->setType('lineitemsubreviewparams', PARAM_TEXT);
 457  
 458          // Launch container is set to 'LTI_LAUNCH_CONTAINER_DEFAULT', meaning it'll delegate to the tool's configuration.
 459          // Existing instances using values other than this can continue to use their existing value but cannot change it.
 460          $mform->addElement('hidden', 'launchcontainer', LTI_LAUNCH_CONTAINER_DEFAULT);
 461          $mform->setType('launchcontainer', PARAM_INT);
 462  
 463          // Included to support deep linking return, but hidden to avoid instructor modification.
 464          $mform->addElement('hidden', 'resourcekey', '', ['id' => 'id_resourcekey']);
 465          $mform->setType('resourcekey', PARAM_TEXT);
 466          $mform->addElement('hidden', 'password', '', ['id' => 'id_password']);
 467          $mform->setType('password', PARAM_TEXT);
 468          $mform->addElement('hidden', 'instructorcustomparameters', '', ['id' => 'id_instructorcustomparameters']);
 469          $mform->setType('instructorcustomparameters', PARAM_TEXT);
 470          $mform->addElement('hidden', 'icon', '', ['id' => 'id_icon']);
 471          $mform->setType('icon', PARAM_URL);
 472          $mform->addElement('hidden', 'secureicon', '', ['id' => 'id_secureicon']);
 473          $mform->setType('secureicon', PARAM_URL);
 474  
 475          // Add standard course module grading elements, and show them if the tool type + instance config permits it.
 476          if (!empty($config['acceptgrades']) && in_array($config['acceptgrades'], [LTI_SETTING_ALWAYS, LTI_SETTING_DELEGATE])) {
 477              $elementnamesbeforegrading = $this->_form->_elementIndex;
 478              $this->standard_grading_coursemodule_elements();
 479              $elementnamesaftergrading = $this->_form->_elementIndex;
 480  
 481              // For all 'real' elements (not hidden or header) added as part of the standard grading elements, add a hideIf rule
 482              // making the element dependent on the 'accept grades from the tool' checkbox (instructorchoiceacceptgrades).
 483              $diff = array_diff($elementnamesaftergrading, $elementnamesbeforegrading);
 484              $diff = array_filter($diff, fn($key) => !in_array($this->_form->_elements[$key]->_type, ['hidden', 'header']));
 485              foreach ($diff as $gradeelementname => $gradeelementindex) {
 486                  $mform->hideIf($gradeelementname, 'instructorchoiceacceptgrades', 'eq', 0);
 487              }
 488  
 489              // Extend the grade section with the 'accept grades from the tool' checkbox, allowing per-instance overrides of that
 490              // value according to the following rules:
 491              // - Site tools with 'acceptgrades' set to 'ALWAYS' do not permit overrides at the instance level; the checkbox is
 492              // omitted in such cases.
 493              // - Site tools with 'acceptgrades' set to 'DELEGATE' result in a checkbox that is defaulted to unchecked but which
 494              // permits overrides to 'yes/checked'.
 495              // - Course tools with 'acceptgrades' set to 'ALWAYS' result in a checkbox that is defaulted to checked but which
 496              // permits overrides to 'no/unchecked'.
 497              // - Course tools with 'acceptgrades' set to 'DELEGATE' result in a checkbox that is defaulted to unchecked but which
 498              // permits overrides to 'yes/checked'.
 499              if (($issitetooltype && $config['acceptgrades'] == LTI_SETTING_DELEGATE) || !$issitetooltype) {
 500                  $mform->insertElementBefore(
 501                      $mform->createElement(
 502                          'advcheckbox',
 503                          'instructorchoiceacceptgrades',
 504                          get_string('accept_grades_from_tool', 'mod_lti', $tooltype->name)
 505                      ),
 506                      array_keys($diff)[0]
 507                  );
 508                  $acceptgradesdefault = !$issitetooltype && $config['acceptgrades'] == LTI_SETTING_ALWAYS ? '1' : '0';
 509                  $mform->setDefault('instructorchoiceacceptgrades', $acceptgradesdefault);
 510                  $mform->disabledIf('instructorchoiceacceptgrades', 'typeid', 'in', [$toolproxytypeid]); // LTI 2 only.
 511              }
 512          }
 513  
 514          // Add standard elements, common to all modules.
 515          $this->standard_coursemodule_elements();
 516          $mform->setAdvanced('cmidnumber');
 517  
 518          // Add standard buttons, common to all modules.
 519          $this->add_action_buttons();
 520  
 521          if ($supportscontentitemselection) {
 522              $PAGE->requires->js_call_amd('mod_lti/mod_form', 'init', [$COURSE->id]);
 523          }
 524      }
 525  
 526      /**
 527       * Sets the current values handled by services in case of update.
 528       *
 529       * @param object $defaultvalues default values to populate the form with.
 530       */
 531      public function set_data($defaultvalues) {
 532          $services = lti_get_services();
 533          if (is_object($defaultvalues)) {
 534              foreach ($services as $service) {
 535                  $service->set_instance_form_values( $defaultvalues );
 536              }
 537          }
 538          parent::set_data($defaultvalues);
 539      }
 540  
 541      public function validation($data, $files) {
 542          $errors = parent::validation($data, $files);
 543  
 544          if (isset($data['selectcontentstatus']) && $data['selectcontentstatus'] === 'false') {
 545              $errors['selectcontentgroup'] = get_string('selectcontentvalidationerror', 'mod_lti');
 546          }
 547  
 548          return $errors;
 549      }
 550  }