From eeb347d90f1dae6cfa6bff77d30b4c0e6761c8b1 Mon Sep 17 00:00:00 2001 From: Tomo <54526084+TomoTsuyuki@users.noreply.github.com> Date: Fri, 12 Jan 2024 04:15:31 +1100 Subject: [PATCH] Issue #86 Add a callback to filter sections from course format (#92) * Issue #86 Add a callback to filter sections from course format * Issue #86 Use value, not key, for massaction_restricted_sections callback * Issue #86 Check if the source section is restricted * Issue #86 Fix phpunit error * Issue #86 Check if the dropbox exists before filtering * Issue #86 Throw exception when selected section is restricted --- amd/build/checkboxmanager.min.js | 2 +- amd/build/checkboxmanager.min.js.map | 2 +- amd/build/massactionblock.min.js | 2 +- amd/build/massactionblock.min.js.map | 2 +- amd/src/checkboxmanager.js | 37 +++++++++++++++++++++++----- amd/src/massactionblock.js | 5 ++-- block_massaction.php | 3 ++- classes/actions.php | 28 +++++++++++++++++++++ classes/form/section_select_form.php | 8 +++++- classes/massactionutils.php | 17 +++++++++++++ lang/en/block_massaction.php | 1 + 11 files changed, 93 insertions(+), 14 deletions(-) diff --git a/amd/build/checkboxmanager.min.js b/amd/build/checkboxmanager.min.js index b323029..b5b94e5 100644 --- a/amd/build/checkboxmanager.min.js +++ b/amd/build/checkboxmanager.min.js @@ -7,6 +7,6 @@ define("block_massaction/checkboxmanager",["exports","core/templates","core/noti * @copyright 2022 ISB Bayern * @author Philipp Memmel * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setSectionSelection=_exports.initCheckboxManager=_exports.getSelectedModIds=void 0,_templates=_interopRequireDefault(_templates),_events=_interopRequireDefault(_events);let localStateUpdating=!1,sectionsChanged=!1,sections=[],moduleNames=[];const sectionBoxes={};_exports.initCheckboxManager=()=>{const courseEditor=(0,_courseeditor.getCurrentCourseEditor)(),eventsToListen_SECTION_UPDATED="section:updated",eventsToListen_CHANGE_FINISHED="transaction:end";courseEditor.stateManager.target.addEventListener(_events.default.stateChanged,(event=>{event.detail.action===eventsToListen_SECTION_UPDATED&&(sectionsChanged=!0),event.detail.action===eventsToListen_CHANGE_FINISHED&&rebuildLocalState()})),sectionsChanged=!0,rebuildLocalState()};const rebuildLocalState=()=>{if(localStateUpdating)return;localStateUpdating=!0;const courseEditor=(0,_courseeditor.getCurrentCourseEditor)();sections=[];for(const prop of Object.getOwnPropertyNames(sectionBoxes))delete sectionBoxes[prop];sections=[...courseEditor.stateManager.state.section.values()].sort(((a,b)=>a.number>b.number?1:-1)),moduleNames=[...courseEditor.stateManager.state.cm.values()];const sectionsUnfiltered=sections;sections=filterVisibleSections(sections),updateSelectionAndMoveToDropdowns(sections,sectionsUnfiltered),addCheckboxesToDataStructure(),localStateUpdating=!1};_exports.getSelectedModIds=()=>{const moduleIds=[];for(let sectionNumber in sectionBoxes)for(let i=0;i{const boxIds=[];if(void 0===sectionNumber||sectionNumber!==_massactionblock.constants.SECTION_SELECT_DESCRIPTION_VALUE){if(void 0!==sectionNumber&§ionNumber===_massactionblock.constants.SECTION_NUMBER_ALL_PLACEHOLDER)for(const sectionId in sectionBoxes)for(let j=0;jboxIds.push(box.boxId)));for(let i=0;i{sections.forEach((section=>{sectionBoxes[section.number]=[];const moduleIds=section.cmlist;if(moduleIds&&moduleIds.length>0&&""!==moduleIds[0]){moduleNames.filter((modinfo=>moduleIds.includes(modinfo.id.toString()))).forEach((modinfo=>{const boxId=_massactionblock.usedMoodleCssClasses.BOX_ID_PREFIX+modinfo.id.toString();sectionBoxes[section.number].push({moduleId:modinfo.id.toString(),boxId:boxId})}))}}))},filterVisibleSections=sections=>sections.filter((section=>0!==section.cmlist.length)).filter((section=>section.cmlist.every((moduleid=>null!==document.getElementById(_massactionblock.usedMoodleCssClasses.MODULE_ID_PREFIX+moduleid))))),updateSelectionAndMoveToDropdowns=(sections,sectionsUnfiltered)=>{sectionsChanged?(_templates.default.renderForPromise("block_massaction/section_select",{sections:sectionsUnfiltered}).then((_ref=>{let{html:html,js:js}=_ref;return _templates.default.replaceNode("#"+_massactionblock.cssIds.SECTION_SELECT,html,js),disableInvisibleAndEmptySections(sections),document.getElementById(_massactionblock.cssIds.SECTION_SELECT).addEventListener("change",(event=>setSectionSelection(!0,event.target.value)),!1),!0})).catch((ex=>(0,_notification.exception)(ex))),_templates.default.renderForPromise("block_massaction/moveto_select",{sections:sectionsUnfiltered}).then((_ref2=>{let{html:html,js:js}=_ref2;return _templates.default.replaceNode("#"+_massactionblock.cssIds.MOVETO_SELECT,html,js),!0})).catch((ex=>(0,_notification.exception)(ex))),_templates.default.renderForPromise("block_massaction/duplicateto_select",{sections:sectionsUnfiltered}).then((_ref3=>{let{html:html,js:js}=_ref3;return _templates.default.replaceNode("#"+_massactionblock.cssIds.DUPLICATETO_SELECT,html,js),!0})).catch((ex=>(0,_notification.exception)(ex)))):disableInvisibleAndEmptySections(sections),sectionsChanged=!1},disableInvisibleAndEmptySections=sections=>{Array.prototype.forEach.call(document.getElementById(_massactionblock.cssIds.SECTION_SELECT).options,(option=>{option.value===_massactionblock.constants.SECTION_SELECT_DESCRIPTION_VALUE||sections.some((section=>parseInt(option.value)===section.number))?option.disabled=!1:option.disabled=!0}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setSectionSelection=_exports.initCheckboxManager=_exports.getSelectedModIds=void 0,_templates=_interopRequireDefault(_templates),_events=_interopRequireDefault(_events);let localStateUpdating=!1,sectionsChanged=!1,sections=[],moduleNames=[];const sectionBoxes={};_exports.initCheckboxManager=sectionsRestricted=>{const courseEditor=(0,_courseeditor.getCurrentCourseEditor)(),eventsToListen_SECTION_UPDATED="section:updated",eventsToListen_CHANGE_FINISHED="transaction:end";courseEditor.stateManager.target.addEventListener(_events.default.stateChanged,(event=>{event.detail.action===eventsToListen_SECTION_UPDATED&&(sectionsChanged=!0),event.detail.action===eventsToListen_CHANGE_FINISHED&&rebuildLocalState(sectionsRestricted)})),sectionsChanged=!0,rebuildLocalState(sectionsRestricted)};const rebuildLocalState=sectionsRestricted=>{if(localStateUpdating)return;localStateUpdating=!0;const courseEditor=(0,_courseeditor.getCurrentCourseEditor)();sections=[];for(const prop of Object.getOwnPropertyNames(sectionBoxes))delete sectionBoxes[prop];sections=[...courseEditor.stateManager.state.section.values()].sort(((a,b)=>a.number>b.number?1:-1)),moduleNames=[...courseEditor.stateManager.state.cm.values()];const sectionsUnfiltered=sections;sections=filterVisibleSections(sections),updateSelectionAndMoveToDropdowns(sections,sectionsUnfiltered,sectionsRestricted),addCheckboxesToDataStructure(),localStateUpdating=!1};_exports.getSelectedModIds=()=>{const moduleIds=[];for(let sectionNumber in sectionBoxes)for(let i=0;i{const boxIds=[];if(void 0===sectionNumber||sectionNumber!==_massactionblock.constants.SECTION_SELECT_DESCRIPTION_VALUE){if(void 0!==sectionNumber&§ionNumber===_massactionblock.constants.SECTION_NUMBER_ALL_PLACEHOLDER)for(const sectionId in sectionBoxes)for(let j=0;jboxIds.push(box.boxId)));for(let i=0;i{sections.forEach((section=>{sectionBoxes[section.number]=[];const moduleIds=section.cmlist;if(moduleIds&&moduleIds.length>0&&""!==moduleIds[0]){moduleNames.filter((modinfo=>moduleIds.includes(modinfo.id.toString()))).forEach((modinfo=>{const boxId=_massactionblock.usedMoodleCssClasses.BOX_ID_PREFIX+modinfo.id.toString();sectionBoxes[section.number].push({moduleId:modinfo.id.toString(),boxId:boxId})}))}}))},filterVisibleSections=sections=>sections.filter((section=>0!==section.cmlist.length)).filter((section=>section.cmlist.every((moduleid=>null!==document.getElementById(_massactionblock.usedMoodleCssClasses.MODULE_ID_PREFIX+moduleid))))),updateSelectionAndMoveToDropdowns=(sections,sectionsUnfiltered,sectionsRestricted)=>{sectionsChanged?(_templates.default.renderForPromise("block_massaction/section_select",{sections:sectionsUnfiltered}).then((_ref=>{let{html:html,js:js}=_ref;return _templates.default.replaceNode("#"+_massactionblock.cssIds.SECTION_SELECT,html,js),disableInvisibleAndEmptySections(sections),document.getElementById(_massactionblock.cssIds.SECTION_SELECT).addEventListener("change",(event=>setSectionSelection(!0,event.target.value)),!1),!0})).catch((ex=>(0,_notification.exception)(ex))),_templates.default.renderForPromise("block_massaction/moveto_select",{sections:sectionsUnfiltered}).then((_ref2=>{let{html:html,js:js}=_ref2;return _templates.default.replaceNode("#"+_massactionblock.cssIds.MOVETO_SELECT,html,js),disableRestrictedSections(_massactionblock.cssIds.MOVETO_SELECT,sectionsRestricted),!0})).catch((ex=>(0,_notification.exception)(ex))),_templates.default.renderForPromise("block_massaction/duplicateto_select",{sections:sectionsUnfiltered}).then((_ref3=>{let{html:html,js:js}=_ref3;return _templates.default.replaceNode("#"+_massactionblock.cssIds.DUPLICATETO_SELECT,html,js),disableRestrictedSections(_massactionblock.cssIds.DUPLICATETO_SELECT,sectionsRestricted),!0})).catch((ex=>(0,_notification.exception)(ex)))):disableInvisibleAndEmptySections(sections),sectionsChanged=!1},disableInvisibleAndEmptySections=sections=>{Array.prototype.forEach.call(document.getElementById(_massactionblock.cssIds.SECTION_SELECT).options,(option=>{option.value===_massactionblock.constants.SECTION_SELECT_DESCRIPTION_VALUE||sections.some((section=>parseInt(option.value)===section.number))?option.disabled=!1:option.disabled=!0}))},disableRestrictedSections=(elementId,sectionsRestricted)=>{null!==document.getElementById(elementId)&&Array.prototype.forEach.call(document.getElementById(elementId).options,(option=>{sectionsRestricted.includes(parseInt(option.value))?option.disabled=!0:option.disabled=!1}))}})); //# sourceMappingURL=checkboxmanager.min.js.map \ No newline at end of file diff --git a/amd/build/checkboxmanager.min.js.map b/amd/build/checkboxmanager.min.js.map index df6d3e7..a252db0 100644 --- a/amd/build/checkboxmanager.min.js.map +++ b/amd/build/checkboxmanager.min.js.map @@ -1 +1 @@ -{"version":3,"file":"checkboxmanager.min.js","sources":["../src/checkboxmanager.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 * Checkbox manager amd module: Adds checkboxes to the activities for selecting and\n * generates a data structure of the activities and checkboxes.\n *\n * @module block_massaction/checkboxmanager\n * @copyright 2022 ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {exception as displayException} from 'core/notification';\nimport {cssIds, constants, usedMoodleCssClasses} from './massactionblock';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport events from 'core_course/events';\n\nlet localStateUpdating = false;\nlet sectionsChanged = false;\nlet sections = [];\nlet moduleNames = [];\n\n/* A registry of checkbox IDs, of the format:\n * 'section_number' => [{'moduleId' : ,\n * 'boxId' : }]\n */\nconst sectionBoxes = {};\n\n/**\n * The checkbox manager takes a given 'sections' data structure object and inserts a checkbox for each of the given\n * course modules in this data object into the DOM.\n * The checkbox manager returns another data object containing the ids of the added checkboxes.\n */\nexport const initCheckboxManager = () => {\n const courseEditor = getCurrentCourseEditor();\n\n const eventsToListen = {\n SECTION_UPDATED: 'section:updated',\n CHANGE_FINISHED: 'transaction:end'\n };\n\n courseEditor.stateManager.target.addEventListener(events.stateChanged, (event) => {\n if (event.detail.action === eventsToListen.SECTION_UPDATED) {\n // Listen to section updated events. We do not want to immediately react to the event, but wait for\n // everything to finish updating.\n sectionsChanged = true;\n }\n if (event.detail.action === eventsToListen.CHANGE_FINISHED) {\n // Before every change to the state there is a transaction:start event. After the change is being commited,\n // we receive an transaction:end event. That is the point we want to react to changes of the state.\n rebuildLocalState();\n }\n });\n // Trigger rendering of sections dropdowns a first time.\n sectionsChanged = true;\n // Get initial state.\n rebuildLocalState();\n};\n\n/**\n * This method rebuilds the local state maintained in this module based on the course editor state.\n *\n * It will be called whenever a change to the courseeditor state is being detected.\n */\nconst rebuildLocalState = () => {\n if (localStateUpdating) {\n return;\n }\n localStateUpdating = true;\n const courseEditor = getCurrentCourseEditor();\n\n // First we rebuild our data structures depending on the course editor state.\n sections = [];\n for (const prop of Object.getOwnPropertyNames(sectionBoxes)) {\n delete sectionBoxes[prop];\n }\n // The section map object is being sorted by section id. We have to sort after order in this course.\n sections = [...courseEditor.stateManager.state.section.values()].sort((a, b) => a.number > b.number ? 1 : -1);\n moduleNames = [...courseEditor.stateManager.state.cm.values()];\n\n // Now we use the new information to rebuild dropdowns and re-apply checkboxes.\n const sectionsUnfiltered = sections;\n sections = filterVisibleSections(sections);\n updateSelectionAndMoveToDropdowns(sections, sectionsUnfiltered);\n addCheckboxesToDataStructure();\n localStateUpdating = false;\n};\n\n/**\n * Returns the currently selected module ids.\n *\n * @returns {[]} Array of module ids currently being selected\n */\nexport const getSelectedModIds = () => {\n const moduleIds = [];\n for (let sectionNumber in sectionBoxes) {\n for (let i = 0; i < sectionBoxes[sectionNumber].length; i++) {\n const checkbox = document.getElementById(sectionBoxes[sectionNumber][i].boxId);\n if (checkbox.checked) {\n moduleIds.push(sectionBoxes[sectionNumber][i].moduleId);\n }\n }\n }\n return moduleIds;\n};\n\n/**\n * Select all module checkboxes in section(s).\n *\n * @param {boolean} value the checked value to set the checkboxes to\n * @param {string} sectionNumber the section number of the section which all modules should be checked/unchecked. Use \"all\" to\n * select/deselect modules in all sections.\n */\nexport const setSectionSelection = (value, sectionNumber) => {\n const boxIds = [];\n\n if (typeof sectionNumber !== 'undefined' && sectionNumber === constants.SECTION_SELECT_DESCRIPTION_VALUE) {\n // Description placeholder has been selected, do nothing.\n return;\n } else if (typeof sectionNumber !== 'undefined' && sectionNumber === constants.SECTION_NUMBER_ALL_PLACEHOLDER) {\n // See if we are toggling all sections.\n for (const sectionId in sectionBoxes) {\n for (let j = 0; j < sectionBoxes[sectionId].length; j++) {\n boxIds.push(sectionBoxes[sectionId][j].boxId);\n }\n }\n } else {\n // We select all boxes of the given section.\n sectionBoxes[sectionNumber].forEach(box => boxIds.push(box.boxId));\n }\n // Un/check the boxes.\n for (let i = 0; i < boxIds.length; i++) {\n document.getElementById(boxIds[i]).checked = value;\n }\n // Reset dropdown to standard placeholder so we trigger a change event when selecting a section, then deselecting\n // everything and again select the same section.\n document.getElementById(cssIds.SECTION_SELECT).value = constants.SECTION_SELECT_DESCRIPTION_VALUE;\n};\n\n/**\n * Scan all available checkboxes and add them to the data structure.\n */\nconst addCheckboxesToDataStructure = () => {\n sections.forEach(section => {\n sectionBoxes[section.number] = [];\n const moduleIds = section.cmlist;\n if (moduleIds && moduleIds.length > 0 && moduleIds[0] !== '') {\n const moduleNamesFiltered = moduleNames.filter(modinfo => moduleIds.includes(modinfo.id.toString()));\n moduleNamesFiltered.forEach(modinfo => {\n // Checkbox should already be created by moodle massactions. Just add it to our data structure.\n const boxId = usedMoodleCssClasses.BOX_ID_PREFIX + modinfo.id.toString();\n sectionBoxes[section.number].push({\n 'moduleId': modinfo.id.toString(),\n 'boxId': boxId,\n });\n });\n }\n });\n};\n\n/**\n * Filter the sections data object depending on the visibility of the course modules contained in\n * the data object. This is neccessary, because some course formats only show specific section(s)\n * in editing mode.\n *\n * @param {[]} sections the sections data object\n * @returns {[]} the filtered sections object\n */\nconst filterVisibleSections = (sections) => {\n // Filter all sections with modules which no checkboxes have been created for.\n // This case should only occur in course formats where some sections are hidden.\n return sections.filter(section => section.cmlist.length !== 0)\n .filter(section => section.cmlist\n .every(moduleid => document.getElementById(usedMoodleCssClasses.MODULE_ID_PREFIX + moduleid) !== null));\n};\n\n/**\n * Update the selection, moveto and duplicateto dropdowns of the massaction block according to the\n * previously filtered sections.\n *\n * This method also has to be called whenever there is a module change event (moving around, adding file by Drag&Drop etc.).\n *\n * @param {[]} sections the sections object filtered before by {@link filterVisibleSections}\n * @param {[]} sectionsUnfiltered the same data object as 'sections', but still containing all sections\n * no matter if containing modules or are visible in the current course format or not\n */\nconst updateSelectionAndMoveToDropdowns = (sections, sectionsUnfiltered) => {\n if (sectionsChanged) {\n Templates.renderForPromise('block_massaction/section_select', {'sections': sectionsUnfiltered})\n .then(({html, js}) => {\n Templates.replaceNode('#' + cssIds.SECTION_SELECT, html, js);\n disableInvisibleAndEmptySections(sections);\n // Re-register event listener.\n document.getElementById(cssIds.SECTION_SELECT).addEventListener('change',\n (event) => setSectionSelection(true, event.target.value), false);\n return true;\n })\n .catch(ex => displayException(ex));\n\n Templates.renderForPromise('block_massaction/moveto_select', {'sections': sectionsUnfiltered})\n .then(({html, js}) => {\n Templates.replaceNode('#' + cssIds.MOVETO_SELECT, html, js);\n return true;\n })\n .catch(ex => displayException(ex));\n\n Templates.renderForPromise('block_massaction/duplicateto_select', {'sections': sectionsUnfiltered})\n .then(({html, js}) => {\n Templates.replaceNode('#' + cssIds.DUPLICATETO_SELECT, html, js);\n return true;\n })\n .catch(ex => displayException(ex));\n } else {\n // If there has not been an event about a section change we do not have to rebuild the sections dropdowns.\n // However there is a chance an section is being emptied or not empty anymore due to drag&dropping of modules.\n // So we have to recalculate if we have to enable/disable the sections.\n disableInvisibleAndEmptySections(sections);\n }\n // Reset the flag.\n sectionsChanged = false;\n};\n\n/**\n * Sets the disabled/enabled status of sections in the section select dropdown:\n * Enabled if section is visible and contains modules.\n * Disabled if section is not visible or doesn't contain any modules.\n *\n * @param {[]} sections the section data structure\n */\nconst disableInvisibleAndEmptySections = (sections) => {\n Array.prototype.forEach.call(document.getElementById(cssIds.SECTION_SELECT).options, option => {\n // Disable every element which doesn't have a visible section, except the placeholder ('description').\n if (option.value !== constants.SECTION_SELECT_DESCRIPTION_VALUE\n && !sections.some(section => parseInt(option.value) === section.number)) {\n option.disabled = true;\n } else {\n option.disabled = false;\n }\n });\n};\n"],"names":["localStateUpdating","sectionsChanged","sections","moduleNames","sectionBoxes","courseEditor","eventsToListen","stateManager","target","addEventListener","events","stateChanged","event","detail","action","rebuildLocalState","prop","Object","getOwnPropertyNames","state","section","values","sort","a","b","number","cm","sectionsUnfiltered","filterVisibleSections","updateSelectionAndMoveToDropdowns","addCheckboxesToDataStructure","moduleIds","sectionNumber","i","length","document","getElementById","boxId","checked","push","moduleId","setSectionSelection","value","boxIds","constants","SECTION_SELECT_DESCRIPTION_VALUE","SECTION_NUMBER_ALL_PLACEHOLDER","sectionId","j","forEach","box","cssIds","SECTION_SELECT","cmlist","filter","modinfo","includes","id","toString","usedMoodleCssClasses","BOX_ID_PREFIX","every","moduleid","MODULE_ID_PREFIX","renderForPromise","then","_ref","html","js","replaceNode","disableInvisibleAndEmptySections","catch","ex","_ref2","MOVETO_SELECT","_ref3","DUPLICATETO_SELECT","Array","prototype","call","options","option","some","parseInt","disabled"],"mappings":";;;;;;;;;mPA+BIA,oBAAqB,EACrBC,iBAAkB,EAClBC,SAAW,GACXC,YAAc,SAMZC,aAAe,gCAOc,WACzBC,cAAe,0CAEfC,+BACe,kBADfA,+BAEe,kBAGrBD,aAAaE,aAAaC,OAAOC,iBAAiBC,gBAAOC,cAAeC,QAChEA,MAAMC,OAAOC,SAAWR,iCAGxBL,iBAAkB,GAElBW,MAAMC,OAAOC,SAAWR,gCAGxBS,uBAIRd,iBAAkB,EAElBc,2BAQEA,kBAAoB,QAClBf,0BAGJA,oBAAqB,QACfK,cAAe,0CAGrBH,SAAW,OACN,MAAMc,QAAQC,OAAOC,oBAAoBd,qBACnCA,aAAaY,MAGxBd,SAAW,IAAIG,aAAaE,aAAaY,MAAMC,QAAQC,UAAUC,MAAK,CAACC,EAAGC,IAAMD,EAAEE,OAASD,EAAEC,OAAS,GAAK,IAC3GtB,YAAc,IAAIE,aAAaE,aAAaY,MAAMO,GAAGL,gBAG/CM,mBAAqBzB,SAC3BA,SAAW0B,sBAAsB1B,UACjC2B,kCAAkC3B,SAAUyB,oBAC5CG,+BACA9B,oBAAqB,8BAQQ,WACvB+B,UAAY,OACb,IAAIC,iBAAiB5B,iBACjB,IAAI6B,EAAI,EAAGA,EAAI7B,aAAa4B,eAAeE,OAAQD,IAAK,CACxCE,SAASC,eAAehC,aAAa4B,eAAeC,GAAGI,OAC3DC,SACTP,UAAUQ,KAAKnC,aAAa4B,eAAeC,GAAGO,iBAInDT,iBAUEU,oBAAsB,CAACC,MAAOV,uBACjCW,OAAS,WAEc,IAAlBX,eAAiCA,gBAAkBY,2BAAUC,kCAGjE,QAA6B,IAAlBb,eAAiCA,gBAAkBY,2BAAUE,mCAEtE,MAAMC,aAAa3C,iBACf,IAAI4C,EAAI,EAAGA,EAAI5C,aAAa2C,WAAWb,OAAQc,IAChDL,OAAOJ,KAAKnC,aAAa2C,WAAWC,GAAGX,YAK/CjC,aAAa4B,eAAeiB,SAAQC,KAAOP,OAAOJ,KAAKW,IAAIb,aAG1D,IAAIJ,EAAI,EAAGA,EAAIU,OAAOT,OAAQD,IAC/BE,SAASC,eAAeO,OAAOV,IAAIK,QAAUI,MAIjDP,SAASC,eAAee,wBAAOC,gBAAgBV,MAAQE,2BAAUC,0FAM/Df,6BAA+B,KACjC5B,SAAS+C,SAAQ7B,UACbhB,aAAagB,QAAQK,QAAU,SACzBM,UAAYX,QAAQiC,UACtBtB,WAAaA,UAAUG,OAAS,GAAsB,KAAjBH,UAAU,GAAW,CAC9B5B,YAAYmD,QAAOC,SAAWxB,UAAUyB,SAASD,QAAQE,GAAGC,cACpET,SAAQM,gBAElBlB,MAAQsB,sCAAqBC,cAAgBL,QAAQE,GAAGC,WAC9DtD,aAAagB,QAAQK,QAAQc,KAAK,UAClBgB,QAAQE,GAAGC,iBACdrB,gBAevBT,sBAAyB1B,UAGpBA,SAASoD,QAAOlC,SAAqC,IAA1BA,QAAQiC,OAAOnB,SAC5CoB,QAAOlC,SAAWA,QAAQiC,OACtBQ,OAAMC,UAA0F,OAA9E3B,SAASC,eAAeuB,sCAAqBI,iBAAmBD,cAazFjC,kCAAoC,CAAC3B,SAAUyB,sBAC7C1B,oCACU+D,iBAAiB,kCAAmC,UAAarC,qBACtEsC,MAAKC,WAACC,KAACA,KAADC,GAAOA,mCACAC,YAAY,IAAMlB,wBAAOC,eAAgBe,KAAMC,IACzDE,iCAAiCpE,UAEjCiC,SAASC,eAAee,wBAAOC,gBAAgB3C,iBAAiB,UAC3DG,OAAU6B,qBAAoB,EAAM7B,MAAMJ,OAAOkC,SAAQ,IACvD,KAEV6B,OAAMC,KAAM,2BAAiBA,yBAExBR,iBAAiB,iCAAkC,UAAarC,qBACrEsC,MAAKQ,YAACN,KAACA,KAADC,GAAOA,oCACAC,YAAY,IAAMlB,wBAAOuB,cAAeP,KAAMC,KACjD,KAEVG,OAAMC,KAAM,2BAAiBA,yBAExBR,iBAAiB,sCAAuC,UAAarC,qBAC1EsC,MAAKU,YAACR,KAACA,KAADC,GAAOA,oCACAC,YAAY,IAAMlB,wBAAOyB,mBAAoBT,KAAMC,KACtD,KAEVG,OAAMC,KAAM,2BAAiBA,OAKlCF,iCAAiCpE,UAGrCD,iBAAkB,GAUhBqE,iCAAoCpE,WACtC2E,MAAMC,UAAU7B,QAAQ8B,KAAK5C,SAASC,eAAee,wBAAOC,gBAAgB4B,SAASC,SAE7EA,OAAOvC,QAAUE,2BAAUC,kCACnB3C,SAASgF,MAAK9D,SAAW+D,SAASF,OAAOvC,SAAWtB,QAAQK,SAGpEwD,OAAOG,UAAW,EAFlBH,OAAOG,UAAW"} \ No newline at end of file +{"version":3,"file":"checkboxmanager.min.js","sources":["../src/checkboxmanager.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 * Checkbox manager amd module: Adds checkboxes to the activities for selecting and\n * generates a data structure of the activities and checkboxes.\n *\n * @module block_massaction/checkboxmanager\n * @copyright 2022 ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {exception as displayException} from 'core/notification';\nimport {cssIds, constants, usedMoodleCssClasses} from './massactionblock';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport events from 'core_course/events';\n\nlet localStateUpdating = false;\nlet sectionsChanged = false;\nlet sections = [];\nlet moduleNames = [];\n\n/* A registry of checkbox IDs, of the format:\n * 'section_number' => [{'moduleId' : ,\n * 'boxId' : }]\n */\nconst sectionBoxes = {};\n\n/**\n * The checkbox manager takes a given 'sections' data structure object and inserts a checkbox for each of the given\n * course modules in this data object into the DOM.\n * The checkbox manager returns another data object containing the ids of the added checkboxes.\n * @param {[]} sectionsRestricted the sections which are restrected for the course format\n */\nexport const initCheckboxManager = sectionsRestricted => {\n const courseEditor = getCurrentCourseEditor();\n\n const eventsToListen = {\n SECTION_UPDATED: 'section:updated',\n CHANGE_FINISHED: 'transaction:end'\n };\n\n courseEditor.stateManager.target.addEventListener(events.stateChanged, (event) => {\n if (event.detail.action === eventsToListen.SECTION_UPDATED) {\n // Listen to section updated events. We do not want to immediately react to the event, but wait for\n // everything to finish updating.\n sectionsChanged = true;\n }\n if (event.detail.action === eventsToListen.CHANGE_FINISHED) {\n // Before every change to the state there is a transaction:start event. After the change is being commited,\n // we receive an transaction:end event. That is the point we want to react to changes of the state.\n rebuildLocalState(sectionsRestricted);\n }\n });\n // Trigger rendering of sections dropdowns a first time.\n sectionsChanged = true;\n // Get initial state.\n rebuildLocalState(sectionsRestricted);\n};\n\n/**\n * This method rebuilds the local state maintained in this module based on the course editor state.\n *\n * It will be called whenever a change to the courseeditor state is being detected.\n * @param {[]} sectionsRestricted the sections which are restrected for the course format\n */\nconst rebuildLocalState = sectionsRestricted => {\n if (localStateUpdating) {\n return;\n }\n localStateUpdating = true;\n const courseEditor = getCurrentCourseEditor();\n\n // First we rebuild our data structures depending on the course editor state.\n sections = [];\n for (const prop of Object.getOwnPropertyNames(sectionBoxes)) {\n delete sectionBoxes[prop];\n }\n // The section map object is being sorted by section id. We have to sort after order in this course.\n sections = [...courseEditor.stateManager.state.section.values()].sort((a, b) => a.number > b.number ? 1 : -1);\n moduleNames = [...courseEditor.stateManager.state.cm.values()];\n\n // Now we use the new information to rebuild dropdowns and re-apply checkboxes.\n const sectionsUnfiltered = sections;\n sections = filterVisibleSections(sections);\n updateSelectionAndMoveToDropdowns(sections, sectionsUnfiltered, sectionsRestricted);\n addCheckboxesToDataStructure();\n localStateUpdating = false;\n};\n\n/**\n * Returns the currently selected module ids.\n *\n * @returns {[]} Array of module ids currently being selected\n */\nexport const getSelectedModIds = () => {\n const moduleIds = [];\n for (let sectionNumber in sectionBoxes) {\n for (let i = 0; i < sectionBoxes[sectionNumber].length; i++) {\n const checkbox = document.getElementById(sectionBoxes[sectionNumber][i].boxId);\n if (checkbox.checked) {\n moduleIds.push(sectionBoxes[sectionNumber][i].moduleId);\n }\n }\n }\n return moduleIds;\n};\n\n/**\n * Select all module checkboxes in section(s).\n *\n * @param {boolean} value the checked value to set the checkboxes to\n * @param {string} sectionNumber the section number of the section which all modules should be checked/unchecked. Use \"all\" to\n * select/deselect modules in all sections.\n */\nexport const setSectionSelection = (value, sectionNumber) => {\n const boxIds = [];\n\n if (typeof sectionNumber !== 'undefined' && sectionNumber === constants.SECTION_SELECT_DESCRIPTION_VALUE) {\n // Description placeholder has been selected, do nothing.\n return;\n } else if (typeof sectionNumber !== 'undefined' && sectionNumber === constants.SECTION_NUMBER_ALL_PLACEHOLDER) {\n // See if we are toggling all sections.\n for (const sectionId in sectionBoxes) {\n for (let j = 0; j < sectionBoxes[sectionId].length; j++) {\n boxIds.push(sectionBoxes[sectionId][j].boxId);\n }\n }\n } else {\n // We select all boxes of the given section.\n sectionBoxes[sectionNumber].forEach(box => boxIds.push(box.boxId));\n }\n // Un/check the boxes.\n for (let i = 0; i < boxIds.length; i++) {\n document.getElementById(boxIds[i]).checked = value;\n }\n // Reset dropdown to standard placeholder so we trigger a change event when selecting a section, then deselecting\n // everything and again select the same section.\n document.getElementById(cssIds.SECTION_SELECT).value = constants.SECTION_SELECT_DESCRIPTION_VALUE;\n};\n\n/**\n * Scan all available checkboxes and add them to the data structure.\n */\nconst addCheckboxesToDataStructure = () => {\n sections.forEach(section => {\n sectionBoxes[section.number] = [];\n const moduleIds = section.cmlist;\n if (moduleIds && moduleIds.length > 0 && moduleIds[0] !== '') {\n const moduleNamesFiltered = moduleNames.filter(modinfo => moduleIds.includes(modinfo.id.toString()));\n moduleNamesFiltered.forEach(modinfo => {\n // Checkbox should already be created by moodle massactions. Just add it to our data structure.\n const boxId = usedMoodleCssClasses.BOX_ID_PREFIX + modinfo.id.toString();\n sectionBoxes[section.number].push({\n 'moduleId': modinfo.id.toString(),\n 'boxId': boxId,\n });\n });\n }\n });\n};\n\n/**\n * Filter the sections data object depending on the visibility of the course modules contained in\n * the data object. This is neccessary, because some course formats only show specific section(s)\n * in editing mode.\n *\n * @param {[]} sections the sections data object\n * @returns {[]} the filtered sections object\n */\nconst filterVisibleSections = (sections) => {\n // Filter all sections with modules which no checkboxes have been created for.\n // This case should only occur in course formats where some sections are hidden.\n return sections.filter(section => section.cmlist.length !== 0)\n .filter(section => section.cmlist\n .every(moduleid => document.getElementById(usedMoodleCssClasses.MODULE_ID_PREFIX + moduleid) !== null));\n};\n\n/**\n * Update the selection, moveto and duplicateto dropdowns of the massaction block according to the\n * previously filtered sections.\n *\n * This method also has to be called whenever there is a module change event (moving around, adding file by Drag&Drop etc.).\n *\n * @param {[]} sections the sections object filtered before by {@link filterVisibleSections}\n * @param {[]} sectionsUnfiltered the same data object as 'sections', but still containing all sections\n * @param {[]} sectionsRestricted the sections which are restrected for the course format\n * no matter if containing modules or are visible in the current course format or not\n */\nconst updateSelectionAndMoveToDropdowns = (sections, sectionsUnfiltered, sectionsRestricted) => {\n if (sectionsChanged) {\n Templates.renderForPromise('block_massaction/section_select', {'sections': sectionsUnfiltered})\n .then(({html, js}) => {\n Templates.replaceNode('#' + cssIds.SECTION_SELECT, html, js);\n disableInvisibleAndEmptySections(sections);\n // Re-register event listener.\n document.getElementById(cssIds.SECTION_SELECT).addEventListener('change',\n (event) => setSectionSelection(true, event.target.value), false);\n return true;\n })\n .catch(ex => displayException(ex));\n\n Templates.renderForPromise('block_massaction/moveto_select', {'sections': sectionsUnfiltered})\n .then(({html, js}) => {\n Templates.replaceNode('#' + cssIds.MOVETO_SELECT, html, js);\n disableRestrictedSections(cssIds.MOVETO_SELECT, sectionsRestricted);\n return true;\n })\n .catch(ex => displayException(ex));\n\n Templates.renderForPromise('block_massaction/duplicateto_select', {'sections': sectionsUnfiltered})\n .then(({html, js}) => {\n Templates.replaceNode('#' + cssIds.DUPLICATETO_SELECT, html, js);\n disableRestrictedSections(cssIds.DUPLICATETO_SELECT, sectionsRestricted);\n return true;\n })\n .catch(ex => displayException(ex));\n } else {\n // If there has not been an event about a section change we do not have to rebuild the sections dropdowns.\n // However there is a chance an section is being emptied or not empty anymore due to drag&dropping of modules.\n // So we have to recalculate if we have to enable/disable the sections.\n disableInvisibleAndEmptySections(sections);\n }\n // Reset the flag.\n sectionsChanged = false;\n};\n\n/**\n * Sets the disabled/enabled status of sections in the section select dropdown:\n * Enabled if section is visible and contains modules.\n * Disabled if section is not visible or doesn't contain any modules.\n *\n * @param {[]} sections the section data structure\n */\nconst disableInvisibleAndEmptySections = (sections) => {\n Array.prototype.forEach.call(document.getElementById(cssIds.SECTION_SELECT).options, option => {\n // Disable every element which doesn't have a visible section, except the placeholder ('description').\n if (option.value !== constants.SECTION_SELECT_DESCRIPTION_VALUE\n && !sections.some(section => parseInt(option.value) === section.number)) {\n option.disabled = true;\n } else {\n option.disabled = false;\n }\n });\n};\n\n/**\n * Sets the disabled/enabled status of sections in the section select dropdown\n * by sectionsRestricted param\n *\n * @param {string} elementId elementId to apply the restriction\n * @param {[]} sectionsRestricted the sections which are restrected for the course format\n */\nconst disableRestrictedSections = (elementId, sectionsRestricted) => {\n if (document.getElementById(elementId) !== null) {\n Array.prototype.forEach.call(document.getElementById(elementId).options, option => {\n // Disable every element which is in the sectionsRestricted list.\n if (sectionsRestricted.includes(parseInt(option.value))) {\n option.disabled = true;\n } else {\n option.disabled = false;\n }\n });\n }\n};\n"],"names":["localStateUpdating","sectionsChanged","sections","moduleNames","sectionBoxes","sectionsRestricted","courseEditor","eventsToListen","stateManager","target","addEventListener","events","stateChanged","event","detail","action","rebuildLocalState","prop","Object","getOwnPropertyNames","state","section","values","sort","a","b","number","cm","sectionsUnfiltered","filterVisibleSections","updateSelectionAndMoveToDropdowns","addCheckboxesToDataStructure","moduleIds","sectionNumber","i","length","document","getElementById","boxId","checked","push","moduleId","setSectionSelection","value","boxIds","constants","SECTION_SELECT_DESCRIPTION_VALUE","SECTION_NUMBER_ALL_PLACEHOLDER","sectionId","j","forEach","box","cssIds","SECTION_SELECT","cmlist","filter","modinfo","includes","id","toString","usedMoodleCssClasses","BOX_ID_PREFIX","every","moduleid","MODULE_ID_PREFIX","renderForPromise","then","_ref","html","js","replaceNode","disableInvisibleAndEmptySections","catch","ex","_ref2","MOVETO_SELECT","disableRestrictedSections","_ref3","DUPLICATETO_SELECT","Array","prototype","call","options","option","some","parseInt","disabled","elementId"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/massactionblock.min.js b/amd/build/massactionblock.min.js index c34f368..665fde2 100644 --- a/amd/build/massactionblock.min.js +++ b/amd/build/massactionblock.min.js @@ -6,6 +6,6 @@ define("block_massaction/massactionblock",["exports","block_massaction/checkboxm * @copyright 2022 ISB Bayern * @author Philipp Memmel * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.usedMoodleCssClasses=_exports.init=_exports.cssIds=_exports.constants=void 0,checkboxmanager=_interopRequireWildcard(checkboxmanager),Str=_interopRequireWildcard(Str),_log=_interopRequireDefault(_log),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_events=_interopRequireDefault(_events);_exports.usedMoodleCssClasses={ACTIVITY_ITEM:".activity-item",SECTION_NAME:"sectionname",MODULE_ID_PREFIX:"module-",BOX_ID_PREFIX:"cmCheckbox"};const cssIds={BLOCK_CONTENT:"block-massaction",BULK_EDITING_DISABLED:"block-massaction-bulk-editing-disabled",SELECT_ALL_LINK:"block-massaction-control-selectall",DESELECT_ALL_LINK:"block-massaction-control-deselectall",HIDE_LINK:"block-massaction-action-hide",SHOW_LINK:"block-massaction-action-show",MAKE_AVAILABLE_LINK:"block-massaction-action-makeavailable",DUPLICATE_LINK:"block-massaction-action-duplicate",DELETE_LINK:"block-massaction-action-delete",SHOW_DESCRIPTION_LINK:"block-massaction-action-showdescription",HIDE_DESCRIPTION_LINK:"block-massaction-action-hidedescription",CONTENT_CHANGED_NOTIFICATION_LINK:"block-massaction-action-contentchangednotification",MOVELEFT_LINK:"block-massaction-action-moveleft",MOVERIGHT_LINK:"block-massaction-action-moveright",MOVETO_ICON_LINK:"block-massaction-action-moveto",DUPLICATETO_ICON_LINK:"block-massaction-action-duplicateto",DUPLICATE_TO_COURSE_ICON_LINK:"block-massaction-action-duplicatetocourse",SECTION_SELECT:"block-massaction-control-section-list-select",MOVETO_SELECT:"block-massaction-control-section-list-moveto",DUPLICATETO_SELECT:"block-massaction-control-section-list-duplicateto",HIDDEN_FIELD_REQUEST_INFORMATION:"block-massaction-control-request",ACTION_FORM:"block-massaction-control-form"};_exports.cssIds=cssIds;const constants={SECTION_SELECT_DESCRIPTION_VALUE:"description",SECTION_NUMBER_ALL_PLACEHOLDER:"all"};_exports.constants=constants;const actions_HIDE="hide",actions_SHOW="show",actions_MAKE_AVAILABLE="makeavailable",actions_DUPLICATE="duplicate",actions_DELETE="delete",actions_SHOW_DESCRIPTION="showdescription",actions_HIDE_DESCRIPTION="hidedescription",actions_MOVE_LEFT="moveleft",actions_MOVE_RIGHT="moveright",actions_CONTENT_CHANGED_NOTIFICATION="contentchangednotification",actions_MOVE_TO="moveto",actions_DUPLICATE_TO="duplicateto",actions_DUPLICATE_TO_COURSE="duplicatetocourse";_exports.init=async()=>{var _document$getElementB3,_document$getElementB4,_document$getElementB5,_document$getElementB6,_document$getElementB7,_document$getElementB8,_document$getElementB9,_document$getElementB10,_document$getElementB11,_document$getElementB12,_document$getElementB13,_document$getElementB14,_document$getElementB15,_document$getElementB16,_document$getElementB17;const pendingPromise=new _pending.default("block_massaction/init"),editor=(0,_courseeditor.getCurrentCourseEditor)();editor.stateManager.getInitialPromise().then((()=>{checkboxmanager.initCheckboxManager(),editor.stateManager.target.addEventListener(_events.default.stateChanged,(event=>{var _document$getElementB,_document$getElementB2;"bulk.enabled:updated"===event.detail.action&&(null===(_document$getElementB=document.getElementById(cssIds.BLOCK_CONTENT))||void 0===_document$getElementB||_document$getElementB.classList.toggle("d-none"),null===(_document$getElementB2=document.getElementById(cssIds.BULK_EDITING_DISABLED))||void 0===_document$getElementB2||_document$getElementB2.classList.toggle("d-none"))}));const enableBulkButton=document.getElementById("block-massaction-enable-bulk-editing");return enableBulkButton.disabled=!1,null==enableBulkButton||enableBulkButton.addEventListener("click",(()=>editor.dispatch("bulkEnable",!0))),!0})).catch((error=>_log.default.debug(error))),null===(_document$getElementB3=document.getElementById(cssIds.SELECT_ALL_LINK))||void 0===_document$getElementB3||_document$getElementB3.addEventListener("click",(()=>checkboxmanager.setSectionSelection(!0,constants.SECTION_NUMBER_ALL_PLACEHOLDER)),!1),null===(_document$getElementB4=document.getElementById(cssIds.DESELECT_ALL_LINK))||void 0===_document$getElementB4||_document$getElementB4.addEventListener("click",(()=>checkboxmanager.setSectionSelection(!1,constants.SECTION_NUMBER_ALL_PLACEHOLDER)),!1),null===(_document$getElementB5=document.getElementById(cssIds.HIDE_LINK))||void 0===_document$getElementB5||_document$getElementB5.addEventListener("click",(()=>submitAction(actions_HIDE)),!1),null===(_document$getElementB6=document.getElementById(cssIds.SHOW_LINK))||void 0===_document$getElementB6||_document$getElementB6.addEventListener("click",(()=>submitAction(actions_SHOW)),!1),null===(_document$getElementB7=document.getElementById(cssIds.MAKE_AVAILABLE_LINK))||void 0===_document$getElementB7||_document$getElementB7.addEventListener("click",(()=>submitAction(actions_MAKE_AVAILABLE)),!1),null===(_document$getElementB8=document.getElementById(cssIds.DUPLICATE_LINK))||void 0===_document$getElementB8||_document$getElementB8.addEventListener("click",(()=>submitAction(actions_DUPLICATE)),!1),null===(_document$getElementB9=document.getElementById(cssIds.DELETE_LINK))||void 0===_document$getElementB9||_document$getElementB9.addEventListener("click",(()=>submitAction(actions_DELETE)),!1),null===(_document$getElementB10=document.getElementById(cssIds.SHOW_DESCRIPTION_LINK))||void 0===_document$getElementB10||_document$getElementB10.addEventListener("click",(()=>submitAction(actions_SHOW_DESCRIPTION)),!1),null===(_document$getElementB11=document.getElementById(cssIds.HIDE_DESCRIPTION_LINK))||void 0===_document$getElementB11||_document$getElementB11.addEventListener("click",(()=>submitAction(actions_HIDE_DESCRIPTION)),!1),null===(_document$getElementB12=document.getElementById(cssIds.CONTENT_CHANGED_NOTIFICATION_LINK))||void 0===_document$getElementB12||_document$getElementB12.addEventListener("click",(()=>submitAction(actions_CONTENT_CHANGED_NOTIFICATION)),!1),null===(_document$getElementB13=document.getElementById(cssIds.MOVELEFT_LINK))||void 0===_document$getElementB13||_document$getElementB13.addEventListener("click",(()=>submitAction(actions_MOVE_LEFT)),!1),null===(_document$getElementB14=document.getElementById(cssIds.MOVERIGHT_LINK))||void 0===_document$getElementB14||_document$getElementB14.addEventListener("click",(()=>submitAction(actions_MOVE_RIGHT)),!1),null===(_document$getElementB15=document.getElementById(cssIds.MOVETO_ICON_LINK))||void 0===_document$getElementB15||_document$getElementB15.addEventListener("click",(()=>submitAction(actions_MOVE_TO)),!1),null===(_document$getElementB16=document.getElementById(cssIds.DUPLICATETO_ICON_LINK))||void 0===_document$getElementB16||_document$getElementB16.addEventListener("click",(()=>submitAction(actions_DUPLICATE_TO)),!1),null===(_document$getElementB17=document.getElementById(cssIds.DUPLICATE_TO_COURSE_ICON_LINK))||void 0===_document$getElementB17||_document$getElementB17.addEventListener("click",(()=>submitAction(actions_DUPLICATE_TO_COURSE)),!1),pendingPromise.resolve()};const submitAction=action=>{const submitData={action:action,moduleIds:[]};if(submitData.moduleIds=checkboxmanager.getSelectedModIds(),0===submitData.moduleIds.length)return displayError(Str.get_string("noitemselected","block_massaction")),!1;switch(action){case actions_HIDE:case actions_SHOW:case actions_MAKE_AVAILABLE:case actions_DUPLICATE:case actions_DUPLICATE_TO_COURSE:case actions_CONTENT_CHANGED_NOTIFICATION:case actions_MOVE_LEFT:case actions_MOVE_RIGHT:case actions_DELETE:case actions_SHOW_DESCRIPTION:case actions_HIDE_DESCRIPTION:break;case actions_MOVE_TO:if(submitData.moveToTarget=document.getElementById(cssIds.MOVETO_SELECT).value,""===submitData.moveToTarget.trim())return displayError(Str.get_string("nomovingtargetselected","block_massaction")),!1;break;case actions_DUPLICATE_TO:if(submitData.duplicateToTarget=document.getElementById(cssIds.DUPLICATETO_SELECT).value,""===submitData.duplicateToTarget.trim())return displayError(Str.get_string("nomovingtargetselected","block_massaction")),!1;break;default:return displayError("Unknown action: "+action+". Coding error."),!1}return document.getElementById(cssIds.HIDDEN_FIELD_REQUEST_INFORMATION).value=JSON.stringify(submitData),document.getElementById(cssIds.ACTION_FORM).submit(),!0},displayError=errorText=>{Promise.resolve([Str.get_string("error","core"),errorText,Str.get_string("back","core")]).then((text=>_notification.default.alert(text[0],text[1],text[2]))).catch((error=>_log.default.debug(error)))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.usedMoodleCssClasses=_exports.init=_exports.cssIds=_exports.constants=void 0,checkboxmanager=_interopRequireWildcard(checkboxmanager),Str=_interopRequireWildcard(Str),_log=_interopRequireDefault(_log),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_events=_interopRequireDefault(_events);_exports.usedMoodleCssClasses={ACTIVITY_ITEM:".activity-item",SECTION_NAME:"sectionname",MODULE_ID_PREFIX:"module-",BOX_ID_PREFIX:"cmCheckbox"};const cssIds={BLOCK_CONTENT:"block-massaction",BULK_EDITING_DISABLED:"block-massaction-bulk-editing-disabled",SELECT_ALL_LINK:"block-massaction-control-selectall",DESELECT_ALL_LINK:"block-massaction-control-deselectall",HIDE_LINK:"block-massaction-action-hide",SHOW_LINK:"block-massaction-action-show",MAKE_AVAILABLE_LINK:"block-massaction-action-makeavailable",DUPLICATE_LINK:"block-massaction-action-duplicate",DELETE_LINK:"block-massaction-action-delete",SHOW_DESCRIPTION_LINK:"block-massaction-action-showdescription",HIDE_DESCRIPTION_LINK:"block-massaction-action-hidedescription",CONTENT_CHANGED_NOTIFICATION_LINK:"block-massaction-action-contentchangednotification",MOVELEFT_LINK:"block-massaction-action-moveleft",MOVERIGHT_LINK:"block-massaction-action-moveright",MOVETO_ICON_LINK:"block-massaction-action-moveto",DUPLICATETO_ICON_LINK:"block-massaction-action-duplicateto",DUPLICATE_TO_COURSE_ICON_LINK:"block-massaction-action-duplicatetocourse",SECTION_SELECT:"block-massaction-control-section-list-select",MOVETO_SELECT:"block-massaction-control-section-list-moveto",DUPLICATETO_SELECT:"block-massaction-control-section-list-duplicateto",HIDDEN_FIELD_REQUEST_INFORMATION:"block-massaction-control-request",ACTION_FORM:"block-massaction-control-form"};_exports.cssIds=cssIds;const constants={SECTION_SELECT_DESCRIPTION_VALUE:"description",SECTION_NUMBER_ALL_PLACEHOLDER:"all"};_exports.constants=constants;const actions_HIDE="hide",actions_SHOW="show",actions_MAKE_AVAILABLE="makeavailable",actions_DUPLICATE="duplicate",actions_DELETE="delete",actions_SHOW_DESCRIPTION="showdescription",actions_HIDE_DESCRIPTION="hidedescription",actions_MOVE_LEFT="moveleft",actions_MOVE_RIGHT="moveright",actions_CONTENT_CHANGED_NOTIFICATION="contentchangednotification",actions_MOVE_TO="moveto",actions_DUPLICATE_TO="duplicateto",actions_DUPLICATE_TO_COURSE="duplicatetocourse";_exports.init=async sectionsRestricted=>{var _document$getElementB3,_document$getElementB4,_document$getElementB5,_document$getElementB6,_document$getElementB7,_document$getElementB8,_document$getElementB9,_document$getElementB10,_document$getElementB11,_document$getElementB12,_document$getElementB13,_document$getElementB14,_document$getElementB15,_document$getElementB16,_document$getElementB17;const pendingPromise=new _pending.default("block_massaction/init"),editor=(0,_courseeditor.getCurrentCourseEditor)();editor.stateManager.getInitialPromise().then((()=>{checkboxmanager.initCheckboxManager(sectionsRestricted),editor.stateManager.target.addEventListener(_events.default.stateChanged,(event=>{var _document$getElementB,_document$getElementB2;"bulk.enabled:updated"===event.detail.action&&(null===(_document$getElementB=document.getElementById(cssIds.BLOCK_CONTENT))||void 0===_document$getElementB||_document$getElementB.classList.toggle("d-none"),null===(_document$getElementB2=document.getElementById(cssIds.BULK_EDITING_DISABLED))||void 0===_document$getElementB2||_document$getElementB2.classList.toggle("d-none"))}));const enableBulkButton=document.getElementById("block-massaction-enable-bulk-editing");return enableBulkButton.disabled=!1,null==enableBulkButton||enableBulkButton.addEventListener("click",(()=>editor.dispatch("bulkEnable",!0))),!0})).catch((error=>_log.default.debug(error))),null===(_document$getElementB3=document.getElementById(cssIds.SELECT_ALL_LINK))||void 0===_document$getElementB3||_document$getElementB3.addEventListener("click",(()=>checkboxmanager.setSectionSelection(!0,constants.SECTION_NUMBER_ALL_PLACEHOLDER)),!1),null===(_document$getElementB4=document.getElementById(cssIds.DESELECT_ALL_LINK))||void 0===_document$getElementB4||_document$getElementB4.addEventListener("click",(()=>checkboxmanager.setSectionSelection(!1,constants.SECTION_NUMBER_ALL_PLACEHOLDER)),!1),null===(_document$getElementB5=document.getElementById(cssIds.HIDE_LINK))||void 0===_document$getElementB5||_document$getElementB5.addEventListener("click",(()=>submitAction(actions_HIDE)),!1),null===(_document$getElementB6=document.getElementById(cssIds.SHOW_LINK))||void 0===_document$getElementB6||_document$getElementB6.addEventListener("click",(()=>submitAction(actions_SHOW)),!1),null===(_document$getElementB7=document.getElementById(cssIds.MAKE_AVAILABLE_LINK))||void 0===_document$getElementB7||_document$getElementB7.addEventListener("click",(()=>submitAction(actions_MAKE_AVAILABLE)),!1),null===(_document$getElementB8=document.getElementById(cssIds.DUPLICATE_LINK))||void 0===_document$getElementB8||_document$getElementB8.addEventListener("click",(()=>submitAction(actions_DUPLICATE)),!1),null===(_document$getElementB9=document.getElementById(cssIds.DELETE_LINK))||void 0===_document$getElementB9||_document$getElementB9.addEventListener("click",(()=>submitAction(actions_DELETE)),!1),null===(_document$getElementB10=document.getElementById(cssIds.SHOW_DESCRIPTION_LINK))||void 0===_document$getElementB10||_document$getElementB10.addEventListener("click",(()=>submitAction(actions_SHOW_DESCRIPTION)),!1),null===(_document$getElementB11=document.getElementById(cssIds.HIDE_DESCRIPTION_LINK))||void 0===_document$getElementB11||_document$getElementB11.addEventListener("click",(()=>submitAction(actions_HIDE_DESCRIPTION)),!1),null===(_document$getElementB12=document.getElementById(cssIds.CONTENT_CHANGED_NOTIFICATION_LINK))||void 0===_document$getElementB12||_document$getElementB12.addEventListener("click",(()=>submitAction(actions_CONTENT_CHANGED_NOTIFICATION)),!1),null===(_document$getElementB13=document.getElementById(cssIds.MOVELEFT_LINK))||void 0===_document$getElementB13||_document$getElementB13.addEventListener("click",(()=>submitAction(actions_MOVE_LEFT)),!1),null===(_document$getElementB14=document.getElementById(cssIds.MOVERIGHT_LINK))||void 0===_document$getElementB14||_document$getElementB14.addEventListener("click",(()=>submitAction(actions_MOVE_RIGHT)),!1),null===(_document$getElementB15=document.getElementById(cssIds.MOVETO_ICON_LINK))||void 0===_document$getElementB15||_document$getElementB15.addEventListener("click",(()=>submitAction(actions_MOVE_TO)),!1),null===(_document$getElementB16=document.getElementById(cssIds.DUPLICATETO_ICON_LINK))||void 0===_document$getElementB16||_document$getElementB16.addEventListener("click",(()=>submitAction(actions_DUPLICATE_TO)),!1),null===(_document$getElementB17=document.getElementById(cssIds.DUPLICATE_TO_COURSE_ICON_LINK))||void 0===_document$getElementB17||_document$getElementB17.addEventListener("click",(()=>submitAction(actions_DUPLICATE_TO_COURSE)),!1),pendingPromise.resolve()};const submitAction=action=>{const submitData={action:action,moduleIds:[]};if(submitData.moduleIds=checkboxmanager.getSelectedModIds(),0===submitData.moduleIds.length)return displayError(Str.get_string("noitemselected","block_massaction")),!1;switch(action){case actions_HIDE:case actions_SHOW:case actions_MAKE_AVAILABLE:case actions_DUPLICATE:case actions_DUPLICATE_TO_COURSE:case actions_CONTENT_CHANGED_NOTIFICATION:case actions_MOVE_LEFT:case actions_MOVE_RIGHT:case actions_DELETE:case actions_SHOW_DESCRIPTION:case actions_HIDE_DESCRIPTION:break;case actions_MOVE_TO:if(submitData.moveToTarget=document.getElementById(cssIds.MOVETO_SELECT).value,""===submitData.moveToTarget.trim())return displayError(Str.get_string("nomovingtargetselected","block_massaction")),!1;break;case actions_DUPLICATE_TO:if(submitData.duplicateToTarget=document.getElementById(cssIds.DUPLICATETO_SELECT).value,""===submitData.duplicateToTarget.trim())return displayError(Str.get_string("nomovingtargetselected","block_massaction")),!1;break;default:return displayError("Unknown action: "+action+". Coding error."),!1}return document.getElementById(cssIds.HIDDEN_FIELD_REQUEST_INFORMATION).value=JSON.stringify(submitData),document.getElementById(cssIds.ACTION_FORM).submit(),!0},displayError=errorText=>{Promise.resolve([Str.get_string("error","core"),errorText,Str.get_string("back","core")]).then((text=>_notification.default.alert(text[0],text[1],text[2]))).catch((error=>_log.default.debug(error)))}})); //# sourceMappingURL=massactionblock.min.js.map \ No newline at end of file diff --git a/amd/build/massactionblock.min.js.map b/amd/build/massactionblock.min.js.map index 8f4753c..5631201 100644 --- a/amd/build/massactionblock.min.js.map +++ b/amd/build/massactionblock.min.js.map @@ -1 +1 @@ -{"version":3,"file":"massactionblock.min.js","sources":["../src/massactionblock.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 * Main module for the massaction block.\n *\n * @module block_massaction/massactionblock\n * @copyright 2022 ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as checkboxmanager from 'block_massaction/checkboxmanager';\nimport * as Str from 'core/str';\nimport Log from 'core/log';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport events from \"core_course/events\";\n\nexport const usedMoodleCssClasses = {\n ACTIVITY_ITEM: '.activity-item',\n SECTION_NAME: 'sectionname',\n MODULE_ID_PREFIX: 'module-',\n BOX_ID_PREFIX: 'cmCheckbox'\n};\n\nexport const cssIds = {\n BLOCK_CONTENT: 'block-massaction',\n BULK_EDITING_DISABLED: 'block-massaction-bulk-editing-disabled',\n SELECT_ALL_LINK: 'block-massaction-control-selectall',\n DESELECT_ALL_LINK: 'block-massaction-control-deselectall',\n HIDE_LINK: 'block-massaction-action-hide',\n SHOW_LINK: 'block-massaction-action-show',\n MAKE_AVAILABLE_LINK: 'block-massaction-action-makeavailable',\n DUPLICATE_LINK: 'block-massaction-action-duplicate',\n DELETE_LINK: 'block-massaction-action-delete',\n SHOW_DESCRIPTION_LINK: 'block-massaction-action-showdescription',\n HIDE_DESCRIPTION_LINK: 'block-massaction-action-hidedescription',\n CONTENT_CHANGED_NOTIFICATION_LINK: 'block-massaction-action-contentchangednotification',\n MOVELEFT_LINK: 'block-massaction-action-moveleft',\n MOVERIGHT_LINK: 'block-massaction-action-moveright',\n MOVETO_ICON_LINK: 'block-massaction-action-moveto',\n DUPLICATETO_ICON_LINK: 'block-massaction-action-duplicateto',\n DUPLICATE_TO_COURSE_ICON_LINK: 'block-massaction-action-duplicatetocourse',\n SECTION_SELECT: 'block-massaction-control-section-list-select',\n MOVETO_SELECT: 'block-massaction-control-section-list-moveto',\n DUPLICATETO_SELECT: 'block-massaction-control-section-list-duplicateto',\n HIDDEN_FIELD_REQUEST_INFORMATION: 'block-massaction-control-request',\n ACTION_FORM: 'block-massaction-control-form',\n};\n\nexport const constants = {\n SECTION_SELECT_DESCRIPTION_VALUE: 'description',\n SECTION_NUMBER_ALL_PLACEHOLDER: 'all',\n};\n\nconst actions = {\n HIDE: 'hide',\n SHOW: 'show',\n MAKE_AVAILABLE: 'makeavailable',\n DUPLICATE: 'duplicate',\n DELETE: 'delete',\n SHOW_DESCRIPTION: 'showdescription',\n HIDE_DESCRIPTION: 'hidedescription',\n MOVE_LEFT: 'moveleft',\n MOVE_RIGHT: 'moveright',\n CONTENT_CHANGED_NOTIFICATION: 'contentchangednotification',\n MOVE_TO: 'moveto',\n DUPLICATE_TO: 'duplicateto',\n DUPLICATE_TO_COURSE: 'duplicatetocourse',\n};\n\n/**\n * Initialize the mass-action block.\n */\nexport const init = async() => {\n const pendingPromise = new Pending('block_massaction/init');\n\n const editor = getCurrentCourseEditor();\n // As soon as courseeditor is available, do some initial setup.\n editor.stateManager.getInitialPromise()\n .then(() => {\n // Initialize the checkbox manager.\n checkboxmanager.initCheckboxManager();\n\n // Show block depending on if the moodle bulk editing util has been activated.\n editor.stateManager.target.addEventListener(events.stateChanged, (event) => {\n // Listen to the event that bulk editing mode has been enabled/disabled.\n if (event.detail.action === 'bulk.enabled:updated') {\n // Hide/show block content depending on the bulk editing enabled state.\n document.getElementById(cssIds.BLOCK_CONTENT)?.classList.toggle('d-none');\n document.getElementById(cssIds.BULK_EDITING_DISABLED)?.classList.toggle('d-none');\n }\n });\n\n // Register click handler for the button in the placeholder text if bulk editing is still disabled.\n const enableBulkButton = document.getElementById('block-massaction-enable-bulk-editing');\n // Remove the initial disabled attribute which is there to avoid too early clicks by users.\n enableBulkButton.disabled = false;\n enableBulkButton?.addEventListener('click', () => editor.dispatch('bulkEnable', true));\n return true;\n })\n .catch(error => Log.debug(error));\n\n document.getElementById(cssIds.SELECT_ALL_LINK)?.addEventListener('click',\n () => checkboxmanager.setSectionSelection(true, constants.SECTION_NUMBER_ALL_PLACEHOLDER), false);\n\n document.getElementById(cssIds.DESELECT_ALL_LINK)?.addEventListener('click',\n () => checkboxmanager.setSectionSelection(false, constants.SECTION_NUMBER_ALL_PLACEHOLDER), false);\n\n document.getElementById(cssIds.HIDE_LINK)?.addEventListener('click',\n () => submitAction(actions.HIDE), false);\n\n document.getElementById(cssIds.SHOW_LINK)?.addEventListener('click',\n () => submitAction(actions.SHOW), false);\n\n document.getElementById(cssIds.MAKE_AVAILABLE_LINK)?.addEventListener('click',\n () => submitAction(actions.MAKE_AVAILABLE), false);\n\n document.getElementById(cssIds.DUPLICATE_LINK)?.addEventListener('click',\n () => submitAction(actions.DUPLICATE), false);\n\n document.getElementById(cssIds.DELETE_LINK)?.addEventListener('click',\n () => submitAction(actions.DELETE), false);\n\n document.getElementById(cssIds.SHOW_DESCRIPTION_LINK)?.addEventListener('click',\n () => submitAction(actions.SHOW_DESCRIPTION), false);\n\n document.getElementById(cssIds.HIDE_DESCRIPTION_LINK)?.addEventListener('click',\n () => submitAction(actions.HIDE_DESCRIPTION), false);\n\n document.getElementById(cssIds.CONTENT_CHANGED_NOTIFICATION_LINK)?.addEventListener('click',\n () => submitAction(actions.CONTENT_CHANGED_NOTIFICATION), false);\n\n document.getElementById(cssIds.MOVELEFT_LINK)?.addEventListener('click',\n () => submitAction(actions.MOVE_LEFT), false);\n\n document.getElementById(cssIds.MOVERIGHT_LINK)?.addEventListener('click',\n () => submitAction(actions.MOVE_RIGHT), false);\n\n document.getElementById(cssIds.MOVETO_ICON_LINK)?.addEventListener('click',\n () => submitAction(actions.MOVE_TO), false);\n\n document.getElementById(cssIds.DUPLICATETO_ICON_LINK)?.addEventListener('click',\n () => submitAction(actions.DUPLICATE_TO), false);\n\n document.getElementById(cssIds.DUPLICATE_TO_COURSE_ICON_LINK)?.addEventListener('click',\n () => submitAction(actions.DUPLICATE_TO_COURSE), false);\n\n pendingPromise.resolve();\n};\n\n/**\n * Submit the selected action to server.\n *\n * @param {string} action\n * @return {boolean} true if action was successful, false otherwise\n */\nconst submitAction = (action) => {\n const submitData = {\n 'action': action,\n 'moduleIds': []\n };\n\n submitData.moduleIds = checkboxmanager.getSelectedModIds();\n\n // Verify that at least one checkbox is checked.\n if (submitData.moduleIds.length === 0) {\n displayError(Str.get_string('noitemselected', 'block_massaction'));\n return false;\n }\n\n // Prep the submission.\n switch (action) {\n case actions.HIDE:\n case actions.SHOW:\n case actions.MAKE_AVAILABLE:\n case actions.DUPLICATE:\n case actions.DUPLICATE_TO_COURSE:\n case actions.CONTENT_CHANGED_NOTIFICATION:\n case actions.MOVE_LEFT:\n case actions.MOVE_RIGHT:\n case actions.DELETE:\n case actions.SHOW_DESCRIPTION:\n case actions.HIDE_DESCRIPTION:\n break;\n\n case actions.MOVE_TO:\n // Get the target section.\n submitData.moveToTarget = document.getElementById(cssIds.MOVETO_SELECT).value;\n if (submitData.moveToTarget.trim() === '') {\n displayError(Str.get_string('nomovingtargetselected', 'block_massaction'));\n return false;\n }\n break;\n\n case actions.DUPLICATE_TO:\n // Get the target section.\n submitData.duplicateToTarget = document.getElementById(cssIds.DUPLICATETO_SELECT).value;\n if (submitData.duplicateToTarget.trim() === '') {\n displayError(Str.get_string('nomovingtargetselected', 'block_massaction'));\n return false;\n }\n break;\n default:\n displayError('Unknown action: ' + action + '. Coding error.');\n return false;\n }\n // Set the form value and submit.\n document.getElementById(cssIds.HIDDEN_FIELD_REQUEST_INFORMATION).value = JSON.stringify(submitData);\n document.getElementById(cssIds.ACTION_FORM).submit();\n return true;\n};\n\nconst displayError = (errorText) => {\n Promise.resolve([Str.get_string('error', 'core'), errorText, Str.get_string('back', 'core')])\n .then(text => Notification.alert(text[0], text[1], text[2]))\n .catch(error => Log.debug(error));\n};\n"],"names":["ACTIVITY_ITEM","SECTION_NAME","MODULE_ID_PREFIX","BOX_ID_PREFIX","cssIds","BLOCK_CONTENT","BULK_EDITING_DISABLED","SELECT_ALL_LINK","DESELECT_ALL_LINK","HIDE_LINK","SHOW_LINK","MAKE_AVAILABLE_LINK","DUPLICATE_LINK","DELETE_LINK","SHOW_DESCRIPTION_LINK","HIDE_DESCRIPTION_LINK","CONTENT_CHANGED_NOTIFICATION_LINK","MOVELEFT_LINK","MOVERIGHT_LINK","MOVETO_ICON_LINK","DUPLICATETO_ICON_LINK","DUPLICATE_TO_COURSE_ICON_LINK","SECTION_SELECT","MOVETO_SELECT","DUPLICATETO_SELECT","HIDDEN_FIELD_REQUEST_INFORMATION","ACTION_FORM","constants","SECTION_SELECT_DESCRIPTION_VALUE","SECTION_NUMBER_ALL_PLACEHOLDER","actions","async","pendingPromise","Pending","editor","stateManager","getInitialPromise","then","checkboxmanager","initCheckboxManager","target","addEventListener","events","stateChanged","event","detail","action","document","getElementById","classList","toggle","enableBulkButton","disabled","dispatch","catch","error","Log","debug","setSectionSelection","submitAction","resolve","submitData","moduleIds","getSelectedModIds","length","displayError","Str","get_string","moveToTarget","value","trim","duplicateToTarget","JSON","stringify","submit","errorText","Promise","text","Notification","alert"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"massactionblock.min.js","sources":["../src/massactionblock.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 * Main module for the massaction block.\n *\n * @module block_massaction/massactionblock\n * @copyright 2022 ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as checkboxmanager from 'block_massaction/checkboxmanager';\nimport * as Str from 'core/str';\nimport Log from 'core/log';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport events from \"core_course/events\";\n\nexport const usedMoodleCssClasses = {\n ACTIVITY_ITEM: '.activity-item',\n SECTION_NAME: 'sectionname',\n MODULE_ID_PREFIX: 'module-',\n BOX_ID_PREFIX: 'cmCheckbox'\n};\n\nexport const cssIds = {\n BLOCK_CONTENT: 'block-massaction',\n BULK_EDITING_DISABLED: 'block-massaction-bulk-editing-disabled',\n SELECT_ALL_LINK: 'block-massaction-control-selectall',\n DESELECT_ALL_LINK: 'block-massaction-control-deselectall',\n HIDE_LINK: 'block-massaction-action-hide',\n SHOW_LINK: 'block-massaction-action-show',\n MAKE_AVAILABLE_LINK: 'block-massaction-action-makeavailable',\n DUPLICATE_LINK: 'block-massaction-action-duplicate',\n DELETE_LINK: 'block-massaction-action-delete',\n SHOW_DESCRIPTION_LINK: 'block-massaction-action-showdescription',\n HIDE_DESCRIPTION_LINK: 'block-massaction-action-hidedescription',\n CONTENT_CHANGED_NOTIFICATION_LINK: 'block-massaction-action-contentchangednotification',\n MOVELEFT_LINK: 'block-massaction-action-moveleft',\n MOVERIGHT_LINK: 'block-massaction-action-moveright',\n MOVETO_ICON_LINK: 'block-massaction-action-moveto',\n DUPLICATETO_ICON_LINK: 'block-massaction-action-duplicateto',\n DUPLICATE_TO_COURSE_ICON_LINK: 'block-massaction-action-duplicatetocourse',\n SECTION_SELECT: 'block-massaction-control-section-list-select',\n MOVETO_SELECT: 'block-massaction-control-section-list-moveto',\n DUPLICATETO_SELECT: 'block-massaction-control-section-list-duplicateto',\n HIDDEN_FIELD_REQUEST_INFORMATION: 'block-massaction-control-request',\n ACTION_FORM: 'block-massaction-control-form',\n};\n\nexport const constants = {\n SECTION_SELECT_DESCRIPTION_VALUE: 'description',\n SECTION_NUMBER_ALL_PLACEHOLDER: 'all',\n};\n\nconst actions = {\n HIDE: 'hide',\n SHOW: 'show',\n MAKE_AVAILABLE: 'makeavailable',\n DUPLICATE: 'duplicate',\n DELETE: 'delete',\n SHOW_DESCRIPTION: 'showdescription',\n HIDE_DESCRIPTION: 'hidedescription',\n MOVE_LEFT: 'moveleft',\n MOVE_RIGHT: 'moveright',\n CONTENT_CHANGED_NOTIFICATION: 'contentchangednotification',\n MOVE_TO: 'moveto',\n DUPLICATE_TO: 'duplicateto',\n DUPLICATE_TO_COURSE: 'duplicatetocourse',\n};\n\n/**\n * Initialize the mass-action block.\n * @param {[]} sectionsRestricted the sections which are restrected for the course format\n */\nexport const init = async(sectionsRestricted) => {\n const pendingPromise = new Pending('block_massaction/init');\n\n const editor = getCurrentCourseEditor();\n // As soon as courseeditor is available, do some initial setup.\n editor.stateManager.getInitialPromise()\n .then(() => {\n // Initialize the checkbox manager.\n checkboxmanager.initCheckboxManager(sectionsRestricted);\n\n // Show block depending on if the moodle bulk editing util has been activated.\n editor.stateManager.target.addEventListener(events.stateChanged, (event) => {\n // Listen to the event that bulk editing mode has been enabled/disabled.\n if (event.detail.action === 'bulk.enabled:updated') {\n // Hide/show block content depending on the bulk editing enabled state.\n document.getElementById(cssIds.BLOCK_CONTENT)?.classList.toggle('d-none');\n document.getElementById(cssIds.BULK_EDITING_DISABLED)?.classList.toggle('d-none');\n }\n });\n\n // Register click handler for the button in the placeholder text if bulk editing is still disabled.\n const enableBulkButton = document.getElementById('block-massaction-enable-bulk-editing');\n // Remove the initial disabled attribute which is there to avoid too early clicks by users.\n enableBulkButton.disabled = false;\n enableBulkButton?.addEventListener('click', () => editor.dispatch('bulkEnable', true));\n return true;\n })\n .catch(error => Log.debug(error));\n\n document.getElementById(cssIds.SELECT_ALL_LINK)?.addEventListener('click',\n () => checkboxmanager.setSectionSelection(true, constants.SECTION_NUMBER_ALL_PLACEHOLDER), false);\n\n document.getElementById(cssIds.DESELECT_ALL_LINK)?.addEventListener('click',\n () => checkboxmanager.setSectionSelection(false, constants.SECTION_NUMBER_ALL_PLACEHOLDER), false);\n\n document.getElementById(cssIds.HIDE_LINK)?.addEventListener('click',\n () => submitAction(actions.HIDE), false);\n\n document.getElementById(cssIds.SHOW_LINK)?.addEventListener('click',\n () => submitAction(actions.SHOW), false);\n\n document.getElementById(cssIds.MAKE_AVAILABLE_LINK)?.addEventListener('click',\n () => submitAction(actions.MAKE_AVAILABLE), false);\n\n document.getElementById(cssIds.DUPLICATE_LINK)?.addEventListener('click',\n () => submitAction(actions.DUPLICATE), false);\n\n document.getElementById(cssIds.DELETE_LINK)?.addEventListener('click',\n () => submitAction(actions.DELETE), false);\n\n document.getElementById(cssIds.SHOW_DESCRIPTION_LINK)?.addEventListener('click',\n () => submitAction(actions.SHOW_DESCRIPTION), false);\n\n document.getElementById(cssIds.HIDE_DESCRIPTION_LINK)?.addEventListener('click',\n () => submitAction(actions.HIDE_DESCRIPTION), false);\n\n document.getElementById(cssIds.CONTENT_CHANGED_NOTIFICATION_LINK)?.addEventListener('click',\n () => submitAction(actions.CONTENT_CHANGED_NOTIFICATION), false);\n\n document.getElementById(cssIds.MOVELEFT_LINK)?.addEventListener('click',\n () => submitAction(actions.MOVE_LEFT), false);\n\n document.getElementById(cssIds.MOVERIGHT_LINK)?.addEventListener('click',\n () => submitAction(actions.MOVE_RIGHT), false);\n\n document.getElementById(cssIds.MOVETO_ICON_LINK)?.addEventListener('click',\n () => submitAction(actions.MOVE_TO), false);\n\n document.getElementById(cssIds.DUPLICATETO_ICON_LINK)?.addEventListener('click',\n () => submitAction(actions.DUPLICATE_TO), false);\n\n document.getElementById(cssIds.DUPLICATE_TO_COURSE_ICON_LINK)?.addEventListener('click',\n () => submitAction(actions.DUPLICATE_TO_COURSE), false);\n\n pendingPromise.resolve();\n};\n\n/**\n * Submit the selected action to server.\n *\n * @param {string} action\n * @return {boolean} true if action was successful, false otherwise\n */\nconst submitAction = (action) => {\n const submitData = {\n 'action': action,\n 'moduleIds': []\n };\n\n submitData.moduleIds = checkboxmanager.getSelectedModIds();\n\n // Verify that at least one checkbox is checked.\n if (submitData.moduleIds.length === 0) {\n displayError(Str.get_string('noitemselected', 'block_massaction'));\n return false;\n }\n\n // Prep the submission.\n switch (action) {\n case actions.HIDE:\n case actions.SHOW:\n case actions.MAKE_AVAILABLE:\n case actions.DUPLICATE:\n case actions.DUPLICATE_TO_COURSE:\n case actions.CONTENT_CHANGED_NOTIFICATION:\n case actions.MOVE_LEFT:\n case actions.MOVE_RIGHT:\n case actions.DELETE:\n case actions.SHOW_DESCRIPTION:\n case actions.HIDE_DESCRIPTION:\n break;\n\n case actions.MOVE_TO:\n // Get the target section.\n submitData.moveToTarget = document.getElementById(cssIds.MOVETO_SELECT).value;\n if (submitData.moveToTarget.trim() === '') {\n displayError(Str.get_string('nomovingtargetselected', 'block_massaction'));\n return false;\n }\n break;\n\n case actions.DUPLICATE_TO:\n // Get the target section.\n submitData.duplicateToTarget = document.getElementById(cssIds.DUPLICATETO_SELECT).value;\n if (submitData.duplicateToTarget.trim() === '') {\n displayError(Str.get_string('nomovingtargetselected', 'block_massaction'));\n return false;\n }\n break;\n default:\n displayError('Unknown action: ' + action + '. Coding error.');\n return false;\n }\n // Set the form value and submit.\n document.getElementById(cssIds.HIDDEN_FIELD_REQUEST_INFORMATION).value = JSON.stringify(submitData);\n document.getElementById(cssIds.ACTION_FORM).submit();\n return true;\n};\n\nconst displayError = (errorText) => {\n Promise.resolve([Str.get_string('error', 'core'), errorText, Str.get_string('back', 'core')])\n .then(text => Notification.alert(text[0], text[1], text[2]))\n .catch(error => Log.debug(error));\n};\n"],"names":["ACTIVITY_ITEM","SECTION_NAME","MODULE_ID_PREFIX","BOX_ID_PREFIX","cssIds","BLOCK_CONTENT","BULK_EDITING_DISABLED","SELECT_ALL_LINK","DESELECT_ALL_LINK","HIDE_LINK","SHOW_LINK","MAKE_AVAILABLE_LINK","DUPLICATE_LINK","DELETE_LINK","SHOW_DESCRIPTION_LINK","HIDE_DESCRIPTION_LINK","CONTENT_CHANGED_NOTIFICATION_LINK","MOVELEFT_LINK","MOVERIGHT_LINK","MOVETO_ICON_LINK","DUPLICATETO_ICON_LINK","DUPLICATE_TO_COURSE_ICON_LINK","SECTION_SELECT","MOVETO_SELECT","DUPLICATETO_SELECT","HIDDEN_FIELD_REQUEST_INFORMATION","ACTION_FORM","constants","SECTION_SELECT_DESCRIPTION_VALUE","SECTION_NUMBER_ALL_PLACEHOLDER","actions","async","pendingPromise","Pending","editor","stateManager","getInitialPromise","then","checkboxmanager","initCheckboxManager","sectionsRestricted","target","addEventListener","events","stateChanged","event","detail","action","document","getElementById","classList","toggle","enableBulkButton","disabled","dispatch","catch","error","Log","debug","setSectionSelection","submitAction","resolve","submitData","moduleIds","getSelectedModIds","length","displayError","Str","get_string","moveToTarget","value","trim","duplicateToTarget","JSON","stringify","submit","errorText","Promise","text","Notification","alert"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/checkboxmanager.js b/amd/src/checkboxmanager.js index 3c4f967..0aafff7 100644 --- a/amd/src/checkboxmanager.js +++ b/amd/src/checkboxmanager.js @@ -44,8 +44,9 @@ const sectionBoxes = {}; * The checkbox manager takes a given 'sections' data structure object and inserts a checkbox for each of the given * course modules in this data object into the DOM. * The checkbox manager returns another data object containing the ids of the added checkboxes. + * @param {[]} sectionsRestricted the sections which are restrected for the course format */ -export const initCheckboxManager = () => { +export const initCheckboxManager = sectionsRestricted => { const courseEditor = getCurrentCourseEditor(); const eventsToListen = { @@ -62,21 +63,22 @@ export const initCheckboxManager = () => { if (event.detail.action === eventsToListen.CHANGE_FINISHED) { // Before every change to the state there is a transaction:start event. After the change is being commited, // we receive an transaction:end event. That is the point we want to react to changes of the state. - rebuildLocalState(); + rebuildLocalState(sectionsRestricted); } }); // Trigger rendering of sections dropdowns a first time. sectionsChanged = true; // Get initial state. - rebuildLocalState(); + rebuildLocalState(sectionsRestricted); }; /** * This method rebuilds the local state maintained in this module based on the course editor state. * * It will be called whenever a change to the courseeditor state is being detected. + * @param {[]} sectionsRestricted the sections which are restrected for the course format */ -const rebuildLocalState = () => { +const rebuildLocalState = sectionsRestricted => { if (localStateUpdating) { return; } @@ -95,7 +97,7 @@ const rebuildLocalState = () => { // Now we use the new information to rebuild dropdowns and re-apply checkboxes. const sectionsUnfiltered = sections; sections = filterVisibleSections(sections); - updateSelectionAndMoveToDropdowns(sections, sectionsUnfiltered); + updateSelectionAndMoveToDropdowns(sections, sectionsUnfiltered, sectionsRestricted); addCheckboxesToDataStructure(); localStateUpdating = false; }; @@ -196,9 +198,10 @@ const filterVisibleSections = (sections) => { * * @param {[]} sections the sections object filtered before by {@link filterVisibleSections} * @param {[]} sectionsUnfiltered the same data object as 'sections', but still containing all sections + * @param {[]} sectionsRestricted the sections which are restrected for the course format * no matter if containing modules or are visible in the current course format or not */ -const updateSelectionAndMoveToDropdowns = (sections, sectionsUnfiltered) => { +const updateSelectionAndMoveToDropdowns = (sections, sectionsUnfiltered, sectionsRestricted) => { if (sectionsChanged) { Templates.renderForPromise('block_massaction/section_select', {'sections': sectionsUnfiltered}) .then(({html, js}) => { @@ -214,6 +217,7 @@ const updateSelectionAndMoveToDropdowns = (sections, sectionsUnfiltered) => { Templates.renderForPromise('block_massaction/moveto_select', {'sections': sectionsUnfiltered}) .then(({html, js}) => { Templates.replaceNode('#' + cssIds.MOVETO_SELECT, html, js); + disableRestrictedSections(cssIds.MOVETO_SELECT, sectionsRestricted); return true; }) .catch(ex => displayException(ex)); @@ -221,6 +225,7 @@ const updateSelectionAndMoveToDropdowns = (sections, sectionsUnfiltered) => { Templates.renderForPromise('block_massaction/duplicateto_select', {'sections': sectionsUnfiltered}) .then(({html, js}) => { Templates.replaceNode('#' + cssIds.DUPLICATETO_SELECT, html, js); + disableRestrictedSections(cssIds.DUPLICATETO_SELECT, sectionsRestricted); return true; }) .catch(ex => displayException(ex)); @@ -252,3 +257,23 @@ const disableInvisibleAndEmptySections = (sections) => { } }); }; + +/** + * Sets the disabled/enabled status of sections in the section select dropdown + * by sectionsRestricted param + * + * @param {string} elementId elementId to apply the restriction + * @param {[]} sectionsRestricted the sections which are restrected for the course format + */ +const disableRestrictedSections = (elementId, sectionsRestricted) => { + if (document.getElementById(elementId) !== null) { + Array.prototype.forEach.call(document.getElementById(elementId).options, option => { + // Disable every element which is in the sectionsRestricted list. + if (sectionsRestricted.includes(parseInt(option.value))) { + option.disabled = true; + } else { + option.disabled = false; + } + }); + } +}; diff --git a/amd/src/massactionblock.js b/amd/src/massactionblock.js index a5072f0..48d159c 100644 --- a/amd/src/massactionblock.js +++ b/amd/src/massactionblock.js @@ -85,8 +85,9 @@ const actions = { /** * Initialize the mass-action block. + * @param {[]} sectionsRestricted the sections which are restrected for the course format */ -export const init = async() => { +export const init = async(sectionsRestricted) => { const pendingPromise = new Pending('block_massaction/init'); const editor = getCurrentCourseEditor(); @@ -94,7 +95,7 @@ export const init = async() => { editor.stateManager.getInitialPromise() .then(() => { // Initialize the checkbox manager. - checkboxmanager.initCheckboxManager(); + checkboxmanager.initCheckboxManager(sectionsRestricted); // Show block depending on if the moodle bulk editing util has been activated. editor.stateManager.target.addEventListener(events.stateChanged, (event) => { diff --git a/block_massaction.php b/block_massaction.php index 97ccb59..163e9dd 100644 --- a/block_massaction.php +++ b/block_massaction.php @@ -127,7 +127,8 @@ public function get_content(): stdClass { } // Initialize the JS module. - $this->page->requires->js_call_amd('block_massaction/massactionblock', 'init'); + $sectionsrestricted = \block_massaction\massactionutils::get_restricted_sections($COURSE->id, $COURSE->format); + $this->page->requires->js_call_amd('block_massaction/massactionblock', 'init', [$sectionsrestricted]); $context = context_course::instance($COURSE->id); // Actions to be rendered later on. diff --git a/classes/actions.php b/classes/actions.php index fe70891..fd986e2 100644 --- a/classes/actions.php +++ b/classes/actions.php @@ -147,9 +147,19 @@ public static function duplicate(array $modules, $sectionnumber = false): void { // sorted by their id: // Let order of mods in a section be mod1, mod2, mod3, mod4, mod5. If we duplicate mod2, mod4, the order afterwards will be // mod1, mod2, mod3, mod4, mod5, mod2(dup), mod4(dup). + $cms = []; $errors = []; + $duplicatedmods = []; + $targetformat = course_get_format($courseid); + $sectionsrestricted = massactionutils::get_restricted_sections($courseid, $targetformat->get_format()); foreach ($idsincourseorder as $cmid) { + $cm = $modinfo->get_cm($cmid); + // Not duplicated if the section is restricted. + if (in_array($cm->sectionnum, $sectionsrestricted)) { + throw new moodle_exception('sectionrestricted', 'block_massaction'); + } + try { $duplicatedmod = duplicate_module($modinfo->get_course(), $modinfo->get_cm($cmid)); } catch (\Exception $e) { @@ -288,7 +298,15 @@ public static function duplicate_to_course(array $modules, int $targetcourseid, $duplicatedmods = []; $cms = []; $errors = []; + $sourceformat = course_get_format($sourcecourseid); + $sourcesectionsrestricted = massactionutils::get_restricted_sections($sourcecourseid, $sourceformat->get_format()); foreach ($idsincourseorder as $cmid) { + $sourcecm = $sourcemodinfo->get_cm($cmid); + // Not duplicated if the section is restricted. + if (in_array($sourcecm->sectionnum, $sourcesectionsrestricted)) { + throw new moodle_exception('sectionrestricted', 'block_massaction'); + } + try { $duplicatedmod = massactionutils::duplicate_cm_to_course($targetmodinfo->get_course(), $sourcemodinfo->get_cm($cmid)); @@ -545,6 +563,11 @@ public static function perform_moveto(array $modules, int $target): void { require_once($CFG->dirroot . '/course/lib.php'); $idsincourseorder = self::sort_course_order($modules); + $sectionsrestricted = []; + if (!empty($modules)) { + $targetformat = course_get_format(reset($modules)->course); + $sectionsrestricted = massactionutils::get_restricted_sections(reset($modules)->course, $targetformat->get_format()); + } foreach ($idsincourseorder as $cmid) { if (!$cm = get_coursemodule_from_id('', $cmid, 0, true)) { @@ -556,6 +579,11 @@ public static function perform_moveto(array $modules, int $target): void { throw new moodle_exception('sectionnotexist', 'block_massaction'); } + // Not moving if the section is restricted. + if (in_array($cm->sectionnum, $sectionsrestricted)) { + throw new moodle_exception('sectionrestricted', 'block_massaction'); + } + // Move each module to the end of their section. moveto_module($cm, $section); } diff --git a/classes/form/section_select_form.php b/classes/form/section_select_form.php index 87ec1c3..0be261a 100644 --- a/classes/form/section_select_form.php +++ b/classes/form/section_select_form.php @@ -25,6 +25,7 @@ namespace block_massaction\form; +use block_massaction\massactionutils; use core\output\notification; use moodleform; @@ -117,10 +118,15 @@ public function definition() { get_string('keepsectionnum', 'block_massaction'), -1, ['class' => 'mt-2']); } + $sectionsrestricted = massactionutils::get_restricted_sections($targetcourseid, $targetformat->get_format()); // Now add the sections of the target course. foreach ($targetsections as $sectionnum => $sectionname) { + $attributes = ['class' => 'mt-2']; + if (in_array($sectionnum, $sectionsrestricted)) { + $attributes['disabled'] = 'disabled'; + } $radioarray[] = $mform->createElement('radio', 'targetsectionnum', - '', $sectionname, $sectionnum, ['class' => 'mt-2']); + '', $sectionname, $sectionnum, $attributes); } if ($canaddsection) { diff --git a/classes/massactionutils.php b/classes/massactionutils.php index d3f87b1..e1cb0ed 100644 --- a/classes/massactionutils.php +++ b/classes/massactionutils.php @@ -164,4 +164,21 @@ public static function duplicate_cm_to_course(object $course, object $cm): int { } return $newcmid; } + + /** + * Get array of restricted sections from course format callback. + * Example return values from pluginname_massaction_restricted_sections: [1, 3, 5] + * + * @param int $courseid + * @param string $format + * @return array + */ + public static function get_restricted_sections($courseid, $format): array { + $sectionsrestricted = []; + $callbacks = get_plugins_with_function('massaction_restricted_sections'); + if (!empty($callbacks['format'][$format])) { + $sectionsrestricted = $callbacks['format'][$format]($courseid); + } + return $sectionsrestricted; + } } diff --git a/lang/en/block_massaction.php b/lang/en/block_massaction.php index 10a32ba..284b234 100644 --- a/lang/en/block_massaction.php +++ b/lang/en/block_massaction.php @@ -98,6 +98,7 @@ $string['nomovingtargetselected'] = 'Please select a target section'; $string['notargetcourseidspecified'] = 'No target course id has been specified'; $string['sectionnotexist'] = 'Target section does not exist'; +$string['sectionrestricted'] = 'Source section is restricted'; $string['sourcecourseidlost'] = 'Source course id could not be found'; $string['applicablecourseformats'] = 'Applicable course formats'; $string['applicablecourseformats_description'] = 'Mass Actions block will only be available for the selected course formats.
Preselected defaults are the ones tested and supported by the plugin maintainer. Add other formats at your own risk.';