From ef81387681e6ff36bfd8d70f46e79cb401cc56fa Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Mon, 9 Dec 2024 00:30:32 +0800 Subject: [PATCH] MDL-82919 mod_lti: Implement the new core_ltix deep linking API --- mod/lti/amd/build/activitycontentitem.min.js | 10 + .../amd/build/activitycontentitem.min.js.map | 1 + mod/lti/amd/build/mod_form.min.js | 3 - mod/lti/amd/build/mod_form.min.js.map | 1 - mod/lti/amd/src/activitycontentitem.js | 241 +++++++++++++++++ mod/lti/amd/src/contentitem.js | 243 ------------------ mod/lti/amd/src/mod_form.js | 78 ------ mod/lti/contentitem.php | 66 ----- mod/lti/mod_form.php | 3 +- .../tool_deeplinking_results.mustache | 2 +- 10 files changed, 255 insertions(+), 393 deletions(-) create mode 100644 mod/lti/amd/build/activitycontentitem.min.js create mode 100644 mod/lti/amd/build/activitycontentitem.min.js.map delete mode 100644 mod/lti/amd/build/mod_form.min.js delete mode 100644 mod/lti/amd/build/mod_form.min.js.map create mode 100644 mod/lti/amd/src/activitycontentitem.js delete mode 100644 mod/lti/amd/src/contentitem.js delete mode 100644 mod/lti/amd/src/mod_form.js delete mode 100644 mod/lti/contentitem.php rename {ltix => mod/lti}/templates/tool_deeplinking_results.mustache (97%) diff --git a/mod/lti/amd/build/activitycontentitem.min.js b/mod/lti/amd/build/activitycontentitem.min.js new file mode 100644 index 0000000000000..1703db1da8815 --- /dev/null +++ b/mod/lti/amd/build/activitycontentitem.min.js @@ -0,0 +1,10 @@ +define("mod_lti/activitycontentitem",["exports","core_ltix/contentitem","mod_lti/form-field","core/templates"],(function(_exports,_contentitem,_formField,_templates){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +/** + * Class that defines content item selection process for creating an LTI external tool activity. + * + * @module mod_lti/activitycontentitem + * @copyright 2024 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_contentitem=_interopRequireDefault(_contentitem),_formField=_interopRequireDefault(_formField),_templates=_interopRequireDefault(_templates);const Selectors_selectContentButton='[name="selectcontent"]',Selectors_activityNameInput="#id_name",Selectors_activityDescriptionTextarea="#id_introeditor",Selectors_submitAndLaunchButton="#id_submitbutton",Selectors_submitAndCourseButton="#id_submitbutton2",Selectors_activityForm="#region-main-box form",Selectors_cancelButton="#id_cancel",Selectors_selectContentIndicator="#id_selectcontentindicator",Selectors_allowGradesCheckbox="#id_instructorchoiceacceptgrades",Selectors_gradeTypeSelect="#id_grade_modgrade_type",Selectors_buttonGroup="#fgroup_id_buttonar";class ActivityContentItem extends _contentitem.default{constructor(toolID,contextID){super(toolID,contextID,arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,arguments.length>3&&void 0!==arguments[3]?arguments[3]:null)}getContentItemTriggerSelector(){return Selectors_selectContentButton}customContentItemTriggerActions(){this.toolInstanceTitle=document.querySelector(Selectors_activityNameInput).value.trim(),this.toolInstanceText=document.querySelector(Selectors_activityDescriptionTextarea).value.trim()}processContentItemReturnData(returnData){if(returnData.multiple){this.getLtiFormFields().forEach((field=>{const value="name"===field.name?"item":null;field.setFieldValue(value)}));const variants=returnData.multiple.map((item=>this.configToVariant(item)));this.showMultipleSummaryAndHideForm(returnData.multiple);const submitAndCourseButton=document.querySelector(Selectors_submitAndCourseButton);submitAndCourseButton.onclick=e=>{e.preventDefault(),submitAndCourseButton.disabled=!0;const form=document.querySelector(Selectors_activityForm),formData=new FormData(form),navigateBackToCourse=()=>document.querySelector(Selectors_cancelButton).click();variants.reduce(((promise,variant)=>{Object.entries(variant).forEach((_ref=>{let[key,value]=_ref;return formData.set(key,value)}));const requestBody=new URLSearchParams(formData),doPost=()=>fetch(document.location.pathname,{method:"POST",body:requestBody});return promise.then(doPost).catch(doPost)}),Promise.resolve()).then(navigateBackToCourse).catch(navigateBackToCourse)}}else{this.getLtiFormFields().forEach((field=>{var _returnData$field$nam;const value=null!==(_returnData$field$nam=returnData[field.name])&&void 0!==_returnData$field$nam?_returnData$field$nam:null;field.setFieldValue(value)}));document.querySelector(Selectors_selectContentIndicator).innerHTML=returnData.selectcontentindicator;const allowGradesCheckbox=document.querySelector(Selectors_allowGradesCheckbox);if(allowGradesCheckbox.dispatchEvent(new Event("change")),allowGradesCheckbox.checked){const gradeTypeSelect=document.querySelector(Selectors_gradeTypeSelect);gradeTypeSelect.value="point",gradeTypeSelect.dispatchEvent(new Event("change"))}}}toggleElementVisibility(element,isVisible){element.toggleAttribute("hidden",!isVisible),element.setAttribute("aria-hidden",isVisible?"false":"true"),element.setAttribute("tab-index",isVisible?"1":"-1")}async showMultipleSummaryAndHideForm(items){const form=document.querySelector(Selectors_activityForm),toolArea=form.querySelector('[data-attribute="dynamic-import"]'),buttonGroup=form.querySelector(Selectors_buttonGroup),submitAndLaunchButton=form.querySelector(Selectors_submitAndLaunchButton);[...form.children].forEach((child=>{this.toggleElementVisibility(child,!1)})),this.toggleElementVisibility(submitAndLaunchButton,!1);const{html:html,js:js}=await _templates.default.renderForPromise("mod_lti/tool_deeplinking_results",{items:items});await _templates.default.replaceNodeContents(toolArea,html,js),this.toggleElementVisibility(toolArea,!0),this.toggleElementVisibility(buttonGroup,!0)}configToVariant(config){const variant={};return["name","toolurl","securetoolurl","instructorcustomparameters","icon","secureicon","launchcontainer","lineitemresourceid","lineitemtag","lineitemsubreviewurl","lineitemsubreviewparams"].forEach((field=>{variant[field]=config[field]||""})),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}getLtiFormFields(){return[new _formField.default("name",_formField.default.TYPES.TEXT,!1,""),new _formField.default("introeditor",_formField.default.TYPES.EDITOR,!1,""),new _formField.default("toolurl",_formField.default.TYPES.TEXT,!0,""),new _formField.default("securetoolurl",_formField.default.TYPES.TEXT,!0,""),new _formField.default("instructorchoiceacceptgrades",_formField.default.TYPES.CHECKBOX,!0,!0),new _formField.default("instructorchoicesendname",_formField.default.TYPES.CHECKBOX,!0,!0),new _formField.default("instructorchoicesendemailaddr",_formField.default.TYPES.CHECKBOX,!0,!0),new _formField.default("instructorcustomparameters",_formField.default.TYPES.TEXT,!0,""),new _formField.default("icon",_formField.default.TYPES.TEXT,!0,""),new _formField.default("secureicon",_formField.default.TYPES.TEXT,!0,""),new _formField.default("launchcontainer",_formField.default.TYPES.SELECT,!0,0),new _formField.default("grade_modgrade_point",_formField.default.TYPES.TEXT,!1,""),new _formField.default("lineitemresourceid",_formField.default.TYPES.TEXT,!0,""),new _formField.default("lineitemtag",_formField.default.TYPES.TEXT,!0,""),new _formField.default("lineitemsubreviewurl",_formField.default.TYPES.TEXT,!0,""),new _formField.default("lineitemsubreviewparams",_formField.default.TYPES.TEXT,!0,"")]}}return _exports.default=ActivityContentItem,_exports.default})); + +//# sourceMappingURL=activitycontentitem.min.js.map \ No newline at end of file diff --git a/mod/lti/amd/build/activitycontentitem.min.js.map b/mod/lti/amd/build/activitycontentitem.min.js.map new file mode 100644 index 0000000000000..32fc1e47c46be --- /dev/null +++ b/mod/lti/amd/build/activitycontentitem.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"activitycontentitem.min.js","sources":["../src/activitycontentitem.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 .\n\n/**\n * Class that defines content item selection process for creating an LTI external tool activity.\n *\n * @module mod_lti/activitycontentitem\n * @copyright 2024 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ContentItem from 'core_ltix/contentitem';\nimport FormField from 'mod_lti/form-field';\nimport Templates from 'core/templates';\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n selectContentButton: '[name=\"selectcontent\"]',\n activityNameInput: '#id_name',\n activityDescriptionTextarea: '#id_introeditor',\n submitAndLaunchButton: '#id_submitbutton',\n submitAndCourseButton: '#id_submitbutton2',\n activityForm: '#region-main-box form',\n cancelButton: '#id_cancel',\n selectContentIndicator: '#id_selectcontentindicator',\n allowGradesCheckbox: '#id_instructorchoiceacceptgrades',\n gradeTypeSelect:'#id_grade_modgrade_type',\n buttonGroup: '#fgroup_id_buttonar'\n};\n\nexport default class ActivityContentItem extends ContentItem {\n\n /**\n * The class constructor.\n *\n * @param {int} toolID The tool ID.\n * @param {int} contextID The context ID.\n * @param {string|null} toolInstanceTitle The tool instance title.\n * @param {string|null} toolInstanceText The tool instance text.\n * @returns {void}\n */\n constructor(toolID, contextID, toolInstanceTitle = null, toolInstanceText = null) {\n super(toolID, contextID, toolInstanceTitle, toolInstanceText);\n }\n\n /**\n * Defines the selector of the element that triggers the opening of content item selection modal.\n *\n * @returns {string} The selector.\n */\n getContentItemTriggerSelector() {\n return Selectors.selectContentButton;\n }\n\n /**\n * Method for defining custom action that will occur right after the trigger action of the content item\n * selection modal.\n *\n * @returns {void}\n */\n customContentItemTriggerActions() {\n this.toolInstanceTitle = document.querySelector(Selectors.activityNameInput).value.trim();\n this.toolInstanceText = document.querySelector(Selectors.activityDescriptionTextarea).value.trim();\n }\n\n /**\n * Method that processes the content item return data and populates the mod_lti configuration form.\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 processContentItemReturnData(returnData) {\n // Handle multiple content items.\n if (returnData.multiple) {\n this.getLtiFormFields().forEach((field) => {\n // Set a placeholder value for the 'name' field; other fields are set to null.\n const value = field.name === 'name' ? 'item' : null;\n field.setFieldValue(value);\n });\n const variants = returnData.multiple.map((item) => this.configToVariant(item));\n this.showMultipleSummaryAndHideForm(returnData.multiple);\n\n const submitAndCourseButton = document.querySelector(Selectors.submitAndCourseButton);\n submitAndCourseButton.onclick = (e) => {\n e.preventDefault();\n submitAndCourseButton.disabled = true;\n\n const form = document.querySelector(Selectors.activityForm);\n const formData = new FormData(form);\n const postVariant = (promise, variant) => {\n // Update the form data with the variant values.\n Object.entries(variant).forEach(([key, value]) => formData.set(key, value));\n // Create a POST request with the updated form data.\n const requestBody = new URLSearchParams(formData);\n const doPost = () => fetch(document.location.pathname, {method: 'POST', body: requestBody});\n\n return promise.then(doPost).catch(doPost);\n };\n const navigateBackToCourse = () => document.querySelector(Selectors.cancelButton).click();\n // Sequentially submit variants and navigate back to the course afterward.\n variants\n .reduce(postVariant, Promise.resolve())\n .then(navigateBackToCourse)\n .catch(navigateBackToCourse);\n };\n } else { // Handle single content item.\n this.getLtiFormFields().forEach((field) => {\n const value = returnData[field.name] ?? null;\n field.setFieldValue(value);\n });\n // Update the content selection indicator UI.\n const selectContentIndicator = document.querySelector(Selectors.selectContentIndicator);\n selectContentIndicator.innerHTML = returnData.selectcontentindicator;\n // Trigger the change event for the grade checkbox to update dependent fields.\n const allowGradesCheckbox = document.querySelector(Selectors.allowGradesCheckbox);\n allowGradesCheckbox.dispatchEvent(new Event('change'));\n // If grades are accepted, set the grade type to \"point\" and trigger its change event.\n if (allowGradesCheckbox.checked) {\n const gradeTypeSelect = document.querySelector(Selectors.gradeTypeSelect);\n gradeTypeSelect.value = 'point';\n gradeTypeSelect.dispatchEvent(new Event('change'));\n }\n }\n }\n\n /**\n * Toggle the visibility of an element, including aria and tab index.\n *\n * @param {HTMLElement} element The element to be toggled.\n * @param {boolean} isVisible Whether the element should be shown (true) or hidden (false).\n */\n toggleElementVisibility(element, isVisible) {\n element.toggleAttribute('hidden', !isVisible);\n element.setAttribute('aria-hidden', isVisible ? 'false' : 'true');\n element.setAttribute('tab-index', isVisible ? '1' : '-1');\n }\n\n /**\n * When more than one item needs to be added, the UI is simplified to just list the items to be added. Form is\n * hidden and the only options is (save and return to course) or cancel. This function injects the summary to the\n * form page, and hides the unneeded elements.\n *\n * @param {Object[]} items Items to be added to the course.\n */\n async showMultipleSummaryAndHideForm(items) {\n const form = document.querySelector(Selectors.activityForm);\n const toolArea = form.querySelector('[data-attribute=\"dynamic-import\"]');\n const buttonGroup = form.querySelector(Selectors.buttonGroup);\n const submitAndLaunchButton = form.querySelector(Selectors.submitAndLaunchButton);\n // Hide all form children and specific elements.\n [...form.children].forEach((child) => {\n this.toggleElementVisibility(child, false);\n });\n this.toggleElementVisibility(submitAndLaunchButton, false);\n // Render the summary template with the provided items.\n const {html, js} = await Templates.renderForPromise('mod_lti/tool_deeplinking_results', {items: items});\n // Replace tool area content with the rendered HTML and execute associated JS.\n await Templates.replaceNodeContents(toolArea, html, js);\n // Show the updated tool area and button group.\n this.toggleElementVisibility(toolArea, true);\n this.toggleElementVisibility(buttonGroup, true);\n }\n\n /**\n * Transforms config values aimed at populating the lti mod form to JSON variant which are used to insert more than\n * one activity modules in one submit by applying variation to the submitted form.\n * See /course/modedit.php.\n *\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 configToVariant(config) {\n const variant = {};\n [\n 'name',\n 'toolurl',\n 'securetoolurl',\n 'instructorcustomparameters',\n 'icon',\n 'secureicon',\n 'launchcontainer',\n 'lineitemresourceid',\n 'lineitemtag',\n 'lineitemsubreviewurl',\n 'lineitemsubreviewparams'\n ].forEach((field) => {\n variant[field] = config[field] || '';\n });\n // Handle intro editor fields.\n variant['introeditor[text]'] = config.introeditor ? config.introeditor.text : '';\n variant['introeditor[format]'] = config.introeditor ? config.introeditor.format : '';\n // Handle grade-related fields.\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 * Returns an array of form fields for LTI tool configuration.\n *\n * @return {Array} The array of form fields.\n */\n getLtiFormFields() {\n return [\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 new FormField('lineitemsubreviewurl', FormField.TYPES.TEXT, true, ''),\n new FormField('lineitemsubreviewparams', FormField.TYPES.TEXT, true, '')\n ];\n }\n}\n"],"names":["Selectors","ActivityContentItem","ContentItem","constructor","toolID","contextID","getContentItemTriggerSelector","customContentItemTriggerActions","toolInstanceTitle","document","querySelector","value","trim","toolInstanceText","processContentItemReturnData","returnData","multiple","getLtiFormFields","forEach","field","name","setFieldValue","variants","map","item","this","configToVariant","showMultipleSummaryAndHideForm","submitAndCourseButton","onclick","e","preventDefault","disabled","form","formData","FormData","navigateBackToCourse","click","reduce","promise","variant","Object","entries","_ref","key","set","requestBody","URLSearchParams","doPost","fetch","location","pathname","method","body","then","catch","Promise","resolve","innerHTML","selectcontentindicator","allowGradesCheckbox","dispatchEvent","Event","checked","gradeTypeSelect","toggleElementVisibility","element","isVisible","toggleAttribute","setAttribute","items","toolArea","buttonGroup","submitAndLaunchButton","children","child","html","js","Templates","renderForPromise","replaceNodeContents","config","introeditor","text","format","instructorchoiceacceptgrades","grade_modgrade_point","FormField","TYPES","TEXT","EDITOR","CHECKBOX","SELECT"],"mappings":"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"} \ No newline at end of file diff --git a/mod/lti/amd/build/mod_form.min.js b/mod/lti/amd/build/mod_form.min.js deleted file mode 100644 index 12492433c4e8e..0000000000000 --- a/mod/lti/amd/build/mod_form.min.js +++ /dev/null @@ -1,3 +0,0 @@ -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 \ No newline at end of file diff --git a/mod/lti/amd/build/mod_form.min.js.map b/mod/lti/amd/build/mod_form.min.js.map deleted file mode 100644 index d1c4c7d62cdf3..0000000000000 --- a/mod/lti/amd/build/mod_form.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"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 .\n\n/**\n * Event handlers for the mod_lti mod_form.\n *\n * @module mod_lti/mod_form\n * @copyright 2023 Jake Dallimore \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"} \ No newline at end of file diff --git a/mod/lti/amd/src/activitycontentitem.js b/mod/lti/amd/src/activitycontentitem.js new file mode 100644 index 0000000000000..a7e41c66c1760 --- /dev/null +++ b/mod/lti/amd/src/activitycontentitem.js @@ -0,0 +1,241 @@ +// 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 . + +/** + * Class that defines content item selection process for creating an LTI external tool activity. + * + * @module mod_lti/activitycontentitem + * @copyright 2024 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import ContentItem from 'core_ltix/contentitem'; +import FormField from 'mod_lti/form-field'; +import Templates from 'core/templates'; + +/** @constant {Object} The object containing the relevant selectors. */ +const Selectors = { + selectContentButton: '[name="selectcontent"]', + activityNameInput: '#id_name', + activityDescriptionTextarea: '#id_introeditor', + submitAndLaunchButton: '#id_submitbutton', + submitAndCourseButton: '#id_submitbutton2', + activityForm: '#region-main-box form', + cancelButton: '#id_cancel', + selectContentIndicator: '#id_selectcontentindicator', + allowGradesCheckbox: '#id_instructorchoiceacceptgrades', + gradeTypeSelect:'#id_grade_modgrade_type', + buttonGroup: '#fgroup_id_buttonar' +}; + +export default class ActivityContentItem extends ContentItem { + + /** + * The class constructor. + * + * @param {int} toolID The tool ID. + * @param {int} contextID The context ID. + * @param {string|null} toolInstanceTitle The tool instance title. + * @param {string|null} toolInstanceText The tool instance text. + * @returns {void} + */ + constructor(toolID, contextID, toolInstanceTitle = null, toolInstanceText = null) { + super(toolID, contextID, toolInstanceTitle, toolInstanceText); + } + + /** + * Defines the selector of the element that triggers the opening of content item selection modal. + * + * @returns {string} The selector. + */ + getContentItemTriggerSelector() { + return Selectors.selectContentButton; + } + + /** + * Method for defining custom action that will occur right after the trigger action of the content item + * selection modal. + * + * @returns {void} + */ + customContentItemTriggerActions() { + this.toolInstanceTitle = document.querySelector(Selectors.activityNameInput).value.trim(); + this.toolInstanceText = document.querySelector(Selectors.activityDescriptionTextarea).value.trim(); + } + + /** + * Method that processes the content item return data and populates the mod_lti configuration form. + * 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. + */ + processContentItemReturnData(returnData) { + // Handle multiple content items. + if (returnData.multiple) { + this.getLtiFormFields().forEach((field) => { + // Set a placeholder value for the 'name' field; other fields are set to null. + const value = field.name === 'name' ? 'item' : null; + field.setFieldValue(value); + }); + const variants = returnData.multiple.map((item) => this.configToVariant(item)); + this.showMultipleSummaryAndHideForm(returnData.multiple); + + const submitAndCourseButton = document.querySelector(Selectors.submitAndCourseButton); + submitAndCourseButton.onclick = (e) => { + e.preventDefault(); + submitAndCourseButton.disabled = true; + + const form = document.querySelector(Selectors.activityForm); + const formData = new FormData(form); + const postVariant = (promise, variant) => { + // Update the form data with the variant values. + Object.entries(variant).forEach(([key, value]) => formData.set(key, value)); + // Create a POST request with the updated form data. + const requestBody = new URLSearchParams(formData); + const doPost = () => fetch(document.location.pathname, {method: 'POST', body: requestBody}); + + return promise.then(doPost).catch(doPost); + }; + const navigateBackToCourse = () => document.querySelector(Selectors.cancelButton).click(); + // Sequentially submit variants and navigate back to the course afterward. + variants + .reduce(postVariant, Promise.resolve()) + .then(navigateBackToCourse) + .catch(navigateBackToCourse); + }; + } else { // Handle single content item. + this.getLtiFormFields().forEach((field) => { + const value = returnData[field.name] ?? null; + field.setFieldValue(value); + }); + // Update the content selection indicator UI. + const selectContentIndicator = document.querySelector(Selectors.selectContentIndicator); + selectContentIndicator.innerHTML = returnData.selectcontentindicator; + // Trigger the change event for the grade checkbox to update dependent fields. + const allowGradesCheckbox = document.querySelector(Selectors.allowGradesCheckbox); + allowGradesCheckbox.dispatchEvent(new Event('change')); + // If grades are accepted, set the grade type to "point" and trigger its change event. + if (allowGradesCheckbox.checked) { + const gradeTypeSelect = document.querySelector(Selectors.gradeTypeSelect); + gradeTypeSelect.value = 'point'; + gradeTypeSelect.dispatchEvent(new Event('change')); + } + } + } + + /** + * Toggle the visibility of an element, including aria and tab index. + * + * @param {HTMLElement} element The element to be toggled. + * @param {boolean} isVisible Whether the element should be shown (true) or hidden (false). + */ + toggleElementVisibility(element, isVisible) { + element.toggleAttribute('hidden', !isVisible); + element.setAttribute('aria-hidden', isVisible ? 'false' : 'true'); + element.setAttribute('tab-index', isVisible ? '1' : '-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. + */ + async showMultipleSummaryAndHideForm(items) { + const form = document.querySelector(Selectors.activityForm); + const toolArea = form.querySelector('[data-attribute="dynamic-import"]'); + const buttonGroup = form.querySelector(Selectors.buttonGroup); + const submitAndLaunchButton = form.querySelector(Selectors.submitAndLaunchButton); + // Hide all form children and specific elements. + [...form.children].forEach((child) => { + this.toggleElementVisibility(child, false); + }); + this.toggleElementVisibility(submitAndLaunchButton, false); + // Render the summary template with the provided items. + const {html, js} = await Templates.renderForPromise('mod_lti/tool_deeplinking_results', {items: items}); + // Replace tool area content with the rendered HTML and execute associated JS. + await Templates.replaceNodeContents(toolArea, html, js); + // Show the updated tool area and button group. + this.toggleElementVisibility(toolArea, true); + this.toggleElementVisibility(buttonGroup, true); + } + + /** + * 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. + * + * @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. + */ + configToVariant(config) { + const variant = {}; + [ + 'name', + 'toolurl', + 'securetoolurl', + 'instructorcustomparameters', + 'icon', + 'secureicon', + 'launchcontainer', + 'lineitemresourceid', + 'lineitemtag', + 'lineitemsubreviewurl', + 'lineitemsubreviewparams' + ].forEach((field) => { + variant[field] = config[field] || ''; + }); + // Handle intro editor fields. + variant['introeditor[text]'] = config.introeditor ? config.introeditor.text : ''; + variant['introeditor[format]'] = config.introeditor ? config.introeditor.format : ''; + // Handle grade-related fields. + if (config.instructorchoiceacceptgrades === 1) { + variant.instructorchoiceacceptgrades = '1'; + variant['grade[modgrade_point]'] = config.grade_modgrade_point || '100'; + } else { + variant.instructorchoiceacceptgrades = '0'; + } + return variant; + } + + /** + * Returns an array of form fields for LTI tool configuration. + * + * @return {Array} The array of form fields. + */ + getLtiFormFields() { + return [ + new FormField('name', FormField.TYPES.TEXT, false, ''), + new FormField('introeditor', FormField.TYPES.EDITOR, false, ''), + new FormField('toolurl', FormField.TYPES.TEXT, true, ''), + new FormField('securetoolurl', FormField.TYPES.TEXT, true, ''), + new FormField('instructorchoiceacceptgrades', FormField.TYPES.CHECKBOX, true, true), + new FormField('instructorchoicesendname', FormField.TYPES.CHECKBOX, true, true), + new FormField('instructorchoicesendemailaddr', FormField.TYPES.CHECKBOX, true, true), + new FormField('instructorcustomparameters', FormField.TYPES.TEXT, true, ''), + new FormField('icon', FormField.TYPES.TEXT, true, ''), + new FormField('secureicon', FormField.TYPES.TEXT, true, ''), + new FormField('launchcontainer', FormField.TYPES.SELECT, true, 0), + new FormField('grade_modgrade_point', FormField.TYPES.TEXT, false, ''), + new FormField('lineitemresourceid', FormField.TYPES.TEXT, true, ''), + new FormField('lineitemtag', FormField.TYPES.TEXT, true, ''), + new FormField('lineitemsubreviewurl', FormField.TYPES.TEXT, true, ''), + new FormField('lineitemsubreviewparams', FormField.TYPES.TEXT, true, '') + ]; + } +} diff --git a/mod/lti/amd/src/contentitem.js b/mod/lti/amd/src/contentitem.js deleted file mode 100644 index 96d4d9059324b..0000000000000 --- a/mod/lti/amd/src/contentitem.js +++ /dev/null @@ -1,243 +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 . - -/** - * Launches the modal dialogue that contains the iframe that sends the Content-Item selection request to an - * LTI tool provider that supports Content-Item type message. - * - * See template: mod_lti/contentitem - * - * @module mod_lti/contentitem - * @copyright 2016 Jun Pataleta - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @since 3.2 - */ -define( - [ - '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; - var doneCallback; - var contentItem = { - /** - * Init function. - * - * @param {string} url The URL for the content item selection. - * @param {object} postData The data to be sent for the content item selection request. - * @param {Function} cb The callback to run once the content item has been processed. - */ - init: function(url, postData, cb) { - doneCallback = cb; - var context = { - url: url, - postData: postData - }; - var bodyPromise = templates.render('mod_lti/contentitem', context); - - if (dialogue) { - // Set dialogue body. - dialogue.setBody(bodyPromise); - // Display the dialogue. - dialogue.show(); - return; - } - - str.get_string('selectcontent', 'lti').then(function(title) { - return Modal.create({ - title: title, - body: bodyPromise, - large: true, - show: true, - }); - }).then(function(modal) { - dialogue = modal; - // On hide handler. - modal.getRoot().on(ModalEvents.hidden, function() { - // Empty modal contents when it's hidden. - modal.setBody(''); - - // Fetch notifications. - notification.fetchNotifications(); - }); - return; - }).catch(notification.exception); - } - }; - - /** - * Array of form fields for LTI tool configuration. - */ - var ltiFormFields = [ - new FormField('name', FormField.TYPES.TEXT, false, ''), - new FormField('introeditor', FormField.TYPES.EDITOR, false, ''), - new FormField('toolurl', FormField.TYPES.TEXT, true, ''), - new FormField('securetoolurl', FormField.TYPES.TEXT, true, ''), - new FormField('instructorchoiceacceptgrades', FormField.TYPES.CHECKBOX, true, true), - new FormField('instructorchoicesendname', FormField.TYPES.CHECKBOX, true, true), - new FormField('instructorchoicesendemailaddr', FormField.TYPES.CHECKBOX, true, true), - new FormField('instructorcustomparameters', FormField.TYPES.TEXT, true, ''), - new FormField('icon', FormField.TYPES.TEXT, true, ''), - new FormField('secureicon', FormField.TYPES.TEXT, true, ''), - new FormField('launchcontainer', FormField.TYPES.SELECT, true, 0), - new FormField('grade_modgrade_point', FormField.TYPES.TEXT, false, ''), - new FormField('lineitemresourceid', FormField.TYPES.TEXT, true, ''), - new FormField('lineitemtag', FormField.TYPES.TEXT, true, ''), - new FormField('lineitemsubreviewurl', FormField.TYPES.TEXT, true, ''), - new FormField('lineitemsubreviewparams', 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', '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 : ''; - 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. - */ - window.processContentItemReturnData = function(returnData) { - if (dialogue) { - dialogue.hide(); - } - var index; - 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('#region-main-box form')); - 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); - - // Update the UI element which signifies content has been selected. - document.querySelector("#id_selectcontentindicator").innerHTML = returnData.selectcontentindicator; - } - - if (doneCallback) { - doneCallback(returnData); - } - }; - - return contentItem; - } -); diff --git a/mod/lti/amd/src/mod_form.js b/mod/lti/amd/src/mod_form.js deleted file mode 100644 index dcf77b1c8c99c..0000000000000 --- a/mod/lti/amd/src/mod_form.js +++ /dev/null @@ -1,78 +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 . - -/** - * Event handlers for the mod_lti mod_form. - * - * @module mod_lti/mod_form - * @copyright 2023 Jake Dallimore - * @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 -}; diff --git a/mod/lti/contentitem.php b/mod/lti/contentitem.php deleted file mode 100644 index 65dec6f0ac26f..0000000000000 --- a/mod/lti/contentitem.php +++ /dev/null @@ -1,66 +0,0 @@ -. - -/** - * Handle sending a user to a tool provider to initiate a content-item selection. - * - * @package mod_lti - * @copyright 2015 Vital Source Technologies http://vitalsource.com - * @author Stephen Vickers - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once('../../config.php'); -require_once($CFG->dirroot . '/mod/lti/lib.php'); -require_once($CFG->dirroot . '/mod/lti/locallib.php'); - -$id = required_param('id', PARAM_INT); -$courseid = required_param('course', PARAM_INT); -$title = optional_param('title', '', PARAM_TEXT); -$text = optional_param('text', '', PARAM_RAW); - -$config = \core_ltix\helper::get_type_type_config($id); -if ($config->lti_ltiversion === LTI_VERSION_1P3) { - if (!isset($SESSION->lti_initiatelogin_status)) { - echo \core_ltix\helper::initiate_login($courseid, 0, null, $config, 'ContentItemSelectionRequest', $title, $text); - exit; - } else { - unset($SESSION->lti_initiatelogin_status); - } -} - -// Check access and capabilities. -$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); -require_login($course); -$context = context_course::instance($courseid); -require_capability('moodle/course:manageactivities', $context); -require_capability('moodle/ltix:addcoursetool', $context); - -// Set the return URL. We send the launch container along to help us avoid frames-within-frames when the user returns. -$returnurlparams = [ - 'course' => $course->id, - 'id' => $id, - 'sesskey' => sesskey() -]; -$returnurl = new \moodle_url('/mod/lti/contentitem_return.php', $returnurlparams); - -// Prepare the request. -$request = \core_ltix\helper::build_content_item_selection_request($id, $course, $returnurl, $title, $text, [], []); - -// Get the launch HTML. -$content = \core_ltix\helper::post_launch_html($request->params, $request->url, false); - -echo $content; diff --git a/mod/lti/mod_form.php b/mod/lti/mod_form.php index d22382ad9ca14..d4748532c018d 100644 --- a/mod/lti/mod_form.php +++ b/mod/lti/mod_form.php @@ -518,7 +518,8 @@ public function definition() { $this->add_action_buttons(); if ($supportscontentitemselection) { - $PAGE->requires->js_call_amd('mod_lti/mod_form', 'init', [$COURSE->id]); + $PAGE->requires->js_call_amd('mod_lti/activitycontentitem', 'init', + [$tooltypeid, $this->context->id]); } } diff --git a/ltix/templates/tool_deeplinking_results.mustache b/mod/lti/templates/tool_deeplinking_results.mustache similarity index 97% rename from ltix/templates/tool_deeplinking_results.mustache rename to mod/lti/templates/tool_deeplinking_results.mustache index 9c3201c893ca1..73aaf84ed7c33 100644 --- a/ltix/templates/tool_deeplinking_results.mustache +++ b/mod/lti/templates/tool_deeplinking_results.mustache @@ -15,7 +15,7 @@ along with Moodle. If not, see . }} {{! - @template core_ltix/tool_deeplinking_results + @template mod_lti/tool_deeplinking_results This template lists the items that will be added to the course after a deep linking flow.