From 6df5ccb42d81754c2d1bd524efe8f0bb367be2b4 Mon Sep 17 00:00:00 2001 From: Philipp Memmel Date: Sun, 27 Aug 2023 12:12:10 +0200 Subject: [PATCH] Refactoring to use moodle core bulk editing --- .github/workflows/ci.yml | 33 +++-------- amd/build/checkboxmanager.min.js | 2 +- amd/build/checkboxmanager.min.js.map | 2 +- amd/build/massactionblock.min.js | 4 +- amd/build/massactionblock.min.js.map | 2 +- amd/src/checkboxmanager.js | 59 +++----------------- amd/src/massactionblock.js | 33 +++++++++-- lang/en/block_massaction.php | 2 + templates/block_massaction.mustache | 8 ++- tests/behat/actions.feature | 83 +++++++++++++++++----------- tests/behat/core.feature | 49 ++++++++-------- tests/behat/grid.feature | 18 +++--- tests/behat/onetopic.feature | 56 ++++++++++++++++--- tests/behat/tiles.feature | 18 +++--- tests/behat/topcoll.feature | 30 +++++----- 15 files changed, 215 insertions(+), 184 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d23672d..51a6d47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,38 +31,23 @@ jobs: fail-fast: false matrix: include: - - php: '8.0' + - php: '8.2' moodle-branch: 'master' database: 'pgsql' - - php: '8.0' - moodle-branch: 'MOODLE_401_STABLE' - database: 'mariadb' - - php: '8.0' - moodle-branch: 'MOODLE_401_STABLE' - database: 'pgsql' - - php: '7.4' - moodle-branch: 'MOODLE_401_STABLE' - database: 'mariadb' - - php: '7.4' - moodle-branch: 'MOODLE_401_STABLE' + - php: '8.1' + moodle-branch: 'master' database: 'pgsql' - php: '8.0' - moodle-branch: 'MOODLE_400_STABLE' + moodle-branch: 'MOODLE_402_STABLE' database: 'mariadb' - php: '8.0' - moodle-branch: 'MOODLE_400_STABLE' - database: 'pgsql' - - php: '7.4' - moodle-branch: 'MOODLE_400_STABLE' - database: 'mariadb' - - php: '7.4' - moodle-branch: 'MOODLE_400_STABLE' + moodle-branch: 'MOODLE_402_STABLE' database: 'pgsql' - - php: '7.3' - moodle-branch: 'MOODLE_400_STABLE' + - php: '8.1' + moodle-branch: 'MOODLE_402_STABLE' database: 'mariadb' - - php: '7.3' - moodle-branch: 'MOODLE_400_STABLE' + - php: '8.1' + moodle-branch: 'MOODLE_402_STABLE' database: 'pgsql' steps: diff --git a/amd/build/checkboxmanager.min.js b/amd/build/checkboxmanager.min.js index dbd11fa..b323029 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),addCheckboxes(),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=>{addCheckboxToModule(section.number,modinfo.id.toString(),modinfo.name)}))}}))},addCheckboxToModule=(sectionNumber,moduleId,moduleName)=>{const boxId=_massactionblock.cssIds.BOX_ID_PREFIX+moduleId;let additionalCssClass,moduleElement=document.getElementById(_massactionblock.usedMoodleCssClasses.MODULE_ID_PREFIX+moduleId).querySelector(_massactionblock.usedMoodleCssClasses.ACTIVITY_ITEM);if(moduleElement||(moduleElement=document.getElementById(_massactionblock.usedMoodleCssClasses.MODULE_ID_PREFIX+moduleId),additionalCssClass="block-massaction-checkbox-legacy"),null===document.getElementById(boxId)){const checkBoxElement=document.createElement("input");if(checkBoxElement.type="checkbox",checkBoxElement.className=_massactionblock.cssIds.CHECKBOX_CLASS,additionalCssClass&&checkBoxElement.classList.add(additionalCssClass),checkBoxElement.id=boxId,null!==moduleElement){const checkboxDescription=moduleName+_massactionblock.constants.CHECKBOX_DESCRIPTION_SUFFIX;checkBoxElement.ariaLabel=checkboxDescription,checkBoxElement.name=checkboxDescription,moduleElement.insertBefore(checkBoxElement,moduleElement.firstChild)}}sectionBoxes[sectionNumber].push({moduleId:moduleId,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=()=>{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}))}})); //# 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 5dc9dea..df6d3e7 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 addCheckboxes();\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 * Add checkboxes to all sections.\n */\nconst addCheckboxes = () => {\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 addCheckboxToModule(section.number, modinfo.id.toString(), modinfo.name);\n });\n }\n });\n};\n\n/**\n * Add a checkbox to a module element\n *\n * @param {number} sectionNumber number of the section of the current course module\n * @param {number} moduleId id of the current course module\n * @param {string} moduleName name of the course module specified by moduleId\n */\nconst addCheckboxToModule = (sectionNumber, moduleId, moduleName) => {\n const boxId = cssIds.BOX_ID_PREFIX + moduleId;\n let moduleElement = document.getElementById(usedMoodleCssClasses.MODULE_ID_PREFIX + moduleId)\n .querySelector(usedMoodleCssClasses.ACTIVITY_ITEM);\n // This additional class is only needed when we are using a legacy (pre moodle 4.0) course format.\n let additionalCssClass;\n if (!moduleElement) {\n // Should only happen in legacy formats (pre moodle 4.0).\n moduleElement = document.getElementById(usedMoodleCssClasses.MODULE_ID_PREFIX + moduleId);\n additionalCssClass = 'block-massaction-checkbox-legacy';\n }\n\n // Avoid creating duplicate checkboxes.\n if (document.getElementById(boxId) === null) {\n // Add the checkbox.\n const checkBoxElement = document.createElement('input');\n checkBoxElement.type = 'checkbox';\n checkBoxElement.className = cssIds.CHECKBOX_CLASS;\n if (additionalCssClass) {\n checkBoxElement.classList.add(additionalCssClass);\n }\n checkBoxElement.id = boxId;\n\n if (moduleElement !== null) {\n const checkboxDescription = moduleName + constants.CHECKBOX_DESCRIPTION_SUFFIX;\n checkBoxElement.ariaLabel = checkboxDescription;\n checkBoxElement.name = checkboxDescription;\n // Finally add the created checkbox element.\n moduleElement.insertBefore(checkBoxElement, moduleElement.firstChild);\n }\n }\n\n // Add the newly created checkbox to our data structure.\n sectionBoxes[sectionNumber].push({\n 'moduleId': moduleId,\n 'boxId': boxId,\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","addCheckboxes","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","addCheckboxToModule","name","moduleName","BOX_ID_PREFIX","additionalCssClass","moduleElement","usedMoodleCssClasses","MODULE_ID_PREFIX","querySelector","ACTIVITY_ITEM","checkBoxElement","createElement","type","className","CHECKBOX_CLASS","classList","add","checkboxDescription","CHECKBOX_DESCRIPTION_SUFFIX","ariaLabel","insertBefore","firstChild","every","moduleid","renderForPromise","then","_ref","html","js","replaceNode","disableInvisibleAndEmptySections","catch","ex","_ref2","MOVETO_SELECT","_ref3","DUPLICATETO_SELECT","Array","prototype","call","options","option","some","parseInt","disabled"],"mappings":"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"} \ 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 */\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":"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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 34ef39b..c34f368 100644 --- a/amd/build/massactionblock.min.js +++ b/amd/build/massactionblock.min.js @@ -1,4 +1,4 @@ -define("block_massaction/massactionblock",["exports","./checkboxmanager","core/str","core/log","core/notification","core/pending","core_courseformat/courseeditor"],(function(_exports,checkboxmanager,Str,_log,_notification,_pending,_courseeditor){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} +define("block_massaction/massactionblock",["exports","block_massaction/checkboxmanager","core/str","core/log","core/notification","core/pending","core_courseformat/courseeditor","core_course/events"],(function(_exports,checkboxmanager,Str,_log,_notification,_pending,_courseeditor,_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} /** * Main module for the massaction block. * @@ -6,6 +6,6 @@ define("block_massaction/massactionblock",["exports","./checkboxmanager","core/s * @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);_exports.usedMoodleCssClasses={ACTIVITY_ITEM:".activity-item",SECTION_NAME:"sectionname",MODULE_ID_PREFIX:"module-"};const cssIds={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",BOX_ID_PREFIX:"block-massaction-module-selector-",CHECKBOX_CLASS:"block-massaction-checkbox",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",CHECKBOX_DESCRIPTION_SUFFIX:" Checkbox"};_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$getElementB,_document$getElementB2,_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;const pendingPromise=new _pending.default("block_massaction/init");(0,_courseeditor.getCurrentCourseEditor)().stateManager.getInitialPromise().then((()=>checkboxmanager.initCheckboxManager())).catch((error=>_log.default.debug(error))),null===(_document$getElementB=document.getElementById(cssIds.SELECT_ALL_LINK))||void 0===_document$getElementB||_document$getElementB.addEventListener("click",(()=>checkboxmanager.setSectionSelection(!0,constants.SECTION_NUMBER_ALL_PLACEHOLDER)),!1),null===(_document$getElementB2=document.getElementById(cssIds.DESELECT_ALL_LINK))||void 0===_document$getElementB2||_document$getElementB2.addEventListener("click",(()=>checkboxmanager.setSectionSelection(!1,constants.SECTION_NUMBER_ALL_PLACEHOLDER)),!1),null===(_document$getElementB3=document.getElementById(cssIds.HIDE_LINK))||void 0===_document$getElementB3||_document$getElementB3.addEventListener("click",(()=>submitAction(actions_HIDE)),!1),null===(_document$getElementB4=document.getElementById(cssIds.SHOW_LINK))||void 0===_document$getElementB4||_document$getElementB4.addEventListener("click",(()=>submitAction(actions_SHOW)),!1),null===(_document$getElementB5=document.getElementById(cssIds.MAKE_AVAILABLE_LINK))||void 0===_document$getElementB5||_document$getElementB5.addEventListener("click",(()=>submitAction(actions_MAKE_AVAILABLE)),!1),null===(_document$getElementB6=document.getElementById(cssIds.DUPLICATE_LINK))||void 0===_document$getElementB6||_document$getElementB6.addEventListener("click",(()=>submitAction(actions_DUPLICATE)),!1),null===(_document$getElementB7=document.getElementById(cssIds.DELETE_LINK))||void 0===_document$getElementB7||_document$getElementB7.addEventListener("click",(()=>submitAction(actions_DELETE)),!1),null===(_document$getElementB8=document.getElementById(cssIds.SHOW_DESCRIPTION_LINK))||void 0===_document$getElementB8||_document$getElementB8.addEventListener("click",(()=>submitAction(actions_SHOW_DESCRIPTION)),!1),null===(_document$getElementB9=document.getElementById(cssIds.HIDE_DESCRIPTION_LINK))||void 0===_document$getElementB9||_document$getElementB9.addEventListener("click",(()=>submitAction(actions_HIDE_DESCRIPTION)),!1),null===(_document$getElementB10=document.getElementById(cssIds.CONTENT_CHANGED_NOTIFICATION_LINK))||void 0===_document$getElementB10||_document$getElementB10.addEventListener("click",(()=>submitAction(actions_CONTENT_CHANGED_NOTIFICATION)),!1),null===(_document$getElementB11=document.getElementById(cssIds.MOVELEFT_LINK))||void 0===_document$getElementB11||_document$getElementB11.addEventListener("click",(()=>submitAction(actions_MOVE_LEFT)),!1),null===(_document$getElementB12=document.getElementById(cssIds.MOVERIGHT_LINK))||void 0===_document$getElementB12||_document$getElementB12.addEventListener("click",(()=>submitAction(actions_MOVE_RIGHT)),!1),null===(_document$getElementB13=document.getElementById(cssIds.MOVETO_ICON_LINK))||void 0===_document$getElementB13||_document$getElementB13.addEventListener("click",(()=>submitAction(actions_MOVE_TO)),!1),null===(_document$getElementB14=document.getElementById(cssIds.DUPLICATETO_ICON_LINK))||void 0===_document$getElementB14||_document$getElementB14.addEventListener("click",(()=>submitAction(actions_DUPLICATE_TO)),!1),null===(_document$getElementB15=document.getElementById(cssIds.DUPLICATE_TO_COURSE_ICON_LINK))||void 0===_document$getElementB15||_document$getElementB15.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()=>{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)))}})); //# 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 8d0c5b5..8f4753c 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 './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';\n\nexport const usedMoodleCssClasses = {\n ACTIVITY_ITEM: '.activity-item',\n SECTION_NAME: 'sectionname',\n MODULE_ID_PREFIX: 'module-',\n};\n\nexport const cssIds = {\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 BOX_ID_PREFIX: 'block-massaction-module-selector-',\n CHECKBOX_CLASS: 'block-massaction-checkbox',\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 CHECKBOX_DESCRIPTION_SUFFIX: ' Checkbox'\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 // Initialize the checkbox manager as soon as the courseeditor is ready.\n editor.stateManager.getInitialPromise()\n .then(() => checkboxmanager.initCheckboxManager())\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","cssIds","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","BOX_ID_PREFIX","CHECKBOX_CLASS","HIDDEN_FIELD_REQUEST_INFORMATION","ACTION_FORM","constants","SECTION_SELECT_DESCRIPTION_VALUE","SECTION_NUMBER_ALL_PLACEHOLDER","CHECKBOX_DESCRIPTION_SUFFIX","actions","async","pendingPromise","Pending","stateManager","getInitialPromise","then","checkboxmanager","initCheckboxManager","catch","error","Log","debug","document","getElementById","addEventListener","setSectionSelection","submitAction","resolve","action","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 */\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 diff --git a/amd/src/checkboxmanager.js b/amd/src/checkboxmanager.js index a450619..3c4f967 100644 --- a/amd/src/checkboxmanager.js +++ b/amd/src/checkboxmanager.js @@ -96,7 +96,7 @@ const rebuildLocalState = () => { const sectionsUnfiltered = sections; sections = filterVisibleSections(sections); updateSelectionAndMoveToDropdowns(sections, sectionsUnfiltered); - addCheckboxes(); + addCheckboxesToDataStructure(); localStateUpdating = false; }; @@ -152,67 +152,26 @@ export const setSectionSelection = (value, sectionNumber) => { }; /** - * Add checkboxes to all sections. + * Scan all available checkboxes and add them to the data structure. */ -const addCheckboxes = () => { +const addCheckboxesToDataStructure = () => { sections.forEach(section => { sectionBoxes[section.number] = []; const moduleIds = section.cmlist; if (moduleIds && moduleIds.length > 0 && moduleIds[0] !== '') { const moduleNamesFiltered = moduleNames.filter(modinfo => moduleIds.includes(modinfo.id.toString())); moduleNamesFiltered.forEach(modinfo => { - addCheckboxToModule(section.number, modinfo.id.toString(), modinfo.name); + // Checkbox should already be created by moodle massactions. Just add it to our data structure. + const boxId = usedMoodleCssClasses.BOX_ID_PREFIX + modinfo.id.toString(); + sectionBoxes[section.number].push({ + 'moduleId': modinfo.id.toString(), + 'boxId': boxId, + }); }); } }); }; -/** - * Add a checkbox to a module element - * - * @param {number} sectionNumber number of the section of the current course module - * @param {number} moduleId id of the current course module - * @param {string} moduleName name of the course module specified by moduleId - */ -const addCheckboxToModule = (sectionNumber, moduleId, moduleName) => { - const boxId = cssIds.BOX_ID_PREFIX + moduleId; - let moduleElement = document.getElementById(usedMoodleCssClasses.MODULE_ID_PREFIX + moduleId) - .querySelector(usedMoodleCssClasses.ACTIVITY_ITEM); - // This additional class is only needed when we are using a legacy (pre moodle 4.0) course format. - let additionalCssClass; - if (!moduleElement) { - // Should only happen in legacy formats (pre moodle 4.0). - moduleElement = document.getElementById(usedMoodleCssClasses.MODULE_ID_PREFIX + moduleId); - additionalCssClass = 'block-massaction-checkbox-legacy'; - } - - // Avoid creating duplicate checkboxes. - if (document.getElementById(boxId) === null) { - // Add the checkbox. - const checkBoxElement = document.createElement('input'); - checkBoxElement.type = 'checkbox'; - checkBoxElement.className = cssIds.CHECKBOX_CLASS; - if (additionalCssClass) { - checkBoxElement.classList.add(additionalCssClass); - } - checkBoxElement.id = boxId; - - if (moduleElement !== null) { - const checkboxDescription = moduleName + constants.CHECKBOX_DESCRIPTION_SUFFIX; - checkBoxElement.ariaLabel = checkboxDescription; - checkBoxElement.name = checkboxDescription; - // Finally add the created checkbox element. - moduleElement.insertBefore(checkBoxElement, moduleElement.firstChild); - } - } - - // Add the newly created checkbox to our data structure. - sectionBoxes[sectionNumber].push({ - 'moduleId': moduleId, - 'boxId': boxId, - }); -}; - /** * Filter the sections data object depending on the visibility of the course modules contained in * the data object. This is neccessary, because some course formats only show specific section(s) diff --git a/amd/src/massactionblock.js b/amd/src/massactionblock.js index 0b699da..a5072f0 100644 --- a/amd/src/massactionblock.js +++ b/amd/src/massactionblock.js @@ -22,20 +22,24 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import * as checkboxmanager from './checkboxmanager'; +import * as checkboxmanager from 'block_massaction/checkboxmanager'; import * as Str from 'core/str'; import Log from 'core/log'; import Notification from 'core/notification'; import Pending from 'core/pending'; import {getCurrentCourseEditor} from 'core_courseformat/courseeditor'; +import events from "core_course/events"; export const usedMoodleCssClasses = { ACTIVITY_ITEM: '.activity-item', SECTION_NAME: 'sectionname', MODULE_ID_PREFIX: 'module-', + BOX_ID_PREFIX: 'cmCheckbox' }; export 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', @@ -54,8 +58,6 @@ export const cssIds = { SECTION_SELECT: 'block-massaction-control-section-list-select', MOVETO_SELECT: 'block-massaction-control-section-list-moveto', DUPLICATETO_SELECT: 'block-massaction-control-section-list-duplicateto', - BOX_ID_PREFIX: 'block-massaction-module-selector-', - CHECKBOX_CLASS: 'block-massaction-checkbox', HIDDEN_FIELD_REQUEST_INFORMATION: 'block-massaction-control-request', ACTION_FORM: 'block-massaction-control-form', }; @@ -63,7 +65,6 @@ export const cssIds = { export const constants = { SECTION_SELECT_DESCRIPTION_VALUE: 'description', SECTION_NUMBER_ALL_PLACEHOLDER: 'all', - CHECKBOX_DESCRIPTION_SUFFIX: ' Checkbox' }; const actions = { @@ -89,9 +90,29 @@ export const init = async() => { const pendingPromise = new Pending('block_massaction/init'); const editor = getCurrentCourseEditor(); - // Initialize the checkbox manager as soon as the courseeditor is ready. + // As soon as courseeditor is available, do some initial setup. editor.stateManager.getInitialPromise() - .then(() => checkboxmanager.initCheckboxManager()) + .then(() => { + // Initialize the checkbox manager. + checkboxmanager.initCheckboxManager(); + + // Show block depending on if the moodle bulk editing util has been activated. + editor.stateManager.target.addEventListener(events.stateChanged, (event) => { + // Listen to the event that bulk editing mode has been enabled/disabled. + if (event.detail.action === 'bulk.enabled:updated') { + // Hide/show block content depending on the bulk editing enabled state. + document.getElementById(cssIds.BLOCK_CONTENT)?.classList.toggle('d-none'); + document.getElementById(cssIds.BULK_EDITING_DISABLED)?.classList.toggle('d-none'); + } + }); + + // Register click handler for the button in the placeholder text if bulk editing is still disabled. + const enableBulkButton = document.getElementById('block-massaction-enable-bulk-editing'); + // Remove the initial disabled attribute which is there to avoid too early clicks by users. + enableBulkButton.disabled = false; + enableBulkButton?.addEventListener('click', () => editor.dispatch('bulkEnable', true)); + return true; + }) .catch(error => Log.debug(error)); document.getElementById(cssIds.SELECT_ALL_LINK)?.addEventListener('click', diff --git a/lang/en/block_massaction.php b/lang/en/block_massaction.php index 4728fb9..702f2df 100644 --- a/lang/en/block_massaction.php +++ b/lang/en/block_massaction.php @@ -55,6 +55,7 @@ $string['action_showdescription'] = 'Show description'; $string['action_hidedescription'] = 'Hide description'; $string['backgroundtaskinformation'] = 'The action you demanded is being executed in the background. You can continue your work while waiting for it to finish.'; +$string['bulkeditingdisabled'] = 'To use this block, you need to enable bulk editing mode.'; $string['choosecoursetoduplicateto'] = 'Choose the course you want to duplicate the selected course modules to'; $string['choosesectiontoduplicateto'] = 'Choose the section you want the selected course modules to be duplicated to.'; $string['choosetargetcourse'] = 'Choose target course'; @@ -65,6 +66,7 @@ $string['deletecheckconfirm'] = 'Are you sure you want to delete the following module(s)?'; $string['duplicatemaxactivities'] = 'Maximum amount of course modules to duplicate'; $string['duplicatemaxactivities_description'] = 'Maximum amount of course modules which can be duplicated at the same time without running the process as background task. If set to "0" all duplication operations will be run as background task.'; +$string['enablebulkediting'] = 'Enable bulk editing'; $string['invalidaction'] = 'Unknown action: {$a}'; $string['invalidmoduleid'] = 'Invalid module ID: {$a}'; $string['invalidcoursemodule'] = 'Invalid course module'; diff --git a/templates/block_massaction.mustache b/templates/block_massaction.mustache index 75b8af2..8d55148 100644 --- a/templates/block_massaction.mustache +++ b/templates/block_massaction.mustache @@ -35,7 +35,13 @@ "helpicon": "help.gif" } }} -
+
+

{{#str}} bulkeditingdisabled, block_massaction{{/str}}

+
+ +
+
+
{{! Only display, if there are any actions available or if we at least can move items }} {{#actions.0.action}}