diff --git a/mod/lti/db/access.php b/mod/lti/db/access.php index f699c47ed11..6722495b971 100644 --- a/mod/lti/db/access.php +++ b/mod/lti/db/access.php @@ -91,6 +91,28 @@ $capabilities = array( ) ), + // The ability to a preconfigured instance to the course. + 'mod/lti:addpreconfiguredinstance' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE, + 'archetypes' => array( + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ), + 'clonepermissionsfrom' => 'mod/lti:addinstance', + ), + + // The ability to add a manual instance (i.e. not from a preconfigured tool) to the course. + 'mod/lti:addmanualinstance' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE, + 'archetypes' => array( + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ), + 'clonepermissionsfrom' => 'mod/lti:addinstance', + ), + // The ability to request the administrator to configure a particular // External tool globally. 'mod/lti:requesttooladd' => array( diff --git a/mod/lti/lang/en/lti.php b/mod/lti/lang/en/lti.php index e9c8a2b52e6..906f364bd5f 100644 --- a/mod/lti/lang/en/lti.php +++ b/mod/lti/lang/en/lti.php @@ -210,7 +210,7 @@ In addition, all web service requests from the tool provider will use SSL. If using this option, confirm that this Moodle site and the tool provider support SSL.'; $string['generaltool'] = 'General tool'; -$string['global_tool_types'] = 'Global preconfigured tools'; +$string['global_tool_types'] = 'Preconfigured tools'; $string['grading'] = 'Grade routing'; $string['icon_url'] = 'Icon URL'; $string['icon_url_help'] = 'The icon URL allows the icon that shows up in the course listing for this activity to be modified. Instead of using the default @@ -254,8 +254,10 @@ real estate to the tool, and others provide a more integrated feel with the Mood $string['launchoptions'] = 'Launch options'; $string['leaveblank'] = 'Leave blank if you do not need them'; $string['lti'] = 'LTI'; -$string['lti:addinstance'] = 'Add a new external tool'; $string['lti:addcoursetool'] = 'Add course-specific tool configurations'; +$string['lti:addmanualinstance'] = 'Add a manually-configured tool'; +$string['lti:addinstance'] = 'Add a new external tool'; +$string['lti:addpreconfiguredinstance'] = 'Add a preconfigured tool'; $string['lti:grade'] = 'View grades returned by the external tool'; $string['lti:manage'] = 'Be an Instructor when the tool is launched'; $string['lti:admin'] = 'Be an administrator when the tool is launched'; diff --git a/mod/lti/lib.php b/mod/lti/lib.php index 0d98e57edef..e450cd6a2f5 100644 --- a/mod/lti/lib.php +++ b/mod/lti/lib.php @@ -219,7 +219,9 @@ function lti_get_shortcuts($defaultitem) { require_once($CFG->dirroot.'/mod/lti/locallib.php'); $types = lti_get_configured_types($COURSE->id, $defaultitem->link->param('sr')); - $types[] = $defaultitem; + if (has_capability('mod/lti:addmanualinstance', context_course::instance($COURSE->id))) { + $types[] = $defaultitem; + } // Add items defined in ltisource plugins. foreach (core_component::get_plugin_list('ltisource') as $pluginname => $dir) { diff --git a/mod/lti/locallib.php b/mod/lti/locallib.php index ec9bd2fb31e..c69d3abbb4f 100644 --- a/mod/lti/locallib.php +++ b/mod/lti/locallib.php @@ -2129,10 +2129,21 @@ function lti_get_lti_types_by_course($courseid, $coursevisible = null) { } list($coursevisiblesql, $coursevisparams) = $DB->get_in_or_equal($coursevisible, SQL_PARAMS_NAMED, 'coursevisible'); + $courseconds = []; + if (has_capability('mod/lti:addmanualinstance', context_course::instance($courseid))) { + $courseconds[] = "course = :courseid"; + } + if (has_capability('mod/lti:addpreconfiguredinstance', context_course::instance($courseid))) { + $courseconds[] = "course = :siteid"; + } + if (!$courseconds) { + return []; + } + $coursecond = implode(" OR ", $courseconds); $query = "SELECT * FROM {lti_types} WHERE coursevisible $coursevisiblesql - AND (course = :siteid OR course = :courseid) + AND ($coursecond) AND state = :active"; return $DB->get_records_sql($query, @@ -2149,7 +2160,9 @@ function lti_get_types_for_add_instance() { $admintypes = lti_get_lti_types_by_course($COURSE->id); $types = array(); - $types[0] = (object)array('name' => get_string('automatic', 'lti'), 'course' => 0, 'toolproxyid' => null); + if (has_capability('mod/lti:addmanualinstance', context_course::instance($COURSE->id))) { + $types[0] = (object)array('name' => get_string('automatic', 'lti'), 'course' => 0, 'toolproxyid' => null); + } foreach ($admintypes as $type) { $types[$type->id] = $type; diff --git a/mod/lti/mod_form.js b/mod/lti/mod_form.js index f341ccc63ab..2b21157bcba 100644 --- a/mod/lti/mod_form.js +++ b/mod/lti/mod_form.js @@ -42,64 +42,68 @@ this.urlCache = {}; this.toolTypeCache = {}; - this.addOptGroups(); - var updateToolMatches = function(){ self.updateAutomaticToolMatch(Y.one('#id_toolurl')); self.updateAutomaticToolMatch(Y.one('#id_securetoolurl')); }; var typeSelector = Y.one('#id_typeid'); - typeSelector.on('change', function(e){ - // Reset configuration fields when another preconfigured tool is selected. - self.resetToolFields(); + if (typeSelector) { + this.addOptGroups(); - updateToolMatches(); + typeSelector.on('change', function(e){ + // Reset configuration fields when another preconfigured tool is selected. + self.resetToolFields(); - self.toggleEditButtons(); + updateToolMatches(); - if (self.getSelectedToolTypeOption().getAttribute('toolproxy')){ - var allowname = Y.one('#id_instructorchoicesendname'); - allowname.set('checked', !self.getSelectedToolTypeOption().getAttribute('noname')); + self.toggleEditButtons(); - var allowemail = Y.one('#id_instructorchoicesendemailaddr'); - allowemail.set('checked', !self.getSelectedToolTypeOption().getAttribute('noemail')); + if (self.getSelectedToolTypeOption().getAttribute('toolproxy')){ + var allowname = Y.one('#id_instructorchoicesendname'); + allowname.set('checked', !self.getSelectedToolTypeOption().getAttribute('noname')); - var allowgrades = Y.one('#id_instructorchoiceacceptgrades'); - allowgrades.set('checked', !self.getSelectedToolTypeOption().getAttribute('nogrades')); - self.toggleGradeSection(); - } - }); + var allowemail = Y.one('#id_instructorchoicesendemailaddr'); + allowemail.set('checked', !self.getSelectedToolTypeOption().getAttribute('noemail')); + + var allowgrades = Y.one('#id_instructorchoiceacceptgrades'); + allowgrades.set('checked', !self.getSelectedToolTypeOption().getAttribute('nogrades')); + self.toggleGradeSection(); + } + }); + + this.createTypeEditorButtons(); + + this.toggleEditButtons(); + } var contentItemButton = Y.one('[name="selectcontent"]'); - var contentItemUrl = contentItemButton.getAttribute('data-contentitemurl'); - // Handle configure from link button click. - contentItemButton.on('click', function() { - var contentItemId = self.getContentItemId(); - if (contentItemId) { - // Get activity name and description values. - var title = Y.one('#id_name').get('value').trim(); - var text = Y.one('#id_introeditor').get('value').trim(); + if (contentItemButton) { + var contentItemUrl = contentItemButton.getAttribute('data-contentitemurl'); + // Handle configure from link button click. + contentItemButton.on('click', function() { + var contentItemId = self.getContentItemId(); + if (contentItemId) { + // Get activity name and description values. + var title = Y.one('#id_name').get('value').trim(); + var text = Y.one('#id_introeditor').get('value').trim(); - // Set data to be POSTed. - var postData = { - id: contentItemId, - course: self.settings.courseId, - title: title, - text: text - }; + // Set data to be POSTed. + var postData = { + id: contentItemId, + course: self.settings.courseId, + title: title, + text: text + }; - require(['mod_lti/contentitem'], function(contentitem) { - contentitem.init(contentItemUrl, postData, function() { - M.mod_lti.editor.toggleGradeSection(); + require(['mod_lti/contentitem'], function(contentitem) { + contentitem.init(contentItemUrl, postData, function() { + M.mod_lti.editor.toggleGradeSection(); + }); }); - }); - } - }); - - this.createTypeEditorButtons(); - - this.toggleEditButtons(); + } + }); + } var textAreas = new Y.NodeList([ Y.one('#id_toolurl'), @@ -121,7 +125,9 @@ var allowgrades = Y.one('#id_instructorchoiceacceptgrades'); allowgrades.on('change', this.toggleGradeSection, this); - updateToolMatches(); + if (typeSelector) { + updateToolMatches(); + } }, toggleGradeSection: function(e) { @@ -143,6 +149,10 @@ }, updateAutomaticToolMatch: function(field){ + if (!field) { + return; + } + var self = this; var toolurl = field; @@ -528,11 +538,20 @@ * @returns {number|boolean} The ID of the tool type if it supports Content-Item selection. False, otherwise. */ getContentItemId: function() { - var selected = this.getSelectedToolTypeOption(); - if (selected.getAttribute('data-contentitem')) { - return selected.getAttribute('data-id'); + try { + var selected = this.getSelectedToolTypeOption(); + if (selected.getAttribute('data-contentitem')) { + return selected.getAttribute('data-id'); + } + return false; + } catch (err) { + // Tool selector not available - check for hidden fields instead. + var content = Y.one('input[name="contentitem"]'); + if (!content || !content.get('value')) { + return false; + } + return Y.one('input[name="typeid"]').get('value'); } - return false; }, /** diff --git a/mod/lti/mod_form.php b/mod/lti/mod_form.php index bc3ba4e3c9e..8eb9fac73f9 100644 --- a/mod/lti/mod_form.php +++ b/mod/lti/mod_form.php @@ -60,6 +60,23 @@ class mod_lti_mod_form extends moodleform_mod { component_callback("ltisource_$type", 'add_instance_hook'); } + // Type ID parameter being passed when adding an preconfigured tool from activity chooser. + $typeid = optional_param('typeid', false, PARAM_INT); + + // Show configuration details only if not preset (when new) or user has the capabilities to do so (when editing). + if ($this->_instance) { + $showtypes = has_capability('mod/lti:addpreconfiguredinstance', $this->context); + $showoptions = has_capability('mod/lti:addmanualinstance', $this->context); + if (!$showoptions && $this->current->typeid == 0) { + // If you cannot add a manual instance and this is already a manual instance, then + // remove the 'types' selector. + $showtypes = false; + } + } else { + $showtypes = !$typeid; + $showoptions = !$typeid && has_capability('mod/lti:addmanualinstance', $this->context); + } + $this->typeid = 0; $mform =& $this->_form; @@ -95,51 +112,63 @@ class mod_lti_mod_form extends moodleform_mod { $mform->addHelpButton('showdescriptionlaunch', 'display_description', 'lti'); // Tool settings. - $tooltypes = $mform->addElement('select', 'typeid', get_string('external_tool_type', 'lti')); - // Type ID parameter being passed when adding an preconfigured tool from activity chooser. - $typeid = optional_param('typeid', false, PARAM_INT); - if ($typeid) { - $mform->getElement('typeid')->setValue($typeid); - } - $mform->addHelpButton('typeid', 'external_tool_type', 'lti'); $toolproxy = array(); - // Array of tool type IDs that don't support ContentItemSelectionRequest. $noncontentitemtypes = []; - foreach (lti_get_types_for_add_instance() as $id => $type) { - if (!empty($type->toolproxyid)) { - $toolproxy[] = $type->id; - $attributes = array( 'globalTool' => 1, 'toolproxy' => 1); - $enabledcapabilities = explode("\n", $type->enabledcapability); - if (!in_array('Result.autocreate', $enabledcapabilities) || in_array('BasicOutcome.url', $enabledcapabilities)) { - $attributes['nogrades'] = 1; - } - if (!in_array('Person.name.full', $enabledcapabilities) && !in_array('Person.name.family', $enabledcapabilities) && - !in_array('Person.name.given', $enabledcapabilities)) { - $attributes['noname'] = 1; - } - if (!in_array('Person.email.primary', $enabledcapabilities)) { - $attributes['noemail'] = 1; - } - } else if ($type->course == $COURSE->id) { - $attributes = array( 'editable' => 1, 'courseTool' => 1, 'domain' => $type->tooldomain ); - } else if ($id != 0) { - $attributes = array( 'globalTool' => 1, 'domain' => $type->tooldomain); - } else { - $attributes = array(); + if ($showtypes) { + $tooltypes = $mform->addElement('select', 'typeid', get_string('external_tool_type', 'lti')); + if ($typeid) { + $mform->getElement('typeid')->setValue($typeid); } + $mform->addHelpButton('typeid', 'external_tool_type', 'lti'); - if ($id) { - $config = lti_get_type_config($id); - if (!empty($config['contentitem'])) { - $attributes['data-contentitem'] = 1; - $attributes['data-id'] = $id; + foreach (lti_get_types_for_add_instance() as $id => $type) { + if (!empty($type->toolproxyid)) { + $toolproxy[] = $type->id; + $attributes = array('globalTool' => 1, 'toolproxy' => 1); + $enabledcapabilities = explode("\n", $type->enabledcapability); + if (!in_array('Result.autocreate', $enabledcapabilities) || + in_array('BasicOutcome.url', $enabledcapabilities)) { + $attributes['nogrades'] = 1; + } + if (!in_array('Person.name.full', $enabledcapabilities) && + !in_array('Person.name.family', $enabledcapabilities) && + !in_array('Person.name.given', $enabledcapabilities)) { + $attributes['noname'] = 1; + } + if (!in_array('Person.email.primary', $enabledcapabilities)) { + $attributes['noemail'] = 1; + } + } else if ($type->course == $COURSE->id) { + $attributes = array('editable' => 1, 'courseTool' => 1, 'domain' => $type->tooldomain); + } else if ($id != 0) { + $attributes = array('globalTool' => 1, 'domain' => $type->tooldomain); } else { - $noncontentitemtypes[] = $id; + $attributes = array(); + } + + if ($id) { + $config = lti_get_type_config($id); + if (!empty($config['contentitem'])) { + $attributes['data-contentitem'] = 1; + $attributes['data-id'] = $id; + } else { + $noncontentitemtypes[] = $id; + } + } + $tooltypes->addOption($type->name, $id, $attributes); + } + } else { + $mform->addElement('hidden', 'typeid', $typeid); + $mform->setType('typeid', PARAM_INT); + if ($typeid) { + $config = lti_get_type_config($typeid); + if (!empty($config['contentitem'])) { + $mform->addElement('hidden', 'contentitem', 1); + $mform->setType('contentitem', PARAM_INT); } } - $tooltypes->addOption($type->name, $id, $attributes); } // Add button that launches the content-item selection dialogue. @@ -148,23 +177,32 @@ class mod_lti_mod_form extends moodleform_mod { $contentbuttonattributes = [ 'data-contentitemurl' => $contentitemurl->out(false) ]; + if (!$showtypes) { + if (!$typeid || empty(lti_get_type_config($typeid)['contentitem'])) { + $contentbuttonattributes['disabled'] = 'disabled'; + } + } $contentbuttonlabel = get_string('selectcontent', 'lti'); $contentbutton = $mform->addElement('button', 'selectcontent', $contentbuttonlabel, $contentbuttonattributes); // Disable select content button if the selected tool doesn't support content item or it's set to Automatic. - $allnoncontentitemtypes = $noncontentitemtypes; - $allnoncontentitemtypes[] = '0'; // Add option value for "Automatic, based on tool URL". - $mform->disabledIf('selectcontent', 'typeid', 'in', $allnoncontentitemtypes); + if ($showtypes) { + $allnoncontentitemtypes = $noncontentitemtypes; + $allnoncontentitemtypes[] = '0'; // Add option value for "Automatic, based on tool URL". + $mform->disabledIf('selectcontent', 'typeid', 'in', $allnoncontentitemtypes); + } - $mform->addElement('text', 'toolurl', get_string('launch_url', 'lti'), array('size' => '64')); - $mform->setType('toolurl', PARAM_URL); - $mform->addHelpButton('toolurl', 'launch_url', 'lti'); - $mform->hideIf('toolurl', 'typeid', 'in', $noncontentitemtypes); + if ($showoptions) { + $mform->addElement('text', 'toolurl', get_string('launch_url', 'lti'), array('size' => '64')); + $mform->setType('toolurl', PARAM_URL); + $mform->addHelpButton('toolurl', 'launch_url', 'lti'); + $mform->hideIf('toolurl', 'typeid', 'in', $noncontentitemtypes); - $mform->addElement('text', 'securetoolurl', get_string('secure_launch_url', 'lti'), array('size' => '64')); - $mform->setType('securetoolurl', PARAM_URL); - $mform->setAdvanced('securetoolurl'); - $mform->addHelpButton('securetoolurl', 'secure_launch_url', 'lti'); - $mform->hideIf('securetoolurl', 'typeid', 'in', $noncontentitemtypes); + $mform->addElement('text', 'securetoolurl', get_string('secure_launch_url', 'lti'), array('size' => '64')); + $mform->setType('securetoolurl', PARAM_URL); + $mform->setAdvanced('securetoolurl'); + $mform->addHelpButton('securetoolurl', 'secure_launch_url', 'lti'); + $mform->hideIf('securetoolurl', 'typeid', 'in', $noncontentitemtypes); + } $mform->addElement('hidden', 'urlmatchedtypeid', '', array( 'id' => 'id_urlmatchedtypeid' )); $mform->setType('urlmatchedtypeid', PARAM_INT); @@ -181,36 +219,38 @@ class mod_lti_mod_form extends moodleform_mod { $mform->addHelpButton('launchcontainer', 'launchinpopup', 'lti'); $mform->setAdvanced('launchcontainer'); - $mform->addElement('text', 'resourcekey', get_string('resourcekey', 'lti')); - $mform->setType('resourcekey', PARAM_TEXT); - $mform->setAdvanced('resourcekey'); - $mform->addHelpButton('resourcekey', 'resourcekey', 'lti'); - $mform->setForceLtr('resourcekey'); - $mform->hideIf('resourcekey', 'typeid', 'in', $noncontentitemtypes); + if ($showoptions) { + $mform->addElement('text', 'resourcekey', get_string('resourcekey', 'lti')); + $mform->setType('resourcekey', PARAM_TEXT); + $mform->setAdvanced('resourcekey'); + $mform->addHelpButton('resourcekey', 'resourcekey', 'lti'); + $mform->setForceLtr('resourcekey'); + $mform->hideIf('resourcekey', 'typeid', 'in', $noncontentitemtypes); - $mform->addElement('passwordunmask', 'password', get_string('password', 'lti')); - $mform->setType('password', PARAM_TEXT); - $mform->setAdvanced('password'); - $mform->addHelpButton('password', 'password', 'lti'); - $mform->hideIf('password', 'typeid', 'in', $noncontentitemtypes); + $mform->addElement('passwordunmask', 'password', get_string('password', 'lti')); + $mform->setType('password', PARAM_TEXT); + $mform->setAdvanced('password'); + $mform->addHelpButton('password', 'password', 'lti'); + $mform->hideIf('password', 'typeid', 'in', $noncontentitemtypes); - $mform->addElement('textarea', 'instructorcustomparameters', get_string('custom', 'lti'), array('rows' => 4, 'cols' => 60)); - $mform->setType('instructorcustomparameters', PARAM_TEXT); - $mform->setAdvanced('instructorcustomparameters'); - $mform->addHelpButton('instructorcustomparameters', 'custom', 'lti'); - $mform->setForceLtr('instructorcustomparameters'); + $mform->addElement('textarea', 'instructorcustomparameters', get_string('custom', 'lti'), array('rows' => 4, 'cols' => 60)); + $mform->setType('instructorcustomparameters', PARAM_TEXT); + $mform->setAdvanced('instructorcustomparameters'); + $mform->addHelpButton('instructorcustomparameters', 'custom', 'lti'); + $mform->setForceLtr('instructorcustomparameters'); - $mform->addElement('text', 'icon', get_string('icon_url', 'lti'), array('size' => '64')); - $mform->setType('icon', PARAM_URL); - $mform->setAdvanced('icon'); - $mform->addHelpButton('icon', 'icon_url', 'lti'); - $mform->hideIf('icon', 'typeid', 'in', $noncontentitemtypes); + $mform->addElement('text', 'icon', get_string('icon_url', 'lti'), array('size' => '64')); + $mform->setType('icon', PARAM_URL); + $mform->setAdvanced('icon'); + $mform->addHelpButton('icon', 'icon_url', 'lti'); + $mform->hideIf('icon', 'typeid', 'in', $noncontentitemtypes); - $mform->addElement('text', 'secureicon', get_string('secure_icon_url', 'lti'), array('size' => '64')); - $mform->setType('secureicon', PARAM_URL); - $mform->setAdvanced('secureicon'); - $mform->addHelpButton('secureicon', 'secure_icon_url', 'lti'); - $mform->hideIf('secureicon', 'typeid', 'in', $noncontentitemtypes); + $mform->addElement('text', 'secureicon', get_string('secure_icon_url', 'lti'), array('size' => '64')); + $mform->setType('secureicon', PARAM_URL); + $mform->setAdvanced('secureicon'); + $mform->addHelpButton('secureicon', 'secure_icon_url', 'lti'); + $mform->hideIf('secureicon', 'typeid', 'in', $noncontentitemtypes); + } // Add privacy preferences fieldset where users choose whether to send their data. $mform->addElement('header', 'privacy', get_string('privacy', 'lti')); diff --git a/mod/lti/version.php b/mod/lti/version.php index 7921b8b6aa4..42ae63789d9 100644 --- a/mod/lti/version.php +++ b/mod/lti/version.php @@ -48,7 +48,7 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2019111800; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2020010800; // The current module version (Date: YYYYMMDDXX). $plugin->requires = 2019111200; // Requires this Moodle version. $plugin->component = 'mod_lti'; // Full name of the plugin (used for diagnostics). $plugin->cron = 0;