From 302a5e43a6455897af8beca103e618d9e88ce997 Mon Sep 17 00:00:00 2001 From: Ferran Recio Date: Thu, 10 Nov 2022 16:37:11 +0100 Subject: [PATCH] MDL-74987 core_courseformat: alt to duplicate modules If a user drags an activity and press the ALT key, the activity will be duplicated in the new place instead of simply moved. This behaviour is quite normal in many softwares and it is a non intrusive usability improvement. --- .../amd/build/local/courseeditor/dndcmitem.min.js | 2 +- .../build/local/courseeditor/dndcmitem.min.js.map | 2 +- .../amd/build/local/courseeditor/dndsection.min.js | 2 +- .../build/local/courseeditor/dndsection.min.js.map | 2 +- .../build/local/courseeditor/dndsectionitem.min.js | 2 +- .../local/courseeditor/dndsectionitem.min.js.map | 2 +- .../amd/build/local/courseeditor/mutations.min.js | 2 +- .../build/local/courseeditor/mutations.min.js.map | 2 +- .../format/amd/src/local/courseeditor/dndcmitem.js | 6 ++++-- .../format/amd/src/local/courseeditor/dndsection.js | 6 ++++-- .../amd/src/local/courseeditor/dndsectionitem.js | 6 ++++-- .../format/amd/src/local/courseeditor/mutations.js | 5 +++-- course/format/classes/stateactions.php | 13 ++++++++++--- 13 files changed, 33 insertions(+), 19 deletions(-) diff --git a/course/format/amd/build/local/courseeditor/dndcmitem.min.js b/course/format/amd/build/local/courseeditor/dndcmitem.min.js index 3f489ccffd7..f079c4a46b0 100644 --- a/course/format/amd/build/local/courseeditor/dndcmitem.min.js +++ b/course/format/amd/build/local/courseeditor/dndcmitem.min.js @@ -10,6 +10,6 @@ define("core_courseformat/local/courseeditor/dndcmitem",["exports","core/reactiv * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class _default extends _reactive.BaseComponent{configDragDrop(cmid){this.id=cmid,this.reactive.isEditing&&this.reactive.supportComponents&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}dragStart(dropdata){this.reactive.dispatch("cmDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("cmDrag",[dropdata.id],!1)}getDraggableData(){return this.reactive.getExporter().cmDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){return"cm"===(null==dropdata?void 0:dropdata.type)}showDropZone(dropdata){dropdata.nextcmid!=this.id&&dropdata.id!=this.id&&this.element.classList.add(this.classes.DROPUP)}hideDropZone(){this.element.classList.remove(this.classes.DROPUP)}drop(dropdata){dropdata.id!=this.id&&dropdata.nextcmid!=this.id&&this.reactive.dispatch("cmMove",[dropdata.id],null,this.id)}}return _exports.default=_default,_exports.default})); +class _default extends _reactive.BaseComponent{configDragDrop(cmid){this.id=cmid,this.reactive.isEditing&&this.reactive.supportComponents&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}dragStart(dropdata){this.reactive.dispatch("cmDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("cmDrag",[dropdata.id],!1)}getDraggableData(){return this.reactive.getExporter().cmDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){return"cm"===(null==dropdata?void 0:dropdata.type)}showDropZone(dropdata){dropdata.nextcmid!=this.id&&dropdata.id!=this.id&&this.element.classList.add(this.classes.DROPUP)}hideDropZone(){this.element.classList.remove(this.classes.DROPUP)}drop(dropdata,event){if(dropdata.id!=this.id&&dropdata.nextcmid!=this.id){const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],null,this.id)}}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=dndcmitem.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndcmitem.min.js.map b/course/format/amd/build/local/courseeditor/dndcmitem.min.js.map index b68056b21e4..dad9c2135b2 100644 --- a/course/format/amd/build/local/courseeditor/dndcmitem.min.js.map +++ b/course/format/amd/build/local/courseeditor/dndcmitem.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dndcmitem.min.js","sources":["../../../src/local/courseeditor/dndcmitem.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 index cm component.\n *\n * This component is used to control specific course modules interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndcmitem\n * @class core_courseformat/local/courseeditor/dndcmitem\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Configure the component drag and drop.\n *\n * @param {number} cmid course module id\n */\n configDragDrop(cmid) {\n\n this.id = cmid;\n\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init element drag and drop.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n // Drag and drop methods.\n\n /**\n * The element drop start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragStart(dropdata) {\n this.reactive.dispatch('cmDrag', [dropdata.id], true);\n }\n\n /**\n * The element drop end hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragEnd(dropdata) {\n this.reactive.dispatch('cmDrag', [dropdata.id], false);\n }\n\n /**\n * Get the draggable data of this component.\n *\n * @returns {Object} exported course module drop data\n */\n getDraggableData() {\n const exporter = this.reactive.getExporter();\n return exporter.cmDraggableData(this.reactive.state, this.id);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n return dropdata?.type === 'cm';\n }\n\n /**\n * Display the component dropzone.\n *\n * @param {Object} dropdata the accepted drop data\n */\n showDropZone(dropdata) {\n // If we are the next cmid of the dragged element we accept the drop because otherwise it\n // will get captured by the section. However, we won't trigger any mutation.\n if (dropdata.nextcmid != this.id && dropdata.id != this.id) {\n this.element.classList.add(this.classes.DROPUP);\n }\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.element.classList.remove(this.classes.DROPUP);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n */\n drop(dropdata) {\n // Call the move mutation if necessary.\n if (dropdata.id != this.id && dropdata.nextcmid != this.id) {\n this.reactive.dispatch('cmMove', [dropdata.id], null, this.id);\n }\n }\n\n}\n"],"names":["BaseComponent","configDragDrop","cmid","id","this","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","destroy","undefined","unregister","dragStart","dropdata","dispatch","dragEnd","getDraggableData","getExporter","cmDraggableData","state","validateDropData","type","showDropZone","nextcmid","element","classList","add","DROPUP","hideDropZone","remove","drop"],"mappings":";;;;;;;;;;;;uBA6B6BA,wBAOzBC,eAAeC,WAENC,GAAKD,KAGNE,KAAKC,SAASC,WAAaF,KAAKC,SAASE,yBAEpCC,SAAW,IAAIC,mBAASL,WAExBM,QAAUN,KAAKI,SAASG,cAOrCC,eAC0BC,IAAlBT,KAAKI,eACAA,SAASM,aAWtBC,UAAUC,eACDX,SAASY,SAAS,SAAU,CAACD,SAASb,KAAK,GAQpDe,QAAQF,eACCX,SAASY,SAAS,SAAU,CAACD,SAASb,KAAK,GAQpDgB,0BACqBf,KAAKC,SAASe,cACfC,gBAAgBjB,KAAKC,SAASiB,MAAOlB,KAAKD,IAS9DoB,iBAAiBP,gBACa,QAAnBA,MAAAA,gBAAAA,SAAUQ,MAQrBC,aAAaT,UAGLA,SAASU,UAAYtB,KAAKD,IAAMa,SAASb,IAAMC,KAAKD,SAC/CwB,QAAQC,UAAUC,IAAIzB,KAAKM,QAAQoB,QAOhDC,oBACSJ,QAAQC,UAAUI,OAAO5B,KAAKM,QAAQoB,QAQ/CG,KAAKjB,UAEGA,SAASb,IAAMC,KAAKD,IAAMa,SAASU,UAAYtB,KAAKD,SAC/CE,SAASY,SAAS,SAAU,CAACD,SAASb,IAAK,KAAMC,KAAKD"} \ No newline at end of file +{"version":3,"file":"dndcmitem.min.js","sources":["../../../src/local/courseeditor/dndcmitem.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 index cm component.\n *\n * This component is used to control specific course modules interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndcmitem\n * @class core_courseformat/local/courseeditor/dndcmitem\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Configure the component drag and drop.\n *\n * @param {number} cmid course module id\n */\n configDragDrop(cmid) {\n\n this.id = cmid;\n\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init element drag and drop.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n // Drag and drop methods.\n\n /**\n * The element drop start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragStart(dropdata) {\n this.reactive.dispatch('cmDrag', [dropdata.id], true);\n }\n\n /**\n * The element drop end hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragEnd(dropdata) {\n this.reactive.dispatch('cmDrag', [dropdata.id], false);\n }\n\n /**\n * Get the draggable data of this component.\n *\n * @returns {Object} exported course module drop data\n */\n getDraggableData() {\n const exporter = this.reactive.getExporter();\n return exporter.cmDraggableData(this.reactive.state, this.id);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n return dropdata?.type === 'cm';\n }\n\n /**\n * Display the component dropzone.\n *\n * @param {Object} dropdata the accepted drop data\n */\n showDropZone(dropdata) {\n // If we are the next cmid of the dragged element we accept the drop because otherwise it\n // will get captured by the section. However, we won't trigger any mutation.\n if (dropdata.nextcmid != this.id && dropdata.id != this.id) {\n this.element.classList.add(this.classes.DROPUP);\n }\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.element.classList.remove(this.classes.DROPUP);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n * @param {Event} event the drop event\n */\n drop(dropdata, event) {\n // Call the move mutation if necessary.\n if (dropdata.id != this.id && dropdata.nextcmid != this.id) {\n const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';\n this.reactive.dispatch(mutation, [dropdata.id], null, this.id);\n }\n }\n\n}\n"],"names":["BaseComponent","configDragDrop","cmid","id","this","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","destroy","undefined","unregister","dragStart","dropdata","dispatch","dragEnd","getDraggableData","getExporter","cmDraggableData","state","validateDropData","type","showDropZone","nextcmid","element","classList","add","DROPUP","hideDropZone","remove","drop","event","mutation","altKey"],"mappings":";;;;;;;;;;;;uBA6B6BA,wBAOzBC,eAAeC,WAENC,GAAKD,KAGNE,KAAKC,SAASC,WAAaF,KAAKC,SAASE,yBAEpCC,SAAW,IAAIC,mBAASL,WAExBM,QAAUN,KAAKI,SAASG,cAOrCC,eAC0BC,IAAlBT,KAAKI,eACAA,SAASM,aAWtBC,UAAUC,eACDX,SAASY,SAAS,SAAU,CAACD,SAASb,KAAK,GAQpDe,QAAQF,eACCX,SAASY,SAAS,SAAU,CAACD,SAASb,KAAK,GAQpDgB,0BACqBf,KAAKC,SAASe,cACfC,gBAAgBjB,KAAKC,SAASiB,MAAOlB,KAAKD,IAS9DoB,iBAAiBP,gBACa,QAAnBA,MAAAA,gBAAAA,SAAUQ,MAQrBC,aAAaT,UAGLA,SAASU,UAAYtB,KAAKD,IAAMa,SAASb,IAAMC,KAAKD,SAC/CwB,QAAQC,UAAUC,IAAIzB,KAAKM,QAAQoB,QAOhDC,oBACSJ,QAAQC,UAAUI,OAAO5B,KAAKM,QAAQoB,QAS/CG,KAAKjB,SAAUkB,UAEPlB,SAASb,IAAMC,KAAKD,IAAMa,SAASU,UAAYtB,KAAKD,GAAI,OAClDgC,SAAYD,MAAME,OAAU,cAAgB,cAC7C/B,SAASY,SAASkB,SAAU,CAACnB,SAASb,IAAK,KAAMC,KAAKD"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndsection.min.js b/course/format/amd/build/local/courseeditor/dndsection.min.js index eda7377498b..a074ee75e74 100644 --- a/course/format/amd/build/local/courseeditor/dndsection.min.js +++ b/course/format/amd/build/local/courseeditor/dndsection.min.js @@ -10,6 +10,6 @@ define("core_courseformat/local/courseeditor/dndsection",["exports","core/reacti * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class _default extends _reactive.BaseComponent{configState(state){this.id=this.element.dataset.id,this.section=state.section.get(this.id),this.course=state.course}configDragDrop(sectionitem){this.reactive.isEditing&&this.reactive.supportComponents&&(this.sectionitem=sectionitem,this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.sectionitem&&this.sectionitem.unregister(),void 0!==this.dragdrop&&this.dragdrop.unregister()}getLastCm(){return null}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type))return!0;if("section"===(null==dropdata?void 0:dropdata.type)){const sectionzeroid=this.course.sectionlist[0];return(null==dropdata?void 0:dropdata.id)!=this.id&&(null==dropdata?void 0:dropdata.id)!=sectionzeroid&&this.id!=sectionzeroid}return!1}showDropZone(dropdata){var _this$getLastCm;"cm"==dropdata.type&&(null===(_this$getLastCm=this.getLastCm())||void 0===_this$getLastCm||_this$getLastCm.classList.add(this.classes.DROPDOWN));"section"==dropdata.type&&(this.section.number>dropdata.number?(this.element.classList.remove(this.classes.DROPUP),this.element.classList.add(this.classes.DROPDOWN)):(this.element.classList.add(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN)))}hideDropZone(){var _this$getLastCm2;null===(_this$getLastCm2=this.getLastCm())||void 0===_this$getLastCm2||_this$getLastCm2.classList.remove(this.classes.DROPDOWN),this.element.classList.remove(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN)}drop(dropdata){"cm"==dropdata.type&&this.reactive.dispatch("cmMove",[dropdata.id],this.id),"section"==dropdata.type&&this.reactive.dispatch("sectionMove",[dropdata.id],this.id)}}return _exports.default=_default,_exports.default})); +class _default extends _reactive.BaseComponent{configState(state){this.id=this.element.dataset.id,this.section=state.section.get(this.id),this.course=state.course}configDragDrop(sectionitem){this.reactive.isEditing&&this.reactive.supportComponents&&(this.sectionitem=sectionitem,this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.sectionitem&&this.sectionitem.unregister(),void 0!==this.dragdrop&&this.dragdrop.unregister()}getLastCm(){return null}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type))return!0;if("section"===(null==dropdata?void 0:dropdata.type)){const sectionzeroid=this.course.sectionlist[0];return(null==dropdata?void 0:dropdata.id)!=this.id&&(null==dropdata?void 0:dropdata.id)!=sectionzeroid&&this.id!=sectionzeroid}return!1}showDropZone(dropdata){var _this$getLastCm;"cm"==dropdata.type&&(null===(_this$getLastCm=this.getLastCm())||void 0===_this$getLastCm||_this$getLastCm.classList.add(this.classes.DROPDOWN));"section"==dropdata.type&&(this.section.number>dropdata.number?(this.element.classList.remove(this.classes.DROPUP),this.element.classList.add(this.classes.DROPDOWN)):(this.element.classList.add(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN)))}hideDropZone(){var _this$getLastCm2;null===(_this$getLastCm2=this.getLastCm())||void 0===_this$getLastCm2||_this$getLastCm2.classList.remove(this.classes.DROPDOWN),this.element.classList.remove(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN)}drop(dropdata,event){if("cm"==dropdata.type){const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],this.id)}"section"==dropdata.type&&this.reactive.dispatch("sectionMove",[dropdata.id],this.id)}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=dndsection.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndsection.min.js.map b/course/format/amd/build/local/courseeditor/dndsection.min.js.map index 522ed6b89f0..44bcb9eb2b6 100644 --- a/course/format/amd/build/local/courseeditor/dndsection.min.js.map +++ b/course/format/amd/build/local/courseeditor/dndsection.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dndsection.min.js","sources":["../../../src/local/courseeditor/dndsection.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 index section component.\n *\n * This component is used to control specific course section interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndsection\n * @class core_courseformat/local/courseeditor/dndsection\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Save some values form the state.\n *\n * @param {Object} state the current state\n */\n configState(state) {\n this.id = this.element.dataset.id;\n this.section = state.section.get(this.id);\n this.course = state.course;\n }\n\n /**\n * Register state values and the drag and drop subcomponent.\n *\n * @param {BaseComponent} sectionitem section item component\n */\n configDragDrop(sectionitem) {\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the inner dragable element.\n this.sectionitem = sectionitem;\n // Init the dropzone.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.sectionitem !== undefined) {\n this.sectionitem.unregister();\n }\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n /**\n * Get the last CM element of that section.\n *\n * @returns {element|null} the las course module element of the section.\n */\n getLastCm() {\n return null;\n }\n\n // Drag and drop methods.\n\n /**\n * The element drop start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragStart(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], true);\n }\n\n /**\n * The element drop end hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragEnd(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], false);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n // We accept any course module.\n if (dropdata?.type === 'cm') {\n return true;\n }\n // We accept any section but the section 0 or ourself\n if (dropdata?.type === 'section') {\n const sectionzeroid = this.course.sectionlist[0];\n return dropdata?.id != this.id && dropdata?.id != sectionzeroid && this.id != sectionzeroid;\n }\n return false;\n }\n\n /**\n * Display the component dropzone.\n *\n * @param {Object} dropdata the accepted drop data\n */\n showDropZone(dropdata) {\n if (dropdata.type == 'cm') {\n this.getLastCm()?.classList.add(this.classes.DROPDOWN);\n }\n if (dropdata.type == 'section') {\n // The relative move of section depends on the section number.\n if (this.section.number > dropdata.number) {\n this.element.classList.remove(this.classes.DROPUP);\n this.element.classList.add(this.classes.DROPDOWN);\n } else {\n this.element.classList.add(this.classes.DROPUP);\n this.element.classList.remove(this.classes.DROPDOWN);\n }\n }\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.getLastCm()?.classList.remove(this.classes.DROPDOWN);\n this.element.classList.remove(this.classes.DROPUP);\n this.element.classList.remove(this.classes.DROPDOWN);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n */\n drop(dropdata) {\n // Call the move mutation.\n if (dropdata.type == 'cm') {\n this.reactive.dispatch('cmMove', [dropdata.id], this.id);\n }\n if (dropdata.type == 'section') {\n this.reactive.dispatch('sectionMove', [dropdata.id], this.id);\n }\n }\n}\n"],"names":["BaseComponent","configState","state","id","this","element","dataset","section","get","course","configDragDrop","sectionitem","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","destroy","undefined","unregister","getLastCm","dragStart","dropdata","dispatch","dragEnd","validateDropData","type","sectionzeroid","sectionlist","showDropZone","classList","add","DROPDOWN","number","remove","DROPUP","hideDropZone","drop"],"mappings":";;;;;;;;;;;;uBA6B6BA,wBAOzBC,YAAYC,YACHC,GAAKC,KAAKC,QAAQC,QAAQH,QAC1BI,QAAUL,MAAMK,QAAQC,IAAIJ,KAAKD,SACjCM,OAASP,MAAMO,OAQxBC,eAAeC,aAEPP,KAAKQ,SAASC,WAAaT,KAAKQ,SAASE,yBAEpCH,YAAcA,iBAEdI,SAAW,IAAIC,mBAASZ,WAExBa,QAAUb,KAAKW,SAASG,cAOrCC,eAC6BC,IAArBhB,KAAKO,kBACAA,YAAYU,kBAECD,IAAlBhB,KAAKW,eACAA,SAASM,aAStBC,mBACW,KAUXC,UAAUC,eACDZ,SAASa,SAAS,cAAe,CAACD,SAASrB,KAAK,GAQzDuB,QAAQF,eACCZ,SAASa,SAAS,cAAe,CAACD,SAASrB,KAAK,GASzDwB,iBAAiBH,aAEU,QAAnBA,MAAAA,gBAAAA,SAAUI,aACH,KAGY,aAAnBJ,MAAAA,gBAAAA,SAAUI,MAAoB,OACxBC,cAAgBzB,KAAKK,OAAOqB,YAAY,UACvCN,MAAAA,gBAAAA,SAAUrB,KAAMC,KAAKD,KAAMqB,MAAAA,gBAAAA,SAAUrB,KAAM0B,eAAiBzB,KAAKD,IAAM0B,qBAE3E,EAQXE,aAAaP,8BACY,MAAjBA,SAASI,oCACJN,wDAAaU,UAAUC,IAAI7B,KAAKa,QAAQiB,WAE5B,WAAjBV,SAASI,OAELxB,KAAKG,QAAQ4B,OAASX,SAASW,aAC1B9B,QAAQ2B,UAAUI,OAAOhC,KAAKa,QAAQoB,aACtChC,QAAQ2B,UAAUC,IAAI7B,KAAKa,QAAQiB,iBAEnC7B,QAAQ2B,UAAUC,IAAI7B,KAAKa,QAAQoB,aACnChC,QAAQ2B,UAAUI,OAAOhC,KAAKa,QAAQiB,YAQvDI,kEACShB,0DAAaU,UAAUI,OAAOhC,KAAKa,QAAQiB,eAC3C7B,QAAQ2B,UAAUI,OAAOhC,KAAKa,QAAQoB,aACtChC,QAAQ2B,UAAUI,OAAOhC,KAAKa,QAAQiB,UAQ/CK,KAAKf,UAEoB,MAAjBA,SAASI,WACJhB,SAASa,SAAS,SAAU,CAACD,SAASrB,IAAKC,KAAKD,IAEpC,WAAjBqB,SAASI,WACJhB,SAASa,SAAS,cAAe,CAACD,SAASrB,IAAKC,KAAKD"} \ No newline at end of file +{"version":3,"file":"dndsection.min.js","sources":["../../../src/local/courseeditor/dndsection.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 index section component.\n *\n * This component is used to control specific course section interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndsection\n * @class core_courseformat/local/courseeditor/dndsection\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Save some values form the state.\n *\n * @param {Object} state the current state\n */\n configState(state) {\n this.id = this.element.dataset.id;\n this.section = state.section.get(this.id);\n this.course = state.course;\n }\n\n /**\n * Register state values and the drag and drop subcomponent.\n *\n * @param {BaseComponent} sectionitem section item component\n */\n configDragDrop(sectionitem) {\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the inner dragable element.\n this.sectionitem = sectionitem;\n // Init the dropzone.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.sectionitem !== undefined) {\n this.sectionitem.unregister();\n }\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n /**\n * Get the last CM element of that section.\n *\n * @returns {element|null} the las course module element of the section.\n */\n getLastCm() {\n return null;\n }\n\n // Drag and drop methods.\n\n /**\n * The element drop start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragStart(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], true);\n }\n\n /**\n * The element drop end hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragEnd(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], false);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n // We accept any course module.\n if (dropdata?.type === 'cm') {\n return true;\n }\n // We accept any section but the section 0 or ourself\n if (dropdata?.type === 'section') {\n const sectionzeroid = this.course.sectionlist[0];\n return dropdata?.id != this.id && dropdata?.id != sectionzeroid && this.id != sectionzeroid;\n }\n return false;\n }\n\n /**\n * Display the component dropzone.\n *\n * @param {Object} dropdata the accepted drop data\n */\n showDropZone(dropdata) {\n if (dropdata.type == 'cm') {\n this.getLastCm()?.classList.add(this.classes.DROPDOWN);\n }\n if (dropdata.type == 'section') {\n // The relative move of section depends on the section number.\n if (this.section.number > dropdata.number) {\n this.element.classList.remove(this.classes.DROPUP);\n this.element.classList.add(this.classes.DROPDOWN);\n } else {\n this.element.classList.add(this.classes.DROPUP);\n this.element.classList.remove(this.classes.DROPDOWN);\n }\n }\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.getLastCm()?.classList.remove(this.classes.DROPDOWN);\n this.element.classList.remove(this.classes.DROPUP);\n this.element.classList.remove(this.classes.DROPDOWN);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n * @param {Event} event the drop event\n */\n drop(dropdata, event) {\n // Call the move mutation.\n if (dropdata.type == 'cm') {\n const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';\n this.reactive.dispatch(mutation, [dropdata.id], this.id);\n }\n if (dropdata.type == 'section') {\n this.reactive.dispatch('sectionMove', [dropdata.id], this.id);\n }\n }\n}\n"],"names":["BaseComponent","configState","state","id","this","element","dataset","section","get","course","configDragDrop","sectionitem","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","destroy","undefined","unregister","getLastCm","dragStart","dropdata","dispatch","dragEnd","validateDropData","type","sectionzeroid","sectionlist","showDropZone","classList","add","DROPDOWN","number","remove","DROPUP","hideDropZone","drop","event","mutation","altKey"],"mappings":";;;;;;;;;;;;uBA6B6BA,wBAOzBC,YAAYC,YACHC,GAAKC,KAAKC,QAAQC,QAAQH,QAC1BI,QAAUL,MAAMK,QAAQC,IAAIJ,KAAKD,SACjCM,OAASP,MAAMO,OAQxBC,eAAeC,aAEPP,KAAKQ,SAASC,WAAaT,KAAKQ,SAASE,yBAEpCH,YAAcA,iBAEdI,SAAW,IAAIC,mBAASZ,WAExBa,QAAUb,KAAKW,SAASG,cAOrCC,eAC6BC,IAArBhB,KAAKO,kBACAA,YAAYU,kBAECD,IAAlBhB,KAAKW,eACAA,SAASM,aAStBC,mBACW,KAUXC,UAAUC,eACDZ,SAASa,SAAS,cAAe,CAACD,SAASrB,KAAK,GAQzDuB,QAAQF,eACCZ,SAASa,SAAS,cAAe,CAACD,SAASrB,KAAK,GASzDwB,iBAAiBH,aAEU,QAAnBA,MAAAA,gBAAAA,SAAUI,aACH,KAGY,aAAnBJ,MAAAA,gBAAAA,SAAUI,MAAoB,OACxBC,cAAgBzB,KAAKK,OAAOqB,YAAY,UACvCN,MAAAA,gBAAAA,SAAUrB,KAAMC,KAAKD,KAAMqB,MAAAA,gBAAAA,SAAUrB,KAAM0B,eAAiBzB,KAAKD,IAAM0B,qBAE3E,EAQXE,aAAaP,8BACY,MAAjBA,SAASI,oCACJN,wDAAaU,UAAUC,IAAI7B,KAAKa,QAAQiB,WAE5B,WAAjBV,SAASI,OAELxB,KAAKG,QAAQ4B,OAASX,SAASW,aAC1B9B,QAAQ2B,UAAUI,OAAOhC,KAAKa,QAAQoB,aACtChC,QAAQ2B,UAAUC,IAAI7B,KAAKa,QAAQiB,iBAEnC7B,QAAQ2B,UAAUC,IAAI7B,KAAKa,QAAQoB,aACnChC,QAAQ2B,UAAUI,OAAOhC,KAAKa,QAAQiB,YAQvDI,kEACShB,0DAAaU,UAAUI,OAAOhC,KAAKa,QAAQiB,eAC3C7B,QAAQ2B,UAAUI,OAAOhC,KAAKa,QAAQoB,aACtChC,QAAQ2B,UAAUI,OAAOhC,KAAKa,QAAQiB,UAS/CK,KAAKf,SAAUgB,UAEU,MAAjBhB,SAASI,KAAc,OACjBa,SAAYD,MAAME,OAAU,cAAgB,cAC7C9B,SAASa,SAASgB,SAAU,CAACjB,SAASrB,IAAKC,KAAKD,IAEpC,WAAjBqB,SAASI,WACJhB,SAASa,SAAS,cAAe,CAACD,SAASrB,IAAKC,KAAKD"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndsectionitem.min.js b/course/format/amd/build/local/courseeditor/dndsectionitem.min.js index 82d5838eaf9..172e9968592 100644 --- a/course/format/amd/build/local/courseeditor/dndsectionitem.min.js +++ b/course/format/amd/build/local/courseeditor/dndsectionitem.min.js @@ -10,6 +10,6 @@ define("core_courseformat/local/courseeditor/dndsectionitem",["exports","core/re * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class _default extends _reactive.BaseComponent{configDragDrop(sectionid,state,fullregion){this.id=sectionid,void 0===this.section&&(this.section=state.section.get(this.id)),void 0===this.course&&(this.course=state.course),this.section.number>0&&(this.getDraggableData=this._getDraggableData),this.fullregion=fullregion,this.reactive.isEditing&&this.reactive.supportComponents&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}_getDraggableData(){return this.reactive.getExporter().sectionDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type)){var _this$section;const firstcmid=null===(_this$section=this.section)||void 0===_this$section?void 0:_this$section.cmlist[0];return dropdata.id!==firstcmid}return!1}showDropZone(){this.element.classList.add(this.classes.DROPZONE)}hideDropZone(){this.element.classList.remove(this.classes.DROPZONE)}drop(dropdata){var _this$section2;"cm"==dropdata.type&&this.reactive.dispatch("cmMove",[dropdata.id],this.id,null===(_this$section2=this.section)||void 0===_this$section2?void 0:_this$section2.cmlist[0])}}return _exports.default=_default,_exports.default})); +class _default extends _reactive.BaseComponent{configDragDrop(sectionid,state,fullregion){this.id=sectionid,void 0===this.section&&(this.section=state.section.get(this.id)),void 0===this.course&&(this.course=state.course),this.section.number>0&&(this.getDraggableData=this._getDraggableData),this.fullregion=fullregion,this.reactive.isEditing&&this.reactive.supportComponents&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}_getDraggableData(){return this.reactive.getExporter().sectionDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type)){var _this$section;const firstcmid=null===(_this$section=this.section)||void 0===_this$section?void 0:_this$section.cmlist[0];return dropdata.id!==firstcmid}return!1}showDropZone(){this.element.classList.add(this.classes.DROPZONE)}hideDropZone(){this.element.classList.remove(this.classes.DROPZONE)}drop(dropdata,event){if("cm"==dropdata.type){var _this$section2;const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],this.id,null===(_this$section2=this.section)||void 0===_this$section2?void 0:_this$section2.cmlist[0])}}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=dndsectionitem.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndsectionitem.min.js.map b/course/format/amd/build/local/courseeditor/dndsectionitem.min.js.map index 9f834dc3438..5954657b2a5 100644 --- a/course/format/amd/build/local/courseeditor/dndsectionitem.min.js.map +++ b/course/format/amd/build/local/courseeditor/dndsectionitem.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dndsectionitem.min.js","sources":["../../../src/local/courseeditor/dndsectionitem.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 index section title draggable component.\n *\n * This component is used to control specific course section interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndsectionitem\n * @class core_courseformat/local/courseeditor/dndsectionitem\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Initial state ready method.\n *\n * @param {number} sectionid the section id\n * @param {Object} state the initial state\n * @param {Element} fullregion the complete section region to mark as dragged\n */\n configDragDrop(sectionid, state, fullregion) {\n\n this.id = sectionid;\n if (this.section === undefined) {\n this.section = state.section.get(this.id);\n }\n if (this.course === undefined) {\n this.course = state.course;\n }\n\n // Prevent topic zero from being draggable.\n if (this.section.number > 0) {\n this.getDraggableData = this._getDraggableData;\n }\n\n this.fullregion = fullregion;\n\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the dropzone.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n // Drag and drop methods.\n\n /**\n * The element drop start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragStart(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], true);\n }\n\n /**\n * The element end start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragEnd(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], false);\n }\n\n /**\n * Get the draggable data of this component.\n *\n * @returns {Object} exported course module drop data\n */\n _getDraggableData() {\n const exporter = this.reactive.getExporter();\n return exporter.sectionDraggableData(this.reactive.state, this.id);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n // Course module validation.\n if (dropdata?.type === 'cm') {\n // The first section element is already there so we can ignore it.\n const firstcmid = this.section?.cmlist[0];\n return dropdata.id !== firstcmid;\n }\n return false;\n }\n\n /**\n * Display the component dropzone.\n */\n showDropZone() {\n this.element.classList.add(this.classes.DROPZONE);\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.element.classList.remove(this.classes.DROPZONE);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n */\n drop(dropdata) {\n // Call the move mutation.\n if (dropdata.type == 'cm') {\n this.reactive.dispatch('cmMove', [dropdata.id], this.id, this.section?.cmlist[0]);\n }\n }\n}\n"],"names":["BaseComponent","configDragDrop","sectionid","state","fullregion","id","undefined","this","section","get","course","number","getDraggableData","_getDraggableData","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","destroy","unregister","dragStart","dropdata","dispatch","dragEnd","getExporter","sectionDraggableData","validateDropData","type","firstcmid","_this$section","cmlist","showDropZone","element","classList","add","DROPZONE","hideDropZone","remove","drop","_this$section2"],"mappings":";;;;;;;;;;;;uBA6B6BA,wBASzBC,eAAeC,UAAWC,MAAOC,iBAExBC,GAAKH,eACWI,IAAjBC,KAAKC,eACAA,QAAUL,MAAMK,QAAQC,IAAIF,KAAKF,UAEtBC,IAAhBC,KAAKG,cACAA,OAASP,MAAMO,QAIpBH,KAAKC,QAAQG,OAAS,SACjBC,iBAAmBL,KAAKM,wBAG5BT,WAAaA,WAGdG,KAAKO,SAASC,WAAaR,KAAKO,SAASE,yBAEpCC,SAAW,IAAIC,mBAASX,WAExBY,QAAUZ,KAAKU,SAASG,cAOrCC,eAC0Bf,IAAlBC,KAAKU,eACAA,SAASK,aAWtBC,UAAUC,eACDV,SAASW,SAAS,cAAe,CAACD,SAASnB,KAAK,GAQzDqB,QAAQF,eACCV,SAASW,SAAS,cAAe,CAACD,SAASnB,KAAK,GAQzDQ,2BACqBN,KAAKO,SAASa,cACfC,qBAAqBrB,KAAKO,SAASX,MAAOI,KAAKF,IASnEwB,iBAAiBL,aAEU,QAAnBA,MAAAA,gBAAAA,SAAUM,MAAe,yBAEnBC,gCAAYxB,KAAKC,wCAALwB,cAAcC,OAAO,UAChCT,SAASnB,KAAO0B,iBAEpB,EAMXG,oBACSC,QAAQC,UAAUC,IAAI9B,KAAKY,QAAQmB,UAM5CC,oBACSJ,QAAQC,UAAUI,OAAOjC,KAAKY,QAAQmB,UAQ/CG,KAAKjB,6BAEoB,MAAjBA,SAASM,WACJhB,SAASW,SAAS,SAAU,CAACD,SAASnB,IAAKE,KAAKF,0BAAIE,KAAKC,yCAALkC,eAAcT,OAAO"} \ No newline at end of file +{"version":3,"file":"dndsectionitem.min.js","sources":["../../../src/local/courseeditor/dndsectionitem.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 index section title draggable component.\n *\n * This component is used to control specific course section interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndsectionitem\n * @class core_courseformat/local/courseeditor/dndsectionitem\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Initial state ready method.\n *\n * @param {number} sectionid the section id\n * @param {Object} state the initial state\n * @param {Element} fullregion the complete section region to mark as dragged\n */\n configDragDrop(sectionid, state, fullregion) {\n\n this.id = sectionid;\n if (this.section === undefined) {\n this.section = state.section.get(this.id);\n }\n if (this.course === undefined) {\n this.course = state.course;\n }\n\n // Prevent topic zero from being draggable.\n if (this.section.number > 0) {\n this.getDraggableData = this._getDraggableData;\n }\n\n this.fullregion = fullregion;\n\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the dropzone.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n // Drag and drop methods.\n\n /**\n * The element drop start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragStart(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], true);\n }\n\n /**\n * The element end start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragEnd(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], false);\n }\n\n /**\n * Get the draggable data of this component.\n *\n * @returns {Object} exported course module drop data\n */\n _getDraggableData() {\n const exporter = this.reactive.getExporter();\n return exporter.sectionDraggableData(this.reactive.state, this.id);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n // Course module validation.\n if (dropdata?.type === 'cm') {\n // The first section element is already there so we can ignore it.\n const firstcmid = this.section?.cmlist[0];\n return dropdata.id !== firstcmid;\n }\n return false;\n }\n\n /**\n * Display the component dropzone.\n */\n showDropZone() {\n this.element.classList.add(this.classes.DROPZONE);\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.element.classList.remove(this.classes.DROPZONE);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n * @param {Event} event the drop event\n */\n drop(dropdata, event) {\n // Call the move mutation.\n if (dropdata.type == 'cm') {\n const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';\n this.reactive.dispatch(mutation, [dropdata.id], this.id, this.section?.cmlist[0]);\n }\n }\n}\n"],"names":["BaseComponent","configDragDrop","sectionid","state","fullregion","id","undefined","this","section","get","course","number","getDraggableData","_getDraggableData","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","destroy","unregister","dragStart","dropdata","dispatch","dragEnd","getExporter","sectionDraggableData","validateDropData","type","firstcmid","_this$section","cmlist","showDropZone","element","classList","add","DROPZONE","hideDropZone","remove","drop","event","mutation","altKey","_this$section2"],"mappings":";;;;;;;;;;;;uBA6B6BA,wBASzBC,eAAeC,UAAWC,MAAOC,iBAExBC,GAAKH,eACWI,IAAjBC,KAAKC,eACAA,QAAUL,MAAMK,QAAQC,IAAIF,KAAKF,UAEtBC,IAAhBC,KAAKG,cACAA,OAASP,MAAMO,QAIpBH,KAAKC,QAAQG,OAAS,SACjBC,iBAAmBL,KAAKM,wBAG5BT,WAAaA,WAGdG,KAAKO,SAASC,WAAaR,KAAKO,SAASE,yBAEpCC,SAAW,IAAIC,mBAASX,WAExBY,QAAUZ,KAAKU,SAASG,cAOrCC,eAC0Bf,IAAlBC,KAAKU,eACAA,SAASK,aAWtBC,UAAUC,eACDV,SAASW,SAAS,cAAe,CAACD,SAASnB,KAAK,GAQzDqB,QAAQF,eACCV,SAASW,SAAS,cAAe,CAACD,SAASnB,KAAK,GAQzDQ,2BACqBN,KAAKO,SAASa,cACfC,qBAAqBrB,KAAKO,SAASX,MAAOI,KAAKF,IASnEwB,iBAAiBL,aAEU,QAAnBA,MAAAA,gBAAAA,SAAUM,MAAe,yBAEnBC,gCAAYxB,KAAKC,wCAALwB,cAAcC,OAAO,UAChCT,SAASnB,KAAO0B,iBAEpB,EAMXG,oBACSC,QAAQC,UAAUC,IAAI9B,KAAKY,QAAQmB,UAM5CC,oBACSJ,QAAQC,UAAUI,OAAOjC,KAAKY,QAAQmB,UAS/CG,KAAKjB,SAAUkB,UAEU,MAAjBlB,SAASM,KAAc,0BACjBa,SAAYD,MAAME,OAAU,cAAgB,cAC7C9B,SAASW,SAASkB,SAAU,CAACnB,SAASnB,IAAKE,KAAKF,0BAAIE,KAAKC,yCAALqC,eAAcZ,OAAO"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/mutations.min.js b/course/format/amd/build/local/courseeditor/mutations.min.js index 72995ce3e70..6fbf299477d 100644 --- a/course/format/amd/build/local/courseeditor/mutations.min.js +++ b/course/format/amd/build/local/courseeditor/mutations.min.js @@ -6,6 +6,6 @@ define("core_courseformat/local/courseeditor/mutations",["exports","core/ajax"], * @class core_courseformat/local/courseeditor/mutations * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};return _exports.default=class{async _callEditWebservice(action,courseId,ids,targetSectionId,targetCmId){const args={action:action,courseid:courseId,ids:ids};targetSectionId&&(args.targetsectionid=targetSectionId),targetCmId&&(args.targetcmid=targetCmId);let ajaxresult=await _ajax.default.call([{methodname:"core_courseformat_update_course",args:args}])[0];return JSON.parse(ajaxresult)}async _sectionBasicAction(stateManager,action,sectionIds,targetSectionId,targetCmId){const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice(action,course.id,sectionIds,targetSectionId,targetCmId);stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async _cmBasicAction(stateManager,action,cmIds,targetSectionId,targetCmId){const course=stateManager.get("course");this.cmLock(stateManager,cmIds,!0);const updates=await this._callEditWebservice(action,course.id,cmIds,targetSectionId,targetCmId);stateManager.processUpdates(updates),this.cmLock(stateManager,cmIds,!1)}init(stateManager){stateManager.addUpdateTypes({prepareFields:this._prepareFields})}_prepareFields(stateManager,updateName,fields){return fields.locked=!1,fields}async sectionHide(stateManager,sectionIds){await this._sectionBasicAction(stateManager,"section_hide",sectionIds)}async sectionShow(stateManager,sectionIds){await this._sectionBasicAction(stateManager,"section_show",sectionIds)}async cmShow(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_show",cmIds)}async cmHide(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_hide",cmIds)}async cmStealth(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_stealth",cmIds)}async cmDuplicate(stateManager,cmIds,targetSectionId){const course=stateManager.get("course"),sectionIds=new Set;targetSectionId?sectionIds.add(targetSectionId):cmIds.forEach((cmId=>{const cm=stateManager.get("cm",cmId);sectionIds.add(cm.sectionid)})),this.sectionLock(stateManager,Array.from(sectionIds),!0);const updates=await this._callEditWebservice("cm_duplicate",course.id,cmIds,targetSectionId);stateManager.processUpdates(updates),this.sectionLock(stateManager,Array.from(sectionIds),!1)}async cmMove(stateManager,cmids,targetSectionId,targetCmId){if(!targetSectionId&&!targetCmId)throw new Error("Mutation cmMove requires targetSectionId or targetCmId");const course=stateManager.get("course");this.cmLock(stateManager,cmids,!0);const updates=await this._callEditWebservice("cm_move",course.id,cmids,targetSectionId,targetCmId);stateManager.processUpdates(updates),this.cmLock(stateManager,cmids,!1)}async sectionMove(stateManager,sectionIds,targetSectionId){if(!targetSectionId)throw new Error("Mutation sectionMove requires targetSectionId");const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice("section_move",course.id,sectionIds,targetSectionId);stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async addSection(stateManager,targetSectionId){targetSectionId||(targetSectionId=0);const course=stateManager.get("course"),updates=await this._callEditWebservice("section_add",course.id,[],targetSectionId);stateManager.processUpdates(updates)}async sectionDelete(stateManager,sectionIds){const course=stateManager.get("course"),updates=await this._callEditWebservice("section_delete",course.id,sectionIds);stateManager.processUpdates(updates)}async cmDelete(stateManager,cmIds){const course=stateManager.get("course");this.cmLock(stateManager,cmIds,!0);const updates=await this._callEditWebservice("cm_delete",course.id,cmIds);this.cmLock(stateManager,cmIds,!1),stateManager.processUpdates(updates)}cmDrag(stateManager,cmIds,dragValue){this.setPageItem(stateManager),this._setElementsValue(stateManager,"cm",cmIds,"dragging",dragValue)}sectionDrag(stateManager,sectionIds,dragValue){this.setPageItem(stateManager),this._setElementsValue(stateManager,"section",sectionIds,"dragging",dragValue)}cmCompletion(stateManager,cmIds,complete){const newValue=complete?1:0;this._setElementsValue(stateManager,"cm",cmIds,"completionstate",newValue)}cmLock(stateManager,cmIds,lockValue){this._setElementsValue(stateManager,"cm",cmIds,"locked",lockValue)}sectionLock(stateManager,sectionIds,lockValue){this._setElementsValue(stateManager,"section",sectionIds,"locked",lockValue)}_setElementsValue(stateManager,name,ids,fieldName,newValue){stateManager.setReadOnly(!1),ids.forEach((id=>{const element=stateManager.get(name,id);element&&(element[fieldName]=newValue)})),stateManager.setReadOnly(!0)}setPageItem(stateManager,type,id,isStatic){let newPageItem;if(void 0!==type&&(newPageItem=stateManager.get(type,id),!newPageItem))return;stateManager.setReadOnly(!1);const course=stateManager.get("course");course.pageItem=null,newPageItem&&(course.pageItem={id:id,type:type,sectionId:"section"==type?newPageItem.id:newPageItem.sectionid,isStatic:isStatic}),stateManager.setReadOnly(!0)}unlockAll(stateManager){const state=stateManager.state;stateManager.setReadOnly(!1),state.section.forEach((section=>{section.locked=!1})),state.cm.forEach((cm=>{cm.locked=!1})),stateManager.setReadOnly(!0)}async sectionIndexCollapsed(stateManager,sectionIds,collapsed){const collapsedIds=this._updateStateSectionPreference(stateManager,"indexcollapsed",sectionIds,collapsed),course=stateManager.get("course");await this._callEditWebservice("section_index_collapsed",course.id,collapsedIds)}async sectionContentCollapsed(stateManager,sectionIds,collapsed){const collapsedIds=this._updateStateSectionPreference(stateManager,"contentcollapsed",sectionIds,collapsed),course=stateManager.get("course");await this._callEditWebservice("section_content_collapsed",course.id,collapsedIds)}_updateStateSectionPreference(stateManager,preferenceName,sectionIds,preferenceValue){stateManager.setReadOnly(!1);const affectedSections=new Set;if(sectionIds.forEach((sectionId=>{const section=stateManager.get("section",sectionId);if(void 0===section)return;const newValue=null!=preferenceValue?preferenceValue:section[preferenceName];section[preferenceName]!=newValue&&(section[preferenceName]=newValue,affectedSections.add(section.id))})),stateManager.setReadOnly(!0),0==affectedSections.size)return[];const collapsedSectionIds=[];return stateManager.state.section.forEach((section=>{section[preferenceName]&&collapsedSectionIds.push(section.id)})),collapsedSectionIds}async cmState(stateManager,cmids){this.cmLock(stateManager,cmids,!0);const course=stateManager.get("course"),updates=await this._callEditWebservice("cm_state",course.id,cmids);stateManager.processUpdates(updates),this.cmLock(stateManager,cmids,!1)}async sectionState(stateManager,sectionIds){this.sectionLock(stateManager,sectionIds,!0);const course=stateManager.get("course"),updates=await this._callEditWebservice("section_state",course.id,sectionIds);stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async courseState(stateManager){const course=stateManager.get("course"),updates=await this._callEditWebservice("course_state",course.id);stateManager.processUpdates(updates)}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};return _exports.default=class{async _callEditWebservice(action,courseId,ids,targetSectionId,targetCmId){const args={action:action,courseid:courseId,ids:ids};targetSectionId&&(args.targetsectionid=targetSectionId),targetCmId&&(args.targetcmid=targetCmId);let ajaxresult=await _ajax.default.call([{methodname:"core_courseformat_update_course",args:args}])[0];return JSON.parse(ajaxresult)}async _sectionBasicAction(stateManager,action,sectionIds,targetSectionId,targetCmId){const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice(action,course.id,sectionIds,targetSectionId,targetCmId);stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async _cmBasicAction(stateManager,action,cmIds,targetSectionId,targetCmId){const course=stateManager.get("course");this.cmLock(stateManager,cmIds,!0);const updates=await this._callEditWebservice(action,course.id,cmIds,targetSectionId,targetCmId);stateManager.processUpdates(updates),this.cmLock(stateManager,cmIds,!1)}init(stateManager){stateManager.addUpdateTypes({prepareFields:this._prepareFields})}_prepareFields(stateManager,updateName,fields){return fields.locked=!1,fields}async sectionHide(stateManager,sectionIds){await this._sectionBasicAction(stateManager,"section_hide",sectionIds)}async sectionShow(stateManager,sectionIds){await this._sectionBasicAction(stateManager,"section_show",sectionIds)}async cmShow(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_show",cmIds)}async cmHide(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_hide",cmIds)}async cmStealth(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_stealth",cmIds)}async cmDuplicate(stateManager,cmIds,targetSectionId,targetCmId){const course=stateManager.get("course"),sectionIds=new Set;targetSectionId?sectionIds.add(targetSectionId):cmIds.forEach((cmId=>{const cm=stateManager.get("cm",cmId);sectionIds.add(cm.sectionid)})),this.sectionLock(stateManager,Array.from(sectionIds),!0);const updates=await this._callEditWebservice("cm_duplicate",course.id,cmIds,targetSectionId,targetCmId);stateManager.processUpdates(updates),this.sectionLock(stateManager,Array.from(sectionIds),!1)}async cmMove(stateManager,cmids,targetSectionId,targetCmId){if(!targetSectionId&&!targetCmId)throw new Error("Mutation cmMove requires targetSectionId or targetCmId");const course=stateManager.get("course");this.cmLock(stateManager,cmids,!0);const updates=await this._callEditWebservice("cm_move",course.id,cmids,targetSectionId,targetCmId);stateManager.processUpdates(updates),this.cmLock(stateManager,cmids,!1)}async sectionMove(stateManager,sectionIds,targetSectionId){if(!targetSectionId)throw new Error("Mutation sectionMove requires targetSectionId");const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice("section_move",course.id,sectionIds,targetSectionId);stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async addSection(stateManager,targetSectionId){targetSectionId||(targetSectionId=0);const course=stateManager.get("course"),updates=await this._callEditWebservice("section_add",course.id,[],targetSectionId);stateManager.processUpdates(updates)}async sectionDelete(stateManager,sectionIds){const course=stateManager.get("course"),updates=await this._callEditWebservice("section_delete",course.id,sectionIds);stateManager.processUpdates(updates)}async cmDelete(stateManager,cmIds){const course=stateManager.get("course");this.cmLock(stateManager,cmIds,!0);const updates=await this._callEditWebservice("cm_delete",course.id,cmIds);this.cmLock(stateManager,cmIds,!1),stateManager.processUpdates(updates)}cmDrag(stateManager,cmIds,dragValue){this.setPageItem(stateManager),this._setElementsValue(stateManager,"cm",cmIds,"dragging",dragValue)}sectionDrag(stateManager,sectionIds,dragValue){this.setPageItem(stateManager),this._setElementsValue(stateManager,"section",sectionIds,"dragging",dragValue)}cmCompletion(stateManager,cmIds,complete){const newValue=complete?1:0;this._setElementsValue(stateManager,"cm",cmIds,"completionstate",newValue)}cmLock(stateManager,cmIds,lockValue){this._setElementsValue(stateManager,"cm",cmIds,"locked",lockValue)}sectionLock(stateManager,sectionIds,lockValue){this._setElementsValue(stateManager,"section",sectionIds,"locked",lockValue)}_setElementsValue(stateManager,name,ids,fieldName,newValue){stateManager.setReadOnly(!1),ids.forEach((id=>{const element=stateManager.get(name,id);element&&(element[fieldName]=newValue)})),stateManager.setReadOnly(!0)}setPageItem(stateManager,type,id,isStatic){let newPageItem;if(void 0!==type&&(newPageItem=stateManager.get(type,id),!newPageItem))return;stateManager.setReadOnly(!1);const course=stateManager.get("course");course.pageItem=null,newPageItem&&(course.pageItem={id:id,type:type,sectionId:"section"==type?newPageItem.id:newPageItem.sectionid,isStatic:isStatic}),stateManager.setReadOnly(!0)}unlockAll(stateManager){const state=stateManager.state;stateManager.setReadOnly(!1),state.section.forEach((section=>{section.locked=!1})),state.cm.forEach((cm=>{cm.locked=!1})),stateManager.setReadOnly(!0)}async sectionIndexCollapsed(stateManager,sectionIds,collapsed){const collapsedIds=this._updateStateSectionPreference(stateManager,"indexcollapsed",sectionIds,collapsed),course=stateManager.get("course");await this._callEditWebservice("section_index_collapsed",course.id,collapsedIds)}async sectionContentCollapsed(stateManager,sectionIds,collapsed){const collapsedIds=this._updateStateSectionPreference(stateManager,"contentcollapsed",sectionIds,collapsed),course=stateManager.get("course");await this._callEditWebservice("section_content_collapsed",course.id,collapsedIds)}_updateStateSectionPreference(stateManager,preferenceName,sectionIds,preferenceValue){stateManager.setReadOnly(!1);const affectedSections=new Set;if(sectionIds.forEach((sectionId=>{const section=stateManager.get("section",sectionId);if(void 0===section)return;const newValue=null!=preferenceValue?preferenceValue:section[preferenceName];section[preferenceName]!=newValue&&(section[preferenceName]=newValue,affectedSections.add(section.id))})),stateManager.setReadOnly(!0),0==affectedSections.size)return[];const collapsedSectionIds=[];return stateManager.state.section.forEach((section=>{section[preferenceName]&&collapsedSectionIds.push(section.id)})),collapsedSectionIds}async cmState(stateManager,cmids){this.cmLock(stateManager,cmids,!0);const course=stateManager.get("course"),updates=await this._callEditWebservice("cm_state",course.id,cmids);stateManager.processUpdates(updates),this.cmLock(stateManager,cmids,!1)}async sectionState(stateManager,sectionIds){this.sectionLock(stateManager,sectionIds,!0);const course=stateManager.get("course"),updates=await this._callEditWebservice("section_state",course.id,sectionIds);stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async courseState(stateManager){const course=stateManager.get("course"),updates=await this._callEditWebservice("course_state",course.id);stateManager.processUpdates(updates)}},_exports.default})); //# sourceMappingURL=mutations.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/mutations.min.js.map b/course/format/amd/build/local/courseeditor/mutations.min.js.map index 2d28478ef43..4095d648b0a 100644 --- a/course/format/amd/build/local/courseeditor/mutations.min.js.map +++ b/course/format/amd/build/local/courseeditor/mutations.min.js.map @@ -1 +1 @@ -{"version":3,"file":"mutations.min.js","sources":["../../../src/local/courseeditor/mutations.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\nimport ajax from 'core/ajax';\n\n/**\n * Default mutation manager\n *\n * @module core_courseformat/local/courseeditor/mutations\n * @class core_courseformat/local/courseeditor/mutations\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class {\n\n // All course editor mutations for Moodle 4.0 will be located in this file.\n\n /**\n * Private method to call core_courseformat_update_course webservice.\n *\n * @method _callEditWebservice\n * @param {string} action\n * @param {number} courseId\n * @param {array} ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _callEditWebservice(action, courseId, ids, targetSectionId, targetCmId) {\n const args = {\n action,\n courseid: courseId,\n ids,\n };\n if (targetSectionId) {\n args.targetsectionid = targetSectionId;\n }\n if (targetCmId) {\n args.targetcmid = targetCmId;\n }\n let ajaxresult = await ajax.call([{\n methodname: 'core_courseformat_update_course',\n args,\n }])[0];\n return JSON.parse(ajaxresult);\n }\n\n /**\n * Execute a basic section state action.\n * @param {StateManager} stateManager the current state manager\n * @param {string} action the action name\n * @param {array} sectionIds the section ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _sectionBasicAction(stateManager, action, sectionIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice(\n action,\n course.id,\n sectionIds,\n targetSectionId,\n targetCmId\n );\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Execute a basic course module state action.\n * @param {StateManager} stateManager the current state manager\n * @param {string} action the action name\n * @param {array} cmIds the cm ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _cmBasicAction(stateManager, action, cmIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmIds, true);\n const updates = await this._callEditWebservice(\n action,\n course.id,\n cmIds,\n targetSectionId,\n targetCmId\n );\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmIds, false);\n }\n\n /**\n * Mutation module initialize.\n *\n * The reactive instance will execute this method when addMutations or setMutation is invoked.\n *\n * @param {StateManager} stateManager the state manager\n */\n init(stateManager) {\n // Add a method to prepare the fields when some update is comming from the server.\n stateManager.addUpdateTypes({\n prepareFields: this._prepareFields,\n });\n }\n\n /**\n * Add default values to state elements.\n *\n * This method is called every time a webservice returns a update state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n * @returns {Object} final fields data\n */\n _prepareFields(stateManager, updateName, fields) {\n // Any update should unlock the element.\n fields.locked = false;\n return fields;\n }\n\n /**\n * Hides sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n */\n async sectionHide(stateManager, sectionIds) {\n await this._sectionBasicAction(stateManager, 'section_hide', sectionIds);\n }\n\n /**\n * Show sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n */\n async sectionShow(stateManager, sectionIds) {\n await this._sectionBasicAction(stateManager, 'section_show', sectionIds);\n }\n\n /**\n * Show cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmShow(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_show', cmIds);\n }\n\n /**\n * Hide cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmHide(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_hide', cmIds);\n }\n\n /**\n * Stealth cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmStealth(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_stealth', cmIds);\n }\n\n /**\n * Duplicate course modules\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {number|undefined} targetSectionId the optional target sectionId\n */\n async cmDuplicate(stateManager, cmIds, targetSectionId) {\n const course = stateManager.get('course');\n // Lock all target sections.\n const sectionIds = new Set();\n if (targetSectionId) {\n sectionIds.add(targetSectionId);\n } else {\n cmIds.forEach((cmId) => {\n const cm = stateManager.get('cm', cmId);\n sectionIds.add(cm.sectionid);\n });\n }\n this.sectionLock(stateManager, Array.from(sectionIds), true);\n\n const updates = await this._callEditWebservice('cm_duplicate', course.id, cmIds, targetSectionId);\n stateManager.processUpdates(updates);\n\n this.sectionLock(stateManager, Array.from(sectionIds), false);\n }\n\n /**\n * Move course modules to specific course location.\n *\n * Note that one of targetSectionId or targetCmId should be provided in order to identify the\n * new location:\n * - targetCmId: the activities will be located avobe the target cm. The targetSectionId\n * value will be ignored in this case.\n * - targetSectionId: the activities will be appended to the section. In this case\n * targetSectionId should not be present.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmids the list of cm ids to move\n * @param {number} targetSectionId the target section id\n * @param {number} targetCmId the target course module id\n */\n async cmMove(stateManager, cmids, targetSectionId, targetCmId) {\n if (!targetSectionId && !targetCmId) {\n throw new Error(`Mutation cmMove requires targetSectionId or targetCmId`);\n }\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmids, true);\n const updates = await this._callEditWebservice('cm_move', course.id, cmids, targetSectionId, targetCmId);\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmids, false);\n }\n\n /**\n * Move course modules to specific course location.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids to move\n * @param {number} targetSectionId the target section id\n */\n async sectionMove(stateManager, sectionIds, targetSectionId) {\n if (!targetSectionId) {\n throw new Error(`Mutation sectionMove requires targetSectionId`);\n }\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice('section_move', course.id, sectionIds, targetSectionId);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Add a new section to a specific course location.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {number} targetSectionId optional the target section id\n */\n async addSection(stateManager, targetSectionId) {\n if (!targetSectionId) {\n targetSectionId = 0;\n }\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_add', course.id, [], targetSectionId);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Delete sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of course modules ids\n */\n async sectionDelete(stateManager, sectionIds) {\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_delete', course.id, sectionIds);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Delete cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of section ids\n */\n async cmDelete(stateManager, cmIds) {\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmIds, true);\n const updates = await this._callEditWebservice('cm_delete', course.id, cmIds);\n this.cmLock(stateManager, cmIds, false);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Mark or unmark course modules as dragging.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} dragValue the new dragging value\n */\n cmDrag(stateManager, cmIds, dragValue) {\n this.setPageItem(stateManager);\n this._setElementsValue(stateManager, 'cm', cmIds, 'dragging', dragValue);\n }\n\n /**\n * Mark or unmark course sections as dragging.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n * @param {bool} dragValue the new dragging value\n */\n sectionDrag(stateManager, sectionIds, dragValue) {\n this.setPageItem(stateManager);\n this._setElementsValue(stateManager, 'section', sectionIds, 'dragging', dragValue);\n }\n\n /**\n * Mark or unmark course modules as complete.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} complete the new completion value\n */\n cmCompletion(stateManager, cmIds, complete) {\n const newValue = (complete) ? 1 : 0;\n this._setElementsValue(stateManager, 'cm', cmIds, 'completionstate', newValue);\n }\n\n /**\n * Lock or unlock course modules.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} lockValue the new locked value\n */\n cmLock(stateManager, cmIds, lockValue) {\n this._setElementsValue(stateManager, 'cm', cmIds, 'locked', lockValue);\n }\n\n /**\n * Lock or unlock course sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n * @param {bool} lockValue the new locked value\n */\n sectionLock(stateManager, sectionIds, lockValue) {\n this._setElementsValue(stateManager, 'section', sectionIds, 'locked', lockValue);\n }\n\n _setElementsValue(stateManager, name, ids, fieldName, newValue) {\n stateManager.setReadOnly(false);\n ids.forEach((id) => {\n const element = stateManager.get(name, id);\n if (element) {\n element[fieldName] = newValue;\n }\n });\n stateManager.setReadOnly(true);\n }\n\n /**\n * Set the page current item.\n *\n * Only one element of the course state can be the page item at a time.\n *\n * There are several actions that can alter the page current item. For example, when the user is in an activity\n * page, the page item is always the activity one. However, in a course page, when the user scrolls to an element,\n * this element get the page item.\n *\n * If the page item is static means that it is not meant to change. This is important because\n * static page items has some special logic. For example, if a cm is the static page item\n * and it is inside a collapsed section, the course index will expand the section to make it visible.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {String|undefined} type the element type (section or cm). Undefined will remove the current page item.\n * @param {Number|undefined} id the element id\n * @param {boolean|undefined} isStatic if the page item is static\n */\n setPageItem(stateManager, type, id, isStatic) {\n let newPageItem;\n if (type !== undefined) {\n newPageItem = stateManager.get(type, id);\n if (!newPageItem) {\n return;\n }\n }\n stateManager.setReadOnly(false);\n // Remove the current page item.\n const course = stateManager.get('course');\n course.pageItem = null;\n // Save the new page item.\n if (newPageItem) {\n course.pageItem = {\n id,\n type,\n sectionId: (type == 'section') ? newPageItem.id : newPageItem.sectionid,\n isStatic,\n };\n }\n stateManager.setReadOnly(true);\n }\n\n /**\n * Unlock all course elements.\n *\n * @param {StateManager} stateManager the current state manager\n */\n unlockAll(stateManager) {\n const state = stateManager.state;\n stateManager.setReadOnly(false);\n state.section.forEach((section) => {\n section.locked = false;\n });\n state.cm.forEach((cm) => {\n cm.locked = false;\n });\n stateManager.setReadOnly(true);\n }\n\n /**\n * Update the course index collapsed attribute of some sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the affected section ids\n * @param {boolean} collapsed the new collapsed value\n */\n async sectionIndexCollapsed(stateManager, sectionIds, collapsed) {\n const collapsedIds = this._updateStateSectionPreference(stateManager, 'indexcollapsed', sectionIds, collapsed);\n const course = stateManager.get('course');\n await this._callEditWebservice('section_index_collapsed', course.id, collapsedIds);\n }\n\n /**\n * Update the course content collapsed attribute of some sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the affected section ids\n * @param {boolean} collapsed the new collapsed value\n */\n async sectionContentCollapsed(stateManager, sectionIds, collapsed) {\n const collapsedIds = this._updateStateSectionPreference(stateManager, 'contentcollapsed', sectionIds, collapsed);\n const course = stateManager.get('course');\n await this._callEditWebservice('section_content_collapsed', course.id, collapsedIds);\n }\n\n /**\n * Private batch update for a section preference attribute.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {string} preferenceName the preference name\n * @param {array} sectionIds the affected section ids\n * @param {boolean} preferenceValue the new preferenceValue value\n * @return {array} the list of all sections with that preference set to true\n */\n _updateStateSectionPreference(stateManager, preferenceName, sectionIds, preferenceValue) {\n stateManager.setReadOnly(false);\n const affectedSections = new Set();\n // Check if we need to update preferences.\n sectionIds.forEach(sectionId => {\n const section = stateManager.get('section', sectionId);\n if (section === undefined) {\n return;\n }\n const newValue = preferenceValue ?? section[preferenceName];\n if (section[preferenceName] != newValue) {\n section[preferenceName] = newValue;\n affectedSections.add(section.id);\n }\n });\n stateManager.setReadOnly(true);\n if (affectedSections.size == 0) {\n return [];\n }\n // Get all collapsed section ids.\n const collapsedSectionIds = [];\n const state = stateManager.state;\n state.section.forEach(section => {\n if (section[preferenceName]) {\n collapsedSectionIds.push(section.id);\n }\n });\n return collapsedSectionIds;\n }\n\n /**\n * Get updated state data related to some cm ids.\n *\n * @method cmState\n * @param {StateManager} stateManager the current state\n * @param {array} cmids the list of cm ids to update\n */\n async cmState(stateManager, cmids) {\n this.cmLock(stateManager, cmids, true);\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('cm_state', course.id, cmids);\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmids, false);\n }\n\n /**\n * Get updated state data related to some section ids.\n *\n * @method sectionState\n * @param {StateManager} stateManager the current state\n * @param {array} sectionIds the list of section ids to update\n */\n async sectionState(stateManager, sectionIds) {\n this.sectionLock(stateManager, sectionIds, true);\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_state', course.id, sectionIds);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Get the full updated state data of the course.\n *\n * @param {StateManager} stateManager the current state\n */\n async courseState(stateManager) {\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('course_state', course.id);\n stateManager.processUpdates(updates);\n }\n\n}\n"],"names":["action","courseId","ids","targetSectionId","targetCmId","args","courseid","targetsectionid","targetcmid","ajaxresult","ajax","call","methodname","JSON","parse","stateManager","sectionIds","course","get","sectionLock","updates","this","_callEditWebservice","id","processUpdates","cmIds","cmLock","init","addUpdateTypes","prepareFields","_prepareFields","updateName","fields","locked","_sectionBasicAction","_cmBasicAction","Set","add","forEach","cmId","cm","sectionid","Array","from","cmids","Error","cmDrag","dragValue","setPageItem","_setElementsValue","sectionDrag","cmCompletion","complete","newValue","lockValue","name","fieldName","setReadOnly","element","type","isStatic","newPageItem","undefined","pageItem","sectionId","unlockAll","state","section","collapsed","collapsedIds","_updateStateSectionPreference","preferenceName","preferenceValue","affectedSections","size","collapsedSectionIds","push"],"mappings":";;;;;;;;iMAuC8BA,OAAQC,SAAUC,IAAKC,gBAAiBC,kBACxDC,KAAO,CACTL,OAAAA,OACAM,SAAUL,SACVC,IAAAA,KAEAC,kBACAE,KAAKE,gBAAkBJ,iBAEvBC,aACAC,KAAKG,WAAaJ,gBAElBK,iBAAmBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,kCACZP,KAAAA,QACA,UACGQ,KAAKC,MAAML,sCAWIM,aAAcf,OAAQgB,WAAYb,gBAAiBC,kBACnEa,OAASF,aAAaG,IAAI,eAC3BC,YAAYJ,aAAcC,YAAY,SACrCI,cAAgBC,KAAKC,oBACvBtB,OACAiB,OAAOM,GACPP,WACAb,gBACAC,YAEJW,aAAaS,eAAeJ,cACvBD,YAAYJ,aAAcC,YAAY,wBAW1BD,aAAcf,OAAQyB,MAAOtB,gBAAiBC,kBACzDa,OAASF,aAAaG,IAAI,eAC3BQ,OAAOX,aAAcU,OAAO,SAC3BL,cAAgBC,KAAKC,oBACvBtB,OACAiB,OAAOM,GACPE,MACAtB,gBACAC,YAEJW,aAAaS,eAAeJ,cACvBM,OAAOX,aAAcU,OAAO,GAUrCE,KAAKZ,cAEDA,aAAaa,eAAe,CACxBC,cAAeR,KAAKS,iBAc5BA,eAAef,aAAcgB,WAAYC,eAErCA,OAAOC,QAAS,EACTD,yBAQOjB,aAAcC,kBACtBK,KAAKa,oBAAoBnB,aAAc,eAAgBC,8BAQ/CD,aAAcC,kBACtBK,KAAKa,oBAAoBnB,aAAc,eAAgBC,yBAQpDD,aAAcU,aACjBJ,KAAKc,eAAepB,aAAc,UAAWU,oBAQ1CV,aAAcU,aACjBJ,KAAKc,eAAepB,aAAc,UAAWU,uBAQvCV,aAAcU,aACpBJ,KAAKc,eAAepB,aAAc,aAAcU,yBASxCV,aAAcU,MAAOtB,uBAC7Bc,OAASF,aAAaG,IAAI,UAE1BF,WAAa,IAAIoB,IACnBjC,gBACAa,WAAWqB,IAAIlC,iBAEfsB,MAAMa,SAASC,aACLC,GAAKzB,aAAaG,IAAI,KAAMqB,MAClCvB,WAAWqB,IAAIG,GAAGC,mBAGrBtB,YAAYJ,aAAc2B,MAAMC,KAAK3B,aAAa,SAEjDI,cAAgBC,KAAKC,oBAAoB,eAAgBL,OAAOM,GAAIE,MAAOtB,iBACjFY,aAAaS,eAAeJ,cAEvBD,YAAYJ,aAAc2B,MAAMC,KAAK3B,aAAa,gBAkB9CD,aAAc6B,MAAOzC,gBAAiBC,gBAC1CD,kBAAoBC,iBACf,IAAIyC,sEAER5B,OAASF,aAAaG,IAAI,eAC3BQ,OAAOX,aAAc6B,OAAO,SAC3BxB,cAAgBC,KAAKC,oBAAoB,UAAWL,OAAOM,GAAIqB,MAAOzC,gBAAiBC,YAC7FW,aAAaS,eAAeJ,cACvBM,OAAOX,aAAc6B,OAAO,qBAUnB7B,aAAcC,WAAYb,qBACnCA,sBACK,IAAI0C,6DAER5B,OAASF,aAAaG,IAAI,eAC3BC,YAAYJ,aAAcC,YAAY,SACrCI,cAAgBC,KAAKC,oBAAoB,eAAgBL,OAAOM,GAAIP,WAAYb,iBACtFY,aAAaS,eAAeJ,cACvBD,YAAYJ,aAAcC,YAAY,oBAS9BD,aAAcZ,iBACtBA,kBACDA,gBAAkB,SAEhBc,OAASF,aAAaG,IAAI,UAC1BE,cAAgBC,KAAKC,oBAAoB,cAAeL,OAAOM,GAAI,GAAIpB,iBAC7EY,aAAaS,eAAeJ,6BASZL,aAAcC,kBACxBC,OAASF,aAAaG,IAAI,UAC1BE,cAAgBC,KAAKC,oBAAoB,iBAAkBL,OAAOM,GAAIP,YAC5ED,aAAaS,eAAeJ,wBAQjBL,aAAcU,aACnBR,OAASF,aAAaG,IAAI,eAC3BQ,OAAOX,aAAcU,OAAO,SAC3BL,cAAgBC,KAAKC,oBAAoB,YAAaL,OAAOM,GAAIE,YAClEC,OAAOX,aAAcU,OAAO,GACjCV,aAAaS,eAAeJ,SAUhC0B,OAAO/B,aAAcU,MAAOsB,gBACnBC,YAAYjC,mBACZkC,kBAAkBlC,aAAc,KAAMU,MAAO,WAAYsB,WAUlEG,YAAYnC,aAAcC,WAAY+B,gBAC7BC,YAAYjC,mBACZkC,kBAAkBlC,aAAc,UAAWC,WAAY,WAAY+B,WAU5EI,aAAapC,aAAcU,MAAO2B,gBACxBC,SAAYD,SAAY,EAAI,OAC7BH,kBAAkBlC,aAAc,KAAMU,MAAO,kBAAmB4B,UAUzE3B,OAAOX,aAAcU,MAAO6B,gBACnBL,kBAAkBlC,aAAc,KAAMU,MAAO,SAAU6B,WAUhEnC,YAAYJ,aAAcC,WAAYsC,gBAC7BL,kBAAkBlC,aAAc,UAAWC,WAAY,SAAUsC,WAG1EL,kBAAkBlC,aAAcwC,KAAMrD,IAAKsD,UAAWH,UAClDtC,aAAa0C,aAAY,GACzBvD,IAAIoC,SAASf,WACHmC,QAAU3C,aAAaG,IAAIqC,KAAMhC,IACnCmC,UACAA,QAAQF,WAAaH,aAG7BtC,aAAa0C,aAAY,GAqB7BT,YAAYjC,aAAc4C,KAAMpC,GAAIqC,cAC5BC,oBACSC,IAATH,OACAE,YAAc9C,aAAaG,IAAIyC,KAAMpC,KAChCsC,oBAIT9C,aAAa0C,aAAY,SAEnBxC,OAASF,aAAaG,IAAI,UAChCD,OAAO8C,SAAW,KAEdF,cACA5C,OAAO8C,SAAW,CACdxC,GAAAA,GACAoC,KAAAA,KACAK,UAAoB,WAARL,KAAqBE,YAAYtC,GAAKsC,YAAYpB,UAC9DmB,SAAAA,WAGR7C,aAAa0C,aAAY,GAQ7BQ,UAAUlD,oBACAmD,MAAQnD,aAAamD,MAC3BnD,aAAa0C,aAAY,GACzBS,MAAMC,QAAQ7B,SAAS6B,UACnBA,QAAQlC,QAAS,KAErBiC,MAAM1B,GAAGF,SAASE,KACdA,GAAGP,QAAS,KAEhBlB,aAAa0C,aAAY,+BAUD1C,aAAcC,WAAYoD,iBAC5CC,aAAehD,KAAKiD,8BAA8BvD,aAAc,iBAAkBC,WAAYoD,WAC9FnD,OAASF,aAAaG,IAAI,gBAC1BG,KAAKC,oBAAoB,0BAA2BL,OAAOM,GAAI8C,4CAU3CtD,aAAcC,WAAYoD,iBAC9CC,aAAehD,KAAKiD,8BAA8BvD,aAAc,mBAAoBC,WAAYoD,WAChGnD,OAASF,aAAaG,IAAI,gBAC1BG,KAAKC,oBAAoB,4BAA6BL,OAAOM,GAAI8C,cAY3EC,8BAA8BvD,aAAcwD,eAAgBvD,WAAYwD,iBACpEzD,aAAa0C,aAAY,SACnBgB,iBAAmB,IAAIrC,OAE7BpB,WAAWsB,SAAQ0B,kBACTG,QAAUpD,aAAaG,IAAI,UAAW8C,mBAC5BF,IAAZK,qBAGEd,SAAWmB,MAAAA,gBAAAA,gBAAmBL,QAAQI,gBACxCJ,QAAQI,iBAAmBlB,WAC3Bc,QAAQI,gBAAkBlB,SAC1BoB,iBAAiBpC,IAAI8B,QAAQ5C,QAGrCR,aAAa0C,aAAY,GACI,GAAzBgB,iBAAiBC,WACV,SAGLC,oBAAsB,UACd5D,aAAamD,MACrBC,QAAQ7B,SAAQ6B,UACdA,QAAQI,iBACRI,oBAAoBC,KAAKT,QAAQ5C,OAGlCoD,kCAUG5D,aAAc6B,YACnBlB,OAAOX,aAAc6B,OAAO,SAC3B3B,OAASF,aAAaG,IAAI,UAC1BE,cAAgBC,KAAKC,oBAAoB,WAAYL,OAAOM,GAAIqB,OACtE7B,aAAaS,eAAeJ,cACvBM,OAAOX,aAAc6B,OAAO,sBAUlB7B,aAAcC,iBACxBG,YAAYJ,aAAcC,YAAY,SACrCC,OAASF,aAAaG,IAAI,UAC1BE,cAAgBC,KAAKC,oBAAoB,gBAAiBL,OAAOM,GAAIP,YAC3ED,aAAaS,eAAeJ,cACvBD,YAAYJ,aAAcC,YAAY,qBAQ7BD,oBACRE,OAASF,aAAaG,IAAI,UAC1BE,cAAgBC,KAAKC,oBAAoB,eAAgBL,OAAOM,IACtER,aAAaS,eAAeJ"} \ No newline at end of file +{"version":3,"file":"mutations.min.js","sources":["../../../src/local/courseeditor/mutations.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\nimport ajax from 'core/ajax';\n\n/**\n * Default mutation manager\n *\n * @module core_courseformat/local/courseeditor/mutations\n * @class core_courseformat/local/courseeditor/mutations\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class {\n\n // All course editor mutations for Moodle 4.0 will be located in this file.\n\n /**\n * Private method to call core_courseformat_update_course webservice.\n *\n * @method _callEditWebservice\n * @param {string} action\n * @param {number} courseId\n * @param {array} ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _callEditWebservice(action, courseId, ids, targetSectionId, targetCmId) {\n const args = {\n action,\n courseid: courseId,\n ids,\n };\n if (targetSectionId) {\n args.targetsectionid = targetSectionId;\n }\n if (targetCmId) {\n args.targetcmid = targetCmId;\n }\n let ajaxresult = await ajax.call([{\n methodname: 'core_courseformat_update_course',\n args,\n }])[0];\n return JSON.parse(ajaxresult);\n }\n\n /**\n * Execute a basic section state action.\n * @param {StateManager} stateManager the current state manager\n * @param {string} action the action name\n * @param {array} sectionIds the section ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _sectionBasicAction(stateManager, action, sectionIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice(\n action,\n course.id,\n sectionIds,\n targetSectionId,\n targetCmId\n );\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Execute a basic course module state action.\n * @param {StateManager} stateManager the current state manager\n * @param {string} action the action name\n * @param {array} cmIds the cm ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _cmBasicAction(stateManager, action, cmIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmIds, true);\n const updates = await this._callEditWebservice(\n action,\n course.id,\n cmIds,\n targetSectionId,\n targetCmId\n );\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmIds, false);\n }\n\n /**\n * Mutation module initialize.\n *\n * The reactive instance will execute this method when addMutations or setMutation is invoked.\n *\n * @param {StateManager} stateManager the state manager\n */\n init(stateManager) {\n // Add a method to prepare the fields when some update is comming from the server.\n stateManager.addUpdateTypes({\n prepareFields: this._prepareFields,\n });\n }\n\n /**\n * Add default values to state elements.\n *\n * This method is called every time a webservice returns a update state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n * @returns {Object} final fields data\n */\n _prepareFields(stateManager, updateName, fields) {\n // Any update should unlock the element.\n fields.locked = false;\n return fields;\n }\n\n /**\n * Hides sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n */\n async sectionHide(stateManager, sectionIds) {\n await this._sectionBasicAction(stateManager, 'section_hide', sectionIds);\n }\n\n /**\n * Show sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n */\n async sectionShow(stateManager, sectionIds) {\n await this._sectionBasicAction(stateManager, 'section_show', sectionIds);\n }\n\n /**\n * Show cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmShow(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_show', cmIds);\n }\n\n /**\n * Hide cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmHide(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_hide', cmIds);\n }\n\n /**\n * Stealth cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmStealth(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_stealth', cmIds);\n }\n\n /**\n * Duplicate course modules\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {number|undefined} targetSectionId the optional target sectionId\n * @param {number|undefined} targetCmId the target course module id\n */\n async cmDuplicate(stateManager, cmIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n // Lock all target sections.\n const sectionIds = new Set();\n if (targetSectionId) {\n sectionIds.add(targetSectionId);\n } else {\n cmIds.forEach((cmId) => {\n const cm = stateManager.get('cm', cmId);\n sectionIds.add(cm.sectionid);\n });\n }\n this.sectionLock(stateManager, Array.from(sectionIds), true);\n\n const updates = await this._callEditWebservice('cm_duplicate', course.id, cmIds, targetSectionId, targetCmId);\n stateManager.processUpdates(updates);\n\n this.sectionLock(stateManager, Array.from(sectionIds), false);\n }\n\n /**\n * Move course modules to specific course location.\n *\n * Note that one of targetSectionId or targetCmId should be provided in order to identify the\n * new location:\n * - targetCmId: the activities will be located avobe the target cm. The targetSectionId\n * value will be ignored in this case.\n * - targetSectionId: the activities will be appended to the section. In this case\n * targetSectionId should not be present.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmids the list of cm ids to move\n * @param {number} targetSectionId the target section id\n * @param {number} targetCmId the target course module id\n */\n async cmMove(stateManager, cmids, targetSectionId, targetCmId) {\n if (!targetSectionId && !targetCmId) {\n throw new Error(`Mutation cmMove requires targetSectionId or targetCmId`);\n }\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmids, true);\n const updates = await this._callEditWebservice('cm_move', course.id, cmids, targetSectionId, targetCmId);\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmids, false);\n }\n\n /**\n * Move course modules to specific course location.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids to move\n * @param {number} targetSectionId the target section id\n */\n async sectionMove(stateManager, sectionIds, targetSectionId) {\n if (!targetSectionId) {\n throw new Error(`Mutation sectionMove requires targetSectionId`);\n }\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice('section_move', course.id, sectionIds, targetSectionId);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Add a new section to a specific course location.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {number} targetSectionId optional the target section id\n */\n async addSection(stateManager, targetSectionId) {\n if (!targetSectionId) {\n targetSectionId = 0;\n }\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_add', course.id, [], targetSectionId);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Delete sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of course modules ids\n */\n async sectionDelete(stateManager, sectionIds) {\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_delete', course.id, sectionIds);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Delete cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of section ids\n */\n async cmDelete(stateManager, cmIds) {\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmIds, true);\n const updates = await this._callEditWebservice('cm_delete', course.id, cmIds);\n this.cmLock(stateManager, cmIds, false);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Mark or unmark course modules as dragging.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} dragValue the new dragging value\n */\n cmDrag(stateManager, cmIds, dragValue) {\n this.setPageItem(stateManager);\n this._setElementsValue(stateManager, 'cm', cmIds, 'dragging', dragValue);\n }\n\n /**\n * Mark or unmark course sections as dragging.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n * @param {bool} dragValue the new dragging value\n */\n sectionDrag(stateManager, sectionIds, dragValue) {\n this.setPageItem(stateManager);\n this._setElementsValue(stateManager, 'section', sectionIds, 'dragging', dragValue);\n }\n\n /**\n * Mark or unmark course modules as complete.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} complete the new completion value\n */\n cmCompletion(stateManager, cmIds, complete) {\n const newValue = (complete) ? 1 : 0;\n this._setElementsValue(stateManager, 'cm', cmIds, 'completionstate', newValue);\n }\n\n /**\n * Lock or unlock course modules.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} lockValue the new locked value\n */\n cmLock(stateManager, cmIds, lockValue) {\n this._setElementsValue(stateManager, 'cm', cmIds, 'locked', lockValue);\n }\n\n /**\n * Lock or unlock course sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n * @param {bool} lockValue the new locked value\n */\n sectionLock(stateManager, sectionIds, lockValue) {\n this._setElementsValue(stateManager, 'section', sectionIds, 'locked', lockValue);\n }\n\n _setElementsValue(stateManager, name, ids, fieldName, newValue) {\n stateManager.setReadOnly(false);\n ids.forEach((id) => {\n const element = stateManager.get(name, id);\n if (element) {\n element[fieldName] = newValue;\n }\n });\n stateManager.setReadOnly(true);\n }\n\n /**\n * Set the page current item.\n *\n * Only one element of the course state can be the page item at a time.\n *\n * There are several actions that can alter the page current item. For example, when the user is in an activity\n * page, the page item is always the activity one. However, in a course page, when the user scrolls to an element,\n * this element get the page item.\n *\n * If the page item is static means that it is not meant to change. This is important because\n * static page items has some special logic. For example, if a cm is the static page item\n * and it is inside a collapsed section, the course index will expand the section to make it visible.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {String|undefined} type the element type (section or cm). Undefined will remove the current page item.\n * @param {Number|undefined} id the element id\n * @param {boolean|undefined} isStatic if the page item is static\n */\n setPageItem(stateManager, type, id, isStatic) {\n let newPageItem;\n if (type !== undefined) {\n newPageItem = stateManager.get(type, id);\n if (!newPageItem) {\n return;\n }\n }\n stateManager.setReadOnly(false);\n // Remove the current page item.\n const course = stateManager.get('course');\n course.pageItem = null;\n // Save the new page item.\n if (newPageItem) {\n course.pageItem = {\n id,\n type,\n sectionId: (type == 'section') ? newPageItem.id : newPageItem.sectionid,\n isStatic,\n };\n }\n stateManager.setReadOnly(true);\n }\n\n /**\n * Unlock all course elements.\n *\n * @param {StateManager} stateManager the current state manager\n */\n unlockAll(stateManager) {\n const state = stateManager.state;\n stateManager.setReadOnly(false);\n state.section.forEach((section) => {\n section.locked = false;\n });\n state.cm.forEach((cm) => {\n cm.locked = false;\n });\n stateManager.setReadOnly(true);\n }\n\n /**\n * Update the course index collapsed attribute of some sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the affected section ids\n * @param {boolean} collapsed the new collapsed value\n */\n async sectionIndexCollapsed(stateManager, sectionIds, collapsed) {\n const collapsedIds = this._updateStateSectionPreference(stateManager, 'indexcollapsed', sectionIds, collapsed);\n const course = stateManager.get('course');\n await this._callEditWebservice('section_index_collapsed', course.id, collapsedIds);\n }\n\n /**\n * Update the course content collapsed attribute of some sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the affected section ids\n * @param {boolean} collapsed the new collapsed value\n */\n async sectionContentCollapsed(stateManager, sectionIds, collapsed) {\n const collapsedIds = this._updateStateSectionPreference(stateManager, 'contentcollapsed', sectionIds, collapsed);\n const course = stateManager.get('course');\n await this._callEditWebservice('section_content_collapsed', course.id, collapsedIds);\n }\n\n /**\n * Private batch update for a section preference attribute.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {string} preferenceName the preference name\n * @param {array} sectionIds the affected section ids\n * @param {boolean} preferenceValue the new preferenceValue value\n * @return {array} the list of all sections with that preference set to true\n */\n _updateStateSectionPreference(stateManager, preferenceName, sectionIds, preferenceValue) {\n stateManager.setReadOnly(false);\n const affectedSections = new Set();\n // Check if we need to update preferences.\n sectionIds.forEach(sectionId => {\n const section = stateManager.get('section', sectionId);\n if (section === undefined) {\n return;\n }\n const newValue = preferenceValue ?? section[preferenceName];\n if (section[preferenceName] != newValue) {\n section[preferenceName] = newValue;\n affectedSections.add(section.id);\n }\n });\n stateManager.setReadOnly(true);\n if (affectedSections.size == 0) {\n return [];\n }\n // Get all collapsed section ids.\n const collapsedSectionIds = [];\n const state = stateManager.state;\n state.section.forEach(section => {\n if (section[preferenceName]) {\n collapsedSectionIds.push(section.id);\n }\n });\n return collapsedSectionIds;\n }\n\n /**\n * Get updated state data related to some cm ids.\n *\n * @method cmState\n * @param {StateManager} stateManager the current state\n * @param {array} cmids the list of cm ids to update\n */\n async cmState(stateManager, cmids) {\n this.cmLock(stateManager, cmids, true);\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('cm_state', course.id, cmids);\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmids, false);\n }\n\n /**\n * Get updated state data related to some section ids.\n *\n * @method sectionState\n * @param {StateManager} stateManager the current state\n * @param {array} sectionIds the list of section ids to update\n */\n async sectionState(stateManager, sectionIds) {\n this.sectionLock(stateManager, sectionIds, true);\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_state', course.id, sectionIds);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Get the full updated state data of the course.\n *\n * @param {StateManager} stateManager the current state\n */\n async courseState(stateManager) {\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('course_state', course.id);\n stateManager.processUpdates(updates);\n }\n\n}\n"],"names":["action","courseId","ids","targetSectionId","targetCmId","args","courseid","targetsectionid","targetcmid","ajaxresult","ajax","call","methodname","JSON","parse","stateManager","sectionIds","course","get","sectionLock","updates","this","_callEditWebservice","id","processUpdates","cmIds","cmLock","init","addUpdateTypes","prepareFields","_prepareFields","updateName","fields","locked","_sectionBasicAction","_cmBasicAction","Set","add","forEach","cmId","cm","sectionid","Array","from","cmids","Error","cmDrag","dragValue","setPageItem","_setElementsValue","sectionDrag","cmCompletion","complete","newValue","lockValue","name","fieldName","setReadOnly","element","type","isStatic","newPageItem","undefined","pageItem","sectionId","unlockAll","state","section","collapsed","collapsedIds","_updateStateSectionPreference","preferenceName","preferenceValue","affectedSections","size","collapsedSectionIds","push"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/src/local/courseeditor/dndcmitem.js b/course/format/amd/src/local/courseeditor/dndcmitem.js index 4373e4d1ad7..3a0077e9d2b 100644 --- a/course/format/amd/src/local/courseeditor/dndcmitem.js +++ b/course/format/amd/src/local/courseeditor/dndcmitem.js @@ -120,11 +120,13 @@ export default class extends BaseComponent { * Drop event handler. * * @param {Object} dropdata the accepted drop data + * @param {Event} event the drop event */ - drop(dropdata) { + drop(dropdata, event) { // Call the move mutation if necessary. if (dropdata.id != this.id && dropdata.nextcmid != this.id) { - this.reactive.dispatch('cmMove', [dropdata.id], null, this.id); + const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove'; + this.reactive.dispatch(mutation, [dropdata.id], null, this.id); } } diff --git a/course/format/amd/src/local/courseeditor/dndsection.js b/course/format/amd/src/local/courseeditor/dndsection.js index 52a3a53ddf6..546e6474c6a 100644 --- a/course/format/amd/src/local/courseeditor/dndsection.js +++ b/course/format/amd/src/local/courseeditor/dndsection.js @@ -151,11 +151,13 @@ export default class extends BaseComponent { * Drop event handler. * * @param {Object} dropdata the accepted drop data + * @param {Event} event the drop event */ - drop(dropdata) { + drop(dropdata, event) { // Call the move mutation. if (dropdata.type == 'cm') { - this.reactive.dispatch('cmMove', [dropdata.id], this.id); + const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove'; + this.reactive.dispatch(mutation, [dropdata.id], this.id); } if (dropdata.type == 'section') { this.reactive.dispatch('sectionMove', [dropdata.id], this.id); diff --git a/course/format/amd/src/local/courseeditor/dndsectionitem.js b/course/format/amd/src/local/courseeditor/dndsectionitem.js index ad3a80249fc..18288b918a8 100644 --- a/course/format/amd/src/local/courseeditor/dndsectionitem.js +++ b/course/format/amd/src/local/courseeditor/dndsectionitem.js @@ -135,11 +135,13 @@ export default class extends BaseComponent { * Drop event handler. * * @param {Object} dropdata the accepted drop data + * @param {Event} event the drop event */ - drop(dropdata) { + drop(dropdata, event) { // Call the move mutation. if (dropdata.type == 'cm') { - this.reactive.dispatch('cmMove', [dropdata.id], this.id, this.section?.cmlist[0]); + const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove'; + this.reactive.dispatch(mutation, [dropdata.id], this.id, this.section?.cmlist[0]); } } } diff --git a/course/format/amd/src/local/courseeditor/mutations.js b/course/format/amd/src/local/courseeditor/mutations.js index 965f096c58a..f01bd77ce61 100644 --- a/course/format/amd/src/local/courseeditor/mutations.js +++ b/course/format/amd/src/local/courseeditor/mutations.js @@ -180,8 +180,9 @@ export default class { * @param {StateManager} stateManager the current state manager * @param {array} cmIds the list of course modules ids * @param {number|undefined} targetSectionId the optional target sectionId + * @param {number|undefined} targetCmId the target course module id */ - async cmDuplicate(stateManager, cmIds, targetSectionId) { + async cmDuplicate(stateManager, cmIds, targetSectionId, targetCmId) { const course = stateManager.get('course'); // Lock all target sections. const sectionIds = new Set(); @@ -195,7 +196,7 @@ export default class { } this.sectionLock(stateManager, Array.from(sectionIds), true); - const updates = await this._callEditWebservice('cm_duplicate', course.id, cmIds, targetSectionId); + const updates = await this._callEditWebservice('cm_duplicate', course.id, cmIds, targetSectionId, targetCmId); stateManager.processUpdates(updates); this.sectionLock(stateManager, Array.from(sectionIds), false); diff --git a/course/format/classes/stateactions.php b/course/format/classes/stateactions.php index de4e120ebac..9be3f4fcfa5 100644 --- a/course/format/classes/stateactions.php +++ b/course/format/classes/stateactions.php @@ -428,8 +428,8 @@ class stateactions { * @param stateupdates $updates the affected course elements track * @param stdClass $course the course object * @param int[] $ids course modules ids to duplicate - * @param int $targetsectionid optional target section id destination - * @param int $targetcmid not used + * @param int|null $targetsectionid optional target section id destination + * @param int|null $targetcmid optional target before cm id destination */ public function cm_duplicate( stateupdates $updates, @@ -461,12 +461,19 @@ class stateactions { $targetsection = $modinfo->get_section_info_by_id($targetsectionid, MUST_EXIST); } + $beforecm = null; + if (!empty($targetcmid)) { + $this->validate_cms($course, [$targetcmid], __FUNCTION__); + $beforecm = $modinfo->get_cm($targetcmid); + $targetsection = $modinfo->get_section_info_by_id($beforecm->section, MUST_EXIST); + } + // Duplicate course modules. $affectedcmids = []; foreach ($cms as $cm) { if ($newcm = duplicate_module($course, $cm)) { if ($targetsection) { - moveto_module($newcm, $targetsection); + moveto_module($newcm, $targetsection, $beforecm); } else { $affectedcmids[] = $newcm->id; }