diff --git a/mod/lti/amd/build/contentitem.min.js b/mod/lti/amd/build/contentitem.min.js index 607b51838fe..7d681e8bd8e 100644 --- a/mod/lti/amd/build/contentitem.min.js +++ b/mod/lti/amd/build/contentitem.min.js @@ -1,2 +1,2 @@ -define ("mod_lti/contentitem",["jquery","core/notification","core/str","core/templates","mod_lti/form-field","core/modal_factory","core/modal_events"],function(a,b,c,d,e,f,g){var h,i,j=[new e("name",e.TYPES.TEXT,!1,""),new e("introeditor",e.TYPES.EDITOR,!1,""),new e("toolurl",e.TYPES.TEXT,!0,""),new e("securetoolurl",e.TYPES.TEXT,!0,""),new e("instructorchoiceacceptgrades",e.TYPES.CHECKBOX,!0,!0),new e("instructorchoicesendname",e.TYPES.CHECKBOX,!0,!0),new e("instructorchoicesendemailaddr",e.TYPES.CHECKBOX,!0,!0),new e("instructorcustomparameters",e.TYPES.TEXT,!0,""),new e("icon",e.TYPES.TEXT,!0,""),new e("secureicon",e.TYPES.TEXT,!0,""),new e("launchcontainer",e.TYPES.SELECT,!0,0),new e("grade_modgrade_point",e.TYPES.TEXT,!1,""),new e("lineitemresourceid",e.TYPES.TEXT,!0,""),new e("lineitemtag",e.TYPES.TEXT,!0,"")];window.processContentItemReturnData=function(a){if(h){h.hide()}for(var b in j){var c=j[b],d=null;if("undefined"!=typeof a[c.name]){d=a[c.name]}c.setFieldValue(d)}if(i){i()}};return{init:function init(a,e,j){i=j;var k=d.render("mod_lti/contentitem",{url:a,postData:e});if(h){h.setBody(k);h.show();return}c.get_string("selectcontent","lti").then(function(a){return f.create({title:a,body:k,large:!0})}).then(function(a){h=a;a.getRoot().on(g.hidden,function(){a.setBody("");b.fetchNotifications()});a.show()}).catch(b.exception)}}}); +function asyncGeneratorStep(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function _asyncToGenerator(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var h=a.apply(b,c);function f(a){asyncGeneratorStep(h,d,e,f,g,"next",a)}function g(a){asyncGeneratorStep(h,d,e,f,g,"throw",a)}f(void 0)})}}define ("mod_lti/contentitem",["jquery","core/notification","core/str","core/templates","mod_lti/form-field","core/modal_factory","core/modal_events"],function(a,b,c,d,e,f,g){var h,i,j=[new e("name",e.TYPES.TEXT,!1,""),new e("introeditor",e.TYPES.EDITOR,!1,""),new e("toolurl",e.TYPES.TEXT,!0,""),new e("securetoolurl",e.TYPES.TEXT,!0,""),new e("instructorchoiceacceptgrades",e.TYPES.CHECKBOX,!0,!0),new e("instructorchoicesendname",e.TYPES.CHECKBOX,!0,!0),new e("instructorchoicesendemailaddr",e.TYPES.CHECKBOX,!0,!0),new e("instructorcustomparameters",e.TYPES.TEXT,!0,""),new e("icon",e.TYPES.TEXT,!0,""),new e("secureicon",e.TYPES.TEXT,!0,""),new e("launchcontainer",e.TYPES.SELECT,!0,0),new e("grade_modgrade_point",e.TYPES.TEXT,!1,""),new e("lineitemresourceid",e.TYPES.TEXT,!0,""),new e("lineitemtag",e.TYPES.TEXT,!0,"")],k=function(a){a.setAttribute("hidden","true");a.setAttribute("aria-hidden","true");a.setAttribute("tab-index","-1")},l=function(a){a.removeAttribute("hidden");a.setAttribute("aria-hidden","false");a.setAttribute("tab-index","1")},m=function(){var a=_asyncToGenerator(regeneratorRuntime.mark(function a(b){var c,e,f,g,h,i,j;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:c=document.querySelector("#region-main-box form");e=c.querySelector("[data-attribute=\"dynamic-import\"]");f=c.querySelector("#fgroup_id_buttonar");g=c.querySelector("#id_submitbutton");Array.from(c.children).forEach(k);k(g);a.next=8;return d.renderForPromise("mod_lti/tool_deeplinking_results",{items:b});case 8:h=a.sent;i=h.html;j=h.js;a.next=13;return d.replaceNodeContents(e,i,j);case 13:l(e);l(f);case 15:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}(),n=function(a){var b={};["name","toolurl","securetoolurl","instructorcustomparameters","icon","secureicon","launchcontainer"].forEach(function(c){b[c]=a[c]||""});b["introeditor[text]"]=a.introeditor?a.introeditor.text:"";b["introeditor[format]"]=a.introeditor?a.introeditor.format:"";if(1===a.instructorchoiceacceptgrades){b.instructorchoiceacceptgrades="1";b["grade[modgrade_point]"]=a.grade_modgrade_point||"100"}else{b.instructorchoiceacceptgrades="0"}return b};window.processContentItemReturnData=function(a){if(h){h.hide()}if(a.multiple){for(var b in j){j[b].setFieldValue("name"===j[b].name?"item":null)}var c=[];a.multiple.forEach(function(a){c.push(n(a))});m(a.multiple);var f=document.querySelector("#id_submitbutton2");f.onclick=function(a){a.preventDefault();f.disabled=!0;var b=new FormData(document.querySelector("form.mform")),d=function(){document.querySelector("#id_cancel").click()};c.reduce(function postVariant(a,c){Object.entries(c).forEach(function(a){return b.set(a[0],a[1])});var d=new URLSearchParams(b),e=function(){return fetch(document.location.pathname,{method:"post",body:d})};return a.then(e).catch(e)},Promise.resolve()).then(d).catch(d)}}else{for(b in j){var d=j[b],e=null;if("undefined"!=typeof a[d.name]){e=a[d.name]}d.setFieldValue(e)}d.setFieldValue(e)}if(i){i(a)}};return{init:function init(a,e,j){i=j;var k=d.render("mod_lti/contentitem",{url:a,postData:e});if(h){h.setBody(k);h.show();return}c.get_string("selectcontent","lti").then(function(a){return f.create({title:a,body:k,large:!0})}).then(function(a){h=a;a.getRoot().on(g.hidden,function(){a.setBody("");b.fetchNotifications()});a.show()}).catch(b.exception)}}}); //# sourceMappingURL=contentitem.min.js.map diff --git a/mod/lti/amd/build/contentitem.min.js.map b/mod/lti/amd/build/contentitem.min.js.map index 32c354f6600..c9ddf825ff8 100644 --- a/mod/lti/amd/build/contentitem.min.js.map +++ b/mod/lti/amd/build/contentitem.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/contentitem.js"],"names":["define","$","notification","str","templates","FormField","ModalFactory","ModalEvents","dialogue","doneCallback","ltiFormFields","TYPES","TEXT","EDITOR","CHECKBOX","SELECT","window","processContentItemReturnData","returnData","hide","index","field","value","name","setFieldValue","init","url","postData","cb","bodyPromise","render","setBody","show","get_string","then","title","create","body","large","modal","getRoot","on","hidden","fetchNotifications","catch","exception"],"mappings":"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","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 .\n\n/**\n * Launches the modal dialogue that contains the iframe that sends the Content-Item selection request to an\n * LTI tool provider that supports Content-Item type message.\n *\n * See template: mod_lti/contentitem\n *\n * @module mod_lti/contentitem\n * @class contentitem\n * @package mod_lti\n * @copyright 2016 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.2\n */\ndefine(\n [\n 'jquery',\n 'core/notification',\n 'core/str',\n 'core/templates',\n 'mod_lti/form-field',\n 'core/modal_factory',\n 'core/modal_events'\n ],\n function($, notification, str, templates, FormField, ModalFactory, ModalEvents) {\n var dialogue;\n var doneCallback;\n var contentItem = {\n /**\n * Init function.\n *\n * @param {string} url The URL for the content item selection.\n * @param {object} postData The data to be sent for the content item selection request.\n * @param {Function} cb The callback to run once the content item has been processed.\n */\n init: function(url, postData, cb) {\n doneCallback = cb;\n var context = {\n url: url,\n postData: postData\n };\n var bodyPromise = templates.render('mod_lti/contentitem', context);\n\n if (dialogue) {\n // Set dialogue body.\n dialogue.setBody(bodyPromise);\n // Display the dialogue.\n dialogue.show();\n return;\n }\n\n str.get_string('selectcontent', 'lti').then(function(title) {\n return ModalFactory.create({\n title: title,\n body: bodyPromise,\n large: true\n });\n }).then(function(modal) {\n dialogue = modal;\n // On hide handler.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Empty modal contents when it's hidden.\n modal.setBody('');\n\n // Fetch notifications.\n notification.fetchNotifications();\n });\n\n // Display the dialogue.\n modal.show();\n return;\n }).catch(notification.exception);\n }\n };\n\n /**\n * Array of form fields for LTI tool configuration.\n *\n * @type {*[]}\n */\n var ltiFormFields = [\n new FormField('name', FormField.TYPES.TEXT, false, ''),\n new FormField('introeditor', FormField.TYPES.EDITOR, false, ''),\n new FormField('toolurl', FormField.TYPES.TEXT, true, ''),\n new FormField('securetoolurl', FormField.TYPES.TEXT, true, ''),\n new FormField('instructorchoiceacceptgrades', FormField.TYPES.CHECKBOX, true, true),\n new FormField('instructorchoicesendname', FormField.TYPES.CHECKBOX, true, true),\n new FormField('instructorchoicesendemailaddr', FormField.TYPES.CHECKBOX, true, true),\n new FormField('instructorcustomparameters', FormField.TYPES.TEXT, true, ''),\n new FormField('icon', FormField.TYPES.TEXT, true, ''),\n new FormField('secureicon', FormField.TYPES.TEXT, true, ''),\n new FormField('launchcontainer', FormField.TYPES.SELECT, true, 0),\n new FormField('grade_modgrade_point', FormField.TYPES.TEXT, false, ''),\n new FormField('lineitemresourceid', FormField.TYPES.TEXT, true, ''),\n new FormField('lineitemtag', FormField.TYPES.TEXT, true, '')\n ];\n\n /**\n * Window function that can be called from mod_lti/contentitem_return to close the dialogue and process the return data.\n *\n * @param {object} returnData The fetched configuration data from the Content-Item selection dialogue.\n */\n window.processContentItemReturnData = function(returnData) {\n if (dialogue) {\n dialogue.hide();\n }\n\n // Populate LTI configuration fields from return data.\n var index;\n for (index in ltiFormFields) {\n var field = ltiFormFields[index];\n var value = null;\n if (typeof returnData[field.name] !== 'undefined') {\n value = returnData[field.name];\n }\n field.setFieldValue(value);\n }\n\n if (doneCallback) {\n doneCallback();\n }\n };\n\n return contentItem;\n }\n);\n"],"file":"contentitem.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/contentitem.js"],"names":["define","$","notification","str","templates","FormField","ModalFactory","ModalEvents","dialogue","doneCallback","ltiFormFields","TYPES","TEXT","EDITOR","CHECKBOX","SELECT","hideElement","e","setAttribute","showElement","removeAttribute","showMultipleSummaryAndHideForm","items","form","document","querySelector","toolArea","buttonGroup","submitAndLaunch","Array","from","children","forEach","renderForPromise","html","js","replaceNodeContents","configToVariant","config","variant","name","introeditor","text","format","instructorchoiceacceptgrades","grade_modgrade_point","window","processContentItemReturnData","returnData","hide","multiple","index","setFieldValue","variants","v","push","submitAndCourse","onclick","preventDefault","disabled","fd","FormData","backToCourse","click","reduce","postVariant","promise","Object","entries","entry","set","body","URLSearchParams","doPost","fetch","location","pathname","method","then","catch","Promise","resolve","field","value","init","url","postData","cb","bodyPromise","render","setBody","show","get_string","title","create","large","modal","getRoot","on","hidden","fetchNotifications","exception"],"mappings":"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","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 .\n\n/**\n * Launches the modal dialogue that contains the iframe that sends the Content-Item selection request to an\n * LTI tool provider that supports Content-Item type message.\n *\n * See template: mod_lti/contentitem\n *\n * @module mod_lti/contentitem\n * @class contentitem\n * @package mod_lti\n * @copyright 2016 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.2\n */\ndefine(\n [\n 'jquery',\n 'core/notification',\n 'core/str',\n 'core/templates',\n 'mod_lti/form-field',\n 'core/modal_factory',\n 'core/modal_events'\n ],\n function($, notification, str, templates, FormField, ModalFactory, ModalEvents) {\n var dialogue;\n var doneCallback;\n var contentItem = {\n /**\n * Init function.\n *\n * @param {string} url The URL for the content item selection.\n * @param {object} postData The data to be sent for the content item selection request.\n * @param {Function} cb The callback to run once the content item has been processed.\n */\n init: function(url, postData, cb) {\n doneCallback = cb;\n var context = {\n url: url,\n postData: postData\n };\n var bodyPromise = templates.render('mod_lti/contentitem', context);\n\n if (dialogue) {\n // Set dialogue body.\n dialogue.setBody(bodyPromise);\n // Display the dialogue.\n dialogue.show();\n return;\n }\n\n str.get_string('selectcontent', 'lti').then(function(title) {\n return ModalFactory.create({\n title: title,\n body: bodyPromise,\n large: true\n });\n }).then(function(modal) {\n dialogue = modal;\n // On hide handler.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Empty modal contents when it's hidden.\n modal.setBody('');\n\n // Fetch notifications.\n notification.fetchNotifications();\n });\n\n // Display the dialogue.\n modal.show();\n return;\n }).catch(notification.exception);\n }\n };\n\n /**\n * Array of form fields for LTI tool configuration.\n *\n * @type {*[]}\n */\n var ltiFormFields = [\n new FormField('name', FormField.TYPES.TEXT, false, ''),\n new FormField('introeditor', FormField.TYPES.EDITOR, false, ''),\n new FormField('toolurl', FormField.TYPES.TEXT, true, ''),\n new FormField('securetoolurl', FormField.TYPES.TEXT, true, ''),\n new FormField('instructorchoiceacceptgrades', FormField.TYPES.CHECKBOX, true, true),\n new FormField('instructorchoicesendname', FormField.TYPES.CHECKBOX, true, true),\n new FormField('instructorchoicesendemailaddr', FormField.TYPES.CHECKBOX, true, true),\n new FormField('instructorcustomparameters', FormField.TYPES.TEXT, true, ''),\n new FormField('icon', FormField.TYPES.TEXT, true, ''),\n new FormField('secureicon', FormField.TYPES.TEXT, true, ''),\n new FormField('launchcontainer', FormField.TYPES.SELECT, true, 0),\n new FormField('grade_modgrade_point', FormField.TYPES.TEXT, false, ''),\n new FormField('lineitemresourceid', FormField.TYPES.TEXT, true, ''),\n new FormField('lineitemtag', FormField.TYPES.TEXT, true, '')\n ];\n\n /**\n * Hide the element, including aria and tab index.\n * @param {HTMLElement} e the element to be hidden.\n */\n const hideElement = (e) => {\n e.setAttribute('hidden', 'true');\n e.setAttribute('aria-hidden', 'true');\n e.setAttribute('tab-index', '-1');\n };\n\n /**\n * Show the element, including aria and tab index (set to 1).\n * @param {HTMLElement} e the element to be shown.\n */\n const showElement = (e) => {\n e.removeAttribute('hidden');\n e.setAttribute('aria-hidden', 'false');\n e.setAttribute('tab-index', '1');\n };\n\n /**\n * When more than one item needs to be added, the UI is simplified\n * to just list the items to be added. Form is hidden and the only\n * options is (save and return to course) or cancel.\n * This function injects the summary to the form page, and hides\n * the unneeded elements.\n * @param {Object[]} items items to be added to the course.\n */\n const showMultipleSummaryAndHideForm = async function(items) {\n const form = document.querySelector('#region-main-box form');\n const toolArea = form.querySelector('[data-attribute=\"dynamic-import\"]');\n const buttonGroup = form.querySelector('#fgroup_id_buttonar');\n const submitAndLaunch = form.querySelector('#id_submitbutton');\n Array.from(form.children).forEach(hideElement);\n hideElement(submitAndLaunch);\n const {html, js} = await templates.renderForPromise('mod_lti/tool_deeplinking_results',\n {items: items});\n\n await templates.replaceNodeContents(toolArea, html, js);\n showElement(toolArea);\n showElement(buttonGroup);\n };\n\n /**\n * Transforms config values aimed at populating the lti mod form to JSON variant\n * which are used to insert more than one activity modules in one submit\n * by applying variation to the submitted form.\n * See /course/modedit.php.\n * @private\n * @param {Object} config transforms a config to an actual form data to be posted.\n * @return {Object} variant that will be used to modify form values on submit.\n */\n var configToVariant = (config) => {\n const variant = {};\n ['name', 'toolurl', 'securetoolurl', 'instructorcustomparameters', 'icon', 'secureicon', 'launchcontainer'].forEach(\n function(name) {\n variant[name] = config[name] || '';\n }\n );\n variant['introeditor[text]'] = config.introeditor ? config.introeditor.text : '';\n variant['introeditor[format]'] = config.introeditor ? config.introeditor.format : '';\n if (config.instructorchoiceacceptgrades === 1) {\n variant.instructorchoiceacceptgrades = '1';\n variant['grade[modgrade_point]'] = config.grade_modgrade_point || '100';\n } else {\n variant.instructorchoiceacceptgrades = '0';\n }\n return variant;\n };\n\n /**\n * Window function that can be called from mod_lti/contentitem_return to close the dialogue and process the return data.\n * If the return data contains more than one item, the form will not be populated with item data\n * but rather hidden, and the item data will be added to a single input field used to create multiple\n * instances in one request.\n *\n * @param {object} returnData The fetched configuration data from the Content-Item selection dialogue.\n */\n window.processContentItemReturnData = function(returnData) {\n if (dialogue) {\n dialogue.hide();\n }\n var index;\n if (returnData.multiple) {\n for (index in ltiFormFields) {\n // Name is required, so putting a placeholder as it will not be used\n // in multi-items add.\n ltiFormFields[index].setFieldValue(ltiFormFields[index].name === 'name' ? 'item' : null);\n }\n var variants = [];\n returnData.multiple.forEach(function(v) {\n variants.push(configToVariant(v));\n });\n showMultipleSummaryAndHideForm(returnData.multiple);\n const submitAndCourse = document.querySelector('#id_submitbutton2');\n submitAndCourse.onclick = (e) => {\n e.preventDefault();\n submitAndCourse.disabled = true;\n const fd = new FormData(document.querySelector('form.mform'));\n const postVariant = (promise, variant) => {\n Object.entries(variant).forEach((entry) => fd.set(entry[0], entry[1]));\n const body = new URLSearchParams(fd);\n const doPost = () => fetch(document.location.pathname, {method: 'post', body});\n return promise.then(doPost).catch(doPost);\n };\n const backToCourse = () => {\n document.querySelector(\"#id_cancel\").click();\n };\n variants.reduce(postVariant, Promise.resolve()).then(backToCourse).catch(backToCourse);\n };\n } else {\n // Populate LTI configuration fields from return data.\n for (index in ltiFormFields) {\n var field = ltiFormFields[index];\n var value = null;\n if (typeof returnData[field.name] !== 'undefined') {\n value = returnData[field.name];\n }\n field.setFieldValue(value);\n }\n field.setFieldValue(value);\n }\n\n if (doneCallback) {\n doneCallback(returnData);\n }\n };\n\n return contentItem;\n }\n);\n"],"file":"contentitem.min.js"} \ No newline at end of file diff --git a/mod/lti/amd/src/contentitem.js b/mod/lti/amd/src/contentitem.js index bc18ae229db..3d10cb4c897 100644 --- a/mod/lti/amd/src/contentitem.js +++ b/mod/lti/amd/src/contentitem.js @@ -109,8 +109,81 @@ define( new FormField('lineitemtag', FormField.TYPES.TEXT, true, '') ]; + /** + * Hide the element, including aria and tab index. + * @param {HTMLElement} e the element to be hidden. + */ + const hideElement = (e) => { + e.setAttribute('hidden', 'true'); + e.setAttribute('aria-hidden', 'true'); + e.setAttribute('tab-index', '-1'); + }; + + /** + * Show the element, including aria and tab index (set to 1). + * @param {HTMLElement} e the element to be shown. + */ + const showElement = (e) => { + e.removeAttribute('hidden'); + e.setAttribute('aria-hidden', 'false'); + e.setAttribute('tab-index', '1'); + }; + + /** + * When more than one item needs to be added, the UI is simplified + * to just list the items to be added. Form is hidden and the only + * options is (save and return to course) or cancel. + * This function injects the summary to the form page, and hides + * the unneeded elements. + * @param {Object[]} items items to be added to the course. + */ + const showMultipleSummaryAndHideForm = async function(items) { + const form = document.querySelector('#region-main-box form'); + const toolArea = form.querySelector('[data-attribute="dynamic-import"]'); + const buttonGroup = form.querySelector('#fgroup_id_buttonar'); + const submitAndLaunch = form.querySelector('#id_submitbutton'); + Array.from(form.children).forEach(hideElement); + hideElement(submitAndLaunch); + const {html, js} = await templates.renderForPromise('mod_lti/tool_deeplinking_results', + {items: items}); + + await templates.replaceNodeContents(toolArea, html, js); + showElement(toolArea); + showElement(buttonGroup); + }; + + /** + * Transforms config values aimed at populating the lti mod form to JSON variant + * which are used to insert more than one activity modules in one submit + * by applying variation to the submitted form. + * See /course/modedit.php. + * @private + * @param {Object} config transforms a config to an actual form data to be posted. + * @return {Object} variant that will be used to modify form values on submit. + */ + var configToVariant = (config) => { + const variant = {}; + ['name', 'toolurl', 'securetoolurl', 'instructorcustomparameters', 'icon', 'secureicon', 'launchcontainer'].forEach( + function(name) { + variant[name] = config[name] || ''; + } + ); + variant['introeditor[text]'] = config.introeditor ? config.introeditor.text : ''; + variant['introeditor[format]'] = config.introeditor ? config.introeditor.format : ''; + if (config.instructorchoiceacceptgrades === 1) { + variant.instructorchoiceacceptgrades = '1'; + variant['grade[modgrade_point]'] = config.grade_modgrade_point || '100'; + } else { + variant.instructorchoiceacceptgrades = '0'; + } + return variant; + }; + /** * Window function that can be called from mod_lti/contentitem_return to close the dialogue and process the return data. + * If the return data contains more than one item, the form will not be populated with item data + * but rather hidden, and the item data will be added to a single input field used to create multiple + * instances in one request. * * @param {object} returnData The fetched configuration data from the Content-Item selection dialogue. */ @@ -118,20 +191,49 @@ define( if (dialogue) { dialogue.hide(); } - - // Populate LTI configuration fields from return data. var index; - for (index in ltiFormFields) { - var field = ltiFormFields[index]; - var value = null; - if (typeof returnData[field.name] !== 'undefined') { - value = returnData[field.name]; + if (returnData.multiple) { + for (index in ltiFormFields) { + // Name is required, so putting a placeholder as it will not be used + // in multi-items add. + ltiFormFields[index].setFieldValue(ltiFormFields[index].name === 'name' ? 'item' : null); + } + var variants = []; + returnData.multiple.forEach(function(v) { + variants.push(configToVariant(v)); + }); + showMultipleSummaryAndHideForm(returnData.multiple); + const submitAndCourse = document.querySelector('#id_submitbutton2'); + submitAndCourse.onclick = (e) => { + e.preventDefault(); + submitAndCourse.disabled = true; + const fd = new FormData(document.querySelector('form.mform')); + const postVariant = (promise, variant) => { + Object.entries(variant).forEach((entry) => fd.set(entry[0], entry[1])); + const body = new URLSearchParams(fd); + const doPost = () => fetch(document.location.pathname, {method: 'post', body}); + return promise.then(doPost).catch(doPost); + }; + const backToCourse = () => { + document.querySelector("#id_cancel").click(); + }; + variants.reduce(postVariant, Promise.resolve()).then(backToCourse).catch(backToCourse); + }; + } else { + // Populate LTI configuration fields from return data. + for (index in ltiFormFields) { + var field = ltiFormFields[index]; + var value = null; + if (typeof returnData[field.name] !== 'undefined') { + value = returnData[field.name]; + } + field.setFieldValue(value); } field.setFieldValue(value); } if (doneCallback) { - doneCallback(); + doneCallback(returnData); } }; diff --git a/mod/lti/auth.php b/mod/lti/auth.php index 68c5ccdeb69..0f57e6a36fc 100644 --- a/mod/lti/auth.php +++ b/mod/lti/auth.php @@ -120,7 +120,7 @@ if ($ok) { $title = base64_decode($titleb64); $text = base64_decode($textb64); $request = lti_build_content_item_selection_request($typeid, $course, $returnurl, $title, $text, - [], [], false, false, false, false, false, $nonce); + [], [], false, true, false, false, false, $nonce); $endpoint = $request->url; $params = $request->params; } diff --git a/mod/lti/edit_form.php b/mod/lti/edit_form.php index 83361f5a407..d0f06fd6905 100644 --- a/mod/lti/edit_form.php +++ b/mod/lti/edit_form.php @@ -214,9 +214,8 @@ class mod_lti_edit_types_form extends moodleform { $mform->addHelpButton('lti_launchcontainer', 'default_launch_container', 'lti'); $mform->setType('lti_launchcontainer', PARAM_INT); - $mform->addElement('advcheckbox', 'lti_contentitem', get_string('contentitem', 'lti')); - $mform->addHelpButton('lti_contentitem', 'contentitem', 'lti'); - $mform->setAdvanced('lti_contentitem'); + $mform->addElement('advcheckbox', 'lti_contentitem', get_string('contentitem_deeplinking', 'lti')); + $mform->addHelpButton('lti_contentitem', 'contentitem_deeplinking', 'lti'); if ($istool) { $mform->disabledIf('lti_contentitem', null); } @@ -224,7 +223,6 @@ class mod_lti_edit_types_form extends moodleform { $mform->addElement('text', 'lti_toolurl_ContentItemSelectionRequest', get_string('toolurl_contentitemselectionrequest', 'lti'), array('size' => '64')); $mform->setType('lti_toolurl_ContentItemSelectionRequest', PARAM_URL); - $mform->setAdvanced('lti_toolurl_ContentItemSelectionRequest'); $mform->addHelpButton('lti_toolurl_ContentItemSelectionRequest', 'toolurl_contentitemselectionrequest', 'lti'); $mform->disabledIf('lti_toolurl_ContentItemSelectionRequest', 'lti_contentitem', 'notchecked'); if ($istool) { @@ -271,7 +269,12 @@ class mod_lti_edit_types_form extends moodleform { // LTI Extensions. // Add grading preferences fieldset where the tool is allowed to return grades. - $mform->addElement('select', 'lti_acceptgrades', get_string('accept_grades_admin', 'lti'), $options); + $gradeoptions = array(); + $gradeoptions[] = get_string('never', 'lti'); + $gradeoptions[] = get_string('always', 'lti'); + $gradeoptions[] = get_string('delegate_tool', 'lti'); + + $mform->addElement('select', 'lti_acceptgrades', get_string('accept_grades_admin', 'lti'), $gradeoptions); $mform->setType('lti_acceptgrades', PARAM_INT); $mform->setDefault('lti_acceptgrades', '2'); $mform->addHelpButton('lti_acceptgrades', 'accept_grades_admin', 'lti'); diff --git a/mod/lti/lang/en/deprecated.txt b/mod/lti/lang/en/deprecated.txt index 0d0691f88ed..33cc7daf8ad 100644 --- a/mod/lti/lang/en/deprecated.txt +++ b/mod/lti/lang/en/deprecated.txt @@ -1,3 +1,5 @@ leaveblank,mod_lti organizationid,mod_lti organizationid_help,mod_lti +contentitem,mod_lti +contentitem_help,mod_lti diff --git a/mod/lti/lang/en/lti.php b/mod/lti/lang/en/lti.php index 01adfc7f16b..51d2593923c 100644 --- a/mod/lti/lang/en/lti.php +++ b/mod/lti/lang/en/lti.php @@ -108,8 +108,10 @@ $string['configtoolurl'] = 'Default remote tool URL'; $string['configtypes'] = 'Enable LTI applications'; $string['configured'] = 'Configured'; $string['confirmtoolactivation'] = 'Are you sure you would like to activate this tool?'; -$string['contentitem'] = 'Content-Item Message'; -$string['contentitem_help'] = 'If ticked, the option \'Select content\' will be available when adding an external tool.'; +$string['contentitem_deeplinking'] = 'Supports Deep Linking (Content-Item Message)'; +$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['courseactivitiesorresources'] = 'Course activities or resources'; $string['courseid'] = 'Course ID number'; @@ -141,6 +143,7 @@ real estate to the tool, and others provide a more integrated feel with the Mood Depending on the browser, it will open in a new tab or a popup window. It is possible that browsers will prevent the new window from opening.'; $string['delegate'] = 'Delegate to teacher'; +$string['delegate_tool'] = 'As specified in Deep Linking definition or Delegate to teacher'; $string['delete'] = 'Delete'; $string['delete_confirmation'] = 'Are you sure you want to delete this preconfigured tool?'; $string['deletetype'] = 'Delete preconfigured tool'; @@ -593,3 +596,8 @@ $string['organizationid'] = 'Organisation ID'; $string['organizationid_help'] = 'A unique identifier for this Moodle instance. Typically, the DNS name of the organisation is used. If this field is left blank, the host name of this Moodle site will be used as the default value.'; + +// Deprecated since Moodle 3.10. +$string['contentitem'] = 'Content-Item Message'; +$string['contentitem_help'] = 'If ticked, the option \'Select content\' will be available when adding an external tool.'; + diff --git a/mod/lti/locallib.php b/mod/lti/locallib.php index 1e548d5e64c..b33e91f5aa5 100644 --- a/mod/lti/locallib.php +++ b/mod/lti/locallib.php @@ -1083,7 +1083,7 @@ function lti_build_custom_parameters($toolproxy, $tool, $instance, $params, $cus * @throws coding_exception For invalid media type and presentation target parameters. */ function lti_build_content_item_selection_request($id, $course, moodle_url $returnurl, $title = '', $text = '', $mediatypes = [], - $presentationtargets = [], $autocreate = false, $multiple = false, + $presentationtargets = [], $autocreate = false, $multiple = true, $unsigned = false, $canconfirm = false, $copyadvice = false, $nonce = '') { global $USER; @@ -1421,6 +1421,109 @@ function lti_verify_jwt_signature($typeid, $consumerkey, $jwtparam) { return $tool; } +/** + * Converts LTI 1.1 Content Item for LTI Link to Form data. + * + * @param object $tool Tool for which the item is created for. + * @param object $typeconfig The tool configuration. + * @param object $item Item populated from JSON to be converted to Form form + * + * @return stdClass Form config for the item + */ +function content_item_to_form(object $tool, object $typeconfig, object $item) : stdClass { + $config = new stdClass(); + $config->name = ''; + if (isset($item->title)) { + $config->name = $item->title; + } + if (empty($config->name)) { + $config->name = $tool->name; + } + if (isset($item->text)) { + $config->introeditor = [ + 'text' => $item->text, + 'format' => FORMAT_PLAIN + ]; + } else { + $config->introeditor = [ + 'text' => '', + 'format' => FORMAT_PLAIN + ]; + } + if (isset($item->icon->{'@id'})) { + $iconurl = new moodle_url($item->icon->{'@id'}); + // Assign item's icon URL to secureicon or icon depending on its scheme. + if (strtolower($iconurl->get_scheme()) === 'https') { + $config->secureicon = $iconurl->out(false); + } else { + $config->icon = $iconurl->out(false); + } + } + if (isset($item->url)) { + $url = new moodle_url($item->url); + $config->toolurl = $url->out(false); + $config->typeid = 0; + } else { + $config->typeid = $tool->id; + } + $config->instructorchoiceacceptgrades = LTI_SETTING_NEVER; + $islti2 = $tool->ltiversion === LTI_VERSION_2; + if (!$islti2 && isset($typeconfig->lti_acceptgrades)) { + $acceptgrades = $typeconfig->lti_acceptgrades; + if ($acceptgrades == LTI_SETTING_ALWAYS) { + // We create a line item regardless if the definition contains one or not. + $config->instructorchoiceacceptgrades = LTI_SETTING_ALWAYS; + $config->grade_modgrade_point = 100; + } + if ($acceptgrades == LTI_SETTING_DELEGATE || $acceptgrades == LTI_SETTING_ALWAYS) { + if (isset($item->lineItem)) { + $lineitem = $item->lineItem; + $config->instructorchoiceacceptgrades = LTI_SETTING_ALWAYS; + $maxscore = 100; + if (isset($lineitem->scoreConstraints)) { + $sc = $lineitem->scoreConstraints; + if (isset($sc->totalMaximum)) { + $maxscore = $sc->totalMaximum; + } else if (isset($sc->normalMaximum)) { + $maxscore = $sc->normalMaximum; + } + } + $config->grade_modgrade_point = $maxscore; + $config->lineitemresourceid = ''; + $config->lineitemtag = ''; + if (isset($lineitem->assignedActivity) && isset($lineitem->assignedActivity->activityId)) { + $config->lineitemresourceid = $lineitem->assignedActivity->activityId?:''; + } + if (isset($lineitem->tag)) { + $config->lineitemtag = $lineitem->tag?:''; + } + } + } + } + $config->instructorchoicesendname = LTI_SETTING_NEVER; + $config->instructorchoicesendemailaddr = LTI_SETTING_NEVER; + $config->launchcontainer = LTI_LAUNCH_CONTAINER_DEFAULT; + if (isset($item->placementAdvice->presentationDocumentTarget)) { + if ($item->placementAdvice->presentationDocumentTarget === 'window') { + $config->launchcontainer = LTI_LAUNCH_CONTAINER_WINDOW; + } else if ($item->placementAdvice->presentationDocumentTarget === 'frame') { + $config->launchcontainer = LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS; + } else if ($item->placementAdvice->presentationDocumentTarget === 'iframe') { + $config->launchcontainer = LTI_LAUNCH_CONTAINER_EMBED; + } + } + if (isset($item->custom)) { + $customparameters = []; + foreach ($item->custom as $key => $value) { + $customparameters[] = "{$key}={$value}"; + } + $config->instructorcustomparameters = implode("\n", $customparameters); + } + // Including a JSON version of the form data to support adding many items in one submit. + $config->contentitemjson = json_encode($item); + return $config; +} + /** * Processes the tool provider's response to the ContentItemSelectionRequest and builds the configuration data from the * selected content item. This configuration data can be then used when adding a tool into the course. @@ -1460,97 +1563,24 @@ function lti_tool_configuration_from_content_item($typeid, $messagetype, $ltiver if (empty($items)) { throw new moodle_exception('errorinvaliddata', 'mod_lti', '', $contentitemsjson); } - if (!isset($items->{'@graph'}) || !is_array($items->{'@graph'}) || (count($items->{'@graph'}) > 1)) { + if (!isset($items->{'@graph'}) || !is_array($items->{'@graph'})) { throw new moodle_exception('errorinvalidresponseformat', 'mod_lti'); } $config = null; - if (!empty($items->{'@graph'})) { - $item = $items->{'@graph'}[0]; + $items = $items->{'@graph'}; + if (!empty($items)) { $typeconfig = lti_get_type_type_config($tool->id); - - $config = new stdClass(); - $config->name = ''; - if (isset($item->title)) { - $config->name = $item->title; - } - if (empty($config->name)) { - $config->name = $tool->name; - } - if (isset($item->text)) { - $config->introeditor = [ - 'text' => $item->text, - 'format' => FORMAT_PLAIN - ]; - } - if (isset($item->icon->{'@id'})) { - $iconurl = new moodle_url($item->icon->{'@id'}); - // Assign item's icon URL to secureicon or icon depending on its scheme. - if (strtolower($iconurl->get_scheme()) === 'https') { - $config->secureicon = $iconurl->out(false); - } else { - $config->icon = $iconurl->out(false); - } - } - if (isset($item->url)) { - $url = new moodle_url($item->url); - $config->toolurl = $url->out(false); - $config->typeid = 0; + if (count($items) == 1) { + $config = content_item_to_form($tool, $typeconfig, $items[0]); } else { - $config->typeid = $tool->id; - } - $config->instructorchoiceacceptgrades = LTI_SETTING_NEVER; - if (!$islti2 && isset($typeconfig->lti_acceptgrades)) { - $acceptgrades = $typeconfig->lti_acceptgrades; - if ($acceptgrades == LTI_SETTING_ALWAYS) { - // We create a line item regardless if the definition contains one or not. - $config->instructorchoiceacceptgrades = LTI_SETTING_ALWAYS; - } - if ($acceptgrades == LTI_SETTING_DELEGATE || $acceptgrades == LTI_SETTING_ALWAYS) { - if (isset($item->lineItem)) { - $lineitem = $item->lineItem; - $config->instructorchoiceacceptgrades = LTI_SETTING_ALWAYS; - $maxscore = 100; - if (isset($lineitem->scoreConstraints)) { - $sc = $lineitem->scoreConstraints; - if (isset($sc->totalMaximum)) { - $maxscore = $sc->totalMaximum; - } else if (isset($sc->normalMaximum)) { - $maxscore = $sc->normalMaximum; - } - } - $config->grade_modgrade_point = $maxscore; - $config->lineitemresourceid = ''; - $config->lineitemtag = ''; - if (isset($lineitem->assignedActivity) && isset($lineitem->assignedActivity->activityId)) { - $config->lineitemresourceid = $lineitem->assignedActivity->activityId ? : ''; - } - if (isset($lineitem->tag)) { - $config->lineitemtag = $lineitem->tag ? : ''; - } - } + $multiple = []; + foreach ($items as $item) { + $multiple[] = content_item_to_form($tool, $typeconfig, $item); } + $config = new stdClass(); + $config->multiple = $multiple; } - $config->instructorchoicesendname = LTI_SETTING_NEVER; - $config->instructorchoicesendemailaddr = LTI_SETTING_NEVER; - $config->launchcontainer = LTI_LAUNCH_CONTAINER_DEFAULT; - if (isset($item->placementAdvice->presentationDocumentTarget)) { - if ($item->placementAdvice->presentationDocumentTarget === 'window') { - $config->launchcontainer = LTI_LAUNCH_CONTAINER_WINDOW; - } else if ($item->placementAdvice->presentationDocumentTarget === 'frame') { - $config->launchcontainer = LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS; - } else if ($item->placementAdvice->presentationDocumentTarget === 'iframe') { - $config->launchcontainer = LTI_LAUNCH_CONTAINER_EMBED; - } - } - if (isset($item->custom)) { - $customparameters = []; - foreach ($item->custom as $key => $value) { - $customparameters[] = "{$key}={$value}"; - } - $config->instructorcustomparameters = implode("\n", $customparameters); - } - $config->contentitemjson = json_encode($item); } return $config; } @@ -1587,7 +1617,35 @@ function lti_convert_content_items($param) { $newitem->text = $item->html; unset($newitem->html); } - if (isset($item->presentation)) { + if (isset($item->iframe)) { + // DeepLinking allows multiple options to be declared as supported. + // We favor iframe over new window if both are specified. + $newitem->placementAdvice = new stdClass(); + $newitem->placementAdvice->presentationDocumentTarget = 'iframe'; + if (isset($item->iframe->width)) { + $newitem->placementAdvice->displayWidth = $item->iframe->width; + } + if (isset($item->iframe->height)) { + $newitem->placementAdvice->displayHeight = $item->iframe->height; + } + unset($newitem->iframe); + unset($newitem->window); + } else if (isset($item->window)) { + $newitem->placementAdvice = new stdClass(); + $newitem->placementAdvice->presentationDocumentTarget = 'window'; + if (isset($item->window->targetName)) { + $newitem->placementAdvice->windowTarget = $item->window->targetName; + } + if (isset($item->window->width)) { + $newitem->placementAdvice->displayWidth = $item->window->width; + } + if (isset($item->window->height)) { + $newitem->placementAdvice->displayHeight = $item->window->height; + } + unset($newitem->window); + } else if (isset($item->presentation)) { + // This may have been part of an early draft but is not in the final spec + // so keeping it around for now in case it's actually been used. $newitem->placementAdvice = new stdClass(); if (isset($item->presentation->documentTarget)) { $newitem->placementAdvice->presentationDocumentTarget = $item->presentation->documentTarget; diff --git a/mod/lti/mod_form.js b/mod/lti/mod_form.js index 2b21157bcba..639823688b9 100644 --- a/mod/lti/mod_form.js +++ b/mod/lti/mod_form.js @@ -97,8 +97,10 @@ }; require(['mod_lti/contentitem'], function(contentitem) { - contentitem.init(contentItemUrl, postData, function() { - M.mod_lti.editor.toggleGradeSection(); + contentitem.init(contentItemUrl, postData, function(returnData) { + if (!returnData.multiple) { + M.mod_lti.editor.toggleGradeSection(); + } }); }); } diff --git a/mod/lti/mod_form.php b/mod/lti/mod_form.php index 70caa9c42bd..adc17baf771 100644 --- a/mod/lti/mod_form.php +++ b/mod/lti/mod_form.php @@ -79,8 +79,11 @@ class mod_lti_mod_form extends moodleform_mod { $this->typeid = 0; $mform =& $this->_form; + // Adding the "general" fieldset, where all the common settings are shown. + $mform->addElement('html', ""); $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->setType('name', PARAM_TEXT); diff --git a/mod/lti/templates/tool_deeplinking_results.mustache b/mod/lti/templates/tool_deeplinking_results.mustache new file mode 100644 index 00000000000..742f9670255 --- /dev/null +++ b/mod/lti/templates/tool_deeplinking_results.mustache @@ -0,0 +1,59 @@ +{{! + 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 . +}} +{{! + @template mod_lti/tool_deeplinking_results + + This template lists the items that will be added to the course after + a deep linking flow. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Context variables required for this template: + * + + Example context (json): + { + "items": [ + { + "name": "Chapter 1", + "instructorchoiceacceptgrades": 0 + }, + { + "name": "Quiz 1", + "instructorchoiceacceptgrades": 1, + "grade_modgrade_point": 20.5 + } + ] + } +}} +

+ {{#str}} contentitem_multiple_description, mod_lti {{/str}} +

+
    + {{#items}} +
  • + {{{name}}} + {{#instructorchoiceacceptgrades}} + {{#str}} contentitem_multiple_graded , mod_lti, {{{grade_modgrade_point}}} {{/str}} + {{/instructorchoiceacceptgrades}} +
  • + {{/items}} +
diff --git a/mod/lti/tests/behat/contentitem.feature b/mod/lti/tests/behat/contentitem.feature index 9b6ed762f99..c75cc2b3438 100644 --- a/mod/lti/tests/behat/contentitem.feature +++ b/mod/lti/tests/behat/contentitem.feature @@ -2,7 +2,7 @@ Feature: Content-Item support In order to easily add activities and content in a course from an external tool As a teacher - I need to utilise a tool that supports the Content-Item Message type + I need to utilise a tool that supports the Deep Linking (Content-Item Message) type Background: Given the following "users" exist: @@ -16,25 +16,25 @@ Feature: Content-Item support | teacher1 | C1 | editingteacher | And I log in as "admin" And I navigate to "Plugins > Activity modules > External tool > Manage tools" in site administration - # Create tool type that supports content-item. + # Create tool type that supports deep linking. And I follow "configure a tool manually" And I set the field "Tool name" to "Teaching Tool 1" And I set the field "Tool URL" to local url "/mod/lti/tests/fixtures/tool_provider.php" And I set the field "Tool configuration usage" to "Show in activity chooser and as a preconfigured tool" And I expand all fieldsets - And I set the field "Content-Item Message" to "1" + And I set the field "Supports Deep Linking (Content-Item Message)" to "1" And I press "Save changes" And I log out @javascript - Scenario: Tool that supports Content-Item Message type should be able to configure a tool via the Select content button + Scenario: Tool that supports Deep Linking should be able to configure a tool via the Select content button 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" Then the "Select content" "button" should be enabled @javascript - Scenario: Editing a tool's settings that was configured from a preconfigured tool that supports Content-Item. + 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" @@ -67,12 +67,12 @@ Feature: Content-Item support And I set the field "Activity name" to "Test tool activity 1" And the "Select content" "button" should be disabled And the "Tool URL" "field" should be enabled - # Selecting a tool that supports content-item: Select content button - enabled, Tool URL - enabled. + # Selecting a tool that supports deep linking: Select content button - enabled, Tool URL - enabled. And I set the field "Preconfigured tool" to "Teaching Tool 1" And I set the field "Activity name" to "Test tool activity 1" Then the "Select content" "button" should be enabled And the "Tool URL" "field" should be enabled - # Selecting a tool that does not support content-item: Select content button - disabled, Tool URL - disabled. + # Selecting a tool that does not support deep linking: Select content button - disabled, Tool URL - disabled. And I set the field "Preconfigured tool" to "Teaching Tool 2" And I set the field "Activity name" to "Test tool activity 1" And the "Select content" "button" should be disabled diff --git a/mod/lti/tests/behat/contentitemregistration.feature b/mod/lti/tests/behat/contentitemregistration.feature index 8b40f69dfb3..dd4d04bd045 100644 --- a/mod/lti/tests/behat/contentitemregistration.feature +++ b/mod/lti/tests/behat/contentitemregistration.feature @@ -1,8 +1,8 @@ @mod @mod_lti -Feature: Create/edit tool configuration that has Content-Item support - In order to provide external tools that support the Content-Item Message type for teachers and learners +Feature: Create/edit tool configuration that has Deep Linking support + In order to provide external tools that support Deep Linking for teachers and learners As an admin - I need to be able to configure external tool registrations that support the Content-Item Message type. + I need to be able to configure external tool registrations that support Deep Linking. Background: Given I log in as "admin" @@ -20,13 +20,13 @@ Feature: Create/edit tool configuration that has Content-Item support And I set the field "Tool URL" to local url "/mod/lti/tests/fixtures/tool_provider.php" And I set the field "Tool configuration usage" to "Show in activity chooser and as a preconfigured tool" And I expand all fieldsets - And I set the field "Content-Item Message" to "1" + And I set the field "Supports Deep Linking (Content-Item Message)" to "1" And I press "Save changes" And I follow "Edit" And I expand all fieldsets - Then the field "Content-Item Message" matches value "1" - And I set the field "Content-Item Message" to "0" + Then the field "Supports Deep Linking (Content-Item Message)" matches value "1" + And I set the field "Supports Deep Linking (Content-Item Message)" to "0" And I press "Save changes" And I follow "Edit" And I expand all fieldsets - And the field "Content-Item Message" matches value "0" + And the field "Supports Deep Linking (Content-Item Message)" matches value "0" diff --git a/mod/lti/tests/locallib_test.php b/mod/lti/tests/locallib_test.php index 680da402dfc..16cb00586dd 100644 --- a/mod/lti/tests/locallib_test.php +++ b/mod/lti/tests/locallib_test.php @@ -377,7 +377,7 @@ class mod_lti_locallib_testcase extends advanced_testcase { $this->assertEquals('frame,iframe,window', $params['accept_presentation_document_targets']); $this->assertEquals($returnurl->out(false), $params['content_item_return_url']); $this->assertEquals('false', $params['accept_unsigned']); - $this->assertEquals('false', $params['accept_multiple']); + $this->assertEquals('true', $params['accept_multiple']); $this->assertEquals('false', $params['accept_copy_advice']); $this->assertEquals('false', $params['auto_create']); $this->assertEquals($type->name, $params['title']); @@ -1185,7 +1185,6 @@ MwIDAQAB */ public function test_lti_verify_jwt_signature_no_public_key() { $this->resetAfterTest(); - $this->setAdminUser(); // Create a tool type, associated with that proxy. @@ -1214,7 +1213,28 @@ MwIDAQAB 'url' => 'http://example.com/messages/launch', 'title' => 'Test title', 'text' => 'Test text', - 'frame' => [] + 'iframe' => [] + ]; + $contentitems[] = [ + 'type' => 'ltiResourceLink', + 'url' => 'http://example.com/messages/launch2', + 'title' => 'Test title2', + 'text' => 'Test text2', + 'iframe' => [ + 'height' => 200, + 'width' => 300 + ], + 'window' => [] + ]; + $contentitems[] = [ + 'type' => 'ltiResourceLink', + 'url' => 'http://example.com/messages/launch3', + 'title' => 'Test title3', + 'text' => 'Test text3', + 'window' => [ + 'targetName' => 'test-win', + 'height' => 400 + ] ]; $contentitems = json_encode($contentitems); @@ -1231,18 +1251,186 @@ MwIDAQAB $objgraph->url = 'http://example.com/messages/launch'; $objgraph->title = 'Test title'; $objgraph->text = 'Test text'; - $objgraph->frame = []; + $objgraph->placementAdvice = new stdClass(); + $objgraph->placementAdvice->presentationDocumentTarget = 'iframe'; $objgraph->{$strtype} = 'LtiLinkItem'; $objgraph->mediaType = 'application\/vnd.ims.lti.v1.ltilink'; + $objgraph2 = new stdClass(); + $objgraph2->url = 'http://example.com/messages/launch2'; + $objgraph2->title = 'Test title2'; + $objgraph2->text = 'Test text2'; + $objgraph2->placementAdvice = new stdClass(); + $objgraph2->placementAdvice->presentationDocumentTarget = 'iframe'; + $objgraph2->placementAdvice->displayHeight = 200; + $objgraph2->placementAdvice->displayWidth = 300; + $objgraph2->{$strtype} = 'LtiLinkItem'; + $objgraph2->mediaType = 'application\/vnd.ims.lti.v1.ltilink'; + + $objgraph3 = new stdClass(); + $objgraph3->url = 'http://example.com/messages/launch3'; + $objgraph3->title = 'Test title3'; + $objgraph3->text = 'Test text3'; + $objgraph3->placementAdvice = new stdClass(); + $objgraph3->placementAdvice->presentationDocumentTarget = 'window'; + $objgraph3->placementAdvice->displayHeight = 400; + $objgraph3->placementAdvice->windowTarget = 'test-win'; + $objgraph3->{$strtype} = 'LtiLinkItem'; + $objgraph3->mediaType = 'application\/vnd.ims.lti.v1.ltilink'; + $expected = new stdClass(); $expected->{$strcontext} = 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem'; $expected->{$strgraph} = []; $expected->{$strgraph}[] = $objgraph; + $expected->{$strgraph}[] = $objgraph2; + $expected->{$strgraph}[] = $objgraph3; $this->assertEquals($expected, $jsondecode); } + /** + * Test adding a single gradable item through content item. + */ + public function test_lti_tool_configuration_from_content_item_single_gradable() { + $this->resetAfterTest(); + $this->setAdminUser(); + + $type = new stdClass(); + $type->name = "Test tool"; + $type->baseurl = "http://example.com"; + $config = new stdClass(); + $config->lti_acceptgrades = LTI_SETTING_DELEGATE; + $typeid = lti_add_type($type, $config); + + $generator = $this->getDataGenerator()->get_plugin_generator('mod_lti'); + $contentitems = []; + $contentitems[] = [ + 'type' => 'ltiResourceLink', + 'url' => 'http://example.com/messages/launch', + 'title' => 'Test title', + 'lineItem' => [ + 'resourceId' => 'r12345', + 'tag' => 'final', + 'scoreMaximum' => 10.0 + ], + 'frame' => [] + ]; + $contentitemsjson13 = json_encode($contentitems); + $json11 = lti_convert_content_items($contentitemsjson13); + + $config = lti_tool_configuration_from_content_item($typeid, + 'ContentItemSelection', + $type->ltiversion, + 'ConsumerKey', + $json11); + + $this->assertEquals($contentitems[0]['url'], $config->toolurl); + $this->assertEquals(LTI_SETTING_ALWAYS, $config->instructorchoiceacceptgrades); + $this->assertEquals($contentitems[0]['lineItem']['tag'], $config->lineitemtag); + $this->assertEquals($contentitems[0]['lineItem']['resourceId'], $config->lineitemresourceid); + $this->assertEquals($contentitems[0]['lineItem']['scoreMaximum'], $config->grade_modgrade_point); + } + + /** + * Test adding multiple gradable items through content item. + */ + public function test_lti_tool_configuration_from_content_item_multiple() { + $this->resetAfterTest(); + $this->setAdminUser(); + + $type = new stdClass(); + $type->name = "Test tool"; + $type->baseurl = "http://example.com"; + $config = new stdClass(); + $config->lti_acceptgrades = LTI_SETTING_DELEGATE; + $typeid = lti_add_type($type, $config); + + $generator = $this->getDataGenerator()->get_plugin_generator('mod_lti'); + $contentitems = []; + $contentitems[] = [ + 'type' => 'ltiResourceLink', + 'url' => 'http://example.com/messages/launch', + 'title' => 'Test title', + 'text' => 'Test text', + 'icon' => [ + 'url' => 'http://lti.example.com/image.jpg', + 'width' => 100 + ], + 'frame' => [] + ]; + $contentitems[] = [ + 'type' => 'ltiResourceLink', + 'url' => 'http://example.com/messages/launchgraded', + 'title' => 'Test Graded', + 'lineItem' => [ + 'resourceId' => 'r12345', + 'tag' => 'final', + 'scoreMaximum' => 10.0 + ], + 'frame' => [] + ]; + $contentitemsjson13 = json_encode($contentitems); + $json11 = lti_convert_content_items($contentitemsjson13); + + $config = lti_tool_configuration_from_content_item($typeid, + 'ContentItemSelection', + $type->ltiversion, + 'ConsumerKey', + $json11); + $this->assertNotNull($config->multiple); + $this->assertEquals(2, count( $config->multiple )); + $this->assertEquals($contentitems[0]['title'], $config->multiple[0]->name); + $this->assertEquals($contentitems[0]['url'], $config->multiple[0]->toolurl); + $this->assertEquals(LTI_SETTING_NEVER, $config->multiple[0]->instructorchoiceacceptgrades); + $this->assertEquals($contentitems[1]['url'], $config->multiple[1]->toolurl); + $this->assertEquals(LTI_SETTING_ALWAYS, $config->multiple[1]->instructorchoiceacceptgrades); + $this->assertEquals($contentitems[1]['lineItem']['tag'], $config->multiple[1]->lineitemtag); + $this->assertEquals($contentitems[1]['lineItem']['resourceId'], $config->multiple[1]->lineitemresourceid); + $this->assertEquals($contentitems[1]['lineItem']['scoreMaximum'], $config->multiple[1]->grade_modgrade_point); + } + + /** + * Test adding a single non gradable item through content item. + */ + public function test_lti_tool_configuration_from_content_item_single() { + $this->resetAfterTest(); + $this->setAdminUser(); + + $type = new stdClass(); + $type->name = "Test tool"; + $type->baseurl = "http://example.com"; + $config = new stdClass(); + $typeid = lti_add_type($type, $config); + + $generator = $this->getDataGenerator()->get_plugin_generator('mod_lti'); + $contentitems = []; + $contentitems[] = [ + 'type' => 'ltiResourceLink', + 'url' => 'http://example.com/messages/launch', + 'title' => 'Test title', + 'text' => 'Test text', + 'icon' => [ + 'url' => 'http://lti.example.com/image.jpg', + 'width' => 100 + ], + 'frame' => [] + ]; + $contentitemsjson13 = json_encode($contentitems); + $json11 = lti_convert_content_items($contentitemsjson13); + + $config = lti_tool_configuration_from_content_item($typeid, + 'ContentItemSelection', + $type->ltiversion, + 'ConsumerKey', + $json11); + $this->assertEquals($contentitems[0]['title'], $config->name); + $this->assertEquals($contentitems[0]['text'], $config->introeditor['text']); + $this->assertEquals($contentitems[0]['url'], $config->toolurl); + $this->assertEquals($contentitems[0]['icon']['url'], $config->icon); + $this->assertEquals(LTI_SETTING_NEVER, $config->instructorchoiceacceptgrades); + + } + /** * Test lti_sign_jwt(). */ diff --git a/mod/lti/upgrade.txt b/mod/lti/upgrade.txt index 4e8e5b9d3e0..a3c01f4f343 100644 --- a/mod/lti/upgrade.txt +++ b/mod/lti/upgrade.txt @@ -1,5 +1,10 @@ This files describes API changes in the lti code. +=== 3.10 === + +* Select Content supports multiple, allowing a tool to return more than one link at a time. + Parameter multiple in function lti_build_content_item_selection_request() is now set to true. + === 3.8 === * The following functions have been finally deprecated and can not be used anymore: