From bf73761ff8fdff66907d5688c0ee28f4782c47a2 Mon Sep 17 00:00:00 2001 From: Nicolas Dunand Date: Thu, 25 Sep 2025 15:01:55 +0200 Subject: [PATCH] MDL-86609 qbank_bulkmove: Target qbank categoryid fix. --- .../build/modal_question_bank_bulkmove.min.js | 2 +- .../modal_question_bank_bulkmove.min.js.map | 2 +- .../amd/src/modal_question_bank_bulkmove.js | 5 ++++ .../bulkmove/tests/behat/bulk_move.feature | 27 +++++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) 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 dfc7ce4d74c..787bb79ce91 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","core/pending"],(function(_exports,_modal,Fragment,_str,_formAutocomplete,_repository,_templates,_notification,_pending){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),_pending=_interopRequireDefault(_pending);class ModalQuestionBankBulkmove extends _modal.default{static init(contextId,categoryId){document.addEventListener("click",(e=>{const trigger=e.target;"dropdown-item"===trigger.className&&"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=>{0!==parseInt(e.target.value)?(await this.updateCategorySelector(e.currentTarget.value),this.updateSaveButtonState()):await this.updateCategorySelector(null)})),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})); +define("qbank_bulkmove/modal_question_bank_bulkmove",["exports","core/modal","core/fragment","core/str","core/form-autocomplete","core_question/repository","core/templates","core/notification","core/pending"],(function(_exports,_modal,Fragment,_str,_formAutocomplete,_repository,_templates,_notification,_pending){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),_pending=_interopRequireDefault(_pending);class ModalQuestionBankBulkmove extends _modal.default{static init(contextId,categoryId){document.addEventListener("click",(e=>{const trigger=e.target;"dropdown-item"===trigger.className&&"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=>{0!==parseInt(e.target.value)?(await this.updateCategorySelector(e.currentTarget.value),this.updateSaveButtonState(),document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY).addEventListener("change",(()=>{this.updateSaveButtonState()}))):await this.updateCategorySelector(null)})),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 cd51e04f591..ea6c2d60360 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';\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.className === '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 if (parseInt(e.target.value) === 0) {\n // The autocomplete contains a dummy option containing the text that the limit has been reached and the user\n // has to refine the search. Selection of this dummy option has to be handled separately.\n await this.updateCategorySelector(null);\n return;\n }\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","className","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","parseInt","value","updateCategorySelector","currentTarget","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","classList","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 +{"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.className === '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 if (parseInt(e.target.value) === 0) {\n // The autocomplete contains a dummy option containing the text that the limit has been reached and the user\n // has to refine the search. Selection of this dummy option has to be handled separately.\n await this.updateCategorySelector(null);\n return;\n }\n await this.updateCategorySelector(e.currentTarget.value);\n this.updateSaveButtonState();\n // In updateCategorySelector() the HTML of the SEARCH_CATEGORY select is replaced,\n // so we need to add a new event listener.\n document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY).addEventListener(\"change\", () => {\n this.updateSaveButtonState();\n });\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","className","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","parseInt","value","updateCategorySelector","currentTarget","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","classList","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 772f3991fa5..7737c2da33c 100644 --- a/question/bank/bulkmove/amd/src/modal_question_bank_bulkmove.js +++ b/question/bank/bulkmove/amd/src/modal_question_bank_bulkmove.js @@ -149,6 +149,11 @@ export default class ModalQuestionBankBulkmove extends Modal { } await this.updateCategorySelector(e.currentTarget.value); this.updateSaveButtonState(); + // In updateCategorySelector() the HTML of the SEARCH_CATEGORY select is replaced, + // so we need to add a new event listener. + document.querySelector(ModalQuestionBankBulkmove.SELECTORS.SEARCH_CATEGORY).addEventListener("change", () => { + this.updateSaveButtonState(); + }); }); this.getModal().on("click", ModalQuestionBankBulkmove.SELECTORS.SAVE_BUTTON, (e) => { diff --git a/question/bank/bulkmove/tests/behat/bulk_move.feature b/question/bank/bulkmove/tests/behat/bulk_move.feature index 33da2440b14..3c6daa2e01d 100644 --- a/question/bank/bulkmove/tests/behat/bulk_move.feature +++ b/question/bank/bulkmove/tests/behat/bulk_move.feature @@ -132,6 +132,33 @@ Feature: Use the qbank plugin manager page for bulkmove And the "class" attribute of "Question 1" "table_row" should not contain "highlight" And the "class" attribute of "Question 2" "table_row" should not contain "highlight" + @javascript + Scenario: Move a question to a different bank in a specified category + Given the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | qbank2 | Test questions 3b | + And the following "questions" exist: + | questioncategory | qtype | name | questiontext | + | Test questions | truefalse | Question 1 | Answer the first question | + And I am on the "Course 1" "core_question > course question bank" page logged in as teacher1 + And I apply question bank filter "Category" with value "Test questions" + And I click on "Question 1" "checkbox" + And I click on "With selected" "button" + And I click on "move" "button" + And I open the autocomplete suggestions list in the ".search-banks" "css_element" + And I click on "C2 - Question bank 2" item in the autocomplete list + And I open the autocomplete suggestions list in the ".search-categories" "css_element" + And I click on "Test questions 3b" 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" + # Confirm that selected questions are moved to selected category while unselected questions are not moved. + Then I should see "Questions successfully moved" + And I should see "Test questions 3b" + And I should see "Question 1" + And I should not see "Question 2" + And I should not see "Question 3" + @javascript Scenario: Unable to bulk move questions from history page Given I am on the "Test quiz" "mod_quiz > question bank" page logged in as "teacher1"