Merge branch 'MDL-78916-master' of https://github.com/junpataleta/moodle

This commit is contained in:
Jun Pataleta 2023-09-15 10:57:41 +08:00 committed by Ilya Tregubov
commit 4370887449
No known key found for this signature in database
GPG Key ID: 0F58186F748E55C1
23 changed files with 530 additions and 1131 deletions

View File

@ -41,7 +41,9 @@ require_once('templatable_form_element.php');
*/
class MoodleQuickForm_button extends HTML_QuickForm_button implements templatable
{
use templatable_form_element;
use templatable_form_element {
export_for_template as export_for_template_base;
}
/** @var string html for help button, if empty then no help */
var $_helpbutton='';
@ -49,6 +51,17 @@ class MoodleQuickForm_button extends HTML_QuickForm_button implements templatabl
/** @var bool if true label will be hidden. */
protected $_hiddenLabel = false;
/**
* Any class apart from 'btn' would be overridden with this content.
*
* By default, buttons will utilize the btn-secondary class. However, there are cases where we
* require a button with different stylings (e.g. btn-primary). In these cases, $customclassoverride will override
* the defaults mentioned previously and utilize the provided class(es).
*
* @var null|string $customclassoverride Custom class override for the input element
*/
protected $customclassoverride;
/**
* constructor
*
@ -56,9 +69,14 @@ class MoodleQuickForm_button extends HTML_QuickForm_button implements templatabl
* @param string $value (optional) value for the button
* @param mixed $attributes (optional) Either a typical HTML attribute string
* or an associative array
* @param array $options Options to further customise the button. Currently accepted options are:
* customclassoverride String The CSS class to use for the button instead of the standard
* btn-primary and btn-secondary classes.
*/
public function __construct($elementName=null, $value=null, $attributes=null) {
public function __construct($elementName=null, $value=null, $attributes=null, $options = []) {
parent::__construct($elementName, $value, $attributes);
$this->customclassoverride = $options['customclassoverride'] ?? null;
}
/**
@ -101,4 +119,13 @@ class MoodleQuickForm_button extends HTML_QuickForm_button implements templatabl
public function setHiddenLabel($hiddenLabel) {
$this->_hiddenLabel = $hiddenLabel;
}
public function export_for_template(renderer_base $output) {
$context = $this->export_for_template_base($output);
if ($this->customclassoverride) {
$context['customclassoverride'] = $this->customclassoverride;
}
return $context;
}
}

View File

@ -2,7 +2,9 @@
{{$element}}
{{^element.frozen}}
<button
class="btn btn-secondary"
class="btn
{{^element.customclassoverride}}btn-secondary{{/element.customclassoverride}}
{{#element.customclassoverride}}{{.}}{{/element.customclassoverride}}"
name="{{element.name}}"
id="{{element.id}}"
type="button"

View File

@ -2,7 +2,9 @@
{{$element}}
{{^element.frozen}}
<button
class="btn btn-secondary ml-0"
class="btn
{{^element.customclassoverride}}btn-secondary ml-0{{/element.customclassoverride}}
{{#element.customclassoverride}}{{.}}{{/element.customclassoverride}}"
name="{{element.name}}"
id="{{element.id}}"
type="button"

View File

@ -1,95 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* AJAX service used when adding an External Tool.
*
* It is used to provide immediate feedback
* of which tool provider is to be used based on the Launch URL.
*
* @package mod_lti
* @subpackage xml
* @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Chris Scribner
*/
define('AJAX_SCRIPT', true);
require_once(__DIR__ . "/../../config.php");
require_once($CFG->dirroot . '/mod/lti/locallib.php');
$courseid = required_param('course', PARAM_INT);
$context = context_course::instance($courseid);
require_login($courseid, false);
$action = required_param('action', PARAM_TEXT);
$response = new stdClass();
switch ($action) {
case 'find_tool_config':
$toolurl = required_param('toolurl', PARAM_RAW);
$toolid = optional_param('toolid', 0, PARAM_INT);
require_capability('moodle/course:manageactivities', $context);
require_capability('mod/lti:addinstance', $context);
if (!empty($toolurl) && lti_is_cartridge($toolurl)) {
$response->cartridge = true;
} else {
if (empty($toolid) && !empty($toolurl)) {
$tool = lti_get_tool_by_url_match($toolurl, $courseid);
if (!empty($tool)) {
$toolid = $tool->id;
$response->toolid = $tool->id;
$response->toolname = s($tool->name);
$response->tooldomain = s($tool->tooldomain);
}
} else {
$response->toolid = $toolid;
}
if (!empty($toolid)) {
// Look up privacy settings.
$query = '
SELECT name, value
FROM {lti_types_config}
WHERE
typeid = :typeid
AND name IN (\'sendname\', \'sendemailaddr\', \'acceptgrades\')
';
$privacyconfigs = $DB->get_records_sql($query, array('typeid' => $toolid));
$success = count($privacyconfigs) > 0;
foreach ($privacyconfigs as $config) {
$configname = $config->name;
$response->$configname = $config->value;
}
if (!$success) {
$response->error = s(get_string('tool_config_not_found', 'mod_lti'));
}
}
}
break;
}
echo $OUTPUT->header();
echo json_encode($response);
die;

View File

@ -9,6 +9,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.2
*/
define("mod_lti/contentitem",["jquery","core/notification","core/str","core/templates","mod_lti/form-field","core/modal","core/modal_events"],(function($,notification,str,templates,FormField,Modal,ModalEvents){var dialogue,doneCallback,contentItem={init:function(url,postData,cb){doneCallback=cb;var context={url:url,postData:postData},bodyPromise=templates.render("mod_lti/contentitem",context);if(dialogue)return dialogue.setBody(bodyPromise),void dialogue.show();str.get_string("selectcontent","lti").then((function(title){return Modal.create({title:title,body:bodyPromise,large:!0,show:!0})})).then((function(modal){dialogue=modal,modal.getRoot().on(ModalEvents.hidden,(function(){modal.setBody(""),notification.fetchNotifications()}))})).catch(notification.exception)}},ltiFormFields=[new FormField("name",FormField.TYPES.TEXT,!1,""),new FormField("introeditor",FormField.TYPES.EDITOR,!1,""),new FormField("toolurl",FormField.TYPES.TEXT,!0,""),new FormField("securetoolurl",FormField.TYPES.TEXT,!0,""),new FormField("instructorchoiceacceptgrades",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorchoicesendname",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorchoicesendemailaddr",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorcustomparameters",FormField.TYPES.TEXT,!0,""),new FormField("icon",FormField.TYPES.TEXT,!0,""),new FormField("secureicon",FormField.TYPES.TEXT,!0,""),new FormField("launchcontainer",FormField.TYPES.SELECT,!0,0),new FormField("grade_modgrade_point",FormField.TYPES.TEXT,!1,""),new FormField("lineitemresourceid",FormField.TYPES.TEXT,!0,""),new FormField("lineitemtag",FormField.TYPES.TEXT,!0,""),new FormField("lineitemsubreviewurl",FormField.TYPES.TEXT,!0,""),new FormField("lineitemsubreviewparams",FormField.TYPES.TEXT,!0,"")];const hideElement=e=>{e.setAttribute("hidden","true"),e.setAttribute("aria-hidden","true"),e.setAttribute("tab-index","-1")},showElement=e=>{e.removeAttribute("hidden"),e.setAttribute("aria-hidden","false"),e.setAttribute("tab-index","1")};return window.processContentItemReturnData=function(returnData){var index;if(dialogue&&dialogue.hide(),returnData.multiple){for(index in ltiFormFields)ltiFormFields[index].setFieldValue("name"===ltiFormFields[index].name?"item":null);var variants=[];returnData.multiple.forEach((function(v){variants.push((config=>{const variant={};return["name","toolurl","securetoolurl","instructorcustomparameters","icon","secureicon","launchcontainer","lineitemresourceid","lineitemtag","lineitemsubreviewurl","lineitemsubreviewparams"].forEach((function(name){variant[name]=config[name]||""})),variant["introeditor[text]"]=config.introeditor?config.introeditor.text:"",variant["introeditor[format]"]=config.introeditor?config.introeditor.format:"",1===config.instructorchoiceacceptgrades?(variant.instructorchoiceacceptgrades="1",variant["grade[modgrade_point]"]=config.grade_modgrade_point||"100"):variant.instructorchoiceacceptgrades="0",variant})(v))})),async function(items){const form=document.querySelector("#region-main-box form"),toolArea=form.querySelector('[data-attribute="dynamic-import"]'),buttonGroup=form.querySelector("#fgroup_id_buttonar"),submitAndLaunch=form.querySelector("#id_submitbutton");Array.from(form.children).forEach(hideElement),hideElement(submitAndLaunch);const{html:html,js:js}=await templates.renderForPromise("mod_lti/tool_deeplinking_results",{items:items});await templates.replaceNodeContents(toolArea,html,js),showElement(toolArea),showElement(buttonGroup)}(returnData.multiple);const submitAndCourse=document.querySelector("#id_submitbutton2");submitAndCourse.onclick=e=>{e.preventDefault(),submitAndCourse.disabled=!0;const fd=new FormData(document.querySelector("#region-main-box form")),backToCourse=()=>{document.querySelector("#id_cancel").click()};variants.reduce(((promise,variant)=>{Object.entries(variant).forEach((entry=>fd.set(entry[0],entry[1])));const body=new URLSearchParams(fd),doPost=()=>fetch(document.location.pathname,{method:"post",body:body});return promise.then(doPost).catch(doPost)}),Promise.resolve()).then(backToCourse).catch(backToCourse)}}else{for(index in ltiFormFields){var field=ltiFormFields[index],value=null;void 0!==returnData[field.name]&&(value=returnData[field.name]),field.setFieldValue(value)}field.setFieldValue(value)}doneCallback&&doneCallback(returnData)},contentItem}));
define("mod_lti/contentitem",["jquery","core/notification","core/str","core/templates","mod_lti/form-field","core/modal","core/modal_events"],(function($,notification,str,templates,FormField,Modal,ModalEvents){var dialogue,doneCallback,contentItem={init:function(url,postData,cb){doneCallback=cb;var context={url:url,postData:postData},bodyPromise=templates.render("mod_lti/contentitem",context);if(dialogue)return dialogue.setBody(bodyPromise),void dialogue.show();str.get_string("selectcontent","lti").then((function(title){return Modal.create({title:title,body:bodyPromise,large:!0,show:!0})})).then((function(modal){dialogue=modal,modal.getRoot().on(ModalEvents.hidden,(function(){modal.setBody(""),notification.fetchNotifications()}))})).catch(notification.exception)}},ltiFormFields=[new FormField("selectcontentstatus",FormField.TYPES.TEXT,!0,""),new FormField("name",FormField.TYPES.TEXT,!1,""),new FormField("introeditor",FormField.TYPES.EDITOR,!1,""),new FormField("toolurl",FormField.TYPES.TEXT,!0,""),new FormField("securetoolurl",FormField.TYPES.TEXT,!0,""),new FormField("instructorchoiceacceptgrades",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorchoicesendname",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorchoicesendemailaddr",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorcustomparameters",FormField.TYPES.TEXT,!0,""),new FormField("icon",FormField.TYPES.TEXT,!0,""),new FormField("secureicon",FormField.TYPES.TEXT,!0,""),new FormField("launchcontainer",FormField.TYPES.SELECT,!0,0),new FormField("grade_modgrade_point",FormField.TYPES.TEXT,!1,""),new FormField("lineitemresourceid",FormField.TYPES.TEXT,!0,""),new FormField("lineitemtag",FormField.TYPES.TEXT,!0,""),new FormField("lineitemsubreviewurl",FormField.TYPES.TEXT,!0,""),new FormField("lineitemsubreviewparams",FormField.TYPES.TEXT,!0,"")];const hideElement=e=>{e.setAttribute("hidden","true"),e.setAttribute("aria-hidden","true"),e.setAttribute("tab-index","-1")},showElement=e=>{e.removeAttribute("hidden"),e.setAttribute("aria-hidden","false"),e.setAttribute("tab-index","1")};return window.processContentItemReturnData=function(returnData){var index;if(dialogue&&dialogue.hide(),returnData.multiple){for(index in ltiFormFields)ltiFormFields[index].setFieldValue("name"===ltiFormFields[index].name?"item":null);var variants=[];returnData.multiple.forEach((function(v){variants.push((config=>{const variant={};return["name","toolurl","securetoolurl","instructorcustomparameters","icon","secureicon","launchcontainer","lineitemresourceid","lineitemtag","lineitemsubreviewurl","lineitemsubreviewparams","selectcontentstatus"].forEach((function(name){variant[name]=config[name]||""})),variant["introeditor[text]"]=config.introeditor?config.introeditor.text:"",variant["introeditor[format]"]=config.introeditor?config.introeditor.format:"",1===config.instructorchoiceacceptgrades?(variant.instructorchoiceacceptgrades="1",variant["grade[modgrade_point]"]=config.grade_modgrade_point||"100"):variant.instructorchoiceacceptgrades="0",variant})(v))})),async function(items){const form=document.querySelector("#region-main-box form"),toolArea=form.querySelector('[data-attribute="dynamic-import"]'),buttonGroup=form.querySelector("#fgroup_id_buttonar"),submitAndLaunch=form.querySelector("#id_submitbutton");Array.from(form.children).forEach(hideElement),hideElement(submitAndLaunch);const{html:html,js:js}=await templates.renderForPromise("mod_lti/tool_deeplinking_results",{items:items});await templates.replaceNodeContents(toolArea,html,js),showElement(toolArea),showElement(buttonGroup)}(returnData.multiple);const submitAndCourse=document.querySelector("#id_submitbutton2");submitAndCourse.onclick=e=>{e.preventDefault(),submitAndCourse.disabled=!0;const fd=new FormData(document.querySelector("#region-main-box form")),backToCourse=()=>{document.querySelector("#id_cancel").click()};variants.reduce(((promise,variant)=>{Object.entries(variant).forEach((entry=>fd.set(entry[0],entry[1])));const body=new URLSearchParams(fd),doPost=()=>fetch(document.location.pathname,{method:"post",body:body});return promise.then(doPost).catch(doPost)}),Promise.resolve()).then(backToCourse).catch(backToCourse)}}else{for(index in ltiFormFields){var field=ltiFormFields[index],value=null;void 0!==returnData[field.name]&&(value=returnData[field.name]),field.setFieldValue(value)}field.setFieldValue(value),document.querySelector("#id_selectcontentindicator").innerHTML=returnData.selectcontentindicator}doneCallback&&doneCallback(returnData)},contentItem}));
//# sourceMappingURL=contentitem.min.js.map

File diff suppressed because one or more lines are too long

3
mod/lti/amd/build/mod_form.min.js vendored Normal file
View File

@ -0,0 +1,3 @@
define("mod_lti/mod_form",["exports","mod_lti/contentitem"],(function(_exports,_contentitem){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_contentitem=(obj=_contentitem)&&obj.__esModule?obj:{default:obj};var _default={init:courseId=>{const contentItemButton=document.querySelector('[name="selectcontent"]');contentItemButton&&contentItemButton.addEventListener("click",(()=>{const contentItemUrl=contentItemButton.getAttribute("data-contentitemurl"),contentItemId=document.querySelector("#hidden_typeid").value;if(contentItemId){const title=document.querySelector("#id_name").value.trim(),text=document.querySelector("#id_introeditor").value.trim(),postData={id:contentItemId,course:courseId,title:title,text:text};_contentitem.default.init(contentItemUrl,postData,(returnData=>{if(!returnData.multiple){const allowGrades=document.querySelector("#id_instructorchoiceacceptgrades");let allowGradesChangeEvent=new Event("change");if(allowGrades.dispatchEvent(allowGradesChangeEvent),allowGrades.checked){const gradeType=document.querySelector("#id_grade_modgrade_type");gradeType.value="point";let gradeTypeChangeEvent=new Event("change");gradeType.dispatchEvent(gradeTypeChangeEvent)}}}))}}))}};return _exports.default=_default,_exports.default}));
//# sourceMappingURL=mod_form.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"mod_form.min.js","sources":["../src/mod_form.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Event handlers for the mod_lti mod_form.\n *\n * @module mod_lti/mod_form\n * @copyright 2023 Jake Dallimore <jrhdallimore@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport ContentItem from 'mod_lti/contentitem';\n\n/**\n * Initialise module.\n *\n * @param {int} courseId the course id.\n */\nconst init = (courseId) => {\n const contentItemButton = document.querySelector('[name=\"selectcontent\"]');\n\n if (!contentItemButton) {\n return;\n }\n\n contentItemButton.addEventListener('click', () => {\n const contentItemUrl = contentItemButton.getAttribute('data-contentitemurl');\n const contentItemId = document.querySelector('#hidden_typeid').value;\n if (contentItemId) {\n const title = document.querySelector('#id_name').value.trim();\n const text = document.querySelector('#id_introeditor').value.trim();\n const postData = {\n id: contentItemId,\n course: courseId,\n title: title,\n text: text\n };\n\n // The callback below is called after the content item has been returned and processed.\n ContentItem.init(contentItemUrl, postData, (returnData) => {\n if (!returnData.multiple) {\n // The state of the grade checkbox has already been set by processContentItemReturnData() but that\n // hasn't fired the click/change event required by formslib to show/hide the dependent grade fields.\n // Fire it now.\n const allowGrades = document.querySelector('#id_instructorchoiceacceptgrades');\n let allowGradesChangeEvent = new Event('change');\n allowGrades.dispatchEvent(allowGradesChangeEvent);\n\n // If the tool is set to accept grades, make sure \"Point\" is selected.\n if (allowGrades.checked) {\n const gradeType = document.querySelector('#id_grade_modgrade_type');\n gradeType.value = \"point\";\n let gradeTypeChangeEvent = new Event('change');\n gradeType.dispatchEvent(gradeTypeChangeEvent);\n }\n }\n });\n }\n });\n};\n\nexport default {\n init: init\n};\n"],"names":["init","courseId","contentItemButton","document","querySelector","addEventListener","contentItemUrl","getAttribute","contentItemId","value","title","trim","text","postData","id","course","returnData","multiple","allowGrades","allowGradesChangeEvent","Event","dispatchEvent","checked","gradeType","gradeTypeChangeEvent"],"mappings":"oQA2Ee,CACXA,KA5CUC,iBACJC,kBAAoBC,SAASC,cAAc,0BAE5CF,mBAILA,kBAAkBG,iBAAiB,SAAS,WAClCC,eAAiBJ,kBAAkBK,aAAa,uBAChDC,cAAgBL,SAASC,cAAc,kBAAkBK,SAC3DD,cAAe,OACTE,MAAQP,SAASC,cAAc,YAAYK,MAAME,OACjDC,KAAOT,SAASC,cAAc,mBAAmBK,MAAME,OACvDE,SAAW,CACbC,GAAIN,cACJO,OAAQd,SACRS,MAAOA,MACPE,KAAMA,2BAIEZ,KAAKM,eAAgBO,UAAWG,iBACnCA,WAAWC,SAAU,OAIhBC,YAAcf,SAASC,cAAc,wCACvCe,uBAAyB,IAAIC,MAAM,aACvCF,YAAYG,cAAcF,wBAGtBD,YAAYI,QAAS,OACfC,UAAYpB,SAASC,cAAc,2BACzCmB,UAAUd,MAAQ,YACde,qBAAuB,IAAIJ,MAAM,UACrCG,UAAUF,cAAcG"}

View File

@ -87,6 +87,7 @@ define(
* Array of form fields for LTI tool configuration.
*/
var ltiFormFields = [
new FormField('selectcontentstatus', FormField.TYPES.TEXT, true, ''),
new FormField('name', FormField.TYPES.TEXT, false, ''),
new FormField('introeditor', FormField.TYPES.EDITOR, false, ''),
new FormField('toolurl', FormField.TYPES.TEXT, true, ''),
@ -161,7 +162,7 @@ define(
const variant = {};
['name', 'toolurl', 'securetoolurl', 'instructorcustomparameters', 'icon', 'secureicon',
'launchcontainer', 'lineitemresourceid', 'lineitemtag', 'lineitemsubreviewurl',
'lineitemsubreviewparams'].forEach(
'lineitemsubreviewparams', 'selectcontentstatus'].forEach(
function(name) {
variant[name] = config[name] || '';
}
@ -228,6 +229,9 @@ define(
field.setFieldValue(value);
}
field.setFieldValue(value);
// Update the UI element which signifies content has been selected.
document.querySelector("#id_selectcontentindicator").innerHTML = returnData.selectcontentindicator;
}
if (doneCallback) {

View File

@ -0,0 +1,78 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Event handlers for the mod_lti mod_form.
*
* @module mod_lti/mod_form
* @copyright 2023 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
"use strict";
import ContentItem from 'mod_lti/contentitem';
/**
* Initialise module.
*
* @param {int} courseId the course id.
*/
const init = (courseId) => {
const contentItemButton = document.querySelector('[name="selectcontent"]');
if (!contentItemButton) {
return;
}
contentItemButton.addEventListener('click', () => {
const contentItemUrl = contentItemButton.getAttribute('data-contentitemurl');
const contentItemId = document.querySelector('#hidden_typeid').value;
if (contentItemId) {
const title = document.querySelector('#id_name').value.trim();
const text = document.querySelector('#id_introeditor').value.trim();
const postData = {
id: contentItemId,
course: courseId,
title: title,
text: text
};
// The callback below is called after the content item has been returned and processed.
ContentItem.init(contentItemUrl, postData, (returnData) => {
if (!returnData.multiple) {
// The state of the grade checkbox has already been set by processContentItemReturnData() but that
// hasn't fired the click/change event required by formslib to show/hide the dependent grade fields.
// Fire it now.
const allowGrades = document.querySelector('#id_instructorchoiceacceptgrades');
let allowGradesChangeEvent = new Event('change');
allowGrades.dispatchEvent(allowGradesChangeEvent);
// If the tool is set to accept grades, make sure "Point" is selected.
if (allowGrades.checked) {
const gradeType = document.querySelector('#id_grade_modgrade_type');
gradeType.value = "point";
let gradeTypeChangeEvent = new Event('change');
gradeType.dispatchEvent(gradeTypeChangeEvent);
}
}
});
}
});
};
export default {
init: init
};

View File

@ -269,16 +269,15 @@ class mod_lti_edit_types_form extends moodleform {
$options = array();
$options[0] = get_string('never', 'lti');
$options[1] = get_string('always', 'lti');
$options[2] = get_string('delegate', 'lti');
$mform->addElement('select', 'lti_sendname', get_string('share_name_admin', 'lti'), $options);
$mform->setType('lti_sendname', PARAM_INT);
$mform->setDefault('lti_sendname', '2');
$mform->setDefault('lti_sendname', '0');
$mform->addHelpButton('lti_sendname', 'share_name_admin', 'lti');
$mform->addElement('select', 'lti_sendemailaddr', get_string('share_email_admin', 'lti'), $options);
$mform->setType('lti_sendemailaddr', PARAM_INT);
$mform->setDefault('lti_sendemailaddr', '2');
$mform->setDefault('lti_sendemailaddr', '0');
$mform->addHelpButton('lti_sendemailaddr', 'share_email_admin', 'lti');
// LTI Extensions.
@ -294,7 +293,7 @@ class mod_lti_edit_types_form extends moodleform {
$mform->setDefault('lti_acceptgrades', '2');
$mform->addHelpButton('lti_acceptgrades', 'accept_grades_admin', 'lti');
$mform->addElement('checkbox', 'lti_forcessl', get_string('force_ssl', 'lti'), '', $options);
$mform->addElement('checkbox', 'lti_forcessl', get_string('force_ssl', 'lti'));
$mform->setType('lti_forcessl', PARAM_BOOL);
if (!empty($CFG->mod_lti_forcessl)) {
$mform->setDefault('lti_forcessl', '1');
@ -431,4 +430,26 @@ class mod_lti_edit_types_form extends moodleform {
}
return $branch;
}
public function definition_after_data() {
// Add the deprecated "Delegate to teacher" option to the "Share launcher's name" and "Share launcher's email" fields for
// existing types which are already using this setting value. This ensures that editing the tool type won't result in a
// change to the existing value. Add the option as a disabled to make this clear. Once changed, it cannot be set again.
// This isn't supported from 4.3 onward in the creation of new tool types.
foreach (['lti_sendname', 'lti_sendemailaddr'] as $elementname) {
if ($this->_form->_defaultValues[$elementname] == LTI_SETTING_DELEGATE) {
$elementarr = array_filter($this->_form->_elements, function ($element) use($elementname) {
return !empty($element->_attributes['name']) && $element->_attributes['name'] == $elementname;
});
/** @var MoodleQuickForm_select $element */
$element = array_shift($elementarr);
$element->addOption(
get_string('delegate', 'mod_lti'),
LTI_SETTING_DELEGATE,
['disabled' => 'disabled', 'selected' => 'selected']
);
}
}
}
}

View File

@ -1,133 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This page allows instructors to configure course level tool providers.
*
* @package mod_lti
* @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Chris Scribner
*/
require_once('../../config.php');
require_once($CFG->dirroot.'/mod/lti/edit_form.php');
require_once($CFG->dirroot.'/mod/lti/lib.php');
$courseid = required_param('course', PARAM_INT);
require_login($courseid, false);
$url = new moodle_url('/mod/lti/instructor_edit_tool_type.php');
$PAGE->set_url($url);
$PAGE->set_pagelayout('popup');
$PAGE->set_title(get_string('edittype', 'mod_lti'));
$action = optional_param('action', null, PARAM_TEXT);
$typeid = optional_param('typeid', null, PARAM_INT);
require_sesskey();
require_capability('mod/lti:addcoursetool', context_course::instance($courseid));
if (!empty($typeid)) {
$type = lti_get_type($typeid);
if ($type->course != $courseid) {
throw new Exception('You do not have permissions to edit this tool type.');
die;
}
}
// Delete action is called via ajax.
if ($action == 'delete') {
lti_delete_type($typeid);
die;
}
// Add a timeout for closing for behat so it can check for errors before switching back to the main window.
$timeout = 0;
if (defined('BEHAT_SITE_RUNNING') && BEHAT_SITE_RUNNING) {
$timeout = 2000;
}
echo $OUTPUT->header();
if ($action == 'edit') {
$type = lti_get_type_type_config($typeid);
} else {
$type = new stdClass();
$type->lti_clientid = null;
}
$form = new mod_lti_edit_types_form($url, (object)array('id' => $typeid, 'clientid' => $type->lti_clientid));
// If the user just opened an add or edit form.
if ($action == 'add' || $action == 'edit') {
if ($action == 'edit') {
$form->set_data($type);
}
echo $OUTPUT->heading(get_string('toolsetup', 'lti'));
$form->display();
} else {
$script = '';
$closewindow = <<<EOF
setTimeout(function() {
window.close();
}, $timeout);
EOF;
if ($data = $form->get_data()) {
$type = new stdClass();
if (!empty($typeid)) {
$type->id = $typeid;
lti_load_type_if_cartridge($data);
lti_update_type($type, $data);
$fromdb = lti_get_type($typeid);
$json = json_encode($fromdb);
// Output script to update the calling window.
$script = <<<EOF
window.opener.M.mod_lti.editor.updateToolType({$json});
EOF;
} else {
$type->state = LTI_TOOL_STATE_CONFIGURED;
$type->course = $COURSE->id;
lti_load_type_if_cartridge($data);
$id = lti_add_type($type, $data);
$fromdb = lti_get_type($id);
$json = json_encode($fromdb);
// Output script to update the calling window.
$script = <<<EOF
window.opener.M.mod_lti.editor.addToolType({$json});
EOF;
}
echo html_writer::script($script . $closewindow);
} else if ($form->is_cancelled()) {
echo html_writer::script($closewindow);
} else {
echo $OUTPUT->heading(get_string('toolsetup', 'lti'));
$form->display();
}
}
echo $OUTPUT->footer();

View File

@ -1 +1,17 @@
lti:addmanualinstance,mod_lti
edittype,mod_lti
deletetype,mod_lti
cannot_delete,mod_lti
cannot_edit,mod_lti
global_tool_types,mod_lti
course_tool_types,mod_lti
using_tool_cartridge,mod_lti
using_tool_configuration,mod_lti
domain_mismatch,mod_lti
custom_config,mod_lti
tool_config_not_found,mod_lti
tooltypeadded,mod_lti
tooltypedeleted,mod_lti
tooltypenotdeleted,mod_lti
tooltypeupdated,mod_lti
forced_help,mod_lti

View File

@ -61,6 +61,7 @@ $string['accept_grades_help'] = 'Specify whether the tool provider can add, upda
Some tool providers support reporting grades back to Moodle based on actions taken within the tool, creating a more integrated experience.
Note that this setting may be overridden in the tool configuration.';
$string['accept_grades_from_tool'] = 'Allow {$a} to add grades in the gradebook';
$string['action'] = 'Action';
$string['activate'] = 'Activate';
$string['activatetoadddescription'] = 'You will need to activate this tool before you can add a description.';
@ -92,8 +93,6 @@ $string['basicltisettings'] = 'Basic Learning Tool Interoperability (LTI) settin
$string['cachedef_keyset'] = 'Caches the keyset information of tools';
$string['cancel'] = 'Cancel';
$string['cancelled'] = 'Cancelled';
$string['cannot_delete'] = 'You may not delete this tool configuration.';
$string['cannot_edit'] = 'You may not edit this tool configuration.';
$string['capabilities'] = 'Capabilities';
$string['capabilitiesrequired'] = 'This tool requires access to the following data in order to activate:';
$string['capabilities_help'] = 'Select those capabilities which you wish to offer to the tool provider. More than one capability can be selected.';
@ -115,7 +114,7 @@ $string['contentitem_deeplinking'] = 'Supports Deep Linking (Content-Item Messag
$string['contentitem_deeplinking_help'] = 'If ticked, the option \'Select content\' will be available when adding an external tool.';
$string['contentitem_multiple_description'] = 'The following items will be added to your course:';
$string['contentitem_multiple_graded'] = 'Graded activity (Maximum grade: {$a})';
$string['course_tool_types'] = 'Course tools';
$string['contentselected'] = 'Content selected';
$string['courseactivitiesorresources'] = 'Course activities or resources';
$string['courseexternaltooladd'] = 'Add new LTI External tool';
$string['courseexternaltooladdsuccess'] = '{$a} added.';
@ -134,7 +133,6 @@ $string['coursetooldeleted'] = '{$a} removed';
$string['createdon'] = 'Created on';
$string['curllibrarymissing'] = 'PHP cURL extension required for the External tool.';
$string['custom'] = 'Custom parameters';
$string['custom_config'] = 'Using custom tool configuration.';
$string['custom_help'] = 'Custom parameters are settings used by the tool provider. For example, a custom parameter may be used to display
a specific resource from the provider. Each parameter should be entered on a separate line using a format of "name=value"; for example, "chapter=3".
@ -162,21 +160,10 @@ $string['delete_confirmation'] = 'Are you sure you want to delete this preconfig
$string['deletecoursetool'] = 'Delete {$a}';
$string['deletecoursetoolconfirm'] = 'This will delete {$a} from the available LTI tools in your course.';
$string['deletecoursetoolwithusageconfirm'] = '{$a} is currently being used in at least one activity in your course. If you delete this tool, the activities that use it will no longer work.<br><br>Are you sure you want to delete {$a}?';
$string['deletetype'] = 'Delete preconfigured tool';
$string['display_description'] = 'Display activity description when launched';
$string['display_description_help'] = 'If selected, the activity description (specified above) will display above the tool provider\'s content.
The description may be used to provide additional instructions for launchers of the tool, but it is not required.
The description is never displayed when the tool\'s launch container is in a new window.';
$string['display_name'] = 'Display activity name when launched';
$string['display_name_help'] = 'If selected, the activity name (specified above) will display above the tool provider\'s content.
It is possible that the tool provider may also display the title. This option can prevent the activity title from
being displayed twice.
The title is never displayed when the tool\'s launch container is in a new window.';
$string['domain_mismatch'] = 'Tool URL\'s domain does not match tool configuration.';
$string['display_description'] = 'Display activity description when students access the tool';
$string['display_description_help'] = 'Content from this tool is displayed embedded in a page in the course. This setting determines if the activity description is shown in that page.';
$string['display_name'] = 'Display activity name when students access the tool';
$string['display_name_help'] = 'Content from this tool is displayed embedded in a page in the course. This setting determines if the activity name is shown in that page.';
$string['donot'] = 'Do not send';
$string['donotaccept'] = 'Do not accept';
$string['donotallow'] = 'Do not allow';
@ -196,7 +183,6 @@ $string['editmanualinstancedeprecationwarning'] = 'Manually configured External
To make any changes to the tool, or to create new activities with it, the tool needs to be added to your course in Course > More > LTI External tools. Then, you will be able to create new activities, selecting the tool directly in the Activity chooser.
<br><br>
You can read more about adding LTI External tools in the documentation <a href="{$a}" target="_blank">External tool</a>.';
$string['edittype'] = 'Edit preconfigured tool';
$string['embed'] = 'Embed';
$string['embed_no_blocks'] = 'Embed, without blocks';
$string['enableemailnotification'] = 'Send notification emails';
@ -237,7 +223,6 @@ $string['fixexistingconf'] = 'Use an existing configuration for the misconfigure
$string['fixnew'] = 'New configuration';
$string['fixnewconf'] = 'Define a new configuration for the misconfigured instance';
$string['fixold'] = 'Use existing';
$string['forced_help'] = 'This setting has been forced in a course or site level tool configuration. You may not change it from this interface.';
$string['force_ssl'] = 'Force SSL';
$string['force_ssl_help'] = 'Selecting this option forces all launches to this tool provider to use SSL.
@ -245,7 +230,6 @@ 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'] = '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
@ -493,6 +477,7 @@ $string['secure_launch_url_help'] = 'Similar to the tool URL, but used instead o
The tool URL may also be set to an https address to force launching through SSL, and this field may be left blank.';
$string['selectcontent'] = 'Select content';
$string['selectcontentvalidationerror'] = 'You need to select content for this activity.';
$string['send'] = 'Send';
$string['services'] = 'Services';
$string['services_help'] = 'Select those services which you wish to offer to the tool provider. More than one service can be selected.';
@ -545,7 +530,6 @@ $string['subplugintype_ltiservice_plural'] = 'LTI services';
$string['subplugintype_ltisource'] = 'LTI source';
$string['subplugintype_ltisource_plural'] = 'LTI sources';
$string['toggle_debug_data'] = 'Toggle debug data';
$string['tool_config_not_found'] = 'Tool configuration not found for this URL.';
$string['tool_settings'] = 'Tool settings';
$string['tooldescription'] = 'Tool description';
$string['tooldescription_help'] = 'The description of the tool that will be displayed to teachers in the activity list.
@ -580,10 +564,6 @@ $string['toolregistration'] = 'External tool registration';
$string['toolsetup'] = 'External tool configuration';
$string['tooltypenotfounderror'] = "The LTI tool used in this activity has been deleted. If you need help, contact your teacher or site administrator.";
$string['tooltypes'] = 'Tools';
$string['tooltypeadded'] = 'Preconfigured tool added';
$string['tooltypedeleted'] = 'Preconfigured tool deleted';
$string['tooltypenotdeleted'] = 'Could not delete preconfigured tool';
$string['tooltypeupdated'] = 'Preconfigured tool updated';
$string['toolurl'] = 'Tool URL';
$string['toolurlplaceholder'] = 'Tool URL...';
$string['toolurl_help'] = 'The tool URL is used to match tool URLs to the correct tool configuration. Prefixing the URL with http(s) is optional.
@ -623,10 +603,24 @@ $string['update'] = 'Update';
$string['usage'] = 'Usage';
$string['useraccountinformation'] = 'User account information';
$string['userpersonalinformation'] = 'User personal information';
$string['using_tool_cartridge'] = 'Using tool cartridge';
$string['using_tool_configuration'] = 'Using tool configuration: ';
$string['validurl'] = 'A valid URL must start with http(s)://';
$string['viewsubmissions'] = 'View submissions and grading screen';
// Deprecated since Moodle 4.3.
$string['lti:addmanualinstance'] = 'Add a manually-configured tool';
$string['edittype'] = 'Edit preconfigured tool';
$string['deletetype'] = 'Delete preconfigured tool';
$string['cannot_delete'] = 'You may not delete this tool configuration.';
$string['cannot_edit'] = 'You may not edit this tool configuration.';
$string['global_tool_types'] = 'Preconfigured tools';
$string['course_tool_types'] = 'Course tools';
$string['using_tool_cartridge'] = 'Using tool cartridge';
$string['using_tool_configuration'] = 'Using tool configuration: ';
$string['domain_mismatch'] = 'Tool URL\'s domain does not match tool configuration.';
$string['custom_config'] = 'Using custom tool configuration.';
$string['tool_config_not_found'] = 'Tool configuration not found for this URL.';
$string['tooltypeadded'] = 'Preconfigured tool added';
$string['tooltypedeleted'] = 'Preconfigured tool deleted';
$string['tooltypenotdeleted'] = 'Could not delete preconfigured tool';
$string['tooltypeupdated'] = 'Preconfigured tool updated';
$string['forced_help'] = 'This setting has been forced in a course or site level tool configuration. You may not change it from this interface.';

View File

@ -1423,6 +1423,8 @@ function params_to_string(object $params) {
* @return stdClass Form config for the item
*/
function content_item_to_form(object $tool, object $typeconfig, object $item) : stdClass {
global $OUTPUT;
$config = new stdClass();
$config->name = '';
if (isset($item->title)) {
@ -1519,6 +1521,11 @@ function content_item_to_form(object $tool, object $typeconfig, object $item) :
if (isset($item->custom)) {
$config->instructorcustomparameters = params_to_string($item->custom);
}
// Set the status, allowing the form to validate, and pass an indicator to the relevant form field.
$config->selectcontentstatus = true;
$config->selectcontentindicator = $OUTPUT->pix_icon('i/valid', get_string('yes')) . get_string('contentselected', 'mod_lti');
return $config;
}

View File

@ -1,578 +0,0 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Javascript extensions for the External Tool activity editor.
*
* @package mod
* @subpackage lti
* @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
(function(){
var Y;
M.mod_lti = M.mod_lti || {};
M.mod_lti.LTI_SETTING_NEVER = 0;
M.mod_lti.LTI_SETTING_ALWAYS = 1;
M.mod_lti.LTI_SETTING_DELEGATE = 2;
M.mod_lti.editor = {
init: function(yui3, settings){
if(yui3){
Y = yui3;
}
var self = this;
this.settings = Y.JSON.parse(settings);
this.urlCache = {};
this.toolTypeCache = {};
var updateToolMatches = function(){
self.updateAutomaticToolMatch(Y.one('#id_toolurl'));
self.updateAutomaticToolMatch(Y.one('#id_securetoolurl'));
};
var typeSelector = Y.one('#id_typeid');
if (typeSelector) {
this.addOptGroups();
typeSelector.on('change', function(e){
// Reset configuration fields when another preconfigured tool is selected.
self.resetToolFields();
updateToolMatches();
self.toggleEditButtons();
if (self.getSelectedToolTypeOption().getAttribute('toolproxy')){
var allowname = Y.one('#id_instructorchoicesendname');
allowname.set('checked', !self.getSelectedToolTypeOption().getAttribute('noname'));
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"]');
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
};
require(['mod_lti/contentitem'], function(contentitem) {
contentitem.init(contentItemUrl, postData, function(returnData) {
if (!returnData.multiple) {
M.mod_lti.editor.toggleGradeSection();
}
});
});
}
});
}
var textAreas = new Y.NodeList([
Y.one('#id_toolurl'),
Y.one('#id_securetoolurl'),
Y.one('#id_resourcekey'),
Y.one('#id_password')
]);
var debounce;
textAreas.on('keyup', function(e){
clearTimeout(debounce);
// If no more changes within 2 seconds, look up the matching tool URL
debounce = setTimeout(function(){
updateToolMatches();
}, 2000);
});
var allowgrades = Y.one('#id_instructorchoiceacceptgrades');
allowgrades.on('change', this.toggleGradeSection, this);
if (typeSelector) {
updateToolMatches();
}
},
toggleGradeSection: function(e) {
if (e) {
e.preventDefault();
}
var allowgrades = Y.one('#id_instructorchoiceacceptgrades');
var gradefieldset = Y.one('#id_modstandardgrade');
if (!allowgrades.get('checked')) {
gradefieldset.hide();
} else {
gradefieldset.show();
}
},
clearToolCache: function(){
this.urlCache = {};
this.toolTypeCache = {};
},
updateAutomaticToolMatch: function(field){
if (!field) {
return;
}
var self = this;
var toolurl = field;
var typeSelector = Y.one('#id_typeid');
var id = field.get('id') + '_lti_automatch_tool';
var automatchToolDisplay = Y.one('#' + id);
if(!automatchToolDisplay){
automatchToolDisplay = Y.Node.create('<span />')
.set('id', id)
.setStyle('padding-left', '1em');
toolurl.insert(automatchToolDisplay, 'after');
}
var url = toolurl.get('value');
// Hide the display if the url box is empty
if(!url){
automatchToolDisplay.setStyle('display', 'none');
} else {
automatchToolDisplay.set('innerHTML', '');
automatchToolDisplay.setStyle('display', '');
}
var selectedToolType = parseInt(typeSelector.get('value'));
var selectedOption = typeSelector.one('option[value="' + selectedToolType + '"]');
// A specific tool type is selected (not "auto")
// We still need to check with the server to get privacy settings
if(selectedToolType > 0){
// If the entered domain matches the domain of the tool configuration...
var domainRegex = /(?:https?:\/\/)?(?:www\.)?([^\/]+)(?:\/|$)/i;
var match = domainRegex.exec(url);
if(match && match[1] && match[1].toLowerCase() === selectedOption.getAttribute('domain').toLowerCase()){
automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.util.get_string('using_tool_configuration', 'lti') + selectedOption.get('text'));
} else {
// The entered URL does not match the domain of the tool configuration
automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.warning_icon_url + '" />' + M.util.get_string('domain_mismatch', 'lti'));
}
}
var key = Y.one('#id_resourcekey');
var secret = Y.one('#id_password');
// Indicate the tool is manually configured
// We still check the Launch URL with the server as course/site tools may override privacy settings
if(key.get('value') !== '' && secret.get('value') !== ''){
automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.util.get_string('custom_config', 'lti'));
}
var continuation = function(toolInfo, inputfield){
if (inputfield === undefined || (inputfield.get('id') != 'id_securetoolurl' || inputfield.get('value'))) {
self.updatePrivacySettings(toolInfo);
}
if(toolInfo.toolname){
automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.util.get_string('using_tool_configuration', 'lti') + toolInfo.toolname);
} else if(!selectedToolType) {
// Inform them custom configuration is in use
if(key.get('value') === '' || secret.get('value') === ''){
automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.warning_icon_url + '" />' + M.util.get_string('tool_config_not_found', 'lti'));
}
}
if (toolInfo.cartridge) {
automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url +
'" />' + M.util.get_string('using_tool_cartridge', 'lti'));
}
};
// Cache urls which have already been checked to increase performance
// Don't use URL cache if tool type manually selected
if(selectedToolType && self.toolTypeCache[selectedToolType]){
return continuation(self.toolTypeCache[selectedToolType]);
} else if(self.urlCache[url] && !selectedToolType){
return continuation(self.urlCache[url]);
} else if(!selectedToolType && !url) {
// No tool type or url set
return continuation({}, field);
} else {
self.findToolByUrl(url, selectedToolType, function(toolInfo){
if(toolInfo){
// Cache the result based on whether the URL or tool type was used to look up the tool
if(!selectedToolType){
self.urlCache[url] = toolInfo;
} else {
self.toolTypeCache[selectedToolType] = toolInfo;
}
Y.one('#id_urlmatchedtypeid').set('value', toolInfo.toolid);
continuation(toolInfo);
}
});
}
},
/**
* Updates display of privacy settings to show course / site tool configuration settings.
*/
updatePrivacySettings: function(toolInfo){
if(!toolInfo || !toolInfo.toolid){
toolInfo = {
sendname: M.mod_lti.LTI_SETTING_DELEGATE,
sendemailaddr: M.mod_lti.LTI_SETTING_DELEGATE,
acceptgrades: M.mod_lti.LTI_SETTING_DELEGATE
}
}
var setting, control;
var privacyControls = {
sendname: Y.one('#id_instructorchoicesendname'),
sendemailaddr: Y.one('#id_instructorchoicesendemailaddr'),
acceptgrades: Y.one('#id_instructorchoiceacceptgrades')
};
// Store a copy of user entered privacy settings as we may overwrite them
if(!this.userPrivacySettings){
this.userPrivacySettings = {};
}
for(setting in privacyControls){
if(privacyControls.hasOwnProperty(setting)){
control = privacyControls[setting];
// Only store the value if it hasn't been forced by the editor
if(!control.get('disabled')){
this.userPrivacySettings[setting] = control.get('checked');
}
}
}
// Update UI based on course / site tool configuration
for(setting in privacyControls){
if(privacyControls.hasOwnProperty(setting)){
var settingValue = toolInfo[setting];
control = privacyControls[setting];
if(settingValue == M.mod_lti.LTI_SETTING_NEVER){
control.set('disabled', true);
control.set('checked', false);
control.set('title', M.util.get_string('forced_help', 'lti'));
} else if(settingValue == M.mod_lti.LTI_SETTING_ALWAYS){
control.set('disabled', true);
control.set('checked', true);
control.set('title', M.util.get_string('forced_help', 'lti'));
} else if(settingValue == M.mod_lti.LTI_SETTING_DELEGATE){
control.set('disabled', false);
// Get the value out of the stored copy
control.set('checked', this.userPrivacySettings[setting]);
control.set('title', '');
}
}
}
this.toggleGradeSection();
},
getSelectedToolTypeOption: function(){
var typeSelector = Y.one('#id_typeid');
return typeSelector.one('option[value="' + typeSelector.get('value') + '"]');
},
/**
* Separate tool listing into option groups. Server-side select control
* doesn't seem to support this.
*/
addOptGroups: function(){
var typeSelector = Y.one('#id_typeid');
if(typeSelector.one('option[courseTool=1]')){
// One ore more course tools exist
var globalGroup = Y.Node.create('<optgroup />')
.set('id', 'global_tool_group')
.set('label', M.util.get_string('global_tool_types', 'lti'));
var courseGroup = Y.Node.create('<optgroup />')
.set('id', 'course_tool_group')
.set('label', M.util.get_string('course_tool_types', 'lti'));
var globalOptions = typeSelector.all('option[globalTool=1]').remove().each(function(node){
globalGroup.append(node);
});
var courseOptions = typeSelector.all('option[courseTool=1]').remove().each(function(node){
courseGroup.append(node);
});
if(globalOptions.size() > 0){
typeSelector.append(globalGroup);
}
if(courseOptions.size() > 0){
typeSelector.append(courseGroup);
}
// Fixes what is presumably a bug in YUI, in which the selected option is not properly set after reorganising the
// options into optgroups.
var selectedOption = typeSelector.one('[selected]');
if (selectedOption) {
selectedOption.set('selected', true);
}
}
},
/**
* Adds buttons for creating, editing, and deleting tool types.
* Javascript is a requirement to edit course level tools at this point.
*/
createTypeEditorButtons: function(){
var self = this;
var typeSelector = Y.one('#id_typeid');
var createIcon = function(id, tooltip, iconUrl){
return Y.Node.create('<a />')
.set('id', id)
.set('title', tooltip)
.setStyle('margin-left', '.5em')
.set('href', 'javascript:void(0);')
.append(Y.Node.create('<img src="' + iconUrl + '" />'));
}
var addIcon = createIcon('lti_add_tool_type', M.util.get_string('addtype', 'lti'), this.settings.add_icon_url);
var editIcon = createIcon('lti_edit_tool_type', M.util.get_string('edittype', 'lti'), this.settings.edit_icon_url);
var deleteIcon = createIcon('lti_delete_tool_type', M.util.get_string('deletetype', 'lti'), this.settings.delete_icon_url);
editIcon.on('click', function(e){
var toolTypeId = typeSelector.get('value');
if(self.getSelectedToolTypeOption().getAttribute('editable')){
window.open(self.settings.instructor_tool_type_edit_url + '&action=edit&typeid=' + toolTypeId, 'edit_tool');
} else {
alert(M.util.get_string('cannot_edit', 'lti'));
}
});
addIcon.on('click', function(e){
window.open(self.settings.instructor_tool_type_edit_url + '&action=add', 'add_tool');
});
deleteIcon.on('click', function(e){
var toolTypeId = typeSelector.get('value');
if(self.getSelectedToolTypeOption().getAttribute('editable')){
if(confirm(M.util.get_string('delete_confirmation', 'lti'))){
self.deleteTool(toolTypeId);
}
} else {
alert(M.util.get_string('cannot_delete', 'lti'));
}
});
typeSelector.insert(addIcon, 'after');
addIcon.insert(editIcon, 'after');
editIcon.insert(deleteIcon, 'after');
},
toggleEditButtons: function(){
var lti_edit_tool_type = Y.one('#lti_edit_tool_type');
var lti_delete_tool_type = Y.one('#lti_delete_tool_type');
// Make the edit / delete icons look enabled / disabled.
// Does not work in older browsers, but alerts will catch those cases.
if(this.getSelectedToolTypeOption().getAttribute('editable')){
lti_edit_tool_type.setStyle('opacity', '1');
lti_delete_tool_type.setStyle('opacity', '1');
} else {
lti_edit_tool_type.setStyle('opacity', '.2');
lti_delete_tool_type.setStyle('opacity', '.2');
}
},
addToolType: function(toolType){
var typeSelector = Y.one('#id_typeid');
var course_tool_group = Y.one('#course_tool_group');
var option = Y.Node.create('<option />')
.set('text', toolType.name)
.set('value', toolType.id)
.set('selected', 'selected')
.setAttribute('editable', '1')
.setAttribute('courseTool', '1')
.setAttribute('domain', toolType.tooldomain);
if(course_tool_group){
course_tool_group.append(option);
} else {
typeSelector.append(option);
}
// Adding the new tool may affect which tool gets matched automatically
this.clearToolCache();
this.updateAutomaticToolMatch(Y.one('#id_toolurl'));
this.updateAutomaticToolMatch(Y.one('#id_securetoolurl'));
this.toggleEditButtons();
require(["core/notification"], function (notification) {
notification.addNotification({
message: M.util.get_string('tooltypeadded', 'lti'),
type: "success"
});
});
},
updateToolType: function(toolType){
var typeSelector = Y.one('#id_typeid');
var option = typeSelector.one('option[value="' + toolType.id + '"]');
option.set('text', toolType.name)
.set('domain', toolType.tooldomain);
// Editing the tool may affect which tool gets matched automatically
this.clearToolCache();
this.updateAutomaticToolMatch(Y.one('#id_toolurl'));
this.updateAutomaticToolMatch(Y.one('#id_securetoolurl'));
require(["core/notification"], function (notification) {
notification.addNotification({
message: M.util.get_string('tooltypeupdated', 'lti'),
type: "success"
});
});
},
deleteTool: function(toolTypeId){
var self = this;
Y.io(self.settings.instructor_tool_type_edit_url + '&action=delete&typeid=' + toolTypeId, {
on: {
success: function(){
self.getSelectedToolTypeOption().remove();
// Editing the tool may affect which tool gets matched automatically
self.clearToolCache();
self.updateAutomaticToolMatch(Y.one('#id_toolurl'));
self.updateAutomaticToolMatch(Y.one('#id_securetoolurl'));
require(["core/notification"], function (notification) {
notification.addNotification({
message: M.util.get_string('tooltypedeleted', 'lti'),
type: "success"
});
});
},
failure: function(){
require(["core/notification"], function (notification) {
notification.addNotification({
message: M.util.get_string('tooltypenotdeleted', 'lti'),
type: "problem"
});
});
}
}
});
},
findToolByUrl: function(url, toolId, callback){
var self = this;
Y.io(self.settings.ajax_url, {
data: {action: 'find_tool_config',
course: self.settings.courseId,
toolurl: url,
toolid: toolId || 0
},
on: {
success: function(transactionid, xhr){
var response = xhr.response;
var toolInfo = Y.JSON.parse(response);
callback(toolInfo);
},
failure: function(){
}
}
});
},
/**
* Gets the tool type ID of the selected tool that supports Content-Item selection.
*
* @returns {number|boolean} The ID of the tool type if it supports Content-Item selection. False, otherwise.
*/
getContentItemId: function() {
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');
}
},
/**
* Resets the values of fields related to the LTI tool settings.
*/
resetToolFields: function() {
// Reset values for all text fields.
var fields = Y.all('#id_toolurl, #id_securetoolurl, #id_instructorcustomparameters, #id_icon, #id_secureicon');
fields.set('value', null);
// Reset value for launch container select box.
Y.one('#id_launchcontainer').set('value', 1);
}
};
})();

View File

@ -53,8 +53,8 @@ require_once($CFG->dirroot.'/mod/lti/locallib.php');
class mod_lti_mod_form extends moodleform_mod {
/** @var int|null the typeid or null if the instance form is being created for a manually configured tool instance.*/
protected ?int $typeid;
/** @var int the tool typeid, or 0 if the instance form is being created for a manually configured tool instance.*/
protected int $typeid;
/** @var string|null type */
protected ?string $type;
@ -74,11 +74,13 @@ class mod_lti_mod_form extends moodleform_mod {
// Setup some of the pieces used to control display in the form definition() method.
// Type ID parameter being passed when adding an preconfigured tool from activity chooser.
$this->typeid = optional_param('typeid', null, PARAM_INT);
$this->typeid = optional_param('typeid', 0, PARAM_INT);
$this->type = optional_param('type', null, PARAM_ALPHA);
// Only permit construction if the form deals with editing an existing instance (current->id not empty), or creating an
// instance from a preconfigured tool type ($this->typeid not empty).
// instance from a preconfigured tool type ($this->typeid not empty). Make an exception for callers, such as core_completion
// which aren't instantiating the form with the expected data, by checking whether the page has been set up, which is the
// case for normal uses.
global $PAGE;
if ($PAGE->has_set_url() && str_contains($PAGE->url, '/course/modedit.php')) {
if (empty($this->typeid) && empty($current->id)) {
@ -89,12 +91,20 @@ class mod_lti_mod_form extends moodleform_mod {
parent::__construct($current, $section, $cm, $course);
}
public function definition() {
global $PAGE, $OUTPUT, $COURSE;
/**
* Defines the form for legacy instances. Here tool config is frozen because the manual configuration method is deprecated.
*
* @param array $instancetypes the array of options for the legacy 'preconfigured tools' select.
* @return void
*/
protected function legacy_instance_form_definition(array $instancetypes): void {
global $OUTPUT;
if ($this->type) {
component_callback("ltisource_$this->type", 'add_instance_hook');
}
// The legacy form handles instances which are either entirely manually configured (current->typeid = 0), or which are
// manually configured and have been domain-matched to a preconfigured tool (current->typeid != 0).
$manualinstance = empty($this->current->typeid);
$matchestoolnotavailabletocourse = !$manualinstance;
$typeid = $manualinstance ? '0' : $this->current->typeid;
// Since 'mod/lti:addmanualinstance' capability is deprecated, determining which users may have had access to the certain
// form fields (the manual config fields) isn't straightforward. Users without 'mod/lti:addmanualinstance' would have only
@ -102,51 +112,32 @@ class mod_lti_mod_form extends moodleform_mod {
// these users. Users who can add/edit course tools (mod/lti:addcoursetool) are able to view tool information anyway, via
// the tool definitions, so this capability is used as a replacement, to control access to these tool config fields.
$canviewmanualconfig = has_capability('mod/lti:addcoursetool', $this->context);
$manualinstance = empty($this->current->typeid) && empty($this->typeid);
$showtypes = has_capability('mod/lti:addpreconfiguredinstance', $this->context);
// 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);
if ($manualinstance && !$canviewmanualconfig) {
// If you cannot add a manual instance and this is already a manual instance, then
// remove the 'types' selector.
$showtypes = false;
}
} else {
$showtypes = !$this->typeid;
}
// Determine whether this tool instance is using a tool which is not visible at the course level, but which does exist.
// This indicates that the instance has either:
// - Been configured manually, and was domain matched to a site tool in the past.
// - Been configured using a preconfigured tool that is now no longer visible in the course.
// In the case of the domain matched tool, tool URL will be set.
$instancetypes = lti_get_types_for_add_instance();
$matchestoolnotavailabletocourse = false;
if (!$manualinstance && !empty($this->current->toolurl) && lti_get_type_config($this->current->typeid)) {
// Type was found, so it's likely been domain matched.
$matchestoolnotavailabletocourse = !in_array($this->current->typeid, array_keys($instancetypes));
if ($manualinstance && !$canviewmanualconfig) {
// If you cannot add a manual instance and this is already a manual instance, then remove the 'types' selector.
$showtypes = false;
}
$mform =& $this->_form;
// Show the deprecation notice when displaying any manually configured instance, regardless of whether the user can view
// the tool configuration details or not. They will still see locked privacy fields and should be told why that is.
if ($manualinstance || $matchestoolnotavailabletocourse) {
$mform->addElement('html', $OUTPUT->notification(
get_string('editmanualinstancedeprecationwarning', 'mod_lti', get_docs_url('External_tool')),
\core\output\notification::NOTIFY_WARNING, false));
}
// Show the deprecation notice, regardless of whether the user can view the tool configuration details or not.
// They will still see locked privacy fields and should be informed as to why that is.
$mform->addElement('html', $OUTPUT->notification(
get_string('editmanualinstancedeprecationwarning', 'mod_lti', get_docs_url('External_tool')),
\core\output\notification::NOTIFY_WARNING, false)
);
// Adding the "general" fieldset, where all the common settings are shown.
$mform->addElement('html', "<div data-attribute='dynamic-import' hidden aria-hidden='true' role='alert'></div>");
$mform->addElement('header', 'general', get_string('general', 'form'));
// Adding the standard "name" field.
$mform->addElement('text', 'name', get_string('basicltiname', 'lti'), array('size' => '64'));
$mform->addElement('text', 'name', get_string('basicltiname', 'lti'), ['size' => '64']);
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'client');
$mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
// Adding the optional "intro" and "introformat" pair of fields.
$this->standard_intro_elements(get_string('basicltiintro', 'lti'));
$mform->setAdvanced('introeditor');
@ -164,18 +155,12 @@ class mod_lti_mod_form extends moodleform_mod {
$mform->addElement('checkbox', 'showtitlelaunch', get_string('display_name', 'lti'));
$mform->setAdvanced('showtitlelaunch');
$mform->setDefault('showtitlelaunch', true);
$mform->addHelpButton('showtitlelaunch', 'display_name', 'lti');
$mform->addElement('checkbox', 'showdescriptionlaunch', get_string('display_description', 'lti'));
$mform->setAdvanced('showdescriptionlaunch');
$mform->addHelpButton('showdescriptionlaunch', 'display_description', 'lti');
// Tool settings.
$toolproxy = array();
// Array of tool type IDs that don't support ContentItemSelectionRequest.
$noncontentitemtypes = [];
if ($showtypes) {
if ($manualinstance) {
// Legacy, manually configured instances: only freeze the element (not hardFreeze) so that disabledIf() still works.
@ -190,141 +175,60 @@ class mod_lti_mod_form extends moodleform_mod {
// Legacy instances domain-matched to site tools: use a hidden field for typeid and a static visual element when
// displaying these instances so that the string value of typeid is still visible when the element is frozen.
// This gets around the fact that a frozen select without a selected option will display nothing.
$mform->addElement('hidden', 'typeid', $this->current->typeid);
$mform->addElement('hidden', 'typeid', $typeid);
$mform->setType('typeid', PARAM_INT);
$manualinstanceoption = $instancetypes[0]; // The 'Automatic, based on tool URL' option.
$mform->addElement('static', 'typeiddisplayonly', get_string('external_tool_type', 'lti'),
$manualinstanceoption->name);
} else {
// To prevent the use of manually configured instances, existing instances which are using a preconfigured tool will
// not display the option "Automatic, based on tool URL" in the preconfigured tools select. This prevents switching
// from an instance configured using a preconfigured tool to an instance that is manually configured.
unset($instancetypes[0]);
$tooltypes = $mform->addElement('select', 'typeid', get_string('external_tool_type', 'lti'));
if ($this->typeid) {
$mform->getElement('typeid')->setValue($this->typeid);
}
$mform->addHelpButton('typeid', 'external_tool_type', 'lti');
foreach ($instancetypes 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 ($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', $this->typeid);
// Need to submit these still, but hidden to avoid instructor modification.
$mform->addElement('hidden', 'typeid', $typeid);
$mform->setType('typeid', PARAM_INT);
if ($this->typeid) {
$config = lti_get_type_config($this->typeid);
if (!empty($config['contentitem'])) {
$mform->addElement('hidden', 'contentitem', 1);
$mform->setType('contentitem', PARAM_INT);
}
}
}
// Add button that launches the content-item selection dialogue.
// Set contentitem URL.
$contentitemurl = new moodle_url('/mod/lti/contentitem.php');
$contentbuttonattributes = [
'data-contentitemurl' => $contentitemurl->out(false)
];
if (!$showtypes) {
if (!$this->typeid || empty(lti_get_type_config($this->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.
if ($showtypes) {
$allnoncontentitemtypes = $noncontentitemtypes;
$allnoncontentitemtypes[] = '0'; // Add option value for "Automatic, based on tool URL".
$mform->disabledIf('selectcontent', 'typeid', 'in', $allnoncontentitemtypes);
// Always disable select content for legacy tool instances domain-matched to site tools.
if ($matchestoolnotavailabletocourse) {
$mform->disabledIf('selectcontent', 'typeid', 'in', [$this->current->typeid]);
}
}
// Disable the content selection button unconditionally. Freeze/hardFreeze is unsuitable for buttons.
$mform->addElement('button', 'selectcontent', get_string('selectcontent', 'lti'), ['disabled' => 'disabled']);
$mform->disabledIf('selectcontent', 'typeid', 'eq', $typeid);
if ($canviewmanualconfig) {
$mform->addElement('text', 'toolurl', get_string('launch_url', 'lti'), array('size' => '64'));
$mform->addElement('text', 'toolurl', get_string('launch_url', 'lti'), ['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->addElement('text', 'securetoolurl', get_string('secure_launch_url', 'lti'), ['size' => '64']);
$mform->setType('securetoolurl', PARAM_URL);
$mform->setAdvanced('securetoolurl');
$mform->addHelpButton('securetoolurl', 'secure_launch_url', 'lti');
$mform->hideIf('securetoolurl', 'typeid', 'in', $noncontentitemtypes);
} else {
// We still need those on page to support deep linking return, but hidden to avoid instructor modification.
$mform->addElement('hidden', 'toolurl', '', array('id' => 'id_toolurl'));
// Need to submit these still, but hidden to avoid instructor modification.
$mform->addElement('hidden', 'toolurl', '', ['id' => 'id_toolurl']);
$mform->setType('toolurl', PARAM_URL);
$mform->addElement('hidden', 'securetoolurl', '', array('id' => 'id_securetoolurl'));
$mform->addElement('hidden', 'securetoolurl', '', ['id' => 'id_securetoolurl']);
$mform->setType('securetoolurl', PARAM_URL);
}
$mform->addElement('hidden', 'urlmatchedtypeid', '', array('id' => 'id_urlmatchedtypeid'));
$mform->setType('urlmatchedtypeid', PARAM_INT);
$mform->addElement('hidden', 'lineitemresourceid', '', array( 'id' => 'id_lineitemresourceid' ));
$mform->addElement('hidden', 'lineitemresourceid', '', ['id' => 'id_lineitemresourceid']);
$mform->setType('lineitemresourceid', PARAM_TEXT);
$mform->addElement('hidden', 'lineitemtag', '', array( 'id' => 'id_lineitemtag'));
$mform->addElement('hidden', 'lineitemtag', '', ['id' => 'id_lineitemtag']);
$mform->setType('lineitemtag', PARAM_TEXT);
$mform->addElement('hidden', 'lineitemsubreviewurl', '', array( 'id' => 'id_lineitemsubreviewurl'));
$mform->addElement('hidden', 'lineitemsubreviewurl', '', ['id' => 'id_lineitemsubreviewurl']);
$mform->setType('lineitemsubreviewurl', PARAM_URL);
$mform->addElement('hidden', 'lineitemsubreviewparams', '', array( 'id' => 'id_lineitemsubreviewparams'));
$mform->addElement('hidden', 'lineitemsubreviewparams', '', ['id' => 'id_lineitemsubreviewparams']);
$mform->setType('lineitemsubreviewparams', PARAM_TEXT);
$launchoptions = array();
$launchoptions[LTI_LAUNCH_CONTAINER_DEFAULT] = get_string('default', 'lti');
$launchoptions[LTI_LAUNCH_CONTAINER_EMBED] = get_string('embed', 'lti');
$launchoptions[LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS] = get_string('embed_no_blocks', 'lti');
$launchoptions[LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW] = get_string('existing_window', 'lti');
$launchoptions[LTI_LAUNCH_CONTAINER_WINDOW] = get_string('new_window', 'lti');
$launchoptions = [
LTI_LAUNCH_CONTAINER_DEFAULT => get_string('default', 'lti'),
LTI_LAUNCH_CONTAINER_EMBED => get_string('embed', 'lti'),
LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS => get_string('embed_no_blocks', 'lti'),
LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW => get_string('existing_window', 'lti'),
LTI_LAUNCH_CONTAINER_WINDOW => get_string('new_window', 'lti')
];
$mform->addElement('select', 'launchcontainer', get_string('launchinpopup', 'lti'), $launchoptions);
$mform->setDefault('launchcontainer', LTI_LAUNCH_CONTAINER_DEFAULT);
$mform->addHelpButton('launchcontainer', 'launchinpopup', 'lti');
$mform->setAdvanced('launchcontainer');
@ -334,42 +238,38 @@ class mod_lti_mod_form extends moodleform_mod {
$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('textarea', 'instructorcustomparameters', get_string('custom', 'lti'), array('rows' => 4, 'cols' => 60));
$mform->addElement('textarea', 'instructorcustomparameters', get_string('custom', 'lti'), ['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->addElement('text', 'icon', get_string('icon_url', 'lti'), ['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->addElement('text', 'secureicon', get_string('secure_icon_url', 'lti'), ['size' => '64']);
$mform->setType('secureicon', PARAM_URL);
$mform->setAdvanced('secureicon');
$mform->addHelpButton('secureicon', 'secure_icon_url', 'lti');
$mform->hideIf('secureicon', 'typeid', 'in', $noncontentitemtypes);
} else {
// Keep those in the form to allow deep linking.
$mform->addElement('hidden', 'resourcekey', '', array('id' => 'id_resourcekey'));
// Need to submit these still, but hidden to avoid instructor modification.
$mform->addElement('hidden', 'resourcekey', '', ['id' => 'id_resourcekey']);
$mform->setType('resourcekey', PARAM_TEXT);
$mform->addElement('hidden', 'password', '', array('id' => 'id_password'));
$mform->addElement('hidden', 'password', '', ['id' => 'id_password']);
$mform->setType('password', PARAM_TEXT);
$mform->addElement('hidden', 'instructorcustomparameters', '', array('id' => 'id_instructorcustomparameters'));
$mform->addElement('hidden', 'instructorcustomparameters', '', ['id' => 'id_instructorcustomparameters']);
$mform->setType('instructorcustomparameters', PARAM_TEXT);
$mform->addElement('hidden', 'icon', '', array('id' => 'id_icon'));
$mform->addElement('hidden', 'icon', '', ['id' => 'id_icon']);
$mform->setType('icon', PARAM_URL);
$mform->addElement('hidden', 'secureicon', '', array('id' => 'id_secureicon'));
$mform->addElement('hidden', 'secureicon', '', ['id' => 'id_secureicon']);
$mform->setType('secureicon', PARAM_URL);
}
@ -377,19 +277,13 @@ class mod_lti_mod_form extends moodleform_mod {
$mform->addElement('header', 'privacy', get_string('privacy', 'lti'));
$mform->addElement('advcheckbox', 'instructorchoicesendname', get_string('share_name', 'lti'));
$mform->setDefault('instructorchoicesendname', '1');
$mform->addHelpButton('instructorchoicesendname', 'share_name', 'lti');
$mform->disabledIf('instructorchoicesendname', 'typeid', 'in', $toolproxy);
$mform->addElement('advcheckbox', 'instructorchoicesendemailaddr', get_string('share_email', 'lti'));
$mform->setDefault('instructorchoicesendemailaddr', '1');
$mform->addHelpButton('instructorchoicesendemailaddr', 'share_email', 'lti');
$mform->disabledIf('instructorchoicesendemailaddr', 'typeid', 'in', $toolproxy);
$mform->addElement('advcheckbox', 'instructorchoiceacceptgrades', get_string('accept_grades', 'lti'));
$mform->setDefault('instructorchoiceacceptgrades', '0');
$mform->addHelpButton('instructorchoiceacceptgrades', 'accept_grades', 'lti');
$mform->disabledIf('instructorchoiceacceptgrades', 'typeid', 'in', $toolproxy);
// Add standard course module grading elements.
$this->standard_grading_coursemodule_elements();
@ -401,69 +295,231 @@ class mod_lti_mod_form extends moodleform_mod {
// Add standard buttons, common to all modules.
$this->add_action_buttons();
$editurl = new moodle_url('/mod/lti/instructor_edit_tool_type.php',
array('sesskey' => sesskey(), 'course' => $COURSE->id));
$ajaxurl = new moodle_url('/mod/lti/ajax.php');
$mform->hardFreeze([
'toolurl',
'securetoolurl',
'launchcontainer',
'resourcekey',
'password',
'instructorcustomparameters',
'icon',
'secureicon',
'instructorchoicesendname',
'instructorchoicesendemailaddr',
'instructorchoiceacceptgrades'
]);
}
if (!empty($this->typeid)) {
$mform->setAdvanced('typeid');
$mform->setAdvanced('toolurl');
public function definition() {
global $PAGE, $OUTPUT, $COURSE;
if ($this->type) {
component_callback("ltisource_$this->type", 'add_instance_hook');
}
if ($manualinstance || $matchestoolnotavailabletocourse) {
$mform->hardFreeze([
'toolurl',
'securetoolurl',
'launchcontainer',
'resourcekey',
'password',
'instructorcustomparameters',
'icon',
'secureicon',
'instructorchoicesendname',
'instructorchoicesendemailaddr',
'instructorchoiceacceptgrades'
]);
} else {
// All these icon uses are incorrect. LTI JS needs updating to use AMD modules and templates so it can use
// the mustache pix helper - until then LTI will have inconsistent icons.
$jsinfo = (object)array(
'edit_icon_url' => (string)$OUTPUT->image_url('t/edit'),
'add_icon_url' => (string)$OUTPUT->image_url('t/add'),
'delete_icon_url' => (string)$OUTPUT->image_url('t/delete'),
'green_check_icon_url' => (string)$OUTPUT->image_url('i/valid'),
'warning_icon_url' => (string)$OUTPUT->image_url('warning', 'lti'),
'instructor_tool_type_edit_url' => $editurl->out(false),
'ajax_url' => $ajaxurl->out(true),
'courseId' => $COURSE->id
);
// Determine whether this tool instance is a manually configure instance (now deprecated).
$manualinstance = empty($this->current->typeid) && empty($this->typeid);
$module = array(
'name' => 'mod_lti_edit',
'fullpath' => '/mod/lti/mod_form.js',
'requires' => array('base', 'io', 'querystring-stringify-simple', 'node', 'event', 'json-parse'),
'strings' => array(
array('addtype', 'lti'),
array('edittype', 'lti'),
array('deletetype', 'lti'),
array('delete_confirmation', 'lti'),
array('cannot_edit', 'lti'),
array('cannot_delete', 'lti'),
array('global_tool_types', 'lti'),
array('course_tool_types', 'lti'),
array('using_tool_configuration', 'lti'),
array('using_tool_cartridge', 'lti'),
array('domain_mismatch', 'lti'),
array('custom_config', 'lti'),
array('tool_config_not_found', 'lti'),
array('tooltypeadded', 'lti'),
array('tooltypedeleted', 'lti'),
array('tooltypenotdeleted', 'lti'),
array('tooltypeupdated', 'lti'),
array('forced_help', 'lti')
),
);
$PAGE->requires->js_init_call('M.mod_lti.editor.init', array(json_encode($jsinfo)), true, $module);
// Determine whether this tool instance is using a domain-matched site tool which is not visible at the course level.
// In such a case, the instance has a typeid (the site tool) and toolurl (the url used to domain match the site tool) set,
// and the type still exists (is not deleted).
$instancetypes = lti_get_types_for_add_instance();
$matchestoolnotavailabletocourse = false;
if (!$manualinstance && !empty($this->current->toolurl)) {
if (lti_get_type_config($this->current->typeid)) {
$matchestoolnotavailabletocourse = !in_array($this->current->typeid, array_keys($instancetypes));
}
}
// Display the legacy form, presenting a read-only view of the configuration for unsupported (since 4.3) instances, which:
// - Are manually configured instances (no longer supported. course tools should be configured and used instead).
// - Are domain-matched to a hidden site level tool (no longer supported. to be replaced by URL-based course tool creation)
// Instances based on preconfigured tools and which are not domain matched as above, are still valid and will be shown using
// the non-legacy form.
if ($manualinstance || $matchestoolnotavailabletocourse) {
$this->legacy_instance_form_definition($instancetypes);
return;
}
$tooltypeid = $this->current->typeid ?? $this->typeid;
$tooltype = lti_get_type($tooltypeid);
// Store the id of the tool type should it be linked to a tool proxy, to aid in disabling certain form elements.
$toolproxytypeid = $tooltype->toolproxyid ? $tooltypeid : '';
$issitetooltype = $tooltype->course == get_site()->id;
$mform =& $this->_form;
// Adding the "general" fieldset, where all the common settings are shown.
$mform->addElement('html', "<div data-attribute='dynamic-import' hidden aria-hidden='true' role='alert'></div>");
$mform->addElement('header', 'general', get_string('general', 'form'));
// For tools supporting content selection, add the 'Select content button'.
$config = lti_get_type_config($tooltypeid);
$supportscontentitemselection = !empty($config['contentitem']);
if ($supportscontentitemselection) {
$contentitemurl = new moodle_url('/mod/lti/contentitem.php');
$contentbuttonattributes = [
'data-contentitemurl' => $contentitemurl->out(false),
];
// If this is an instance, was it created based on content selection in a prior-edit (need to infer since not stored).
$iscontentitem = !empty($this->current->id)
&& (!empty($this->current->toolurl) || !empty($this->current->instructorcustomparameters)
|| !empty($this->current->secureicon) || !empty($this->current->icon));
$selectcontentindicatorinner = $iscontentitem ?
$OUTPUT->pix_icon('i/valid', get_string('contentselected', 'mod_lti'), 'moodle', ['class' => 'mr-1'])
. get_string('contentselected', 'mod_lti') : '';
$selectcontentindicator = html_writer::div($selectcontentindicatorinner, '',
['aria-role' => 'status', 'id' => 'id_selectcontentindicator']);
$selectcontentstatus = $iscontentitem ? 'true' : 'false';
$selectcontentgrp = [
$mform->createElement('button', 'selectcontent', get_string('selectcontent', 'mod_lti'), $contentbuttonattributes,
['customclassoverride' => 'btn-primary']),
$mform->createElement('html', $selectcontentindicator),
$mform->createElement('hidden', 'selectcontentstatus', $selectcontentstatus),
];
$mform->setType('selectcontentstatus', PARAM_TEXT);
$mform->addGroup($selectcontentgrp, 'selectcontentgroup', get_string('content'), ' ', false);
$mform->addRule('selectcontentgroup', get_string('selectcontentvalidationerror', 'mod_lti'), 'required');
}
// Adding the standard "name" field.
$mform->addElement('text', 'name', get_string('basicltiname', 'lti'), ['size' => '64']);
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'server');
$mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'server');
// Show activity name when launched only applies to embedded type launches.
if (in_array($config['launchcontainer'], [LTI_LAUNCH_CONTAINER_EMBED, LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS])) {
$mform->addElement('checkbox', 'showtitlelaunch', get_string('display_name', 'lti'));
$mform->setDefault('showtitlelaunch', true);
$mform->addHelpButton('showtitlelaunch', 'display_name', 'lti');
} else {
// Include in the form anyway, so we retain the setting value in case the tool launch container is changed back.
$mform->addElement('hidden', 'showtitlelaunch');
$mform->setType('showtitlelaunch', PARAM_BOOL);
}
// Adding the optional "intro" and "introformat" pair of fields.
$this->standard_intro_elements(get_string('basicltiintro', 'lti'));
// Display the label to the right of the checkbox so it looks better & matches rest of the form.
if ($mform->elementExists('showdescription')) {
$coursedesc = $mform->getElement('showdescription');
if (!empty($coursedesc)) {
$coursedesc->setText(' ' . $coursedesc->getLabel());
$coursedesc->setLabel('&nbsp');
}
}
// Show activity description when launched only applies to embedded type launches.
if (in_array($config['launchcontainer'], [LTI_LAUNCH_CONTAINER_EMBED, LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS])) {
$mform->addElement('checkbox', 'showdescriptionlaunch', get_string('display_description', 'lti'));
$mform->addHelpButton('showdescriptionlaunch', 'display_description', 'lti');
} else {
// Include in the form anyway, so we retain the setting value in case the tool launch container is changed back.
$mform->addElement('hidden', 'showdescriptionlaunch');
$mform->setType('showdescriptionlaunch', PARAM_BOOL);
}
// Tool settings.
$mform->addElement('hidden', 'typeid', $tooltypeid, ['id' => 'hidden_typeid']);
$mform->setType('typeid', PARAM_INT);
if (!empty($config['contentitem'])) {
$mform->addElement('hidden', 'contentitem', 1);
$mform->setType('contentitem', PARAM_INT);
}
// Included to support deep linking return, but hidden to avoid instructor modification.
$mform->addElement('hidden', 'toolurl', '', ['id' => 'id_toolurl']);
$mform->setType('toolurl', PARAM_URL);
$mform->addElement('hidden', 'securetoolurl', '', ['id' => 'id_securetoolurl']);
$mform->setType('securetoolurl', PARAM_URL);
$mform->addElement('hidden', 'urlmatchedtypeid', '', ['id' => 'id_urlmatchedtypeid']);
$mform->setType('urlmatchedtypeid', PARAM_INT);
$mform->addElement('hidden', 'lineitemresourceid', '', ['id' => 'id_lineitemresourceid']);
$mform->setType('lineitemresourceid', PARAM_TEXT);
$mform->addElement('hidden', 'lineitemtag', '', ['id' => 'id_lineitemtag']);
$mform->setType('lineitemtag', PARAM_TEXT);
$mform->addElement('hidden', 'lineitemsubreviewurl', '', ['id' => 'id_lineitemsubreviewurl']);
$mform->setType('lineitemsubreviewurl', PARAM_URL);
$mform->addElement('hidden', 'lineitemsubreviewparams', '', ['id' => 'id_lineitemsubreviewparams']);
$mform->setType('lineitemsubreviewparams', PARAM_TEXT);
// Launch container is set to 'LTI_LAUNCH_CONTAINER_DEFAULT', meaning it'll delegate to the tool's configuration.
// Existing instances using values other than this can continue to use their existing value but cannot change it.
$mform->addElement('hidden', 'launchcontainer', LTI_LAUNCH_CONTAINER_DEFAULT);
$mform->setType('launchcontainer', PARAM_INT);
// Included to support deep linking return, but hidden to avoid instructor modification.
$mform->addElement('hidden', 'resourcekey', '', ['id' => 'id_resourcekey']);
$mform->setType('resourcekey', PARAM_TEXT);
$mform->addElement('hidden', 'password', '', ['id' => 'id_password']);
$mform->setType('password', PARAM_TEXT);
$mform->addElement('hidden', 'instructorcustomparameters', '', ['id' => 'id_instructorcustomparameters']);
$mform->setType('instructorcustomparameters', PARAM_TEXT);
$mform->addElement('hidden', 'icon', '', ['id' => 'id_icon']);
$mform->setType('icon', PARAM_URL);
$mform->addElement('hidden', 'secureicon', '', ['id' => 'id_secureicon']);
$mform->setType('secureicon', PARAM_URL);
// Add standard course module grading elements, and show them if the tool type + instance config permits it.
if (in_array($config['acceptgrades'], [LTI_SETTING_ALWAYS, LTI_SETTING_DELEGATE])) {
$elementnamesbeforegrading = $this->_form->_elementIndex;
$this->standard_grading_coursemodule_elements();
$elementnamesaftergrading = $this->_form->_elementIndex;
// For all 'real' elements (not hidden or header) added as part of the standard grading elements, add a hideIf rule
// making the element dependent on the 'accept grades from the tool' checkbox (instructorchoiceacceptgrades).
$diff = array_diff($elementnamesaftergrading, $elementnamesbeforegrading);
$diff = array_filter($diff, fn($key) => !in_array($this->_form->_elements[$key]->_type, ['hidden', 'header']));
foreach ($diff as $gradeelementname => $gradeelementindex) {
$mform->hideIf($gradeelementname, 'instructorchoiceacceptgrades', 'eq', 0);
}
// Extend the grade section with the 'accept grades from the tool' checkbox, allowing per-instance overrides of that
// value according to the following rules:
// - Site tools with 'acceptgrades' set to 'ALWAYS' do not permit overrides at the instance level; the checkbox is
// omitted in such cases.
// - Site tools with 'acceptgrades' set to 'DELEGATE' result in a checkbox that is defaulted to unchecked but which
// permits overrides to 'yes/checked'.
// - Course tools with 'acceptgrades' set to 'ALWAYS' result in a checkbox that is defaulted to checked but which
// permits overrides to 'no/unchecked'.
// - Course tools with 'acceptgrades' set to 'DELEGATE' result in a checkbox that is defaulted to unchecked but which
// permits overrides to 'yes/checked'.
if (($issitetooltype && $config['acceptgrades'] == LTI_SETTING_DELEGATE) || !$issitetooltype) {
$mform->insertElementBefore(
$mform->createElement(
'advcheckbox',
'instructorchoiceacceptgrades',
get_string('accept_grades_from_tool', 'mod_lti', $tooltype->name)
),
array_keys($diff)[0]
);
$acceptgradesdefault = !$issitetooltype && $config['acceptgrades'] == LTI_SETTING_ALWAYS ? '1' : '0';
$mform->setDefault('instructorchoiceacceptgrades', $acceptgradesdefault);
$mform->disabledIf('instructorchoiceacceptgrades', 'typeid', 'in', [$toolproxytypeid]); // LTI 2 only.
}
}
// Add standard elements, common to all modules.
$this->standard_coursemodule_elements();
$mform->setAdvanced('cmidnumber');
// Add standard buttons, common to all modules.
$this->add_action_buttons();
if ($supportscontentitemselection) {
$PAGE->requires->js_call_amd('mod_lti/mod_form', 'init', [$COURSE->id]);
}
}
@ -481,4 +537,14 @@ class mod_lti_mod_form extends moodleform_mod {
}
parent::set_data($defaultvalues);
}
public function validation($data, $files) {
$errors = parent::validation($data, $files);
if (isset($data['selectcontentstatus']) && $data['selectcontentstatus'] === 'false') {
$errors['selectcontentgroup'] = get_string('selectcontentvalidationerror', 'mod_lti');
}
return $errors;
}
}

View File

@ -82,8 +82,7 @@ if (!empty($errormsg)) {
$links = new stdClass();
if (has_capability('mod/lti:addcoursetool', $contextcourse)) {
$coursetooleditor = new moodle_url('/mod/lti/instructor_edit_tool_type.php',
array('course' => $courseid, 'action' => 'add', 'sesskey' => sesskey()));
$coursetooleditor = new moodle_url('mod/lti/coursetools.php', ['id' => $courseid]);
$links->course_tool_editor = $coursetooleditor->out(false);
echo get_string('lti_launch_error_unsigned_help', 'lti', $links);

View File

@ -54,12 +54,6 @@
padding-right: 1em;
} /* Prevent setting titles from wrapping */
/* Styles for instructor_edit_tool_type.php */
#page-mod-lti-instructor_edit_tool_type .mform .fitem .fitemtitle {
min-width: 18em;
padding-right: 1em;
} /* Prevent setting titles from wrapping */
/* Styling for tool_configure page */
#registration-choice-container .buffer-text {
margin: 20px;

View File

@ -28,36 +28,33 @@ Feature: Add tools
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
When I add a "Teaching Tool 1" to section "1"
# For tool that does not support Content-Item message type, the Select content button must be disabled.
And I set the field "Activity name" to "Test tool activity 1"
And I expand all fieldsets
And I set the field "Launch container" to "Embed"
And the "Select content" "button" should be disabled
And "Launch container" "field" should not be visible
# For tool that does not support Content-Item message type, the Select content button must be disabled.
And "Select content" "button" should not be visible
And "Tool URL" "field" should not be visible
And I press "Save and return to course"
And I open "Test tool activity 1" actions menu
And I choose "Edit settings" in the open action menu
Then the field "Preconfigured tool" matches value "Teaching Tool 1"
And the "Select content" "button" should be disabled
And the "Tool URL" "field" should be disabled
And I am on the "Test tool activity 1" "lti activity editing" page
Then the field "Activity name" matches value "Test tool activity 1"
And "Launch container" "field" should not be visible
And "Select content" "button" should not be visible
And "Tool URL" "field" should not be visible
@javascript
Scenario: Add a course tool via the activity picker
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
When I add a "Course tool 1" to section "1"
# For tool that does not support Content-Item message type, the Select content button must be disabled.
And I set the field "Activity name" to "Test tool activity 2"
And I expand all fieldsets
And I set the field "Launch container" to "Embed"
And the "Select content" "button" should be disabled
And "Launch container" "field" should not be visible
# For tool that does not support Content-Item message type, the Select content button must be disabled.
And "Select content" "button" should not be visible
And I press "Save and return to course"
And I open "Test tool activity 2" actions menu
And I choose "Edit settings" in the open action menu
Then the field "Preconfigured tool" matches value "Course tool 1"
And the "Select content" "button" should be disabled
And the "Tool URL" "field" should be disabled
And I click on "Preconfigured tool" "select"
And I should not see "Automatic, based on tool URL"
And I am on the "Test tool activity 2" "lti activity editing" page
Then the field "Activity name" matches value "Test tool activity 2"
And "Launch container" "field" should not be visible
And "Select content" "button" should not be visible
And "Tool URL" "field" should not be visible
@javascript
Scenario: Editing a (deprecated) manually configured activity instance, confirming that config changes aren't possible

View File

@ -28,14 +28,11 @@ Feature: Restoring Moodle 2 backup restores LTI configuration
And I restore "test_backup.mbz" backup into a new course using this options:
And I am on site homepage
And I follow "Course 1 copy 1"
And I turn editing mode on
And I open "My LTI module" actions menu
And I choose "Edit settings" in the open action menu
Then the field "Preconfigured tool" matches value "My site tool"
Then I should see "My LTI module"
And I navigate to "Plugins > Activity modules > External tool > Manage tools" in site administration
And "This tool is being used 2 times" "text" should exist in the "//div[contains(@id,'tool-card-container') and contains(., 'My site tool')]" "xpath_element"
@javascript @_switch_window
@javascript
Scenario: Backup and restore course with preconfigured course LTI tool on the same site
Given the following "mod_lti > course tools" exist:
| name | description | baseurl | course | lti_resourcekey | lti_password | lti_launchcontainer |
@ -48,17 +45,18 @@ Feature: Restoring Moodle 2 backup restores LTI configuration
# Backup course and restore into another course
And I backup "Course 1" course using this options:
| Confirmation | Filename | test_backup.mbz |
And I restore "test_backup.mbz" backup into "Course 2" course using this options:
And I am on "Course 2" course homepage with editing mode on
When I restore "test_backup.mbz" backup into "Course 2" course using this options:
# Make sure the copy of the preconfigured tool was created in the second course with both encrypted and non-encrypted properties.
And I am on "Course 2" course homepage with editing mode on
And I open "Test tool activity 2" actions menu
And I choose "Edit settings" in the open action menu
Then the field "Preconfigured tool" matches value "My course tool"
And I follow "Edit preconfigured tool"
And I switch to "edit_tool" window
Then the field "Tool URL" matches value "http://www.example.com/lti/provider.php"
And the field "Activity name" matches value "Test tool activity 2"
And I am on "Course 1" course homepage
And I navigate to "LTI External tools" in current page administration
Then I should see "My course tool"
And I open the action menu in "My course tool" "table_row"
And I choose "Edit" in the open action menu
And the field "Tool URL" matches value "http://www.example.com/lti/provider.php"
And the field "Consumer key" matches value "my key"
And the field "Shared secret" matches value "my secret"
And the field "Default launch container" matches value "Existing window"
And I press "Cancel"
And I switch to the main window

View File

@ -20,22 +20,16 @@ Feature: Content-Item support
@javascript
Scenario: Tool that supports Deep Linking should be able to configure a tool via the Select content button
When I log in as "teacher1"
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add a "Teaching Tool 1" to section "1"
Then the "Select content" "button" should be enabled
When I add a "Teaching Tool 1" to section "1"
Then "Select content" "button" should be visible
And the "Select content" "button" should be enabled
@javascript
Scenario: Editing a tool's settings that was configured from a preconfigured tool that supports Deep Linking.
When I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add a "Teaching Tool 1" to section "1"
And the "Select content" "button" should be enabled
And I set the field "Activity name" to "Test tool activity 1"
And I expand all fieldsets
And I set the field "Launch container" to "Embed"
And I press "Save and return to course"
And I open "Test tool activity 1" actions menu
And I choose "Edit settings" in the open action menu
Then the field "Preconfigured tool" matches value "Teaching Tool 1"
Scenario: Editing the settings for an instance of a tool configured with Deep Linking support
Given the following "mod_lti > tool instances" exist:
| name | tool | course |
| Test tool activity 1 | Teaching Tool 1 | C1 |
When I am on the "Test tool activity 1" "lti activity editing" page logged in as teacher1
Then I should see "Select content"
And the "Select content" "button" should be enabled

View File

@ -128,10 +128,11 @@ class mod_lti_generator extends testing_module_generator {
$data['lti_acceptgrades'] = $data['lti_acceptgrades'] ?? LTI_SETTING_ALWAYS;
$data['lti_sendname'] = $data['lti_sendname'] ?? LTI_SETTING_ALWAYS;
$data['lti_sendemailaddr'] = $data['lti_sendname'] ?? LTI_SETTING_ALWAYS;
$data['lti_launchcontainer'] = $data['lti_launchcontainer'] ?? LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS;
['type' => $type, 'config' => $config] = $this->get_type_and_config_from_data($data);
return lti_add_type(type: (object) $type, config: (object) $config);
return lti_add_type(type: $type, config: $config);
}
/**
@ -160,6 +161,7 @@ class mod_lti_generator extends testing_module_generator {
$type['lti_sendname'] = $type['lti_sendname'] ?? LTI_SETTING_ALWAYS;
$type['lti_sendemailaddr'] = $type['lti_sendemailaddr'] ?? LTI_SETTING_ALWAYS;
$type['lti_coursevisible'] = $type['coursevisible'] ?? LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
$type['lti_launchcontainer'] = $type['lti_launchcontainer'] ?? LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS;
// Required for cartridge processing support.
$type['lti_toolurl'] = $type['baseurl'];