diff --git a/mod/quiz/tests/behat/random_question.feature b/mod/quiz/tests/behat/random_question.feature index e99abc3abf1..0f2ed508e78 100644 --- a/mod/quiz/tests/behat/random_question.feature +++ b/mod/quiz/tests/behat/random_question.feature @@ -49,7 +49,7 @@ Feature: Moving a question to another category should not affect random question And I click on "With selected" "button" And I click on question bulk action "move" And I open the autocomplete suggestions list in the ".search-categories" "css_element" - And I click on "Subcategory" item in the autocomplete list + And I click on "Subcategory" "list_item" in the "Move the selected questions to..." "dialogue" And I click on "Move questions" "button" And I should see "Are you sure you want to move these questions?" And I click on "Confirm" "button" diff --git a/question/bank/bulkmove/amd/build/modal_question_bank_bulkmove.min.js b/question/bank/bulkmove/amd/build/modal_question_bank_bulkmove.min.js index 67d6c775fec..848ff31809b 100644 --- a/question/bank/bulkmove/amd/build/modal_question_bank_bulkmove.min.js +++ b/question/bank/bulkmove/amd/build/modal_question_bank_bulkmove.min.js @@ -1,3 +1,3 @@ -define("qbank_bulkmove/modal_question_bank_bulkmove",["exports","core/modal","core/fragment","core/str","core/form-autocomplete","core_question/repository","core/templates","core/notification"],(function(_exports,_modal,Fragment,_str,_formAutocomplete,_repository,_templates,_notification){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=_interopRequireDefault(_modal),Fragment=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Fragment),_formAutocomplete=_interopRequireDefault(_formAutocomplete),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification);class ModalQuestionBankBulkmove extends _modal.default{static init(contextId,categoryId){document.addEventListener("click",(e=>{const trigger=e.target;trigger.classList.contains("dropdown-item")&&"move"===trigger.getAttribute("name")&&(e.preventDefault(),ModalQuestionBankBulkmove.create({contextId:contextId,title:(0,_str.getString)("bulkmoveheader","qbank_bulkmove"),show:!0,categoryId:categoryId}))}))}configure(modalConfig){this.contextId=modalConfig.contextId,this.targetBankContextId=modalConfig.contextId,this.initSelectedCategoryId(modalConfig.categoryId),modalConfig.removeOnClose=!0,super.configure(modalConfig)}initSelectedCategoryId(categoryId){const filter=new URLSearchParams(window.location.href).get("filter");if(filter){var _JSON$parse;const filteredCategoryId=null===(_JSON$parse=JSON.parse(filter))||void 0===_JSON$parse?void 0:_JSON$parse.category.values[0];return this.currentCategoryId=filteredCategoryId>0?filteredCategoryId:null,void(this.targetCategoryId=filteredCategoryId)}this.currentCategoryId=categoryId,this.targetCategoryId=categoryId}show(){return this.display(this.contextId,this.currentCategoryId),super.show()}async display(currentBankContextId,currentCategoryId){this.bodyPromise=await Fragment.loadFragment("qbank_bulkmove","bulk_move",currentBankContextId,{categoryid:currentCategoryId}),await this.setBody(this.bodyPromise),await this.enhanceSelects(document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.ORIGINAL_SELECTS)),this.registerEnhancedEventListeners(),this.mapCategoryContextIds(),this.updateSaveButtonState()}registerEnhancedEventListeners(){document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY).addEventListener("change",(e=>{const targetCategoryId=e.currentTarget.value;this.targetCategoryId=targetCategoryId,this.rebuildOptions(this.targetBankContextId,targetCategoryId),this.updateSaveButtonState()})),document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_BANK).addEventListener("change",(e=>{const selectedBankContextId=e.currentTarget.value;this.targetBankContextId=selectedBankContextId,this.rebuildOptions(selectedBankContextId,this.targetCategoryId)})),this.getModal().on("click",ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON,(e=>{e.preventDefault(),this.displayConfirmMove()}))}mapCategoryContextIds(){const customSelectCategoryOptions=document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_OPTIONS);if(0===customSelectCategoryOptions.length)return;const categoryContextIds=[];customSelectCategoryOptions.forEach((option=>{categoryContextIds[option.value]=option.dataset.bankContextid})),this.categoryContextIds=categoryContextIds}async displayConfirmMove(){this.setTitle((0,_str.getString)("confirm","core")),this.setBody((0,_str.getString)("confirmmove","qbank_bulkmove")),this.hasFooterContent()?this.showFooter():(this.setFooter(_templates.default.render("qbank_bulkmove/bulk_move_footer",{})),await this.getFooterPromise(),document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CONFIRM_BUTTON).addEventListener("click",(e=>{e.preventDefault(),this.moveQuestionsAfterConfirm(this.targetBankContextId,this.targetCategoryId)})),document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CANCEL_BUTTON).addEventListener("click",(e=>{e.preventDefault(),this.setTitle((0,_str.getString)("bulkmoveheader","qbank_bulkmove")),this.setBodyContent(_templates.default.renderForPromise("core/loading",{})),this.hideFooter(),this.display(this.targetBankContextId,this.targetCategoryId)})))}rebuildOptions(selectedBankContextId,selectedCategoryId){const categoryContextIds=this.categoryContextIds,customSelectCategoryOptions=document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_OPTIONS);if(selectedBankContextId?(customSelectCategoryOptions.forEach((option=>{option.dataset.bankContextid!=selectedBankContextId?option.dataset.enabled="disabled":option.dataset.enabled="enabled"})),this.updateCategorySelectorState(!0)):this.updateCategorySelectorState(!1),selectedCategoryId&&selectedBankContextId&&categoryContextIds[selectedCategoryId]!=selectedBankContextId){document.querySelector('.search-categories span[role="option"][data-value="'+selectedCategoryId+'"]').click()}}updateCategorySelectorState(toEnable){const warning=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_WARNING),enhancedInput=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_ENHANCED_INPUT),suggestionButton=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_SUGGESTION);toEnable?(warning.classList.add("d-none"),enhancedInput.removeAttribute("disabled"),suggestionButton.classList.remove("d-none")):(warning.classList.remove("d-none"),enhancedInput.setAttribute("disabled","disabled"),suggestionButton.classList.add("d-none"))}updateSaveButtonState(){const saveButton=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON),targetCategoryId=this.targetCategoryId;targetCategoryId&&targetCategoryId!=this.currentCategoryId?saveButton.removeAttribute("disabled"):saveButton.setAttribute("disabled","disabled")}async moveQuestionsAfterConfirm(targetContextId,targetCategoryId){await this.setBody(_templates.default.render("core/loading",{}));const qelements=document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.SELECTED_QUESTIONS),questionids=[];qelements.forEach((element=>{if(element.checked){const name=element.getAttribute("name");questionids.push(name.substr(1,name.length))}})),0===questionids.length&&await _notification.default.exception("No questions selected");try{window.location.href=await(0,_repository.moveQuestions)(targetContextId,targetCategoryId,questionids.join(),window.location.href)}catch(error){await _notification.default.exception(error)}}async enhanceSelects(selects){const placeholder=await(0,_str.getString)("searchbyname","mod_quiz"),enhanced=[];if(selects.length>0){for(let i=0;i{const trigger=e.target;trigger.classList.contains("dropdown-item")&&"move"===trigger.getAttribute("name")&&(e.preventDefault(),ModalQuestionBankBulkmove.create({contextId:contextId,title:(0,_str.getString)("bulkmoveheader","qbank_bulkmove"),show:!0,categoryId:categoryId}))}))}configure(modalConfig){this.contextId=modalConfig.contextId,this.targetBankContextId=modalConfig.contextId,this.initSelectedCategoryId(modalConfig.categoryId),modalConfig.removeOnClose=!0,super.configure(modalConfig)}initSelectedCategoryId(categoryId){const filter=new URLSearchParams(window.location.href).get("filter");if(filter){var _JSON$parse;const filteredCategoryId=null===(_JSON$parse=JSON.parse(filter))||void 0===_JSON$parse?void 0:_JSON$parse.category.values[0];return this.currentCategoryId=filteredCategoryId>0?filteredCategoryId:null,void(this.targetCategoryId=filteredCategoryId)}this.currentCategoryId=categoryId,this.targetCategoryId=categoryId}show(){return this.display(this.contextId,this.currentCategoryId),super.show()}async display(currentBankContextId,currentCategoryId){const displayPending=new _pending.default("qbank_bulkmove/bulk_move_modal");this.bodyPromise=await Fragment.loadFragment("qbank_bulkmove","bulk_move",currentBankContextId,{categoryid:currentCategoryId}),await this.setBody(this.bodyPromise),await this.enhanceSelects(),this.registerEnhancedEventListeners(),this.updateSaveButtonState(),displayPending.resolve()}registerEnhancedEventListeners(){document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY).addEventListener("change",(()=>{this.updateSaveButtonState()})),document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_BANK).addEventListener("change",(async e=>{await this.updateCategorySelector(e.currentTarget.value),this.updateSaveButtonState()})),this.getModal().on("click",ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON,(e=>{e.preventDefault(),this.displayConfirmMove()}))}async displayConfirmMove(){this.setTitle((0,_str.getString)("confirm","core")),this.setBody((0,_str.getString)("confirmmove","qbank_bulkmove")),this.hasFooterContent()?this.showFooter():(this.setFooter(_templates.default.render("qbank_bulkmove/bulk_move_footer",{})),await this.getFooterPromise(),document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CONFIRM_BUTTON).addEventListener("click",(e=>{e.preventDefault(),this.moveQuestionsAfterConfirm(this.targetBankContextId,this.targetCategoryId)})),document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CANCEL_BUTTON).addEventListener("click",(e=>{e.preventDefault(),this.setTitle((0,_str.getString)("bulkmoveheader","qbank_bulkmove")),this.setBodyContent(_templates.default.renderForPromise("core/loading",{})),this.hideFooter(),this.display(this.targetBankContextId,this.targetCategoryId)})))}updateCategorySelector(selectedBankCmId){return selectedBankCmId?Fragment.loadFragment("core_question","category_selector",this.contextId,{bankcmid:selectedBankCmId}).then(((html,js)=>{const categorySelector=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.QUESTION_CATEGORY_SELECTOR);return _templates.default.replaceNode(categorySelector,html,js)})).then((()=>(document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_WARNING).classList.add("d-none"),this.enhanceSelects()))).catch(_notification.default.exception):(this.updateCategorySelectorState(!1),Promise.resolve())}updateCategorySelectorState(toEnable){const warning=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_WARNING),enhancedInput=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_ENHANCED_INPUT),suggestionButton=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_SUGGESTION),selection=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_SELECTION);toEnable?(warning.classList.add("d-none"),enhancedInput.removeAttribute("disabled"),suggestionButton.classList.remove("d-none")):(warning.classList.remove("d-none"),enhancedInput.setAttribute("disabled","disabled"),suggestionButton.classList.add("d-none"),selection.click())}updateSaveButtonState(){const saveButton=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON),categorySelector=document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY);[this.targetCategoryId,this.targetBankContextId]=categorySelector.value.split(","),this.targetCategoryId&&this.targetCategoryId!==this.currentCategoryId?saveButton.removeAttribute("disabled"):saveButton.setAttribute("disabled","disabled")}async moveQuestionsAfterConfirm(targetContextId,targetCategoryId){await this.setBody(_templates.default.render("core/loading",{}));const qelements=document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.SELECTED_QUESTIONS),questionids=[];qelements.forEach((element=>{if(element.checked){const name=element.getAttribute("name");questionids.push(name.substr(1,name.length))}})),0===questionids.length&&await _notification.default.exception("No questions selected");try{window.location.href=await(0,_repository.moveQuestions)(targetContextId,targetCategoryId,questionids.join(),window.location.href)}catch(error){await _notification.default.exception(error)}}async enhanceSelects(){const placeholder=await(0,_str.getString)("searchbyname","mod_quiz");await _formAutocomplete.default.enhance(ModalQuestionBankBulkmove.SELECTORS.SEARCH_BANK,!1,"core_question/question_banks_datasource",placeholder,!1,!0,"",!0),await _formAutocomplete.default.enhance(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY,!1,null,placeholder,!1,!0,"",!0)}}return _exports.default=ModalQuestionBankBulkmove,_defineProperty(ModalQuestionBankBulkmove,"TYPE","qbank_bulkmove/bulkmove"),_defineProperty(ModalQuestionBankBulkmove,"SELECTORS",{SAVE_BUTTON:'[data-action="bulkmovesave"]',SELECTED_QUESTIONS:'table#categoryquestions input[id^="checkq"]',SEARCH_BANK:"#searchbanks",SEARCH_CATEGORY:".selectcategory",QUESTION_CATEGORY_SELECTOR:".question_category_selector",CATEGORY_OPTIONS:".selectcategory option",BANK_OPTIONS:"#searchbanks option",CATEGORY_ENHANCED_INPUT:".search-categories input",ORIGINAL_SELECTS:"select.bulk-move",CATEGORY_WARNING:"#searchcatwarning",CATEGORY_SUGGESTION:".search-categories span.form-autocomplete-downarrow",CATEGORY_SELECTION:'.search-categories span[role="option"][data-active-selection="true"]',CONFIRM_BUTTON:'.bulk-move-footer button[data-action="save"]',CANCEL_BUTTON:'.bulk-move-footer button[data-action="cancel"]'}),_exports.default})); //# sourceMappingURL=modal_question_bank_bulkmove.min.js.map \ No newline at end of file diff --git a/question/bank/bulkmove/amd/build/modal_question_bank_bulkmove.min.js.map b/question/bank/bulkmove/amd/build/modal_question_bank_bulkmove.min.js.map index 7b194d981f2..e7e76da3dca 100644 --- a/question/bank/bulkmove/amd/build/modal_question_bank_bulkmove.min.js.map +++ b/question/bank/bulkmove/amd/build/modal_question_bank_bulkmove.min.js.map @@ -1 +1 @@ -{"version":3,"file":"modal_question_bank_bulkmove.min.js","sources":["../src/modal_question_bank_bulkmove.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 * Contain the logic for the bulkmove questions modal.\n *\n * @module qbank_bulkmove/modal_question_bank_bulkmove\n * @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}\n * @author Simon Adams \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport * as Fragment from 'core/fragment';\nimport {getString} from 'core/str';\nimport AutoComplete from 'core/form-autocomplete';\nimport {moveQuestions} from 'core_question/repository';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\n\n\nexport default class ModalQuestionBankBulkmove extends Modal {\n static TYPE = 'qbank_bulkmove/bulkmove';\n\n static SELECTORS = {\n SAVE_BUTTON: '[data-action=\"bulkmovesave\"]',\n SELECTED_QUESTIONS: 'table#categoryquestions input[id^=\"checkq\"]',\n SEARCH_BANK: '#searchbanks',\n SEARCH_CATEGORY: '#searchcategories',\n CATEGORY_OPTIONS: '#searchcategories option',\n BANK_OPTIONS: '#searchbanks option',\n CATEGORY_ENHANCED_INPUT: '.search-categories input',\n ORIGINAL_SELECTS: 'select.bulk-move',\n CATEGORY_WARNING: '#searchcatwarning',\n CATEGORY_SUGGESTION: '.search-categories span.form-autocomplete-downarrow',\n CONFIRM_BUTTON: '.bulk-move-footer button[data-action=\"save\"]',\n CANCEL_BUTTON: '.bulk-move-footer button[data-action=\"cancel\"]'\n };\n\n /**\n * @param {integer} contextId The current bank context id.\n * @param {integer} categoryId The current question category id.\n */\n static init(contextId, categoryId) {\n document.addEventListener('click', (e) => {\n const trigger = e.target;\n if (trigger.classList.contains('dropdown-item') && trigger.getAttribute('name') === 'move') {\n e.preventDefault();\n ModalQuestionBankBulkmove.create({\n contextId,\n title: getString('bulkmoveheader', 'qbank_bulkmove'),\n show: true,\n categoryId: categoryId,\n });\n }\n });\n }\n\n /**\n * Set the initialised config on the class.\n *\n * @param {Object} modalConfig\n */\n configure(modalConfig) {\n this.contextId = modalConfig.contextId;\n this.targetBankContextId = modalConfig.contextId;\n this.initSelectedCategoryId(modalConfig.categoryId);\n modalConfig.removeOnClose = true;\n super.configure(modalConfig);\n }\n\n /**\n * Initialise the category select based on the data passed to the JS or if a filter is applied in the url.\n * @param {integer} categoryId\n */\n initSelectedCategoryId(categoryId) {\n const filter = new URLSearchParams(window.location.href).get('filter');\n if (filter) {\n const filteredCategoryId = JSON.parse(filter)?.category.values[0];\n this.currentCategoryId = filteredCategoryId > 0 ? filteredCategoryId : null;\n this.targetCategoryId = filteredCategoryId;\n return;\n }\n this.currentCategoryId = categoryId;\n this.targetCategoryId = categoryId;\n }\n\n /**\n * Render the modal contents.\n * @return {Promise}\n */\n show() {\n void this.display(this.contextId, this.currentCategoryId);\n return super.show();\n }\n\n /**\n * Get the content to display and enhance the selects into auto complete fields.\n * @param {integer} currentBankContextId\n * @param {integer} currentCategoryId\n */\n async display(currentBankContextId, currentCategoryId) {\n this.bodyPromise = await Fragment.loadFragment(\n 'qbank_bulkmove',\n 'bulk_move',\n currentBankContextId,\n {\n 'categoryid': currentCategoryId,\n }\n );\n\n await this.setBody(this.bodyPromise);\n await this.enhanceSelects(document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.ORIGINAL_SELECTS));\n this.registerEnhancedEventListeners();\n this.mapCategoryContextIds();\n this.updateSaveButtonState();\n }\n\n /**\n * Register event listeners on the enhanced selects. Must be done after they have been enhanced.\n */\n registerEnhancedEventListeners() {\n document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY).addEventListener(\"change\", (e) => {\n const targetCategoryId = e.currentTarget.value;\n this.targetCategoryId = targetCategoryId;\n this.rebuildOptions(this.targetBankContextId, targetCategoryId);\n this.updateSaveButtonState();\n });\n\n document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_BANK).addEventListener(\"change\", (e) => {\n const selectedBankContextId = e.currentTarget.value;\n this.targetBankContextId = selectedBankContextId;\n this.rebuildOptions(selectedBankContextId, this.targetCategoryId);\n });\n\n this.getModal().on(\"click\", ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON, (e) => {\n e.preventDefault();\n void this.displayConfirmMove();\n });\n }\n\n /**\n * Set a map, so we can determine which bank belongs to which category.\n */\n mapCategoryContextIds() {\n const customSelectCategoryOptions = document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_OPTIONS);\n\n if (customSelectCategoryOptions.length === 0) {\n return;\n }\n\n const categoryContextIds = [];\n\n customSelectCategoryOptions.forEach((option) => {\n categoryContextIds[option.value] = option.dataset.bankContextid;\n });\n\n this.categoryContextIds = categoryContextIds;\n }\n\n /**\n * Update the body with a confirmation prompt and set confirm cancel buttons in the footer.\n * @return {Promise}\n */\n async displayConfirmMove() {\n this.setTitle(getString('confirm', 'core'));\n this.setBody(getString('confirmmove', 'qbank_bulkmove'));\n if (!this.hasFooterContent()) {\n // We don't have the footer yet so go grab it and register event listeners on the buttons.\n this.setFooter(Templates.render('qbank_bulkmove/bulk_move_footer', {}));\n await this.getFooterPromise();\n\n document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CONFIRM_BUTTON).addEventListener(\"click\", (e) => {\n e.preventDefault();\n this.moveQuestionsAfterConfirm(this.targetBankContextId, this.targetCategoryId);\n });\n\n document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CANCEL_BUTTON).addEventListener(\"click\", (e) => {\n e.preventDefault();\n this.setTitle(getString('bulkmoveheader', 'qbank_bulkmove'));\n this.setBodyContent(Templates.renderForPromise('core/loading', {}));\n this.hideFooter();\n this.display(this.targetBankContextId, this.targetCategoryId);\n });\n } else {\n // We already have a footer so just show it.\n this.showFooter();\n }\n }\n\n /**\n * Dynamically update all enhanced selects options based on what is selected.\n *\n * @param {integer} selectedBankContextId\n * @param {integer} selectedCategoryId\n */\n rebuildOptions(selectedBankContextId, selectedCategoryId) {\n const categoryContextIds = this.categoryContextIds;\n const customSelectCategoryOptions = document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_OPTIONS);\n\n // Disable the category selector if no bank selected.\n if (!selectedBankContextId) {\n this.updateCategorySelectorState(false);\n } else {\n // Mark to be disabled all the categories not belonging to the selected bank.\n // This will then be handled by the enhanced selects event handlers.\n customSelectCategoryOptions.forEach((option) => {\n if (option.dataset.bankContextid != selectedBankContextId) {\n option.dataset.enabled = 'disabled';\n } else {\n option.dataset.enabled = 'enabled';\n }\n });\n this.updateCategorySelectorState(true);\n }\n\n // De-select the selected category if it does not belong to the selected bank.\n if (selectedCategoryId && selectedBankContextId && categoryContextIds[selectedCategoryId] != selectedBankContextId) {\n const selectedCategoryElement = document.querySelector(\n '.search-categories span[role=\"option\"][data-value=\"' + selectedCategoryId + '\"]'\n );\n selectedCategoryElement.click();\n }\n }\n\n /**\n * Disable/enable the enhanced category selector field.\n * @param {boolean} toEnable True to enable, false to disable the field.\n */\n updateCategorySelectorState(toEnable) {\n const warning = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_WARNING);\n const enhancedInput = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_ENHANCED_INPUT);\n const suggestionButton = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_SUGGESTION);\n\n if (toEnable) {\n warning.classList.add('d-none');\n enhancedInput.removeAttribute('disabled');\n suggestionButton.classList.remove('d-none');\n } else {\n warning.classList.remove('d-none');\n enhancedInput.setAttribute('disabled', 'disabled');\n suggestionButton.classList.add('d-none');\n }\n }\n\n /**\n * Disable the button if the selected category is the same as the one the questions already belong to. Enable it otherwise.\n */\n updateSaveButtonState() {\n const saveButton = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON);\n const targetCategoryId = this.targetCategoryId;\n\n if (targetCategoryId && targetCategoryId != this.currentCategoryId) {\n saveButton.removeAttribute('disabled');\n } else {\n saveButton.setAttribute('disabled', 'disabled');\n }\n }\n\n /**\n * Move the selected questions to their new target category.\n * @param {integer} targetContextId the target bank context id.\n * @param {integer} targetCategoryId the target question category id.\n * @return {Promise}\n */\n async moveQuestionsAfterConfirm(targetContextId, targetCategoryId) {\n await this.setBody(Templates.render('core/loading', {}));\n const qelements = document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.SELECTED_QUESTIONS);\n const questionids = [];\n qelements.forEach((element) => {\n if (element.checked) {\n const name = element.getAttribute('name');\n questionids.push(name.substr(1, name.length));\n }\n });\n if (questionids.length === 0) {\n await Notification.exception('No questions selected');\n }\n\n try {\n window.location.href = await moveQuestions(\n targetContextId,\n targetCategoryId,\n questionids.join(),\n window.location.href\n );\n } catch (error) {\n await Notification.exception(error);\n }\n }\n\n /**\n * Take the provided select options and enhance them into auto-complete fields.\n * @param {NodeList} selects Custom select elements to enhance.\n * @return {Promise}\n */\n async enhanceSelects(selects) {\n const placeholder = await getString('searchbyname', 'mod_quiz');\n const enhanced = [];\n\n if (selects.length > 0) {\n for (let i = 0; i < selects.length; i++) {\n enhanced.push(AutoComplete.enhance(\n selects.item(i),\n false,\n '',\n placeholder,\n false,\n true,\n '',\n true\n )\n );\n }\n\n return Promise.all(enhanced);\n }\n\n return Promise.reject('No selects to enhance');\n }\n}\n"],"names":["ModalQuestionBankBulkmove","Modal","contextId","categoryId","document","addEventListener","e","trigger","target","classList","contains","getAttribute","preventDefault","create","title","show","configure","modalConfig","targetBankContextId","initSelectedCategoryId","removeOnClose","filter","URLSearchParams","window","location","href","get","filteredCategoryId","JSON","parse","_JSON$parse","category","values","currentCategoryId","targetCategoryId","this","display","super","currentBankContextId","bodyPromise","Fragment","loadFragment","setBody","enhanceSelects","querySelectorAll","SELECTORS","ORIGINAL_SELECTS","registerEnhancedEventListeners","mapCategoryContextIds","updateSaveButtonState","querySelector","SEARCH_CATEGORY","currentTarget","value","rebuildOptions","SEARCH_BANK","selectedBankContextId","getModal","on","SAVE_BUTTON","displayConfirmMove","customSelectCategoryOptions","CATEGORY_OPTIONS","length","categoryContextIds","forEach","option","dataset","bankContextid","setTitle","hasFooterContent","showFooter","setFooter","Templates","render","getFooterPromise","CONFIRM_BUTTON","moveQuestionsAfterConfirm","CANCEL_BUTTON","setBodyContent","renderForPromise","hideFooter","selectedCategoryId","enabled","updateCategorySelectorState","click","toEnable","warning","CATEGORY_WARNING","enhancedInput","CATEGORY_ENHANCED_INPUT","suggestionButton","CATEGORY_SUGGESTION","add","removeAttribute","remove","setAttribute","saveButton","targetContextId","qelements","SELECTED_QUESTIONS","questionids","element","checked","name","push","substr","Notification","exception","join","error","selects","placeholder","enhanced","i","AutoComplete","enhance","item","Promise","all","reject","BANK_OPTIONS"],"mappings":"quDAiCqBA,kCAAkCC,2BAsBvCC,UAAWC,YACnBC,SAASC,iBAAiB,SAAUC,UAC1BC,QAAUD,EAAEE,OACdD,QAAQE,UAAUC,SAAS,kBAAqD,SAAjCH,QAAQI,aAAa,UACpEL,EAAEM,iBACFZ,0BAA0Ba,OAAO,CAC7BX,UAAAA,UACAY,OAAO,kBAAU,iBAAkB,kBACnCC,MAAM,EACNZ,WAAYA,iBAW5Ba,UAAUC,kBACDf,UAAYe,YAAYf,eACxBgB,oBAAsBD,YAAYf,eAClCiB,uBAAuBF,YAAYd,YACxCc,YAAYG,eAAgB,QACtBJ,UAAUC,aAOpBE,uBAAuBhB,kBACbkB,OAAS,IAAIC,gBAAgBC,OAAOC,SAASC,MAAMC,IAAI,aACzDL,OAAQ,uBACFM,uCAAqBC,KAAKC,MAAMR,sCAAXS,YAAoBC,SAASC,OAAO,eAC1DC,kBAAoBN,mBAAqB,EAAIA,mBAAqB,eAClEO,iBAAmBP,yBAGvBM,kBAAoB9B,gBACpB+B,iBAAmB/B,WAO5BY,cACSoB,KAAKC,QAAQD,KAAKjC,UAAWiC,KAAKF,mBAChCI,MAAMtB,qBAQHuB,qBAAsBL,wBAC3BM,kBAAoBC,SAASC,aAC9B,iBACA,YACAH,qBACA,YACkBL,0BAIhBE,KAAKO,QAAQP,KAAKI,mBAClBJ,KAAKQ,eAAevC,SAASwC,iBAAiB5C,0BAA0B6C,UAAUC,wBACnFC,sCACAC,6BACAC,wBAMTF,iCACI3C,SAAS8C,cAAclD,0BAA0B6C,UAAUM,iBAAiB9C,iBAAiB,UAAWC,UAC9F4B,iBAAmB5B,EAAE8C,cAAcC,WACpCnB,iBAAmBA,sBACnBoB,eAAenB,KAAKjB,oBAAqBgB,uBACzCe,2BAGT7C,SAAS8C,cAAclD,0BAA0B6C,UAAUU,aAAalD,iBAAiB,UAAWC,UAC1FkD,sBAAwBlD,EAAE8C,cAAcC,WACzCnC,oBAAsBsC,2BACtBF,eAAeE,sBAAuBrB,KAAKD,0BAG/CuB,WAAWC,GAAG,QAAS1D,0BAA0B6C,UAAUc,aAAcrD,IAC1EA,EAAEM,iBACGuB,KAAKyB,wBAOlBZ,8BACUa,4BAA8BzD,SAASwC,iBAAiB5C,0BAA0B6C,UAAUiB,qBAEvD,IAAvCD,4BAA4BE,oBAI1BC,mBAAqB,GAE3BH,4BAA4BI,SAASC,SACjCF,mBAAmBE,OAAOb,OAASa,OAAOC,QAAQC,sBAGjDJ,mBAAqBA,mDAQrBK,UAAS,kBAAU,UAAW,cAC9B3B,SAAQ,kBAAU,cAAe,mBACjCP,KAAKmC,wBAmBDC,mBAjBAC,UAAUC,mBAAUC,OAAO,kCAAmC,WAC7DvC,KAAKwC,mBAEXvE,SAAS8C,cAAclD,0BAA0B6C,UAAU+B,gBAAgBvE,iBAAiB,SAAUC,IAClGA,EAAEM,sBACGiE,0BAA0B1C,KAAKjB,oBAAqBiB,KAAKD,qBAGlE9B,SAAS8C,cAAclD,0BAA0B6C,UAAUiC,eAAezE,iBAAiB,SAAUC,IACjGA,EAAEM,sBACGyD,UAAS,kBAAU,iBAAkB,wBACrCU,eAAeN,mBAAUO,iBAAiB,eAAgB,UAC1DC,kBACA7C,QAAQD,KAAKjB,oBAAqBiB,KAAKD,sBAcxDoB,eAAeE,sBAAuB0B,0BAC5BlB,mBAAqB7B,KAAK6B,mBAC1BH,4BAA8BzD,SAASwC,iBAAiB5C,0BAA0B6C,UAAUiB,qBAG7FN,uBAKDK,4BAA4BI,SAASC,SAC7BA,OAAOC,QAAQC,eAAiBZ,sBAChCU,OAAOC,QAAQgB,QAAU,WAEzBjB,OAAOC,QAAQgB,QAAU,kBAG5BC,6BAA4B,SAX5BA,6BAA4B,GAejCF,oBAAsB1B,uBAAyBQ,mBAAmBkB,qBAAuB1B,sBAAuB,CAChFpD,SAAS8C,cACrC,sDAAwDgC,mBAAqB,MAEzDG,SAQhCD,4BAA4BE,gBAClBC,QAAUnF,SAAS8C,cAAclD,0BAA0B6C,UAAU2C,kBACrEC,cAAgBrF,SAAS8C,cAAclD,0BAA0B6C,UAAU6C,yBAC3EC,iBAAmBvF,SAAS8C,cAAclD,0BAA0B6C,UAAU+C,qBAEhFN,UACAC,QAAQ9E,UAAUoF,IAAI,UACtBJ,cAAcK,gBAAgB,YAC9BH,iBAAiBlF,UAAUsF,OAAO,YAElCR,QAAQ9E,UAAUsF,OAAO,UACzBN,cAAcO,aAAa,WAAY,YACvCL,iBAAiBlF,UAAUoF,IAAI,WAOvC5C,8BACUgD,WAAa7F,SAAS8C,cAAclD,0BAA0B6C,UAAUc,aACxEzB,iBAAmBC,KAAKD,iBAE1BA,kBAAoBA,kBAAoBC,KAAKF,kBAC7CgE,WAAWH,gBAAgB,YAE3BG,WAAWD,aAAa,WAAY,4CAUZE,gBAAiBhE,wBACvCC,KAAKO,QAAQ+B,mBAAUC,OAAO,eAAgB,WAC9CyB,UAAY/F,SAASwC,iBAAiB5C,0BAA0B6C,UAAUuD,oBAC1EC,YAAc,GACpBF,UAAUlC,SAASqC,aACXA,QAAQC,QAAS,OACXC,KAAOF,QAAQ3F,aAAa,QAClC0F,YAAYI,KAAKD,KAAKE,OAAO,EAAGF,KAAKzC,aAGlB,IAAvBsC,YAAYtC,cACN4C,sBAAaC,UAAU,6BAI7BrF,OAAOC,SAASC,WAAa,6BACzByE,gBACAhE,iBACAmE,YAAYQ,OACZtF,OAAOC,SAASC,MAEtB,MAAOqF,aACCH,sBAAaC,UAAUE,6BAShBC,eACXC,kBAAoB,kBAAU,eAAgB,YAC9CC,SAAW,MAEbF,QAAQhD,OAAS,EAAG,KACf,IAAImD,EAAI,EAAGA,EAAIH,QAAQhD,OAAQmD,IAChCD,SAASR,KAAKU,0BAAaC,QACnBL,QAAQM,KAAKH,IACb,EACA,GACAF,aACA,GACA,EACA,IACA,WAKLM,QAAQC,IAAIN,iBAGhBK,QAAQE,OAAO,4FAzSTxH,iCACH,2CADGA,sCAGE,CACf2D,YAAa,+BACbyC,mBAAoB,8CACpB7C,YAAa,eACbJ,gBAAiB,oBACjBW,iBAAkB,2BAClB2D,aAAc,sBACd/B,wBAAyB,2BACzB5C,iBAAkB,mBAClB0C,iBAAkB,oBAClBI,oBAAqB,sDACrBhB,eAAgB,+CAChBE,cAAe"} \ No newline at end of file +{"version":3,"file":"modal_question_bank_bulkmove.min.js","sources":["../src/modal_question_bank_bulkmove.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 * Contain the logic for the bulkmove questions modal.\n *\n * @module qbank_bulkmove/modal_question_bank_bulkmove\n * @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}\n * @author Simon Adams \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport * as Fragment from 'core/fragment';\nimport {getString} from 'core/str';\nimport AutoComplete from 'core/form-autocomplete';\nimport {moveQuestions} from 'core_question/repository';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n\nexport default class ModalQuestionBankBulkmove extends Modal {\n static TYPE = 'qbank_bulkmove/bulkmove';\n\n static SELECTORS = {\n SAVE_BUTTON: '[data-action=\"bulkmovesave\"]',\n SELECTED_QUESTIONS: 'table#categoryquestions input[id^=\"checkq\"]',\n SEARCH_BANK: '#searchbanks',\n SEARCH_CATEGORY: '.selectcategory',\n QUESTION_CATEGORY_SELECTOR: '.question_category_selector',\n CATEGORY_OPTIONS: '.selectcategory option',\n BANK_OPTIONS: '#searchbanks option',\n CATEGORY_ENHANCED_INPUT: '.search-categories input',\n ORIGINAL_SELECTS: 'select.bulk-move',\n CATEGORY_WARNING: '#searchcatwarning',\n CATEGORY_SUGGESTION: '.search-categories span.form-autocomplete-downarrow',\n CATEGORY_SELECTION: '.search-categories span[role=\"option\"][data-active-selection=\"true\"]',\n CONFIRM_BUTTON: '.bulk-move-footer button[data-action=\"save\"]',\n CANCEL_BUTTON: '.bulk-move-footer button[data-action=\"cancel\"]'\n };\n\n /**\n * @param {integer} contextId The current bank context id.\n * @param {integer} categoryId The current question category id.\n */\n static init(contextId, categoryId) {\n document.addEventListener('click', (e) => {\n const trigger = e.target;\n if (trigger.classList.contains('dropdown-item') && trigger.getAttribute('name') === 'move') {\n e.preventDefault();\n ModalQuestionBankBulkmove.create({\n contextId,\n title: getString('bulkmoveheader', 'qbank_bulkmove'),\n show: true,\n categoryId: categoryId,\n });\n }\n });\n }\n\n /**\n * Set the initialised config on the class.\n *\n * @param {Object} modalConfig\n */\n configure(modalConfig) {\n this.contextId = modalConfig.contextId;\n this.targetBankContextId = modalConfig.contextId;\n this.initSelectedCategoryId(modalConfig.categoryId);\n modalConfig.removeOnClose = true;\n super.configure(modalConfig);\n }\n\n /**\n * Initialise the category select based on the data passed to the JS or if a filter is applied in the url.\n * @param {integer} categoryId\n */\n initSelectedCategoryId(categoryId) {\n const filter = new URLSearchParams(window.location.href).get('filter');\n if (filter) {\n const filteredCategoryId = JSON.parse(filter)?.category.values[0];\n this.currentCategoryId = filteredCategoryId > 0 ? filteredCategoryId : null;\n this.targetCategoryId = filteredCategoryId;\n return;\n }\n this.currentCategoryId = categoryId;\n this.targetCategoryId = categoryId;\n }\n\n /**\n * Render the modal contents.\n * @return {Promise}\n */\n show() {\n void this.display(this.contextId, this.currentCategoryId);\n return super.show();\n }\n\n /**\n * Get the content to display and enhance the selects into auto complete fields.\n * @param {integer} currentBankContextId\n * @param {integer} currentCategoryId\n */\n async display(currentBankContextId, currentCategoryId) {\n const displayPending = new Pending('qbank_bulkmove/bulk_move_modal');\n this.bodyPromise = await Fragment.loadFragment(\n 'qbank_bulkmove',\n 'bulk_move',\n currentBankContextId,\n {\n 'categoryid': currentCategoryId,\n }\n );\n\n await this.setBody(this.bodyPromise);\n await this.enhanceSelects();\n this.registerEnhancedEventListeners();\n this.updateSaveButtonState();\n displayPending.resolve();\n }\n\n /**\n * Register event listeners on the enhanced selects. Must be done after they have been enhanced.\n */\n registerEnhancedEventListeners() {\n document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY).addEventListener(\"change\", () => {\n this.updateSaveButtonState();\n });\n\n document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_BANK).addEventListener(\"change\", async(e) => {\n await this.updateCategorySelector(e.currentTarget.value);\n this.updateSaveButtonState();\n });\n\n this.getModal().on(\"click\", ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON, (e) => {\n e.preventDefault();\n void this.displayConfirmMove();\n });\n }\n\n /**\n * Update the body with a confirmation prompt and set confirm cancel buttons in the footer.\n * @return {Promise}\n */\n async displayConfirmMove() {\n this.setTitle(getString('confirm', 'core'));\n this.setBody(getString('confirmmove', 'qbank_bulkmove'));\n if (!this.hasFooterContent()) {\n // We don't have the footer yet so go grab it and register event listeners on the buttons.\n this.setFooter(Templates.render('qbank_bulkmove/bulk_move_footer', {}));\n await this.getFooterPromise();\n\n document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CONFIRM_BUTTON).addEventListener(\"click\", (e) => {\n e.preventDefault();\n this.moveQuestionsAfterConfirm(this.targetBankContextId, this.targetCategoryId);\n });\n\n document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CANCEL_BUTTON).addEventListener(\"click\", (e) => {\n e.preventDefault();\n this.setTitle(getString('bulkmoveheader', 'qbank_bulkmove'));\n this.setBodyContent(Templates.renderForPromise('core/loading', {}));\n this.hideFooter();\n this.display(this.targetBankContextId, this.targetCategoryId);\n });\n } else {\n // We already have a footer so just show it.\n this.showFooter();\n }\n }\n\n /**\n * Update the category selector based on the selected question bank.\n *\n * @param {Number} selectedBankCmId\n * @return {Promise} Resolved when the update is complete.\n */\n updateCategorySelector(selectedBankCmId) {\n if (!selectedBankCmId) {\n this.updateCategorySelectorState(false);\n return Promise.resolve();\n } else {\n return Fragment.loadFragment(\n 'core_question',\n 'category_selector',\n this.contextId,\n {\n 'bankcmid': selectedBankCmId,\n }\n )\n .then((html, js) => {\n const categorySelector = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.QUESTION_CATEGORY_SELECTOR);\n return Templates.replaceNode(categorySelector, html, js);\n })\n .then(() => {\n document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_WARNING).classList.add('d-none');\n return this.enhanceSelects();\n })\n .catch(Notification.exception);\n }\n }\n\n /**\n * Disable/enable the enhanced category selector field.\n * @param {boolean} toEnable True to enable, false to disable the field.\n */\n updateCategorySelectorState(toEnable) {\n const warning = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_WARNING);\n const enhancedInput = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_ENHANCED_INPUT);\n const suggestionButton = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_SUGGESTION);\n const selection = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_SELECTION);\n\n if (toEnable) {\n warning.classList.add('d-none');\n enhancedInput.removeAttribute('disabled');\n suggestionButton.classList.remove('d-none');\n } else {\n warning.classList.remove('d-none');\n enhancedInput.setAttribute('disabled', 'disabled');\n suggestionButton.classList.add('d-none');\n selection.click(); // Clear selected category.\n }\n }\n\n /**\n * Disable the button if the selected category is the same as the one the questions already belong to. Enable it otherwise.\n */\n updateSaveButtonState() {\n const saveButton = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON);\n const categorySelector = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY);\n [this.targetCategoryId, this.targetBankContextId] = categorySelector.value.split(',');\n\n if (this.targetCategoryId && this.targetCategoryId !== this.currentCategoryId) {\n saveButton.removeAttribute('disabled');\n } else {\n saveButton.setAttribute('disabled', 'disabled');\n }\n }\n\n /**\n * Move the selected questions to their new target category.\n * @param {integer} targetContextId the target bank context id.\n * @param {integer} targetCategoryId the target question category id.\n * @return {Promise}\n */\n async moveQuestionsAfterConfirm(targetContextId, targetCategoryId) {\n await this.setBody(Templates.render('core/loading', {}));\n const qelements = document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.SELECTED_QUESTIONS);\n const questionids = [];\n qelements.forEach((element) => {\n if (element.checked) {\n const name = element.getAttribute('name');\n questionids.push(name.substr(1, name.length));\n }\n });\n if (questionids.length === 0) {\n await Notification.exception('No questions selected');\n }\n\n try {\n window.location.href = await moveQuestions(\n targetContextId,\n targetCategoryId,\n questionids.join(),\n window.location.href\n );\n } catch (error) {\n await Notification.exception(error);\n }\n }\n\n /**\n * Take the provided select options and enhance them into auto-complete fields.\n *\n * @return {Promise}\n */\n async enhanceSelects() {\n const placeholder = await getString('searchbyname', 'mod_quiz');\n\n await AutoComplete.enhance(\n ModalQuestionBankBulkmove.SELECTORS.SEARCH_BANK,\n false,\n 'core_question/question_banks_datasource',\n placeholder,\n false,\n true,\n '',\n true,\n );\n\n await AutoComplete.enhance(\n ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY,\n false,\n null,\n placeholder,\n false,\n true,\n '',\n true,\n );\n }\n}\n"],"names":["ModalQuestionBankBulkmove","Modal","contextId","categoryId","document","addEventListener","e","trigger","target","classList","contains","getAttribute","preventDefault","create","title","show","configure","modalConfig","targetBankContextId","initSelectedCategoryId","removeOnClose","filter","URLSearchParams","window","location","href","get","filteredCategoryId","JSON","parse","_JSON$parse","category","values","currentCategoryId","targetCategoryId","this","display","super","currentBankContextId","displayPending","Pending","bodyPromise","Fragment","loadFragment","setBody","enhanceSelects","registerEnhancedEventListeners","updateSaveButtonState","resolve","querySelector","SELECTORS","SEARCH_CATEGORY","SEARCH_BANK","async","updateCategorySelector","currentTarget","value","getModal","on","SAVE_BUTTON","displayConfirmMove","setTitle","hasFooterContent","showFooter","setFooter","Templates","render","getFooterPromise","CONFIRM_BUTTON","moveQuestionsAfterConfirm","CANCEL_BUTTON","setBodyContent","renderForPromise","hideFooter","selectedBankCmId","then","html","js","categorySelector","QUESTION_CATEGORY_SELECTOR","replaceNode","CATEGORY_WARNING","add","catch","Notification","exception","updateCategorySelectorState","Promise","toEnable","warning","enhancedInput","CATEGORY_ENHANCED_INPUT","suggestionButton","CATEGORY_SUGGESTION","selection","CATEGORY_SELECTION","removeAttribute","remove","setAttribute","click","saveButton","split","targetContextId","qelements","querySelectorAll","SELECTED_QUESTIONS","questionids","forEach","element","checked","name","push","substr","length","join","error","placeholder","AutoComplete","enhance","CATEGORY_OPTIONS","BANK_OPTIONS","ORIGINAL_SELECTS"],"mappings":"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"} \ No newline at end of file diff --git a/question/bank/bulkmove/amd/src/modal_question_bank_bulkmove.js b/question/bank/bulkmove/amd/src/modal_question_bank_bulkmove.js index 2e4bcb8e1ce..9d398bd8230 100644 --- a/question/bank/bulkmove/amd/src/modal_question_bank_bulkmove.js +++ b/question/bank/bulkmove/amd/src/modal_question_bank_bulkmove.js @@ -29,6 +29,7 @@ import AutoComplete from 'core/form-autocomplete'; import {moveQuestions} from 'core_question/repository'; import Templates from 'core/templates'; import Notification from 'core/notification'; +import Pending from 'core/pending'; export default class ModalQuestionBankBulkmove extends Modal { @@ -38,13 +39,15 @@ export default class ModalQuestionBankBulkmove extends Modal { SAVE_BUTTON: '[data-action="bulkmovesave"]', SELECTED_QUESTIONS: 'table#categoryquestions input[id^="checkq"]', SEARCH_BANK: '#searchbanks', - SEARCH_CATEGORY: '#searchcategories', - CATEGORY_OPTIONS: '#searchcategories option', + SEARCH_CATEGORY: '.selectcategory', + QUESTION_CATEGORY_SELECTOR: '.question_category_selector', + CATEGORY_OPTIONS: '.selectcategory option', BANK_OPTIONS: '#searchbanks option', CATEGORY_ENHANCED_INPUT: '.search-categories input', ORIGINAL_SELECTS: 'select.bulk-move', CATEGORY_WARNING: '#searchcatwarning', CATEGORY_SUGGESTION: '.search-categories span.form-autocomplete-downarrow', + CATEGORY_SELECTION: '.search-categories span[role="option"][data-active-selection="true"]', CONFIRM_BUTTON: '.bulk-move-footer button[data-action="save"]', CANCEL_BUTTON: '.bulk-move-footer button[data-action="cancel"]' }; @@ -112,6 +115,7 @@ export default class ModalQuestionBankBulkmove extends Modal { * @param {integer} currentCategoryId */ async display(currentBankContextId, currentCategoryId) { + const displayPending = new Pending('qbank_bulkmove/bulk_move_modal'); this.bodyPromise = await Fragment.loadFragment( 'qbank_bulkmove', 'bulk_move', @@ -122,27 +126,23 @@ export default class ModalQuestionBankBulkmove extends Modal { ); await this.setBody(this.bodyPromise); - await this.enhanceSelects(document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.ORIGINAL_SELECTS)); + await this.enhanceSelects(); this.registerEnhancedEventListeners(); - this.mapCategoryContextIds(); this.updateSaveButtonState(); + displayPending.resolve(); } /** * Register event listeners on the enhanced selects. Must be done after they have been enhanced. */ registerEnhancedEventListeners() { - document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY).addEventListener("change", (e) => { - const targetCategoryId = e.currentTarget.value; - this.targetCategoryId = targetCategoryId; - this.rebuildOptions(this.targetBankContextId, targetCategoryId); + document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY).addEventListener("change", () => { this.updateSaveButtonState(); }); - document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_BANK).addEventListener("change", (e) => { - const selectedBankContextId = e.currentTarget.value; - this.targetBankContextId = selectedBankContextId; - this.rebuildOptions(selectedBankContextId, this.targetCategoryId); + document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_BANK).addEventListener("change", async(e) => { + await this.updateCategorySelector(e.currentTarget.value); + this.updateSaveButtonState(); }); this.getModal().on("click", ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON, (e) => { @@ -151,25 +151,6 @@ export default class ModalQuestionBankBulkmove extends Modal { }); } - /** - * Set a map, so we can determine which bank belongs to which category. - */ - mapCategoryContextIds() { - const customSelectCategoryOptions = document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_OPTIONS); - - if (customSelectCategoryOptions.length === 0) { - return; - } - - const categoryContextIds = []; - - customSelectCategoryOptions.forEach((option) => { - categoryContextIds[option.value] = option.dataset.bankContextid; - }); - - this.categoryContextIds = categoryContextIds; - } - /** * Update the body with a confirmation prompt and set confirm cancel buttons in the footer. * @return {Promise} @@ -201,37 +182,33 @@ export default class ModalQuestionBankBulkmove extends Modal { } /** - * Dynamically update all enhanced selects options based on what is selected. + * Update the category selector based on the selected question bank. * - * @param {integer} selectedBankContextId - * @param {integer} selectedCategoryId + * @param {Number} selectedBankCmId + * @return {Promise} Resolved when the update is complete. */ - rebuildOptions(selectedBankContextId, selectedCategoryId) { - const categoryContextIds = this.categoryContextIds; - const customSelectCategoryOptions = document.querySelectorAll(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_OPTIONS); - - // Disable the category selector if no bank selected. - if (!selectedBankContextId) { + updateCategorySelector(selectedBankCmId) { + if (!selectedBankCmId) { this.updateCategorySelectorState(false); + return Promise.resolve(); } else { - // Mark to be disabled all the categories not belonging to the selected bank. - // This will then be handled by the enhanced selects event handlers. - customSelectCategoryOptions.forEach((option) => { - if (option.dataset.bankContextid != selectedBankContextId) { - option.dataset.enabled = 'disabled'; - } else { - option.dataset.enabled = 'enabled'; + return Fragment.loadFragment( + 'core_question', + 'category_selector', + this.contextId, + { + 'bankcmid': selectedBankCmId, } - }); - this.updateCategorySelectorState(true); - } - - // De-select the selected category if it does not belong to the selected bank. - if (selectedCategoryId && selectedBankContextId && categoryContextIds[selectedCategoryId] != selectedBankContextId) { - const selectedCategoryElement = document.querySelector( - '.search-categories span[role="option"][data-value="' + selectedCategoryId + '"]' - ); - selectedCategoryElement.click(); + ) + .then((html, js) => { + const categorySelector = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.QUESTION_CATEGORY_SELECTOR); + return Templates.replaceNode(categorySelector, html, js); + }) + .then(() => { + document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_WARNING).classList.add('d-none'); + return this.enhanceSelects(); + }) + .catch(Notification.exception); } } @@ -243,6 +220,7 @@ export default class ModalQuestionBankBulkmove extends Modal { const warning = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_WARNING); const enhancedInput = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_ENHANCED_INPUT); const suggestionButton = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_SUGGESTION); + const selection = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.CATEGORY_SELECTION); if (toEnable) { warning.classList.add('d-none'); @@ -252,6 +230,7 @@ export default class ModalQuestionBankBulkmove extends Modal { warning.classList.remove('d-none'); enhancedInput.setAttribute('disabled', 'disabled'); suggestionButton.classList.add('d-none'); + selection.click(); // Clear selected category. } } @@ -260,9 +239,10 @@ export default class ModalQuestionBankBulkmove extends Modal { */ updateSaveButtonState() { const saveButton = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON); - const targetCategoryId = this.targetCategoryId; + const categorySelector = document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY); + [this.targetCategoryId, this.targetBankContextId] = categorySelector.value.split(','); - if (targetCategoryId && targetCategoryId != this.currentCategoryId) { + if (this.targetCategoryId && this.targetCategoryId !== this.currentCategoryId) { saveButton.removeAttribute('disabled'); } else { saveButton.setAttribute('disabled', 'disabled'); @@ -303,31 +283,32 @@ export default class ModalQuestionBankBulkmove extends Modal { /** * Take the provided select options and enhance them into auto-complete fields. - * @param {NodeList} selects Custom select elements to enhance. + * * @return {Promise} */ - async enhanceSelects(selects) { + async enhanceSelects() { const placeholder = await getString('searchbyname', 'mod_quiz'); - const enhanced = []; - if (selects.length > 0) { - for (let i = 0; i < selects.length; i++) { - enhanced.push(AutoComplete.enhance( - selects.item(i), - false, - '', - placeholder, - false, - true, - '', - true - ) - ); - } + await AutoComplete.enhance( + ModalQuestionBankBulkmove.SELECTORS.SEARCH_BANK, + false, + 'core_question/question_banks_datasource', + placeholder, + false, + true, + '', + true, + ); - return Promise.all(enhanced); - } - - return Promise.reject('No selects to enhance'); + await AutoComplete.enhance( + ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY, + false, + null, + placeholder, + false, + true, + '', + true, + ); } } diff --git a/question/bank/bulkmove/classes/output/bulk_move.php b/question/bank/bulkmove/classes/output/bulk_move.php index d7b59154320..0ffc7ca4f40 100644 --- a/question/bank/bulkmove/classes/output/bulk_move.php +++ b/question/bank/bulkmove/classes/output/bulk_move.php @@ -27,6 +27,7 @@ namespace qbank_bulkmove\output; use cm_info; use core_question\local\bank\question_bank_helper; +use core_question\output\question_category_selector; use moodle_url; use renderer_base; use single_button; @@ -62,44 +63,26 @@ class bulk_move implements \renderable, \templatable { public function export_for_template(renderer_base $output) { [, $cmrec] = get_module_from_cmid($this->currentbankid); - $currentbank = cm_info::create($cmrec); + $currentbankcm = cm_info::create($cmrec); - // Get all shared banks and categories and make the current bank/category pre-selected, i.e. ordered first in the list. - $bankstorender = question_bank_helper::get_activity_instances_with_shareable_questions( - [], - [], - ['moodle/question:add'], - true, - $this->currentbankid - ); - - $allcategories = array_map(function($bank) { - if ($bank->modid == $this->currentbankid) { - // If this is the current bank then sort the categories so that our current categoryid is first in the list. - $this->sort_categories($bank->questioncategories, $this->currentcategoryid); - } - return $bank->questioncategories; - }, $bankstorender); - - // The current bank is not a shared bank, but grab the category records anyway so that we can at least allow them - // to be moved to another local category in the bank. - if (!plugin_supports('mod', $currentbank->modname, FEATURE_PUBLISHES_QUESTIONS, false)) { - $currentbank = question_bank_helper::get_activity_instances_with_private_questions( - incourseids: [$currentbank->course], - getcategories: true, + // Get the current bank and its categories. All other banks and categories will be loaded dynamically. + if (plugin_supports('mod', $currentbankcm->modname, FEATURE_PUBLISHES_QUESTIONS, false)) { + $banktorender = question_bank_helper::get_activity_instances_with_shareable_questions( + havingcap: ['moodle/question:add'], currentbankid: $this->currentbankid, + filtercontext: $currentbankcm->context, + limit: 1, + )[0]; + } else { + $banktorender = question_bank_helper::get_activity_instances_with_private_questions( + incourseids: [$currentbankcm->course], + havingcap: ['moodle/question:add'], + currentbankid: $this->currentbankid, + filtercontext: $currentbankcm->context, )[0]; - $currentbankcats = $currentbank->questioncategories; - // Move the current category to the top of the list. - $this->sort_categories($currentbankcats, $this->currentcategoryid); - // Add the current bank categories to the front of the categories list. - array_unshift($allcategories, $currentbankcats); - // Add the current bank to the front of the banks list. - array_unshift($bankstorender, $currentbank); } - // Flatten all the categories into a 2D array. - $allcategories = array_merge(...array_values($allcategories)); + $categoryselector = new question_category_selector([$currentbankcm->context], autocomplete: true); $savebutton = new single_button( new moodle_url('#'), @@ -113,29 +96,10 @@ class bulk_move implements \renderable, \templatable { ); return [ - 'allsharedbanks' => $bankstorender, - 'allcategories' => $allcategories, + 'bank' => $banktorender, + 'categories' => $categoryselector->export_for_template($output), 'save' => $savebutton->export_for_template($output), + 'contextid' => $currentbankcm->context->id, ]; } - - /** - * Wrapped usort to move the currentcategoryid to the top of the list of question categories. - * - * @param array $categories categories to sort - * @param int $currentcategoryid the category to be sorted to the top of the list - * @return void - */ - protected function sort_categories(array &$categories, int $currentcategoryid): void { - usort($categories, static function($categorya, $categoryb) use ($currentcategoryid) { - if ($categorya->id != $currentcategoryid && $categoryb->id == $currentcategoryid) { - return 1; - } - if ($categorya->id == $currentcategoryid && $categoryb->id != $currentcategoryid) { - return -1; - } - - return $categoryb->id <=> $categorya->id; - }); - } } diff --git a/question/bank/bulkmove/templates/bulk_move.mustache b/question/bank/bulkmove/templates/bulk_move.mustache index 7ffce4d05da..0a021484856 100644 --- a/question/bank/bulkmove/templates/bulk_move.mustache +++ b/question/bank/bulkmove/templates/bulk_move.mustache @@ -21,12 +21,7 @@ Example context (json): { - "allsharedbanks": [ - { - "name": "Quiz 1", - "contextid": 1, - "coursenamebankname": "c1 - Quiz 1" - }, + "bank": [ { "name": "Question bank 1", "modid": "2", @@ -34,55 +29,27 @@ "coursenamebankname": "c1 - Question bank 1", "cminfo": {}, "questioncategories": [ - { - "id": "22", - "name": "Default for Question bank 1", - "contextid": "22", - "enabled": "disabled" - } - ] - }, - { - "name": "Question bank 2", - "modid": "3", - "contextid": 33, - "coursenamebankname": "c2 - Question bank 2", - "cminfo": {}, - "questioncategories": [ - { - "id": "23", - "name": "Default for Question bank 2", - "contextid": "23", - "enabled": "disabled" - } ] } ], - "allcategories": [ - { - "id": "22", - "name": "Default for Question bank 1", - "contextid": "22" - }, - { - "id": "23", - "name": "Default for Question bank 2", - "contextid": "23", - "enabled": "disabled" - }, - { - "id": "24", - "name": "Default for Question bank 3", - "contextid": "24", - "enabled": "disabled" - }, - { - "id": "25", - "name": "Default for Question bank 4", - "contextid": "25", - "enabled": "disabled" - } - ], + "categories": { + "categories": [ + { + "value": "0", + "disabled": true, + "label": "Bank name 1" + }, + { + "value": "123,456", + "selected": true, + "label": "Category 1" + }, + { + "value": "124,456", + "label": "Category 2" + } + ] + }, "save": { "id": "single_button669e368d496707", "formid": null, @@ -111,25 +78,30 @@ ], "actions": [], "hasactions": false - } + }, + "contextid": 1000 } }}
{{#str}}questionbank, question{{/str}}
- + {{#bank}} + + {{/bank}}
{{#str}}questioncategories, question{{/str}}
- + {{#categories}} + {{> core_question/question_category_selector }} + {{/categories}}
{{#str}}warning, qbank_bulkmove{{/str}}
diff --git a/question/bank/bulkmove/tests/behat/bulk_move.feature b/question/bank/bulkmove/tests/behat/bulk_move.feature index ce047a2fe72..428284704b3 100644 --- a/question/bank/bulkmove/tests/behat/bulk_move.feature +++ b/question/bank/bulkmove/tests/behat/bulk_move.feature @@ -78,8 +78,8 @@ Feature: Use the qbank plugin manager page for bulkmove Then I should not see "C3 - Question bank 3" in the ".search-banks" "css_element" And I click on "C1 - Question bank 1" item in the autocomplete list Then I should not see "Test questions 1" in the ".search-categories .form-autocomplete-selection" "css_element" + And the field "selectcategory" matches value "Test questions 2 (1)" And I open the autocomplete suggestions list in the ".search-categories" "css_element" - And "Test questions 2" "autocomplete_suggestions" should exist And "Test questions 3" "autocomplete_suggestions" should not exist And "Test questions 4" "autocomplete_suggestions" should not exist And "Test questions 5" "autocomplete_suggestions" should exist @@ -92,7 +92,7 @@ Feature: Use the qbank plugin manager page for bulkmove And I click on "With selected" "button" And I click on "move" "button" And I open the autocomplete suggestions list in the ".search-categories" "css_element" - And I click on "Test questions 6" item in the autocomplete list + And I click on "Test questions 6 (1)" item in the autocomplete list And I click on "Move questions" "button" Then I should see "Are you sure you want to move these questions?" And I click on "Confirm" "button" @@ -136,7 +136,30 @@ Feature: Use the qbank plugin manager page for bulkmove And I should not see question bulk action "move" @javascript - Scenario: Questions can be moved to a different bank + Scenario: Questions can be moved to a different bank, if the user has the correct capability + Given the following "role" exists: + | name | Question adder | + | shortname | adder | + And the following "role capability" exists: + | role | adder | + | moodle/question:add | allow | + And the following "course" exists: + | fullname | Course 4 | + | shortname | C4 | + | category | 0 | + And the following "course enrolment" exists: + | course | C4 | + | user | teacher1 | + | role | adder | + And the following "activity" exists: + | activity | qbank | + | course | C4 | + | name | Question bank 4 | + | idnumber | qbank4 | + And the following "question category" exists: + | contextlevel | Activity module | + | reference | qbank4 | + | name | Test questions 7 | Given I am on the "Test quiz" "mod_quiz > question bank" page logged in as "teacher1" And I press "Create a new question ..." And I set the field "item_qtype_truefalse" to "1" @@ -151,11 +174,14 @@ Feature: Use the qbank plugin manager page for bulkmove And I click on "With selected" "button" And I click on "move" "button" And the field "searchbanks" matches value "C1 - Test quiz" - And the field "searchcategories" matches value "Test questions 1" + And the field "selectcategory" matches value "Test questions 1 (2)" And I open the autocomplete suggestions list in the ".search-banks" "css_element" + And I should see "C1 - Question bank 1" in the ".search-banks .form-autocomplete-suggestions" "css_element" + And I should see "C2 - Question bank 2" in the ".search-banks .form-autocomplete-suggestions" "css_element" + And I should see "C4 - Question bank 4" in the ".search-banks .form-autocomplete-suggestions" "css_element" + And I should not see "C3 - Question bank 3" in the ".search-banks .form-autocomplete-suggestions" "css_element" And I click on "C1 - Question bank 1" item in the autocomplete list - And I open the autocomplete suggestions list in the ".search-categories" "css_element" - And I click on "Test questions 2" item in the autocomplete list + And the field "selectcategory" matches value "Test questions 2 (1)" And I click on "Move questions" "button" And I should see "Are you sure you want to move these questions?" When I click on "Confirm" "button" @@ -165,6 +191,6 @@ Feature: Use the qbank plugin manager page for bulkmove And I click on "With selected" "button" And I click on "move" "button" And the field "searchbanks" matches value "C1 - Question bank 1" - And the field "searchcategories" matches value "Test questions 2" + And the field "selectcategory" matches value "Test questions 2 (2)" # The moved question should be highlighted - And the "class" attribute of "Seventh question" "table_row" should contain "highlight" \ No newline at end of file + And the "class" attribute of "Seventh question" "table_row" should contain "highlight" diff --git a/question/tests/behat/question_categories_idnumber.feature b/question/tests/behat/question_categories_idnumber.feature index e57c931683c..c557c0cb2de 100644 --- a/question/tests/behat/question_categories_idnumber.feature +++ b/question/tests/behat/question_categories_idnumber.feature @@ -83,7 +83,7 @@ Feature: A teacher can put questions with idnumbers in categories in the questio And I click on "With selected" "button" And I click on question bulk action "move" And I open the autocomplete suggestions list in the ".search-categories" "css_element" - And I click on "Used category" item in the autocomplete list + And I click on "Used category" "list_item" in the "Move the selected questions to..." "dialogue" And I click on "Move questions" "button" And I should see "Are you sure you want to move these questions?" And I click on "Confirm" "button"