diff --git a/mod/lti/amd/build/course_tools_list.min.js b/mod/lti/amd/build/course_tools_list.min.js
index df23cfcb606..b9fab6a4486 100644
--- a/mod/lti/amd/build/course_tools_list.min.js
+++ b/mod/lti/amd/build/course_tools_list.min.js
@@ -1,3 +1,3 @@
-define("mod_lti/course_tools_list",["exports","core/notification","core/pending","core/ajax","core/toast","core/str","core_table/dynamic","core_table/local/dynamic/selectors"],(function(_exports,_notification,_pending,_ajax,_toast,_str,_dynamic,Selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_ajax=_interopRequireDefault(_ajax),Selectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Selectors);_exports.init=()=>{document.addEventListener("click",(event=>{const courseToolDelete=event.target.closest('[data-action="course-tool-delete"]');if(courseToolDelete){event.preventDefault();const deleteBodyStringId=courseToolDelete.dataset.courseToolUsage>0?"deletecoursetoolwithusageconfirm":"deletecoursetoolconfirm",requiredStrings=[{key:"deletecoursetool",component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:deleteBodyStringId,component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:"delete",component:"core",param:courseToolDelete.dataset.courseToolName},{key:"coursetooldeleted",component:"mod_lti",param:courseToolDelete.dataset.courseToolName}],triggerElement=courseToolDelete.closest(".dropdown").querySelector(".dropdown-toggle");(0,_str.getStrings)(requiredStrings).then((_ref=>{let[modalTitle,modalBody,deleteLabel]=_ref;return _notification.default.deleteCancelPromise(modalTitle,modalBody,deleteLabel,{triggerElement:triggerElement})})).then((()=>{const pendingPromise=new _pending.default("mod_lti/course_tools:delete"),request={methodname:"mod_lti_delete_course_tool_type",args:{tooltypeid:courseToolDelete.dataset.courseToolId}};return _ajax.default.call([request])[0].then((0,_toast.add)((0,_str.getString)("coursetooldeleted","mod_lti",courseToolDelete.dataset.courseToolName))).then((()=>{const tableRoot=triggerElement.closest(Selectors.main.region);return(0,_dynamic.refreshTableContent)(tableRoot)})).then(pendingPromise.resolve).catch(_notification.default.exception)})).catch((()=>{}))}}))}}));
+define("mod_lti/course_tools_list",["exports","core/notification","core/pending","core/ajax","core/toast","core/str","core_table/dynamic","core_table/local/dynamic/selectors","./repository"],(function(_exports,_notification,_pending,_ajax,_toast,_str,_dynamic,Selectors,_repository){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_ajax=_interopRequireDefault(_ajax),Selectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Selectors);_exports.init=()=>{document.addEventListener("click",(event=>{const courseToolDelete=event.target.closest('[data-action="course-tool-delete"]');if(courseToolDelete){event.preventDefault();const deleteBodyStringId=courseToolDelete.dataset.courseToolUsage>0?"deletecoursetoolwithusageconfirm":"deletecoursetoolconfirm",requiredStrings=[{key:"deletecoursetool",component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:deleteBodyStringId,component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:"delete",component:"core",param:courseToolDelete.dataset.courseToolName},{key:"coursetooldeleted",component:"mod_lti",param:courseToolDelete.dataset.courseToolName}],triggerElement=courseToolDelete.closest(".dropdown").querySelector(".dropdown-toggle");(0,_str.getStrings)(requiredStrings).then((_ref=>{let[modalTitle,modalBody,deleteLabel]=_ref;return _notification.default.deleteCancelPromise(modalTitle,modalBody,deleteLabel,{triggerElement:triggerElement})})).then((()=>{const pendingPromise=new _pending.default("mod_lti/course_tools:delete"),request={methodname:"mod_lti_delete_course_tool_type",args:{tooltypeid:courseToolDelete.dataset.courseToolId}};return _ajax.default.call([request])[0].then((0,_toast.add)((0,_str.getString)("coursetooldeleted","mod_lti",courseToolDelete.dataset.courseToolName))).then((()=>{const tableRoot=triggerElement.closest(Selectors.main.region);return(0,_dynamic.refreshTableContent)(tableRoot)})).then(pendingPromise.resolve).catch(_notification.default.exception)})).catch((()=>{}))}const courseShowInActivityChooser=event.target.closest('[data-action="showinactivitychooser-toggle"]');if(courseShowInActivityChooser){const showInActivityChooserStateToggle="0"===courseShowInActivityChooser.dataset.state?1:0;return(0,_repository.toggleShowInActivityChooser)(courseShowInActivityChooser.dataset.id,courseShowInActivityChooser.dataset.courseid,showInActivityChooserStateToggle)}}))}}));
//# sourceMappingURL=course_tools_list.min.js.map
\ No newline at end of file
diff --git a/mod/lti/amd/build/course_tools_list.min.js.map b/mod/lti/amd/build/course_tools_list.min.js.map
index e4ee0474cee..1d32cc4d836 100644
--- a/mod/lti/amd/build/course_tools_list.min.js.map
+++ b/mod/lti/amd/build/course_tools_list.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"course_tools_list.min.js","sources":["../src/course_tools_list.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 * Course LTI External tools list management.\n *\n * @module mod_lti/course_tools_list\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 Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Ajax from 'core/ajax';\nimport {add as addToast} from 'core/toast';\nimport {getString, getStrings} from 'core/str';\nimport {refreshTableContent} from 'core_table/dynamic';\nimport * as Selectors from 'core_table/local/dynamic/selectors';\n\n/**\n * Initialise module.\n */\nexport const init = () => {\n document.addEventListener('click', event => {\n\n const courseToolDelete = event.target.closest('[data-action=\"course-tool-delete\"]');\n if (courseToolDelete) {\n event.preventDefault();\n\n // A different message is used in the modal if the tool has usages within the course.\n const usage = courseToolDelete.dataset.courseToolUsage;\n const deleteBodyStringId = usage > 0 ? 'deletecoursetoolwithusageconfirm' : 'deletecoursetoolconfirm';\n const requiredStrings = [\n {key: 'deletecoursetool', component: 'mod_lti', param: courseToolDelete.dataset.courseToolName},\n {key: deleteBodyStringId, component: 'mod_lti', param: courseToolDelete.dataset.courseToolName},\n {key: 'delete', component: 'core', param: courseToolDelete.dataset.courseToolName},\n {key: 'coursetooldeleted', component: 'mod_lti', param: courseToolDelete.dataset.courseToolName}\n ];\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = courseToolDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n\n getStrings(requiredStrings).then(([modalTitle, modalBody, deleteLabel]) => {\n return Notification.deleteCancelPromise(\n modalTitle,\n modalBody,\n deleteLabel,\n {triggerElement});\n }).then(() => {\n const pendingPromise = new Pending('mod_lti/course_tools:delete');\n\n const request = {\n methodname: 'mod_lti_delete_course_tool_type',\n args: {tooltypeid: courseToolDelete.dataset.courseToolId}\n };\n return Ajax.call([request])[0]\n .then(addToast(getString('coursetooldeleted', 'mod_lti', courseToolDelete.dataset.courseToolName)))\n .then(() => {\n const tableRoot = triggerElement.closest(Selectors.main.region);\n return refreshTableContent(tableRoot);\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["document","addEventListener","event","courseToolDelete","target","closest","preventDefault","deleteBodyStringId","dataset","courseToolUsage","requiredStrings","key","component","param","courseToolName","triggerElement","querySelector","then","_ref","modalTitle","modalBody","deleteLabel","Notification","deleteCancelPromise","pendingPromise","Pending","request","methodname","args","tooltypeid","courseToolId","Ajax","call","tableRoot","Selectors","main","region","resolve","catch","exception"],"mappings":"o+CAoCoB,KAChBA,SAASC,iBAAiB,SAASC,cAEzBC,iBAAmBD,MAAME,OAAOC,QAAQ,yCAC1CF,iBAAkB,CAClBD,MAAMI,uBAIAC,mBADQJ,iBAAiBK,QAAQC,gBACJ,EAAI,mCAAqC,0BACtEC,gBAAkB,CACpB,CAACC,IAAK,mBAAoBC,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,gBAChF,CAACH,IAAKJ,mBAAoBK,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,gBAChF,CAACH,IAAK,SAAUC,UAAW,OAAQC,MAAOV,iBAAiBK,QAAQM,gBACnE,CAACH,IAAK,oBAAqBC,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,iBAG/EC,eAAiBZ,iBAAiBE,QAAQ,aAAaW,cAAc,wCAEhEN,iBAAiBO,MAAKC,WAAEC,WAAYC,UAAWC,yBAC/CC,sBAAaC,oBAChBJ,WACAC,UACAC,YACA,CAACN,eAAAA,oBACNE,MAAK,WACEO,eAAiB,IAAIC,iBAAQ,+BAE7BC,QAAU,CACZC,WAAY,kCACZC,KAAM,CAACC,WAAY1B,iBAAiBK,QAAQsB,sBAEzCC,cAAKC,KAAK,CAACN,UAAU,GACvBT,MAAK,eAAS,kBAAU,oBAAqB,UAAWd,iBAAiBK,QAAQM,kBACjFG,MAAK,WACIgB,UAAYlB,eAAeV,QAAQ6B,UAAUC,KAAKC,eACjD,gCAAoBH,cAE9BhB,KAAKO,eAAea,SACpBC,MAAMhB,sBAAaiB,cACzBD,OAAM"}
\ No newline at end of file
+{"version":3,"file":"course_tools_list.min.js","sources":["../src/course_tools_list.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 * Course LTI External tools list management.\n *\n * @module mod_lti/course_tools_list\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 Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Ajax from 'core/ajax';\nimport {add as addToast} from 'core/toast';\nimport {getString, getStrings} from 'core/str';\nimport {refreshTableContent} from 'core_table/dynamic';\nimport * as Selectors from 'core_table/local/dynamic/selectors';\nimport {toggleShowInActivityChooser} from \"./repository\";\n\n/**\n * Initialise module.\n */\nexport const init = () => {\n document.addEventListener('click', event => {\n\n const courseToolDelete = event.target.closest('[data-action=\"course-tool-delete\"]');\n if (courseToolDelete) {\n event.preventDefault();\n\n // A different message is used in the modal if the tool has usages within the course.\n const usage = courseToolDelete.dataset.courseToolUsage;\n const deleteBodyStringId = usage > 0 ? 'deletecoursetoolwithusageconfirm' : 'deletecoursetoolconfirm';\n const requiredStrings = [\n {key: 'deletecoursetool', component: 'mod_lti', param: courseToolDelete.dataset.courseToolName},\n {key: deleteBodyStringId, component: 'mod_lti', param: courseToolDelete.dataset.courseToolName},\n {key: 'delete', component: 'core', param: courseToolDelete.dataset.courseToolName},\n {key: 'coursetooldeleted', component: 'mod_lti', param: courseToolDelete.dataset.courseToolName}\n ];\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = courseToolDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n\n getStrings(requiredStrings).then(([modalTitle, modalBody, deleteLabel]) => {\n return Notification.deleteCancelPromise(\n modalTitle,\n modalBody,\n deleteLabel,\n {triggerElement});\n }).then(() => {\n const pendingPromise = new Pending('mod_lti/course_tools:delete');\n\n const request = {\n methodname: 'mod_lti_delete_course_tool_type',\n args: {tooltypeid: courseToolDelete.dataset.courseToolId}\n };\n return Ajax.call([request])[0]\n .then(addToast(getString('coursetooldeleted', 'mod_lti', courseToolDelete.dataset.courseToolName)))\n .then(() => {\n const tableRoot = triggerElement.closest(Selectors.main.region);\n return refreshTableContent(tableRoot);\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n\n const courseShowInActivityChooser = event.target.closest('[data-action=\"showinactivitychooser-toggle\"]');\n if (courseShowInActivityChooser) {\n const showInActivityChooserStateToggle = courseShowInActivityChooser.dataset.state === \"0\" ? 1 : 0;\n return toggleShowInActivityChooser(\n courseShowInActivityChooser.dataset.id,\n courseShowInActivityChooser.dataset.courseid,\n showInActivityChooserStateToggle,\n );\n }\n });\n};\n"],"names":["document","addEventListener","event","courseToolDelete","target","closest","preventDefault","deleteBodyStringId","dataset","courseToolUsage","requiredStrings","key","component","param","courseToolName","triggerElement","querySelector","then","_ref","modalTitle","modalBody","deleteLabel","Notification","deleteCancelPromise","pendingPromise","Pending","request","methodname","args","tooltypeid","courseToolId","Ajax","call","tableRoot","Selectors","main","region","resolve","catch","exception","courseShowInActivityChooser","showInActivityChooserStateToggle","state","id","courseid"],"mappings":"+/CAqCoB,KAChBA,SAASC,iBAAiB,SAASC,cAEzBC,iBAAmBD,MAAME,OAAOC,QAAQ,yCAC1CF,iBAAkB,CAClBD,MAAMI,uBAIAC,mBADQJ,iBAAiBK,QAAQC,gBACJ,EAAI,mCAAqC,0BACtEC,gBAAkB,CACpB,CAACC,IAAK,mBAAoBC,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,gBAChF,CAACH,IAAKJ,mBAAoBK,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,gBAChF,CAACH,IAAK,SAAUC,UAAW,OAAQC,MAAOV,iBAAiBK,QAAQM,gBACnE,CAACH,IAAK,oBAAqBC,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,iBAG/EC,eAAiBZ,iBAAiBE,QAAQ,aAAaW,cAAc,wCAEhEN,iBAAiBO,MAAKC,WAAEC,WAAYC,UAAWC,yBAC/CC,sBAAaC,oBAChBJ,WACAC,UACAC,YACA,CAACN,eAAAA,oBACNE,MAAK,WACEO,eAAiB,IAAIC,iBAAQ,+BAE7BC,QAAU,CACZC,WAAY,kCACZC,KAAM,CAACC,WAAY1B,iBAAiBK,QAAQsB,sBAEzCC,cAAKC,KAAK,CAACN,UAAU,GACvBT,MAAK,eAAS,kBAAU,oBAAqB,UAAWd,iBAAiBK,QAAQM,kBACjFG,MAAK,WACIgB,UAAYlB,eAAeV,QAAQ6B,UAAUC,KAAKC,eACjD,gCAAoBH,cAE9BhB,KAAKO,eAAea,SACpBC,MAAMhB,sBAAaiB,cACzBD,OAAM,eAKPE,4BAA8BtC,MAAME,OAAOC,QAAQ,mDACrDmC,4BAA6B,OACvBC,iCAAiF,MAA9CD,4BAA4BhC,QAAQkC,MAAgB,EAAI,SAC1F,2CACHF,4BAA4BhC,QAAQmC,GACpCH,4BAA4BhC,QAAQoC,SACpCH"}
\ No newline at end of file
diff --git a/mod/lti/amd/build/repository.min.js b/mod/lti/amd/build/repository.min.js
new file mode 100644
index 00000000000..0463554ddc1
--- /dev/null
+++ b/mod/lti/amd/build/repository.min.js
@@ -0,0 +1,10 @@
+define("mod_lti/repository",["exports","core/ajax"],(function(_exports,_ajax){var obj;
+/**
+ * Module to handle AJAX interactions.
+ *
+ * @module mod_lti/repository
+ * @copyright 2023 Ilya Tregubov
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.toggleShowInActivityChooser=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.toggleShowInActivityChooser=(tooltypeid,courseid,coursevisible)=>_ajax.default.call([{methodname:"mod_lti_toggle_showinactivitychooser",args:{tooltypeid:tooltypeid,courseid:courseid,coursevisible:coursevisible}}])[0]}));
+
+//# sourceMappingURL=repository.min.js.map
\ No newline at end of file
diff --git a/mod/lti/amd/build/repository.min.js.map b/mod/lti/amd/build/repository.min.js.map
new file mode 100644
index 00000000000..a86f231996d
--- /dev/null
+++ b/mod/lti/amd/build/repository.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"repository.min.js","sources":["../src/repository.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 * Module to handle AJAX interactions.\n *\n * @module mod_lti/repository\n * @copyright 2023 Ilya Tregubov \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Toggle coursevisible of a tool\n *\n * @param {Number} tooltypeid Too type id\n * @param {Number} courseid Course ID\n * @param {Number} coursevisible coursevisible state\n * @return {Promise}\n */\nexport const toggleShowInActivityChooser = (\n tooltypeid,\n courseid,\n coursevisible,\n) => Ajax.call([{\n methodname: 'mod_lti_toggle_showinactivitychooser',\n args: {\n tooltypeid,\n courseid,\n coursevisible,\n },\n}])[0];\n"],"names":["tooltypeid","courseid","coursevisible","Ajax","call","methodname","args"],"mappings":";;;;;;;kMAiC2C,CACvCA,WACAC,SACAC,gBACCC,cAAKC,KAAK,CAAC,CACZC,WAAY,uCACZC,KAAM,CACFN,WAAAA,WACAC,SAAAA,SACAC,cAAAA,kBAEJ"}
\ No newline at end of file
diff --git a/mod/lti/amd/src/course_tools_list.js b/mod/lti/amd/src/course_tools_list.js
index da17abb72b2..6fac7469f05 100644
--- a/mod/lti/amd/src/course_tools_list.js
+++ b/mod/lti/amd/src/course_tools_list.js
@@ -30,6 +30,7 @@ import {add as addToast} from 'core/toast';
import {getString, getStrings} from 'core/str';
import {refreshTableContent} from 'core_table/dynamic';
import * as Selectors from 'core_table/local/dynamic/selectors';
+import {toggleShowInActivityChooser} from "./repository";
/**
* Initialise module.
@@ -78,5 +79,15 @@ export const init = () => {
return;
});
}
+
+ const courseShowInActivityChooser = event.target.closest('[data-action="showinactivitychooser-toggle"]');
+ if (courseShowInActivityChooser) {
+ const showInActivityChooserStateToggle = courseShowInActivityChooser.dataset.state === "0" ? 1 : 0;
+ return toggleShowInActivityChooser(
+ courseShowInActivityChooser.dataset.id,
+ courseShowInActivityChooser.dataset.courseid,
+ showInActivityChooserStateToggle,
+ );
+ }
});
};
diff --git a/mod/lti/amd/src/repository.js b/mod/lti/amd/src/repository.js
new file mode 100644
index 00000000000..0f1b4a9a326
--- /dev/null
+++ b/mod/lti/amd/src/repository.js
@@ -0,0 +1,45 @@
+// 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 .
+
+/**
+ * Module to handle AJAX interactions.
+ *
+ * @module mod_lti/repository
+ * @copyright 2023 Ilya Tregubov
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+import Ajax from 'core/ajax';
+
+/**
+ * Toggle coursevisible of a tool
+ *
+ * @param {Number} tooltypeid Too type id
+ * @param {Number} courseid Course ID
+ * @param {Number} coursevisible coursevisible state
+ * @return {Promise}
+ */
+export const toggleShowInActivityChooser = (
+ tooltypeid,
+ courseid,
+ coursevisible,
+) => Ajax.call([{
+ methodname: 'mod_lti_toggle_showinactivitychooser',
+ args: {
+ tooltypeid,
+ courseid,
+ coursevisible,
+ },
+}])[0];
diff --git a/mod/lti/backup/moodle2/backup_lti_stepslib.php b/mod/lti/backup/moodle2/backup_lti_stepslib.php
index eae88a71bf4..ae211860f3d 100644
--- a/mod/lti/backup/moodle2/backup_lti_stepslib.php
+++ b/mod/lti/backup/moodle2/backup_lti_stepslib.php
@@ -146,6 +146,12 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
'state'
));
+ $lticoursevisible = new backup_nested_element('lticoursevisible', ['id'], [
+ 'typeid',
+ 'courseid',
+ 'coursevisible',
+ ]);
+
// Build the tree
$lti->add_child($ltitype);
$ltitype->add_child($ltitypesconfigs);
@@ -156,6 +162,7 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
$ltitoolsettings->add_child($ltitoolsetting);
$lti->add_child($ltisubmissions);
$ltisubmissions->add_child($ltisubmission);
+ $lti->add_child($lticoursevisible);
// Define sources.
$ltirecord = $DB->get_record('lti', ['id' => $this->task->get_activityid()]);
@@ -193,6 +200,9 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
$ltisubmission->set_source_table('lti_submission', array('ltiid' => backup::VAR_ACTIVITYID));
}
+ $lticoursevisibledata = $this->retrieve_lti_coursevisible($ltirecord);
+ $lticoursevisible->set_source_array($lticoursevisibledata ? [$lticoursevisibledata] : []);
+
// Define id annotations
$ltitype->annotate_ids('user', 'createdby');
$ltitype->annotate_ids('course', 'course');
@@ -238,4 +248,18 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
return $record;
}
+
+ /**
+ * Retrieves a record from {lti_coursevisible} table associated with the current type
+ *
+ * @param stdClass $ltirecord record from {lti} table
+ * @return mixed
+ */
+ protected function retrieve_lti_coursevisible(stdClass $ltirecord): mixed {
+ global $DB;
+ if (!$ltirecord->typeid) {
+ return null;
+ }
+ return $DB->get_record('lti_coursevisible', ['typeid' => $ltirecord->typeid, 'courseid' => $ltirecord->course]);
+ }
}
diff --git a/mod/lti/backup/moodle2/restore_lti_stepslib.php b/mod/lti/backup/moodle2/restore_lti_stepslib.php
index f01440b0e73..02bcf846fb1 100644
--- a/mod/lti/backup/moodle2/restore_lti_stepslib.php
+++ b/mod/lti/backup/moodle2/restore_lti_stepslib.php
@@ -78,6 +78,8 @@ class restore_lti_activity_structure_step extends restore_activity_structure_ste
$paths[] = $submission;
}
+ $paths[] = new restore_path_element('lticoursevisible', '/activity/lti/lticoursevisible');
+
// Add support for subplugin structures.
$this->add_subplugin_structure('ltisource', $lti);
$this->add_subplugin_structure('ltiservice', $lti);
@@ -149,6 +151,23 @@ class restore_lti_activity_structure_step extends restore_activity_structure_ste
$DB->update_record('lti', ['id' => $this->get_new_parentid('lti'), 'typeid' => $ltitypeid]);
}
+ /**
+ * Process an lti coursevisible restore
+ * @param mixed $data The data from backup XML file
+ * @return void
+ */
+ protected function process_lticoursevisible($data) {
+ global $DB;
+
+ $data = (object)$data;
+ $data->typeid = $this->get_new_parentid('ltitype');
+ $data->courseid = $this->get_courseid();
+
+ if ($data->typeid) {
+ $DB->insert_record('lti_coursevisible', $data);
+ }
+ }
+
/**
* Attempts to find existing record in lti_type
* @param stdClass $data
diff --git a/mod/lti/classes/external/toggle_showinactivitychooser.php b/mod/lti/classes/external/toggle_showinactivitychooser.php
new file mode 100644
index 00000000000..5c44241615d
--- /dev/null
+++ b/mod/lti/classes/external/toggle_showinactivitychooser.php
@@ -0,0 +1,116 @@
+.
+
+namespace mod_lti\external;
+
+use core_external\external_api;
+use core_external\external_function_parameters;
+use core_external\external_value;
+use mod_lti\local\ltiopenid\registration_helper;
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/mod/lti/locallib.php');
+
+/**
+ * External function to toggle showinactivitychooser setting.
+ *
+ * @package mod_lti
+ * @copyright 2023 Ilya Tregubov
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class toggle_showinactivitychooser extends external_api {
+
+ /**
+ * Get parameter definition.
+ *
+ * @return external_function_parameters
+ */
+ public static function execute_parameters(): external_function_parameters {
+ return new external_function_parameters([
+ 'tooltypeid' => new external_value(PARAM_INT, 'Tool type ID'),
+ 'courseid' => new external_value(PARAM_INT, 'Course ID'),
+ 'coursevisible' => new external_value(PARAM_BOOL, 'Show in activity chooser'),
+ ]);
+ }
+
+ /**
+ * Toggles showinactivitychooser setting.
+ *
+ * @param int $tooltypeid the id of the course external tool type.
+ * @param int $courseid the id of the course we are in.
+ * @param bool $showinactivitychooser Show in activity chooser setting.
+ * @return bool true
+ */
+ public static function execute(int $tooltypeid, int $courseid, bool $showinactivitychooser): bool {
+ global $DB;
+
+ [
+ 'tooltypeid' => $tooltypeid,
+ 'courseid' => $courseid,
+ 'coursevisible' => $showinactivitychooser,
+ ] = self::validate_parameters(self::execute_parameters(), [
+ 'tooltypeid' => $tooltypeid,
+ 'courseid' => $courseid,
+ 'coursevisible' => $showinactivitychooser,
+ ]);
+
+ $context = \context_course::instance($courseid);
+ self::validate_context($context);
+ require_capability('mod/lti:addcoursetool', $context);
+
+ if ($showinactivitychooser) {
+ $coursevisible = LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
+ } else {
+ $coursevisible = LTI_COURSEVISIBLE_PRECONFIGURED;
+ }
+ $ltitype = $DB->get_record('lti_types', ['id' => $tooltypeid]);
+ $ltitype->coursevisible = $coursevisible;
+
+ $config = new \stdClass();
+ $config->lti_coursevisible = $coursevisible;
+
+ if (intval($ltitype->course) !== intval(get_site()->id)) {
+ // It is course tool - just update it.
+ lti_update_type($ltitype, $config);
+ } else {
+ // This is site tool, but we would like to have course level setting for it.
+ $lticoursevisible = $DB->get_record('lti_coursevisible', ['typeid' => $tooltypeid, 'courseid' => $courseid]);
+ if (!$lticoursevisible) {
+ $lticoursevisible = new \stdClass();
+ $lticoursevisible->typeid = $tooltypeid;
+ $lticoursevisible->courseid = $courseid;
+ $lticoursevisible->coursevisible = $coursevisible;
+ $DB->insert_record('lti_coursevisible', $lticoursevisible);
+ } else {
+ $lticoursevisible->coursevisible = $coursevisible;
+ $DB->update_record('lti_coursevisible', $lticoursevisible);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Get service returns definition.
+ *
+ * @return external_value
+ */
+ public static function execute_returns(): external_value {
+ return new external_value(PARAM_BOOL, 'Success');
+ }
+}
diff --git a/mod/lti/classes/local/types_helper.php b/mod/lti/classes/local/types_helper.php
index 45dd6bd3e3b..a462082a9c8 100644
--- a/mod/lti/classes/local/types_helper.php
+++ b/mod/lti/classes/local/types_helper.php
@@ -48,25 +48,36 @@ class types_helper {
if (empty($coursevisible)) {
$coursevisible = [LTI_COURSEVISIBLE_PRECONFIGURED, LTI_COURSEVISIBLE_ACTIVITYCHOOSER];
}
- list($coursevisiblesql, $coursevisparams) = $DB->get_in_or_equal($coursevisible, SQL_PARAMS_NAMED, 'coursevisible');
+ [$coursevisiblesql, $coursevisparams] = $DB->get_in_or_equal($coursevisible, SQL_PARAMS_NAMED, 'coursevisible');
+ [$coursevisiblesql1, $coursevisparams1] = $DB->get_in_or_equal($coursevisible, SQL_PARAMS_NAMED, 'coursevisible');
+ [$coursevisibleoverriddensql, $coursevisoverriddenparams] = $DB->get_in_or_equal(
+ $coursevisible,
+ SQL_PARAMS_NAMED,
+ 'coursevisibleoverridden');
$coursecond = implode(" OR ", ["t.course = :courseid", "t.course = :siteid"]);
$coursecategory = $DB->get_field('course', 'category', ['id' => $courseid]);
- $query = "SELECT t.*
- FROM {lti_types} t
- LEFT JOIN {lti_types_categories} tc ON t.id = tc.typeid
- WHERE t.coursevisible $coursevisiblesql
- AND ($coursecond)
- AND t.state = :active
- AND (tc.id IS NULL OR tc.categoryid = :categoryid)
- ORDER BY t.name ASC";
+ $query = "SELECT *
+ FROM (SELECT t.*, c.coursevisible as coursevisibleoverridden
+ FROM {lti_types} t
+ LEFT JOIN {lti_types_categories} tc ON t.id = tc.typeid
+ LEFT JOIN {lti_coursevisible} c ON c.typeid = t.id AND c.courseid = $courseid
+ WHERE (t.coursevisible $coursevisiblesql OR c.coursevisible $coursevisiblesql1)
+ AND ($coursecond)
+ AND t.state = :active
+ AND (tc.id IS NULL OR tc.categoryid = :categoryid)) tt
+ WHERE tt.coursevisibleoverridden IS NULL
+ OR tt.coursevisibleoverridden $coursevisibleoverriddensql";
- return $DB->get_records_sql($query,
+ return $DB->get_records_sql(
+ $query,
[
'siteid' => $SITE->id,
'courseid' => $courseid,
'active' => LTI_TOOL_STATE_CONFIGURED,
- 'categoryid' => $coursecategory
- ] + $coursevisparams);
+ 'categoryid' => $coursecategory,
+ 'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER,
+ ] + $coursevisparams + $coursevisparams1 + $coursevisoverriddenparams
+ );
}
}
diff --git a/mod/lti/classes/reportbuilder/local/systemreports/course_external_tools_list.php b/mod/lti/classes/reportbuilder/local/systemreports/course_external_tools_list.php
index 14cab9dd3c4..2dd78c7fd7a 100644
--- a/mod/lti/classes/reportbuilder/local/systemreports/course_external_tools_list.php
+++ b/mod/lti/classes/reportbuilder/local/systemreports/course_external_tools_list.php
@@ -20,6 +20,7 @@ use core_reportbuilder\local\helpers\database;
use core_reportbuilder\local\report\column;
use mod_lti\reportbuilder\local\entities\tool_types;
use core_reportbuilder\system_report;
+use stdClass;
/**
* Course external tools list system report class implementation.
@@ -109,6 +110,7 @@ class course_external_tools_list extends system_report {
*/
protected function add_columns(tool_types $tooltypesentity): void {
$entitymainalias = $tooltypesentity->get_table_alias('lti_types');
+ $courseid = $this->course->id;
$columns = [
'tool_types:name',
@@ -129,6 +131,57 @@ class course_external_tools_list extends system_report {
->add_field("{$entitymainalias}.id")
->add_callback(fn() => $this->perrowtoolusage);
+ // Enable toggle column.
+ $this->add_column((new column(
+ 'showinactivitychooser',
+ new \lang_string('showinactivitychooser', 'mod_lti'),
+ $tooltypesentity->get_entity_name()
+ ))
+ // Site tools can be overridden on course level.
+ ->add_join("LEFT JOIN {lti_coursevisible} lc ON lc.typeid = {$entitymainalias}.id AND lc.courseid = $courseid")
+ ->set_type(column::TYPE_INTEGER)
+ ->add_fields("{$entitymainalias}.id, {$entitymainalias}.coursevisible, lc.coursevisible as coursevisibleoverridden")
+ ->set_is_sortable(false)
+ ->set_callback(static function(int $id, stdClass $row): string {
+ global $PAGE, $COURSE;
+ $coursevisible = $row->coursevisible;
+ $courseid = $COURSE->id;
+ if (!empty($row->coursevisibleoverridden)) {
+ $coursevisible = $row->coursevisibleoverridden;
+ }
+
+ if ($coursevisible == LTI_COURSEVISIBLE_ACTIVITYCHOOSER) {
+ $coursevisible = true;
+ } else {
+ $coursevisible = false;
+ }
+
+ $renderer = $PAGE->get_renderer('core_reportbuilder');
+ $attributes = [
+ ['name' => 'id', 'value' => $row->id],
+ ['name' => 'courseid', 'value' => $courseid],
+ ['name' => 'action', 'value' => 'showinactivitychooser-toggle'],
+ ['name' => 'state', 'value' => $coursevisible],
+ ];
+ $label = $coursevisible ? get_string('dontshowinactivitychooser', 'mod_lti')
+ : get_string('showinactivitychooser', 'mod_lti');
+
+ $disabled = false;
+ if (!has_capability('mod/lti:addcoursetool', \context_course::instance($courseid))) {
+ $disabled = true;
+ }
+
+ return $renderer->render_from_template('core/toggle', [
+ 'id' => 'showinactivitychooser-toggle-' . $row->id,
+ 'checked' => $coursevisible,
+ 'disabled' => $disabled,
+ 'dataattributes' => $attributes,
+ 'label' => $label,
+ 'labelclasses' => 'sr-only'
+ ]);
+ })
+ );
+
// Attempt to create a dummy actions column, working around the limitations of the official actions feature.
$this->add_column(new column(
'actions', new \lang_string('actions'),
diff --git a/mod/lti/db/install.xml b/mod/lti/db/install.xml
index 5e3b8578113..1b9c717a2de 100644
--- a/mod/lti/db/install.xml
+++ b/mod/lti/db/install.xml
@@ -1,5 +1,5 @@
-
@@ -175,5 +175,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mod/lti/db/services.php b/mod/lti/db/services.php
index 93c385382cb..aeef21484a5 100644
--- a/mod/lti/db/services.php
+++ b/mod/lti/db/services.php
@@ -154,6 +154,14 @@ $functions = array(
'ajax' => true
),
+ 'mod_lti_toggle_showinactivitychooser' => array(
+ 'classname' => 'mod_lti\external\toggle_showinactivitychooser',
+ 'description' => 'Toggle showinactivitychooser for a course tool type',
+ 'type' => 'write',
+ 'capabilities' => 'mod/lti:addcoursetool',
+ 'ajax' => true
+ ),
+
'mod_lti_is_cartridge' => array(
'classname' => 'mod_lti_external',
'methodname' => 'is_cartridge',
diff --git a/mod/lti/db/upgrade.php b/mod/lti/db/upgrade.php
index 46b8aa138d9..4a4c2ebd7e0 100644
--- a/mod/lti/db/upgrade.php
+++ b/mod/lti/db/upgrade.php
@@ -133,10 +133,31 @@ function xmldb_lti_upgrade($oldversion) {
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
-
// Lti savepoint reached.
upgrade_mod_savepoint(true, 2023070501, 'lti');
}
+ if ($oldversion < 2023081101) {
+ // Define communication table.
+ $table = new xmldb_table('lti_coursevisible');
+
+ // Adding fields to table lti_coursevisible.
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE);
+ $table->add_field('typeid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id');
+ $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'typeid');
+ $table->add_field('coursevisible', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'courseid');
+
+ // Add key.
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
+
+ // Conditionally launch create table for communication.
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+ // Lti savepoint reached.
+ upgrade_mod_savepoint(true, 2023081101, 'lti');
+ }
+
return true;
}
diff --git a/mod/lti/lang/en/lti.php b/mod/lti/lang/en/lti.php
index d74124d0d3b..7cf4420ddea 100644
--- a/mod/lti/lang/en/lti.php
+++ b/mod/lti/lang/en/lti.php
@@ -523,6 +523,8 @@ $string['share_roster_help'] = 'Specify whether the tool can access the list of
Note that this setting may be overridden in the tool configuration.';
$string['show_in_course_activity_chooser'] = 'Show in activity chooser and as a preconfigured tool';
+$string['showinactivitychooser'] = 'Show in activity chooser';
+$string['dontshowinactivitychooser'] = 'Don\'t show in activity chooser';
$string['show_in_course_lti1'] = 'Tool configuration usage';
$string['show_in_course_lti1_help'] = 'This tool may be shown in the activity chooser for a teacher to select to add to a course. Alternatively, it may be shown in the preconfigured tool drop-down menu when adding an external tool to a course. A further option is for the tool configuration to only be used if the exact tool URL is entered when adding an external tool to a course.';
$string['show_in_course_lti2'] = 'Tool configuration usage';
diff --git a/mod/lti/tests/behat/managecoursetools.feature b/mod/lti/tests/behat/managecoursetools.feature
index 6a50146f973..c51cf74e212 100644
--- a/mod/lti/tests/behat/managecoursetools.feature
+++ b/mod/lti/tests/behat/managecoursetools.feature
@@ -147,3 +147,136 @@ Feature: Manage course tools
And the field "Tool URL" matches value "http://www.example.com/lti/provider.php"
And the field "Icon URL" matches value "http://download.moodle.org/unittest/test.jpg"
And the field "Secure icon URL" matches value "https://download.moodle.org/unittest/test.jpg"
+
+ @javascript
+ Scenario: Site tool appearing in activity chooser according to settings
+ Given the following "mod_lti > tool types" exist:
+ | name | baseurl | coursevisible | state |
+ | Teaching Tool 1 | /mod/lti/tests/fixtures/tool_provider.php | 2 | 1 |
+ | Teaching Tool 2 | /mod/lti/tests/fixtures/tool_provider.php | 1 | 1 |
+ | Teaching Tool 3 | /mod/lti/tests/fixtures/tool_provider.php | 0 | 1 |
+ And the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 2 | C2 | 0 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | teacher1 | C2 | editingteacher |
+ And I log in as "teacher1"
+ And I am on "Course 1" course homepage with editing mode on
+ And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
+ And I should see "Teaching Tool 1" in the ".modal-body" "css_element"
+ And I should not see "Teaching Tool 2" in the ".modal-body" "css_element"
+ And I should not see "Teaching Tool 3" in the ".modal-body" "css_element"
+ And I click on "Close" "button" in the ".modal-dialog" "css_element"
+ And I navigate to "LTI External tools" in current page administration
+ And I should not see "Teaching Tool 3"
+ And I click on "Don't show in activity chooser" "field" in the "Teaching Tool 1" "table_row"
+ And I click on "Show in activity chooser" "field" in the "Teaching Tool 2" "table_row"
+ And I am on "Course 1" course homepage
+ And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
+ And I should not see "Teaching Tool 1" in the ".modal-body" "css_element"
+ And I should see "Teaching Tool 2" in the ".modal-body" "css_element"
+ And I should not see "Teaching Tool 3" in the ".modal-body" "css_element"
+ And I click on "Close" "button" in the ".modal-dialog" "css_element"
+
+ # Should not affect other courses.
+ And I am on "Course 2" course homepage
+ And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
+ And I should see "Teaching Tool 1" in the ".modal-body" "css_element"
+ And I should not see "Teaching Tool 2" in the ".modal-body" "css_element"
+ And I should not see "Teaching Tool 3" in the ".modal-body" "css_element"
+ And I click on "Close" "button" in the ".modal-dialog" "css_element"
+
+ And I am on "Course 1" course homepage
+ And I navigate to "LTI External tools" in current page administration
+ And I click on "Show in activity chooser" "field" in the "Teaching Tool 1" "table_row"
+ And I click on "Don't show in activity chooser" "field" in the "Teaching Tool 2" "table_row"
+ And I am on "Course 1" course homepage
+ And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
+ And I should see "Teaching Tool 1" in the ".modal-body" "css_element"
+ And I should not see "Teaching Tool 2" in the ".modal-body" "css_element"
+ And I should not see "Teaching Tool 3" in the ".modal-body" "css_element"
+
+ When the following "role capability" exists:
+ | role | editingteacher |
+ | mod/lti:addcoursetool | prohibit |
+ And I am on "Course 1" course homepage with editing mode on
+ And I navigate to "LTI External tools" in current page administration
+ Then the "Don't show in activity chooser" "field" should be disabled
+ And the "Show in activity chooser" "field" should be disabled
+
+ @javascript
+ Scenario: Course tool appearing in activity chooser according to settings
+ Given the following "mod_lti > course tools" exist:
+ | name | baseurl | course | coursevisible |
+ | Course Tool 1 | /mod/lti/tests/fixtures/tool_provider.php | C1 | 2 |
+ | Course Tool 2 | /mod/lti/tests/fixtures/tool_provider.php | C1 | 1 |
+ And I log in as "teacher1"
+ And I am on "Course 1" course homepage with editing mode on
+ And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
+ And I should see "Course Tool 1" in the ".modal-body" "css_element"
+ And I should not see "Course Tool 2" in the ".modal-body" "css_element"
+ And I click on "Close" "button" in the ".modal-dialog" "css_element"
+ And I navigate to "LTI External tools" in current page administration
+ And I click on "Don't show in activity chooser" "field" in the "Course Tool 1" "table_row"
+ And I click on "Show in activity chooser" "field" in the "Course Tool 2" "table_row"
+ And I am on "Course 1" course homepage
+ And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
+ And I should not see "Course Tool 1" in the ".modal-body" "css_element"
+ And I should see "Course Tool 2" in the ".modal-body" "css_element"
+ And I click on "Close" "button" in the ".modal-dialog" "css_element"
+ And I navigate to "LTI External tools" in current page administration
+ And I click on "Show in activity chooser" "field" in the "Course Tool 1" "table_row"
+ And I click on "Don't show in activity chooser" "field" in the "Course Tool 2" "table_row"
+ And I am on "Course 1" course homepage
+ And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
+ And I should see "Course Tool 1" in the ".modal-body" "css_element"
+ And I should not see "Course Tool 2" in the ".modal-body" "css_element"
+
+ When the following "role capability" exists:
+ | role | editingteacher |
+ | mod/lti:addcoursetool | prohibit |
+ And I am on "Course 1" course homepage with editing mode on
+ And I navigate to "LTI External tools" in current page administration
+ Then the "Don't show in activity chooser" "field" should be disabled
+ And the "Show in activity chooser" "field" should be disabled
+
+ @javascript
+ Scenario: Site and course tools settings are preserved when backup and restore
+ Given the following "mod_lti > tool types" exist:
+ | name | baseurl | coursevisible | state |
+ | Teaching Tool 1 | /mod/lti/tests/fixtures/tool_provider.php | 2 | 1 |
+ | Teaching Tool 2 | /mod/lti/tests/fixtures/tool_provider.php | 1 | 1 |
+ And the following "mod_lti > course tools" exist:
+ | name | description | baseurl | course |
+ | Course Tool 1 | Example description | https://example.com/tool | C1 |
+ And I log in as "admin"
+ And I am on "Course 1" course homepage with editing mode on
+ And I add a "Teaching Tool 1" to section "1"
+ And I set the field "Activity name" to "Test tool activity 1"
+ And I press "Save and return to course"
+ And I add a "Course Tool 1" to section "1"
+ And I set the field "Activity name" to "Course tool activity 1"
+ And I press "Save and return to course"
+ And I navigate to "LTI External tools" in current page administration
+ And I click on "Don't show in activity chooser" "field" in the "Teaching Tool 1" "table_row"
+ And I click on "Show in activity chooser" "field" in the "Teaching Tool 2" "table_row"
+ And I click on "Don't show in activity chooser" "field" in the "Course Tool 1" "table_row"
+ And I am on "Course 1" course homepage
+ And I add a "Teaching Tool 2" to section "1"
+ And I set the field "Activity name" to "Test tool activity 2"
+ And I press "Save and return to course"
+ When I backup "Course 1" course using this options:
+ | Confirmation | Filename | test_backup.mbz |
+ And I restore "test_backup.mbz" backup into a new course using this options:
+ | Schema | Course name | Restored course |
+ And I should see "Restored course"
+ And I click on "Add an activity or resource" "button" in the "Topic 1" "section"
+ Then I should not see "Teaching Tool 1" in the ".modal-body" "css_element"
+ And I should see "Teaching Tool 2" in the ".modal-body" "css_element"
+ And I should not see "Course Tool 2" in the ".modal-body" "css_element"
+ And I click on "Close" "button" in the ".modal-dialog" "css_element"
+ And I navigate to "LTI External tools" in current page administration
+ And I should see "Show in activity chooser" in the "Teaching Tool 1" "table_row"
+ And I should see "Don't show in activity chooser" in the "Teaching Tool 2" "table_row"
+ And I should see "Show in activity chooser" in the "Course Tool 1" "table_row"
diff --git a/mod/lti/tests/external/toggle_showinactivitychooser_test.php b/mod/lti/tests/external/toggle_showinactivitychooser_test.php
new file mode 100644
index 00000000000..650c82c555f
--- /dev/null
+++ b/mod/lti/tests/external/toggle_showinactivitychooser_test.php
@@ -0,0 +1,111 @@
+.
+
+namespace mod_lti\external;
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/mod/lti/tests/mod_lti_testcase.php');
+
+/**
+ * PHPUnit tests for toggle_showinactivitychooser external function.
+ *
+ * @package mod_lti
+ * @copyright 2023 Ilya Tregubov
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @coversDefaultClass \mod_lti\external\toggle_showinactivitychooser
+ */
+class toggle_showinactivitychooser_test extends \mod_lti_testcase {
+
+ /**
+ * Test toggle_showinactivitychooser for course tool.
+ * @covers ::execute
+ */
+ public function test_toggle_showinactivitychooser_course_tool() {
+ global $DB;
+ $this->resetAfterTest();
+
+ $course = $this->getDataGenerator()->create_course();
+ $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
+ $this->setUser($editingteacher);
+
+ $typeid = lti_add_type(
+ (object) [
+ 'state' => LTI_TOOL_STATE_CONFIGURED,
+ 'course' => $course->id,
+ 'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER
+ ],
+ (object) [
+ 'lti_typename' => "My course tool",
+ 'lti_toolurl' => 'http://example.com',
+ 'lti_ltiversion' => 'LTI-1p0',
+ 'lti_coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER
+ ]
+ );
+ toggle_showinactivitychooser::execute($typeid, $course->id, false);
+ $sql = "SELECT lt.coursevisible coursevisible1, ltc.value AS coursevisible2
+ FROM {lti_types} lt
+ LEFT JOIN {lti_types_config} ltc ON lt.id = ltc.typeid
+ WHERE lt.id = ?
+ AND ltc.name = 'coursevisible'";
+ $actual = $DB->get_record_sql($sql, [$typeid]);
+ $this->assertEquals(LTI_COURSEVISIBLE_PRECONFIGURED, $actual->coursevisible1);
+ $this->assertEquals(LTI_COURSEVISIBLE_PRECONFIGURED, $actual->coursevisible2);
+
+ toggle_showinactivitychooser::execute($typeid, $course->id, true);
+ $actual = $DB->get_record_sql($sql, [$typeid]);
+ $this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible1);
+ $this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible2);
+ }
+
+ /**
+ * Test toggle_showinactivitychooser for site tool.
+ * @covers ::execute
+ */
+ public function test_toggle_showinactivitychooser_site_tool() {
+ global $DB;
+
+ $this->resetAfterTest();
+
+ $course = $this->getDataGenerator()->create_course();
+ $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
+ $this->setUser($editingteacher);
+
+ $type = $this->generate_tool_type(123); // Creates a site tool.
+
+ toggle_showinactivitychooser::execute($type->id, $course->id, false);
+ $sql = "SELECT lt.coursevisible coursevisible1, ltc.value AS coursevisible2, lc.coursevisible AS coursevisible3
+ FROM {lti_types} lt
+ LEFT JOIN {lti_types_config} ltc ON lt.id = ltc.typeid
+ LEFT JOIN {lti_coursevisible} lc ON lt.id = lc.typeid
+ WHERE lt.id = ?
+ AND lc.courseid = ?
+ AND ltc.name = 'coursevisible'";
+ $actual = $DB->get_record_sql($sql, [$type->id, $course->id]);
+ $this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible1);
+ $this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible2);
+ $this->assertEquals(LTI_COURSEVISIBLE_PRECONFIGURED, $actual->coursevisible3);
+
+ toggle_showinactivitychooser::execute($type->id, $course->id, true);
+ $actual = $DB->get_record_sql($sql, [$type->id, $course->id]);
+ $this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible1);
+ $this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible2);
+ $this->assertEquals(LTI_COURSEVISIBLE_ACTIVITYCHOOSER, $actual->coursevisible3);
+ }
+
+}
diff --git a/mod/lti/tests/generator/lib.php b/mod/lti/tests/generator/lib.php
index d6837c0c30a..d4032b67588 100644
--- a/mod/lti/tests/generator/lib.php
+++ b/mod/lti/tests/generator/lib.php
@@ -151,13 +151,14 @@ class mod_lti_generator extends testing_module_generator {
}
$type['baseurl'] = (new moodle_url($type['baseurl']))->out(false); // Permits relative URLs in behat features.
- $type['coursevisible'] = LTI_COURSEVISIBLE_ACTIVITYCHOOSER; // The default for course tools.
+ $type['coursevisible'] = $type['coursevisible'] ?? LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
$type['state'] = LTI_TOOL_STATE_CONFIGURED; // The default for course tools.
// Sensible defaults permitting the tool type to be used in a launch.
$type['lti_acceptgrades'] = $type['lti_acceptgrades'] ?? LTI_SETTING_ALWAYS;
$type['lti_sendname'] = $type['lti_sendname'] ?? LTI_SETTING_ALWAYS;
$type['lti_sendemailaddr'] = $type['lti_sendemailaddr'] ?? LTI_SETTING_ALWAYS;
+ $type['lti_coursevisible'] = $type['coursevisible'] ?? LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
// Required for cartridge processing support.
$type['lti_toolurl'] = $type['baseurl'];
diff --git a/mod/lti/tests/local/types_helper_test.php b/mod/lti/tests/local/types_helper_test.php
index 30283f38299..7a6ab969ef7 100644
--- a/mod/lti/tests/local/types_helper_test.php
+++ b/mod/lti/tests/local/types_helper_test.php
@@ -69,12 +69,20 @@ class types_helper_test extends mod_lti_testcase {
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
$teacher2 = $this->getDataGenerator()->create_and_enrol($course2, 'editingteacher');
- // Create the following tool types for testing:
- // - Site tool configured as "Do not show" (LTI_COURSEVISIBLE_NO).
- // - Site tool configured as "Show as a preconfigured tool only" (LTI_COURSEVISIBLE_PRECONFIGURED).
- // - Site tool configured as "Show as a preconfigured tool and in the activity chooser" (LTI_COURSEVISIBLE_ACTIVITYCHOOSER).
- // - Course tool which, by default, is configured as LTI_COURSEVISIBLE_ACTIVITYCHOOSER).
- // - Site tool configured to "Show as a preconfigured tool and in the activity chooser" but restricted to a category.
+ /*
+ Create the following tool types for testing:
+ | tooltype | sitecoursevisible | coursecoursevisible | restrictedtocategory |
+ | site | LTI_COURSEVISIBLE_NO | | |
+ | site | LTI_COURSEVISIBLE_PRECONFIGURED | | |
+ | site | LTI_COURSEVISIBLE_PRECONFIGURED | LTI_COURSEVISIBLE_PRECONFIGURED | |
+ | site | LTI_COURSEVISIBLE_PRECONFIGURED | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | |
+ | site | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | | |
+ | site | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | |
+ | site | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | LTI_COURSEVISIBLE_PRECONFIGURED | |
+ | site | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | yes |
+ | course | LTI_COURSEVISIBLE_ACTIVITYCHOOSER | | |
+ | course | LTI_COURSEVISIBLE_PRECONFIGURED | | |
+ */
/** @var \mod_lti_generator $ltigenerator */
$ltigenerator = $this->getDataGenerator()->get_plugin_generator('mod_lti');
@@ -108,41 +116,129 @@ class types_helper_test extends mod_lti_testcase {
'state' => LTI_TOOL_STATE_CONFIGURED,
'lti_coursecategories' => $coursecat2->id
]);
+ $ltigenerator->create_tool_types([
+ 'name' => 'site tool preconfigured only, overridden to preconfigured only in course',
+ 'baseurl' => 'http://example.com/tool/6',
+ 'coursevisible' => LTI_COURSEVISIBLE_PRECONFIGURED,
+ 'state' => LTI_TOOL_STATE_CONFIGURED
+ ]);
+ $tool = $DB->get_record('lti_types',
+ ['name' => 'site tool preconfigured only, overridden to preconfigured only in course']);
+ $record = new \stdClass();
+ $record->typeid = $tool->id;
+ $record->courseid = $course->id;
+ $record->coursevisible = LTI_COURSEVISIBLE_PRECONFIGURED;
+ $DB->insert_record('lti_coursevisible', $record);
+
+ $ltigenerator->create_tool_types([
+ 'name' => 'site tool preconfigured only, overridden to activity chooser in course',
+ 'baseurl' => 'http://example.com/tool/7',
+ 'coursevisible' => LTI_COURSEVISIBLE_PRECONFIGURED,
+ 'state' => LTI_TOOL_STATE_CONFIGURED
+ ]);
+ $tool = $DB->get_record('lti_types', ['name' => 'site tool preconfigured only, overridden to activity chooser in course']);
+ $record = new \stdClass();
+ $record->typeid = $tool->id;
+ $record->courseid = $course->id;
+ $record->coursevisible = LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
+ $DB->insert_record('lti_coursevisible', $record);
+
+ $ltigenerator->create_tool_types([
+ 'name' => 'site tool preconfigured and activity chooser, overridden to activity chooser in course',
+ 'baseurl' => 'http://example.com/tool/8',
+ 'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER,
+ 'state' => LTI_TOOL_STATE_CONFIGURED
+ ]);
+ $tool = $DB->get_record('lti_types',
+ ['name' => 'site tool preconfigured and activity chooser, overridden to activity chooser in course']);
+ $record = new \stdClass();
+ $record->typeid = $tool->id;
+ $record->courseid = $course->id;
+ $record->coursevisible = LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
+ $DB->insert_record('lti_coursevisible', $record);
+
+ $ltigenerator->create_tool_types([
+ 'name' => 'site tool preconfigured and activity chooser, overridden to preconfigured in course',
+ 'baseurl' => 'http://example.com/tool/9',
+ 'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER,
+ 'state' => LTI_TOOL_STATE_CONFIGURED
+ ]);
+ $tool = $DB->get_record('lti_types',
+ ['name' => 'site tool preconfigured and activity chooser, overridden to preconfigured in course']);
+ $record = new \stdClass();
+ $record->typeid = $tool->id;
+ $record->courseid = $course->id;
+ $record->coursevisible = LTI_COURSEVISIBLE_PRECONFIGURED;
+ $DB->insert_record('lti_coursevisible', $record);
+
+ $ltigenerator->create_course_tool_types([
+ 'name' => 'course tool preconfigured',
+ 'baseurl' => 'http://example.com/tool/91',
+ 'course' => $course->id,
+ 'coursevisible' => LTI_COURSEVISIBLE_PRECONFIGURED
+ ]);
// Request using the default 'coursevisible' param will include all tools except the one configured as "Do not show" and
// the tool restricted to category 2.
$coursetooltypes = types_helper::get_lti_types_by_course($course->id, $teacher->id);
- $this->assertCount(3, $coursetooltypes);
- $this->assertEmpty(array_diff(
- ['http://example.com/tool/2', 'http://example.com/tool/3', 'http://example.com/tool/4'],
- array_column($coursetooltypes, 'baseurl')
- ));
+ $this->assertCount(8, $coursetooltypes);
+ $this->assertEmpty(array_diff([
+ 'http://example.com/tool/2',
+ 'http://example.com/tool/3',
+ 'http://example.com/tool/4',
+ 'http://example.com/tool/6',
+ 'http://example.com/tool/7',
+ 'http://example.com/tool/8',
+ 'http://example.com/tool/9',
+ 'http://example.com/tool/91',
+ ], array_column($coursetooltypes, 'baseurl')));
// Request for only those tools configured to show in the activity chooser for the teacher.
$coursetooltypes = types_helper::get_lti_types_by_course($course->id, $teacher->id,
[LTI_COURSEVISIBLE_ACTIVITYCHOOSER]);
- $this->assertCount(2, $coursetooltypes);
- $this->assertEmpty(array_diff(
- ['http://example.com/tool/3', 'http://example.com/tool/4'],
- array_column($coursetooltypes, 'baseurl')
- ));
+ $this->assertCount(4, $coursetooltypes);
+ $expected = [
+ 'http://example.com/tool/3',
+ 'http://example.com/tool/4',
+ 'http://example.com/tool/7',
+ 'http://example.com/tool/8'
+ ];
+ sort($expected);
+ $actual = array_column($coursetooltypes, 'baseurl');
+ sort($actual);
+ $this->assertEquals($expected, $actual);
// Request for only those tools configured to show as a preconfigured tool for the teacher.
$coursetooltypes = types_helper::get_lti_types_by_course($course->id, $teacher->id,
[LTI_COURSEVISIBLE_PRECONFIGURED]);
- $this->assertCount(1, $coursetooltypes);
- $this->assertEmpty(array_diff(
- ['http://example.com/tool/2'],
- array_column($coursetooltypes, 'baseurl')
- ));
+ $this->assertCount(4, $coursetooltypes);
+ $expected = [
+ 'http://example.com/tool/2',
+ 'http://example.com/tool/6',
+ 'http://example.com/tool/9',
+ 'http://example.com/tool/91'
+ ];
+ sort($expected);
+ $actual = array_column($coursetooltypes, 'baseurl');
+ sort($actual);
+ $this->assertEquals($expected, $actual);
// Request for teacher2 in course2 (course category 2).
$coursetooltypes = types_helper::get_lti_types_by_course($course2->id, $teacher2->id);
- $this->assertCount(3, $coursetooltypes);
- $this->assertEmpty(array_diff(
- ['http://example.com/tool/2', 'http://example.com/tool/3', 'http://example.com/tool/5'],
- array_column($coursetooltypes, 'baseurl')
- ));
+ $this->assertCount(7, $coursetooltypes);
+ $expected = [
+ 'http://example.com/tool/2',
+ 'http://example.com/tool/3',
+ 'http://example.com/tool/5',
+ 'http://example.com/tool/6',
+ 'http://example.com/tool/7',
+ 'http://example.com/tool/8',
+ 'http://example.com/tool/9'
+ ];
+ sort($expected);
+ $actual = array_column($coursetooltypes, 'baseurl');
+ sort($actual);
+ $this->assertEquals($expected, $actual);
// Request for a teacher who cannot use preconfigured tools in the course.
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
diff --git a/mod/lti/tests/mod_lti_testcase.php b/mod/lti/tests/mod_lti_testcase.php
index cdd82f866c7..9fe7b76d441 100644
--- a/mod/lti/tests/mod_lti_testcase.php
+++ b/mod/lti/tests/mod_lti_testcase.php
@@ -48,8 +48,11 @@ abstract class mod_lti_testcase extends externallib_advanced_testcase {
$type->description = "Example description $uniqueid";
$type->toolproxyid = $toolproxyid;
$type->baseurl = $this->getExternalTestFileUrl("/test$uniqueid.html");
+ $type->coursevisible = LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
+ $config = new stdClass();
+ $config->lti_coursevisible = LTI_COURSEVISIBLE_ACTIVITYCHOOSER;
- $type->id = lti_add_type($type, new stdClass());
+ $type->id = lti_add_type($type, $config);
return $type;
}
diff --git a/mod/lti/version.php b/mod/lti/version.php
index 542dcd5b45d..9ef820be421 100644
--- a/mod/lti/version.php
+++ b/mod/lti/version.php
@@ -48,7 +48,7 @@
defined('MOODLE_INTERNAL') || die;
-$plugin->version = 2023081100; // The current module version (Date: YYYYMMDDXX).
+$plugin->version = 2023081101; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2023041800; // Requires this Moodle version.
$plugin->component = 'mod_lti'; // Full name of the plugin (used for diagnostics).
$plugin->cron = 0;