Merge branch 'MDL-80750-main' of https://github.com/mihailges/moodle

This commit is contained in:
Ilya Tregubov
2024-09-19 09:51:33 +08:00
78 changed files with 1809 additions and 395 deletions
@@ -0,0 +1,19 @@
issueNumber: MDL-80750
notes:
mod_assign:
- message: >
A new method named `get_grading_batch_operation_details` has been added
to the `assign_feedback_plugin` abstract class. Assignment feedback
plugins can now override this method to define bulk action buttons that
will appear in the sticky footer on the assignment grading page.
type: improved
- message: >
The `assign_feedback_plugin::get_grading_batch_operations` method is now
deprecated. Use
`assign_feedback_plugin::get_grading_batch_operation_details` instead.
type: deprecated
- message: >
The `assign_grading_table::plugingradingbatchoperations` property is
removed. You can use
`assign_feedback_plugin::get_grading_batch_operation_details` instead.
type: deprecated
+1 -1
View File
@@ -6,6 +6,6 @@ define("core_grades/bulkactions/edit/tree/bulk_actions",["exports","core/bulkact
* @copyright 2023 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const Selectors_selectBulkItemCheckbox='input[type="checkbox"].itemselect';class GradebookEditTreeBulkActions extends _bulk_actions.default{static init(courseID){return new this(courseID)}constructor(courseID){var obj,key,value;super(),value=null,(key="courseID")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.courseID=courseID}getBulkActions(){return[new _move.default(this.courseID)]}getSelectedItems(){return document.querySelectorAll("".concat(Selectors_selectBulkItemCheckbox,":checked"))}registerItemSelectChangeEvent(eventHandler){document.querySelectorAll(Selectors_selectBulkItemCheckbox).forEach((checkbox=>{checkbox.addEventListener("change",eventHandler.bind(this))}))}}return _exports.default=GradebookEditTreeBulkActions,_exports.default}));
const Selectors_selectBulkItemCheckbox='input[type="checkbox"].itemselect';class GradebookEditTreeBulkActions extends _bulk_actions.default{static init(courseID){return new this(courseID)}constructor(courseID){var obj,key,value;super(),value=null,(key="courseID")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.courseID=courseID}getBulkActions(){return[new _move.default(this.courseID)]}getSelectedItems(){return document.querySelectorAll("".concat(Selectors_selectBulkItemCheckbox,":checked"))}registerItemSelectChangeEvent(eventHandler){document.querySelectorAll(Selectors_selectBulkItemCheckbox).forEach((checkbox=>{checkbox.addEventListener("change",eventHandler.bind(this))}))}deselectItem(selectedItem){selectedItem.checked=!1}}return _exports.default=GradebookEditTreeBulkActions,_exports.default}));
//# sourceMappingURL=bulk_actions.min.js.map
@@ -1 +1 @@
{"version":3,"file":"bulk_actions.min.js","sources":["../../../../src/bulkactions/edit/tree/bulk_actions.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 <http://www.gnu.org/licenses/>.\n\nimport BulkActions from \"core/bulkactions/bulk_actions\";\nimport GradebookEditTreeBulkMove from \"core_grades/bulkactions/edit/tree/move\";\n\n/**\n * Class for defining the bulk actions area in the gradebook setup page.\n *\n * @module core_grades/bulkactions/edit/tree/bulk_actions\n * @copyright 2023 Mihail Geshoski <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"].itemselect'\n};\n\nexport default class GradebookEditTreeBulkActions extends BulkActions {\n\n /** @property {int|null} courseID The course ID. */\n courseID = null;\n\n /**\n * Returns the instance of the class.\n *\n * @param {int} courseID\n * @returns {GradebookEditTreeBulkActions}\n */\n static init(courseID) {\n return new this(courseID);\n }\n\n /**\n * The class constructor.\n *\n * @param {int} courseID The course ID.\n * @returns {void}\n */\n constructor(courseID) {\n super();\n this.courseID = courseID;\n }\n\n /**\n * Returns the array of the relevant bulk action objects for the gradebook setup page.\n *\n * @method getBulkActions\n * @returns {Array}\n */\n getBulkActions() {\n return [\n new GradebookEditTreeBulkMove(this.courseID)\n ];\n }\n\n /**\n * Returns the array of selected items.\n *\n * @method getSelectedItems\n * @returns {Array}\n */\n getSelectedItems() {\n return document.querySelectorAll(`${Selectors.selectBulkItemCheckbox}:checked`);\n }\n\n /**\n * Adds the listener for the item select change event.\n *\n * @method registerItemSelectChangeEvent\n * @param {function} eventHandler The event handler function.\n * @returns {void}\n */\n registerItemSelectChangeEvent(eventHandler) {\n const itemSelectCheckboxes = document.querySelectorAll(Selectors.selectBulkItemCheckbox);\n itemSelectCheckboxes.forEach((checkbox) => {\n checkbox.addEventListener('change', eventHandler.bind(this));\n });\n }\n}\n"],"names":["Selectors","GradebookEditTreeBulkActions","BulkActions","courseID","this","constructor","getBulkActions","GradebookEditTreeBulkMove","getSelectedItems","document","querySelectorAll","registerItemSelectChangeEvent","eventHandler","forEach","checkbox","addEventListener","bind"],"mappings":";;;;;;;;MA0BMA,iCACsB,0CAGPC,qCAAqCC,kCAW1CC,iBACD,IAAIC,KAAKD,UASpBE,YAAYF,0CAlBD,6IAoBFA,SAAWA,SASpBG,uBACW,CACH,IAAIC,cAA0BH,KAAKD,WAU3CK,0BACWC,SAASC,2BAAoBV,8CAUxCW,8BAA8BC,cACGH,SAASC,iBAAiBV,kCAClCa,SAASC,WAC1BA,SAASC,iBAAiB,SAAUH,aAAaI,KAAKZ"}
{"version":3,"file":"bulk_actions.min.js","sources":["../../../../src/bulkactions/edit/tree/bulk_actions.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 <http://www.gnu.org/licenses/>.\n\nimport BulkActions from \"core/bulkactions/bulk_actions\";\nimport GradebookEditTreeBulkMove from \"core_grades/bulkactions/edit/tree/move\";\n\n/**\n * Class for defining the bulk actions area in the gradebook setup page.\n *\n * @module core_grades/bulkactions/edit/tree/bulk_actions\n * @copyright 2023 Mihail Geshoski <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"].itemselect'\n};\n\nexport default class GradebookEditTreeBulkActions extends BulkActions {\n\n /** @property {int|null} courseID The course ID. */\n courseID = null;\n\n /**\n * Returns the instance of the class.\n *\n * @param {int} courseID\n * @returns {GradebookEditTreeBulkActions}\n */\n static init(courseID) {\n return new this(courseID);\n }\n\n /**\n * The class constructor.\n *\n * @param {int} courseID The course ID.\n * @returns {void}\n */\n constructor(courseID) {\n super();\n this.courseID = courseID;\n }\n\n /**\n * Returns the array of the relevant bulk action objects for the gradebook setup page.\n *\n * @method getBulkActions\n * @returns {Array}\n */\n getBulkActions() {\n return [\n new GradebookEditTreeBulkMove(this.courseID)\n ];\n }\n\n /**\n * Returns the array of selected items.\n *\n * @method getSelectedItems\n * @returns {Array}\n */\n getSelectedItems() {\n return document.querySelectorAll(`${Selectors.selectBulkItemCheckbox}:checked`);\n }\n\n /**\n * Adds the listener for the item select change event.\n *\n * @method registerItemSelectChangeEvent\n * @param {function} eventHandler The event handler function.\n * @returns {void}\n */\n registerItemSelectChangeEvent(eventHandler) {\n const itemSelectCheckboxes = document.querySelectorAll(Selectors.selectBulkItemCheckbox);\n itemSelectCheckboxes.forEach((checkbox) => {\n checkbox.addEventListener('change', eventHandler.bind(this));\n });\n }\n\n /**\n * Defines the action for deselecting a selected item.\n *\n * @method deselectItem\n * @param {HTMLElement} selectedItem The selected element.\n * @returns {void}\n */\n deselectItem(selectedItem) {\n selectedItem.checked = false;\n }\n}\n"],"names":["Selectors","GradebookEditTreeBulkActions","BulkActions","courseID","this","constructor","getBulkActions","GradebookEditTreeBulkMove","getSelectedItems","document","querySelectorAll","registerItemSelectChangeEvent","eventHandler","forEach","checkbox","addEventListener","bind","deselectItem","selectedItem","checked"],"mappings":";;;;;;;;MA0BMA,iCACsB,0CAGPC,qCAAqCC,kCAW1CC,iBACD,IAAIC,KAAKD,UASpBE,YAAYF,0CAlBD,6IAoBFA,SAAWA,SASpBG,uBACW,CACH,IAAIC,cAA0BH,KAAKD,WAU3CK,0BACWC,SAASC,2BAAoBV,8CAUxCW,8BAA8BC,cACGH,SAASC,iBAAiBV,kCAClCa,SAASC,WAC1BA,SAASC,iBAAiB,SAAUH,aAAaI,KAAKZ,UAW9Da,aAAaC,cACTA,aAAaC,SAAU"}
+1 -1
View File
@@ -1,3 +1,3 @@
define("core_grades/bulkactions/edit/tree/move",["exports","core/bulkactions/bulk_action","core/str","core/modal_save_cancel","core/templates","core/ajax","core/modal_events","core_grades/bulkactions/edit/tree/move_options_tree"],(function(_exports,_bulk_action,_str,_modal_save_cancel,_templates,_ajax,_modal_events,_move_options_tree){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,_bulk_action=_interopRequireDefault(_bulk_action),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_templates=_interopRequireDefault(_templates),_ajax=_interopRequireDefault(_ajax),_modal_events=_interopRequireDefault(_modal_events),_move_options_tree=_interopRequireDefault(_move_options_tree);const Selectors_editTreeForm="#gradetreeform",Selectors_bulkMoveInput='input[name="bulkmove"]',Selectors_bulkMoveAfterInput='input[name="moveafter"]';class GradebookEditTreeBulkMove extends _bulk_action.default{constructor(courseId){super(),_defineProperty(this,"courseId",null),_defineProperty(this,"moveOptionsTree",null),_defineProperty(this,"gradeTree",null),this.courseId=courseId}getBulkActionTriggerSelector(){return'button[data-action="move"]'}async triggerBulkAction(){const modal=await this.showModal();this.registerCustomListenerEvents(modal)}async renderBulkActionTrigger(){return _templates.default.render("core_grades/bulkactions/edit/tree/bulk_move_trigger",{})}async registerCustomListenerEvents(modal){await modal.getBody(),modal.getRoot().on(_modal_events.default.shown,(()=>{this.moveOptionsTree=new _move_options_tree.default((()=>{modal.setButtonDisabled("save",!1)}))})),modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy()})),modal.getRoot().on(_modal_events.default.save,(()=>{this.moveOptionsTree&&this.moveOptionsTree.selectedMoveOption&&(document.querySelector(Selectors_bulkMoveInput).value=1,document.querySelector(Selectors_bulkMoveAfterInput).value=this.moveOptionsTree.selectedMoveOption.dataset.id,document.querySelector(Selectors_editTreeForm).submit())}))}fetchGradeTree(){const request={methodname:"core_grades_get_grade_tree",args:{courseid:this.courseId}};return _ajax.default.call([request])[0]}async renderModalBody(){return null===this.gradeTree&&(this.gradeTree=await this.fetchGradeTree()),_templates.default.render("core_grades/bulkactions/edit/tree/bulk_move_grade_tree",JSON.parse(this.gradeTree))}async showModal(){const modal=await _modal_save_cancel.default.create({title:await(0,_str.get_string)("movesitems","grades"),body:await this.renderModalBody(),buttons:{save:await(0,_str.get_string)("move")},large:!0});return modal.setButtonDisabled("save",!0),modal.show(),modal}}return _exports.default=GradebookEditTreeBulkMove,_exports.default}));
define("core_grades/bulkactions/edit/tree/move",["exports","core/bulkactions/bulk_action","core/str","core/modal_save_cancel","core/templates","core/ajax","core/modal_events","core_grades/bulkactions/edit/tree/move_options_tree"],(function(_exports,_bulk_action,_str,_modal_save_cancel,_templates,_ajax,_modal_events,_move_options_tree){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,_bulk_action=_interopRequireDefault(_bulk_action),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_templates=_interopRequireDefault(_templates),_ajax=_interopRequireDefault(_ajax),_modal_events=_interopRequireDefault(_modal_events),_move_options_tree=_interopRequireDefault(_move_options_tree);const Selectors_editTreeForm="#gradetreeform",Selectors_bulkMoveInput='input[name="bulkmove"]',Selectors_bulkMoveAfterInput='input[name="moveafter"]';class GradebookEditTreeBulkMove extends _bulk_action.default{constructor(courseId){super(),_defineProperty(this,"courseId",null),_defineProperty(this,"moveOptionsTree",null),_defineProperty(this,"gradeTree",null),this.courseId=courseId}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="move"]'}async triggerBulkAction(){const modal=await this.showModal();this.registerCustomListenerEvents(modal)}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("core_grades/bulkactions/edit/tree/bulk_move_trigger",{showindropdown:showInDropdown,isfirst:0===index})}async registerCustomListenerEvents(modal){await modal.getBody(),modal.getRoot().on(_modal_events.default.shown,(()=>{this.moveOptionsTree=new _move_options_tree.default((()=>{modal.setButtonDisabled("save",!1)}))})),modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy()})),modal.getRoot().on(_modal_events.default.save,(()=>{this.moveOptionsTree&&this.moveOptionsTree.selectedMoveOption&&(document.querySelector(Selectors_bulkMoveInput).value=1,document.querySelector(Selectors_bulkMoveAfterInput).value=this.moveOptionsTree.selectedMoveOption.dataset.id,document.querySelector(Selectors_editTreeForm).submit())}))}fetchGradeTree(){const request={methodname:"core_grades_get_grade_tree",args:{courseid:this.courseId}};return _ajax.default.call([request])[0]}async renderModalBody(){return null===this.gradeTree&&(this.gradeTree=await this.fetchGradeTree()),_templates.default.render("core_grades/bulkactions/edit/tree/bulk_move_grade_tree",JSON.parse(this.gradeTree))}async showModal(){const modal=await _modal_save_cancel.default.create({title:await(0,_str.get_string)("movesitems","grades"),body:await this.renderModalBody(),buttons:{save:await(0,_str.get_string)("move")},large:!0});return modal.setButtonDisabled("save",!0),modal.show(),modal}}return _exports.default=GradebookEditTreeBulkMove,_exports.default}));
//# sourceMappingURL=move.min.js.map
File diff suppressed because one or more lines are too long
@@ -89,4 +89,15 @@ export default class GradebookEditTreeBulkActions extends BulkActions {
checkbox.addEventListener('change', eventHandler.bind(this));
});
}
/**
* Defines the action for deselecting a selected item.
*
* @method deselectItem
* @param {HTMLElement} selectedItem The selected element.
* @returns {void}
*/
deselectItem(selectedItem) {
selectedItem.checked = false;
}
}
+8 -3
View File
@@ -64,7 +64,7 @@ export default class GradebookEditTreeBulkMove extends BulkAction {
* @returns {string} The bulk move action trigger selector.
*/
getBulkActionTriggerSelector() {
return 'button[data-action="move"]';
return '[data-type="bulkactions"] [data-action="move"]';
}
/**
@@ -82,10 +82,15 @@ export default class GradebookEditTreeBulkMove extends BulkAction {
* Renders the bulk move action trigger element.
*
* @method renderBulkActionTrigger
* @param {boolean} showInDropdown Whether the action is displayed under a 'More' dropdown or as a separate button.
* @param {number} index The index of the action.
* @returns {Promise} The bulk move action trigger promise
*/
async renderBulkActionTrigger() {
return Templates.render('core_grades/bulkactions/edit/tree/bulk_move_trigger', {});
async renderBulkActionTrigger(showInDropdown, index) {
return Templates.render('core_grades/bulkactions/edit/tree/bulk_move_trigger', {
showindropdown: showInDropdown,
isfirst: index === 0,
});
}
/**
@@ -26,7 +26,7 @@ Feature: Admin can set Recover grades default setting
And I navigate to "Submissions" in current page administration
And I click on "Quick grading" "checkbox"
And I set the field "User grade" to "60.00"
And I press "Save all quick grading changes"
And I click on "Save" "button" in the "sticky-footer" "region"
# Confirm that assigned grade was saved
And I am on the "Course 1" "grades > Grader report > View" page
And I should see "60.00" in the "Student One" "table_row"
+1 -1
View File
@@ -6,6 +6,6 @@ define("core/bulkactions/bulk_action",["exports"],(function(_exports){Object.def
* @copyright 2023 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class{constructor(){var obj,key,value;value=[],(key="selectedItems")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}registerListenerEvents(containerElement){containerElement.addEventListener("click",(e=>{e.target.closest(this.getBulkActionTriggerSelector())&&(e.preventDefault(),this.triggerBulkAction())}))}setSelectedItems(selectedItems){this.selectedItems=selectedItems}getBulkActionTriggerSelector(){throw new Error("getBulkActionTriggerSelector() must be implemented in ".concat(this.constructor.name))}triggerBulkAction(){throw new Error("triggerBulkAction() must be implemented in ".concat(this.constructor.name))}renderBulkActionTrigger(){throw new Error("renderBulkActionTrigger() must be implemented in ".concat(this.constructor.name))}},_exports.default}));
class{constructor(){var obj,key,value;value=[],(key="selectedItems")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}registerListenerEvents(containerElement){containerElement.addEventListener("click",(e=>{e.target.closest(this.getBulkActionTriggerSelector())&&(e.preventDefault(),this.triggerBulkAction())}))}setSelectedItems(selectedItems){this.selectedItems=selectedItems}getBulkActionTriggerSelector(){throw new Error("getBulkActionTriggerSelector() must be implemented in ".concat(this.constructor.name))}triggerBulkAction(){throw new Error("triggerBulkAction() must be implemented in ".concat(this.constructor.name))}renderBulkActionTrigger(){let index=arguments.length>1?arguments[1]:void 0;throw new Error("renderBulkActionTrigger(".concat(arguments.length>0&&void 0!==arguments[0]&&arguments[0],", ").concat(index,") must be implemented in ").concat(this.constructor.name))}},_exports.default}));
//# sourceMappingURL=bulk_action.min.js.map
@@ -1 +1 @@
{"version":3,"file":"bulk_action.min.js","sources":["../../src/bulkactions/bulk_action.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Base class for defining a bulk action.\n *\n * @module core/bulkactions/bulk_action\n * @copyright 2023 Mihail Geshoski <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class BulkAction {\n\n /** @property {array} selectedItems The array of selected item elements. */\n selectedItems = [];\n\n /**\n * Registers the listener events for the bulk actions.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} containerElement The container element for the bulk actions.\n * @returns {void}\n */\n registerListenerEvents(containerElement) {\n // Listen for the click event on the bulk action trigger element.\n containerElement.addEventListener('click', (e) => {\n if (e.target.closest(this.getBulkActionTriggerSelector())) {\n e.preventDefault();\n this.triggerBulkAction();\n }\n });\n }\n\n /**\n * Setter method for the selectedItems property.\n *\n * @method setSelectedItems\n * @param {Array} selectedItems The array of selected item elements..\n * @returns {void}\n */\n setSelectedItems(selectedItems) {\n this.selectedItems = selectedItems;\n }\n\n /**\n * Defines the selector of the element that triggers the bulk action.\n *\n * @method getBulkActionTriggerSelector\n * @returns {string}\n */\n getBulkActionTriggerSelector() {\n throw new Error(`getBulkActionTriggerSelector() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Defines the behavior once the bulk action is triggered.\n *\n * @method triggerBulkAction\n */\n triggerBulkAction() {\n throw new Error(`triggerBulkAction() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Renders the bulk action trigger element.\n *\n * @method renderBulkActionTrigger\n * @returns {Promise}\n */\n renderBulkActionTrigger() {\n throw new Error(`renderBulkActionTrigger() must be implemented in ${this.constructor.name}`);\n }\n}\n"],"names":["registerListenerEvents","containerElement","addEventListener","e","target","closest","this","getBulkActionTriggerSelector","preventDefault","triggerBulkAction","setSelectedItems","selectedItems","Error","constructor","name","renderBulkActionTrigger"],"mappings":";;;;;;;;4CA0BoB,2IAShBA,uBAAuBC,kBAEnBA,iBAAiBC,iBAAiB,SAAUC,IACpCA,EAAEC,OAAOC,QAAQC,KAAKC,kCACtBJ,EAAEK,sBACGC,wBAYjBC,iBAAiBC,oBACRA,cAAgBA,cASzBJ,qCACU,IAAIK,sEAA+DN,KAAKO,YAAYC,OAQ9FL,0BACU,IAAIG,2DAAoDN,KAAKO,YAAYC,OASnFC,gCACU,IAAIH,iEAA0DN,KAAKO,YAAYC"}
{"version":3,"file":"bulk_action.min.js","sources":["../../src/bulkactions/bulk_action.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Base class for defining a bulk action.\n *\n * @module core/bulkactions/bulk_action\n * @copyright 2023 Mihail Geshoski <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class BulkAction {\n\n /** @property {array} selectedItems The array of selected item elements. */\n selectedItems = [];\n\n /**\n * Registers the listener events for the bulk actions.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} containerElement The container element for the bulk actions.\n * @returns {void}\n */\n registerListenerEvents(containerElement) {\n // Listen for the click event on the bulk action trigger element.\n containerElement.addEventListener('click', (e) => {\n if (e.target.closest(this.getBulkActionTriggerSelector())) {\n e.preventDefault();\n this.triggerBulkAction();\n }\n });\n }\n\n /**\n * Setter method for the selectedItems property.\n *\n * @method setSelectedItems\n * @param {Array} selectedItems The array of selected item elements..\n * @returns {void}\n */\n setSelectedItems(selectedItems) {\n this.selectedItems = selectedItems;\n }\n\n /**\n * Defines the selector of the element that triggers the bulk action.\n *\n * @method getBulkActionTriggerSelector\n * @returns {string}\n */\n getBulkActionTriggerSelector() {\n throw new Error(`getBulkActionTriggerSelector() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Defines the behavior once the bulk action is triggered.\n *\n * @method triggerBulkAction\n */\n triggerBulkAction() {\n throw new Error(`triggerBulkAction() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Renders the bulk action trigger element.\n *\n * @method renderBulkActionTrigger\n * @param {boolean} showInDropdown Whether the action is displayed under a 'More' dropdown or as a separate button.\n * @param {number} index The index of the action.\n * @returns {Promise}\n */\n renderBulkActionTrigger(showInDropdown = false, index) {\n throw new Error(`renderBulkActionTrigger(${showInDropdown}, ${index}) must be implemented in ${this.constructor.name}`);\n }\n}\n"],"names":["registerListenerEvents","containerElement","addEventListener","e","target","closest","this","getBulkActionTriggerSelector","preventDefault","triggerBulkAction","setSelectedItems","selectedItems","Error","constructor","name","renderBulkActionTrigger","index"],"mappings":";;;;;;;;4CA0BoB,2IAShBA,uBAAuBC,kBAEnBA,iBAAiBC,iBAAiB,SAAUC,IACpCA,EAAEC,OAAOC,QAAQC,KAAKC,kCACtBJ,EAAEK,sBACGC,wBAYjBC,iBAAiBC,oBACRA,cAAgBA,cASzBJ,qCACU,IAAIK,sEAA+DN,KAAKO,YAAYC,OAQ9FL,0BACU,IAAIG,2DAAoDN,KAAKO,YAAYC,OAWnFC,8BAAgDC,mDACtC,IAAIJ,6GAAoDI,0CAAiCV,KAAKO,YAAYC"}
+1 -1
View File
@@ -5,6 +5,6 @@ define("core/bulkactions/bulk_actions",["exports","core/templates","core/str","c
* @module core/bulkactions/bulk_actions
* @copyright 2023 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};const Selectors_stickyFooterContainer="#sticky-footer",Selectors_selectedItemsCountContainer='[data-type="bulkactions"] [data-for="bulkcount"]',Selectors_cancelBulkActionModeElement='[data-type="bulkactions"] [data-action="bulkcancel"]',Selectors_bulkModeContainer='[data-type="bulkactions"]',Selectors_bulkActionsContainer='[data-type="bulkactions"] [data-for="bulktools"]';return _exports.default=class{constructor(){if(_defineProperty(this,"initialStickyFooterContent",null),_defineProperty(this,"selectedItems",[]),_defineProperty(this,"isBulkActionsModeEnabled",!1),!this.getStickyFooterContainer())throw new Error("Sticky footer not found.");this.initialStickyFooterContent=this.getStickyFooterContainer().innerHTML,this.registerItemSelectChangeEvent((async()=>{this.selectedItems=this.getSelectedItems(),this.selectedItems.length>0?this.isBulkActionsModeEnabled?await this.updateBulkItemSelection():await this.enableBulkActionsMode():this.disableBulkActionsMode()}))}getBulkActions(){throw new Error("getBulkActions() must be implemented in ".concat(this.constructor.name))}getSelectedItems(){throw new Error("getSelectedItems() must be implemented in ".concat(this.constructor.name))}registerItemSelectChangeEvent(eventHandler){throw new Error("registerItemSelectChangeEvent(".concat(eventHandler,") must be implemented in ").concat(this.constructor.name))}getStickyFooterContainer(){return document.querySelector(Selectors_stickyFooterContainer)}async enableBulkActionsMode(){(0,_stickyFooter.enableStickyFooter)(),this.getStickyFooterContainer().innerHTML=await this.renderBulkActions();const bulkModeContainer=this.getStickyFooterContainer().querySelector(Selectors_bulkModeContainer),bulkActionsContainer=bulkModeContainer.querySelector(Selectors_bulkActionsContainer);this.getBulkActions().forEach((bulkAction=>{bulkAction.registerListenerEvents(bulkActionsContainer),bulkAction.setSelectedItems(this.selectedItems)})),bulkModeContainer.addEventListener("click",(e=>{e.target.closest(Selectors_cancelBulkActionModeElement)&&(this.selectedItems.forEach((item=>{item.checked=!1})),this.disableBulkActionsMode())})),this.isBulkActionsModeEnabled=!0}disableBulkActionsMode(){this.initialStickyFooterContent.length>0?this.getStickyFooterContainer().innerHTML=this.initialStickyFooterContent:(0,_stickyFooter.disableStickyFooter)(),this.isBulkActionsModeEnabled=!1}async renderBulkActions(){let data={bulkselectioncount:this.selectedItems.length,actions:[]};return await Promise.all(this.getBulkActions().map((async bulkAction=>{data.actions.push({actiontrigger:await bulkAction.renderBulkActionTrigger()})}))),_templates.default.render("core/bulkactions/bulk_actions",data)}async updateBulkItemSelection(){const bulkSelection=await(0,_str.get_string)("bulkselection","core",this.selectedItems.length);document.querySelector(Selectors_selectedItemsCountContainer).innerHTML=bulkSelection}},_exports.default}));
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};const Selectors_stickyFooterContainer="#sticky-footer",Selectors_selectedItemsCountContainer='[data-type="bulkactions"] [data-for="bulkcount"]',Selectors_cancelBulkActionModeElement='[data-type="bulkactions"] [data-action="bulkcancel"]',Selectors_bulkModeContainer='[data-type="bulkactions"]',Selectors_bulkActionsContainer='[data-type="bulkactions"] [data-for="bulktools"]';return _exports.default=class{constructor(){let maxButtons=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;if(_defineProperty(this,"initialStickyFooterContent",null),_defineProperty(this,"selectedItems",[]),_defineProperty(this,"isBulkActionsModeEnabled",!1),_defineProperty(this,"maxButtons",5),!this.getStickyFooterContainer())throw new Error("Sticky footer not found.");this.initialStickyFooterContent=this.getStickyFooterContainer().innerHTML,maxButtons&&(this.maxButtons=maxButtons),this.registerItemSelectChangeEvent((async()=>{this.selectedItems=this.getSelectedItems(),this.selectedItems.length>0?this.isBulkActionsModeEnabled?await this.updateBulkItemSelection():await this.enableBulkActionsMode():this.disableBulkActionsMode()}))}getBulkActions(){throw new Error("getBulkActions() must be implemented in ".concat(this.constructor.name))}getSelectedItems(){throw new Error("getSelectedItems() must be implemented in ".concat(this.constructor.name))}registerItemSelectChangeEvent(eventHandler){throw new Error("registerItemSelectChangeEvent(".concat(eventHandler,") must be implemented in ").concat(this.constructor.name))}deselectItem(selectedItem){throw new Error("deselectItem(".concat(selectedItem,") must be implemented in ").concat(this.constructor.name))}getStickyFooterContainer(){return document.querySelector(Selectors_stickyFooterContainer)}async enableBulkActionsMode(){(0,_stickyFooter.enableStickyFooter)(),this.getStickyFooterContainer().innerHTML=await this.renderBulkActions();const bulkModeContainer=this.getStickyFooterContainer().querySelector(Selectors_bulkModeContainer),bulkActionsContainer=bulkModeContainer.querySelector(Selectors_bulkActionsContainer);this.getBulkActions().forEach((bulkAction=>{bulkAction.registerListenerEvents(bulkActionsContainer),bulkAction.setSelectedItems(this.selectedItems)})),bulkModeContainer.addEventListener("click",(e=>{e.target.closest(Selectors_cancelBulkActionModeElement)&&(this.selectedItems.forEach((item=>{this.deselectItem(item)})),this.disableBulkActionsMode())})),this.isBulkActionsModeEnabled=!0}disableBulkActionsMode(){this.initialStickyFooterContent.length>0?this.getStickyFooterContainer().innerHTML=this.initialStickyFooterContent:(0,_stickyFooter.disableStickyFooter)(),this.isBulkActionsModeEnabled=!1}async renderBulkActions(){const data={bulkselectioncount:this.selectedItems.length,actions:[],moreactions:[],hasmoreactions:!1},bulkActions=this.getBulkActions(),showMoreButton=bulkActions.length>this.maxButtons;return(await Promise.all(bulkActions.map(((bulkAction,index)=>bulkAction.renderBulkActionTrigger(showMoreButton&&index>=this.maxButtons-1,index))))).forEach(((actionTrigger,index)=>{showMoreButton&&index>=this.maxButtons-1?data.moreactions.push({actiontrigger:actionTrigger}):data.actions.push({actiontrigger:actionTrigger})})),data.hasmoreactions=data.moreactions.length>0,_templates.default.render("core/bulkactions/bulk_actions",data)}async updateBulkItemSelection(){const bulkSelection=await(0,_str.get_string)("bulkselection","core",this.selectedItems.length);document.querySelector(Selectors_selectedItemsCountContainer).innerHTML=bulkSelection}},_exports.default}));
//# sourceMappingURL=bulk_actions.min.js.map
File diff suppressed because one or more lines are too long
+4 -2
View File
@@ -77,9 +77,11 @@ export default class BulkAction {
* Renders the bulk action trigger element.
*
* @method renderBulkActionTrigger
* @param {boolean} showInDropdown Whether the action is displayed under a 'More' dropdown or as a separate button.
* @param {number} index The index of the action.
* @returns {Promise}
*/
renderBulkActionTrigger() {
throw new Error(`renderBulkActionTrigger() must be implemented in ${this.constructor.name}`);
renderBulkActionTrigger(showInDropdown = false, index) {
throw new Error(`renderBulkActionTrigger(${showInDropdown}, ${index}) must be implemented in ${this.constructor.name}`);
}
}
+59 -12
View File
@@ -45,18 +45,29 @@ export default class BulkActions {
/** @property {boolean} isBulkActionsModeEnabled Whether the bulk actions mode is enabled. */
isBulkActionsModeEnabled = false;
/**
* @property {int} maxButtons Sets the maximum number of action buttons to display. If exceeded, additional actions
* are shown in a dropdown menu.
*/
maxButtons = 5;
/**
* The class constructor.
*
* @param {int|null} maxButtons Sets the maximum number of action buttons to display. If exceeded, additional actions
* are shown in a dropdown menu.
* @returns {void}
*/
constructor() {
constructor(maxButtons = null) {
if (!this.getStickyFooterContainer()) {
throw new Error('Sticky footer not found.');
}
// Store any pre-existing content in the sticky footer. When bulk actions mode is enabled, this content will be
// replaced with the bulk actions content and restored when bulk actions mode is disabled.
this.initialStickyFooterContent = this.getStickyFooterContainer().innerHTML;
if (maxButtons) {
this.maxButtons = maxButtons;
}
// Register and handle the item select change event.
this.registerItemSelectChangeEvent(async() => {
this.selectedItems = this.getSelectedItems();
@@ -105,6 +116,21 @@ export default class BulkActions {
throw new Error(`registerItemSelectChangeEvent(${eventHandler}) must be implemented in ${this.constructor.name}`);
}
/**
* Defines the action for deselecting a selected item.
*
* The base bulk actions class supports deselecting all selected items but does not have knowledge of the type of the
* selected element. Therefore, each subclass must explicitly define the action of resetting the attributes that
* indicate a selected state.
*
* @method deselectItem
* @param {HTMLElement} selectedItem The selected element.
* @returns {void}
*/
deselectItem(selectedItem) {
throw new Error(`deselectItem(${selectedItem}) must be implemented in ${this.constructor.name}`);
}
/**
* Returns the sticky footer container.
*
@@ -119,7 +145,7 @@ export default class BulkActions {
* Enables the bulk action mode.
*
* @method enableBulkActionsMode
* @returns {Promise}
* @returns {Promise<void>}
*/
async enableBulkActionsMode() {
// Make sure that the sticky footer is enabled.
@@ -137,9 +163,9 @@ export default class BulkActions {
// Register the click listener event for the cancel bulk mode button.
bulkModeContainer.addEventListener('click', (e) => {
if (e.target.closest(Selectors.cancelBulkActionModeElement)) {
// Uncheck all selected items.
// Deselect all selected items.
this.selectedItems.forEach((item) => {
item.checked = false;
this.deselectItem(item);
});
// Disable the bulk action mode.
this.disableBulkActionsMode();
@@ -168,17 +194,38 @@ export default class BulkActions {
* Renders the bulk actions content.
*
* @method renderBulkActions
* @returns {Promise}
* @returns {Promise<string>}
*/
async renderBulkActions() {
let data = {
'bulkselectioncount': this.selectedItems.length,
'actions': []
const data = {
bulkselectioncount: this.selectedItems.length,
actions: [],
moreactions: [],
hasmoreactions: false,
};
// Render the bulk actions trigger element for each available bulk action.
await Promise.all(this.getBulkActions().map(async(bulkAction) => {
data.actions.push({'actiontrigger': await bulkAction.renderBulkActionTrigger()});
}));
const bulkActions = this.getBulkActions();
const showMoreButton = bulkActions.length > this.maxButtons;
// Get all bulk actions and render them in order.
const actions = await Promise.all(
bulkActions.map((bulkAction, index) =>
bulkAction.renderBulkActionTrigger(
showMoreButton && (index >= this.maxButtons - 1),
index
)
)
);
// Separate rendered actions into data.actions and data.moreactions in the correct order.
actions.forEach((actionTrigger, index) => {
if (showMoreButton && (index >= this.maxButtons - 1)) {
data.moreactions.push({'actiontrigger': actionTrigger});
} else {
data.actions.push({'actiontrigger': actionTrigger});
}
});
data.hasmoreactions = data.moreactions.length > 0;
return Templates.render('core/bulkactions/bulk_actions', data);
}
+1 -1
View File
@@ -1197,7 +1197,7 @@ class flexible_table {
} else {
// Print empty rows to fill the table to the current pagesize.
// This is done so the header aria-controls attributes do not point to
// non existant elements.
// non-existent elements.
$emptyrow = array_fill(0, count($this->columns), '');
while ($this->currentrow < $this->pagesize) {
$this->print_row($emptyrow, 'emptyrow');
@@ -20,18 +20,27 @@
Renders a trigger element for a given bulk action.
Context variables required for this template:
* none
* showindropdown - Whether the action is displayed under a 'More' dropdown or as a separate button.
* isfirst - Whether the action is the first one in the list.
Example context (json):
{
"showindropdown": false,
"isfirst": true
}
}}
<button
type="button"
class="btn py-0 d-flex flex-column"
data-action="{{$action}}{{/action}}"
title="{{$title}}{{/title}}"
>
<span class="w-100 ps-2">{{$icon}}{{/icon}}</span>
<span>{{$title}}{{/title}}</span>
</button>
{{#showindropdown}}
<a class="dropdown-item" href="#" data-action="{{$action}}{{/action}}" role="menuitem">{{$title}}{{/title}}</a>
{{/showindropdown}}
{{^showindropdown}}
<button
type="button"
class="btn py-0 d-flex flex-column"
data-action="{{$action}}{{/action}}"
title="{{$title}}{{/title}}"
{{^isfirst}}tabindex="-1"{{/isfirst}}
>
<span class="w-100 ps-2">{{$icon}}{{/icon}}</span>
<span>{{$title}}{{/title}}</span>
</button>
{{/showindropdown}}
@@ -22,6 +22,9 @@
Context variables required for this template:
* actions - Array of objects with the following properties:
* actiontrigger - The HTML for the action trigger.
* hasmoreactions - Whether there are more actions than the ones displayed.
* moreactions - Array of objects with the following properties:
* actiontrigger - The HTML for the action trigger.
Example context (json):
{
@@ -29,6 +32,12 @@
{
"actiontrigger": "<button>Move</button>"
}
],
"hasmoreactions": true,
"moreactions": [
{
"actiontrigger": "<button>Delete</button>"
}
]
}
}}
@@ -36,13 +45,34 @@
<div class="d-flex">
</div>
<div data-for="bulktools">
<ul class="actions nav" data-for="bulkactions">
<div class="btn-toolbar" role="toolbar" aria-label="{{#str}} bulkactions, core {{/str}}" data-for="bulkactions">
{{#actions}}
<li class="nav-item">
{{{actiontrigger}}}
</li>
{{{actiontrigger}}}
{{/actions}}
</ul>
{{#hasmoreactions}}
<div class="dropup">
<button
class="btn py-0 d-flex flex-column"
type="button"
id="more-actions-toggle-{{uniqid}}"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
tabindex="-1"
>
<span class="w-100 pl-2">
{{#pix}} i/menu, core {{/pix}}
</span>
<span>{{#str}} moremenu, core {{/str}}</span>
</button>
<div class="dropdown-menu" aria-labelledby="more-actions-toggle-{{uniqid}}" role="menu">
{{#moreactions}}
{{{actiontrigger}}}
{{/moreactions}}
</div>
</div>
{{/hasmoreactions}}
</div>
</div>
<div class="d-flex flex-column">
<div class="ms-auto">
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
define("mod_assign/bulkactions/grading/delete",["exports","core/bulkactions/bulk_action","core/templates","core/str","core/modal_delete_cancel","core/modal_events"],(function(_exports,_bulk_action,_templates,_str,_modal_delete_cancel,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates),_modal_delete_cancel=_interopRequireDefault(_modal_delete_cancel),_modal_events=_interopRequireDefault(_modal_events);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';var _cmid=new WeakMap,_sesskey=new WeakMap;class _default extends _bulk_action.default{constructor(cmid,sesskey){super(),_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_sesskey,sesskey)}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="removesubmission"]'}async triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value)),modal=await _modal_delete_cancel.default.create({title:await(0,_str.getString)("removesubmission","mod_assign"),buttons:{save:await(0,_str.getString)("batchoperationremovesubmission","mod_assign")},body:_templates.default.render("mod_assign/bulkactions/grading/bulk_action_modal_body",{text:await(0,_str.getString)("batchoperationconfirmremovesubmission","mod_assign"),operation:"removesubmission",cmid:_classPrivateFieldGet(this,_cmid),selectedusers:selectedUsers.join(","),sesskey:_classPrivateFieldGet(this,_sesskey)}),show:!0,removeOnClose:!0});modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.getRoot().find("form").submit()}))}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_delete_trigger",{showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=delete.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"delete.min.js","sources":["../../../src/bulkactions/grading/delete.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Class that defines the bulk action for removing submissions in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/delete\n * @copyright 2024 Shamim Rezaie <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport DeleteCancelModal from 'core/modal_delete_cancel';\nimport ModalEvents from 'core/modal_events';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n /** @type {number} The course module ID. */\n #cmid;\n\n /** @type {string} The session key. */\n #sesskey;\n\n /**\n * The class constructor.\n *\n * @param {number} cmid The course module ID.\n * @param {string} sesskey The session key.\n */\n constructor(cmid, sesskey) {\n super();\n this.#cmid = cmid;\n this.#sesskey = sesskey;\n }\n\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"removesubmission\"]';\n }\n\n async triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n\n const modal = await DeleteCancelModal.create({\n title: await getString('removesubmission', 'mod_assign'),\n buttons: {\n save: await getString('batchoperationremovesubmission', 'mod_assign'),\n },\n body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {\n text: await getString('batchoperationconfirmremovesubmission', 'mod_assign'),\n operation: 'removesubmission',\n cmid: this.#cmid,\n selectedusers: selectedUsers.join(','),\n sesskey: this.#sesskey\n }),\n show: true,\n removeOnClose: true,\n });\n\n // Handle delete event.\n modal.getRoot().on(ModalEvents.delete, (e) => {\n e.preventDefault();\n modal.getRoot().find('form').submit();\n });\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_delete_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","constructor","cmid","sesskey","getBulkActionTriggerSelector","selectedUsers","document","querySelectorAll","map","checkbox","value","modal","DeleteCancelModal","create","title","buttons","save","body","Templates","render","text","operation","this","selectedusers","join","show","removeOnClose","getRoot","on","ModalEvents","delete","e","preventDefault","find","submit","showInDropdown","index","showindropdown","isfirst"],"mappings":"uoDA6BMA,iCACsB,yHAGCC,qBAazBC,YAAYC,KAAMC,yLAEDD,0CACGC,SAGpBC,qCACW,6FAIDC,cAAgB,IAAIC,SAASC,iBAAiBR,mCAAmCS,KAAIC,UAAYA,SAASC,QAE1GC,YAAcC,6BAAkBC,OAAO,CACzCC,YAAa,kBAAU,mBAAoB,cAC3CC,QAAS,CACLC,WAAY,kBAAU,iCAAkC,eAE5DC,KAAMC,mBAAUC,OAAO,wDAAyD,CAC5EC,WAAY,kBAAU,wCAAyC,cAC/DC,UAAW,mBACXnB,2BAAMoB,YACNC,cAAelB,cAAcmB,KAAK,KAClCrB,8BAASmB,iBAEbG,MAAM,EACNC,eAAe,IAInBf,MAAMgB,UAAUC,GAAGC,sBAAYC,QAASC,IACpCA,EAAEC,iBACFrB,MAAMgB,UAAUM,KAAK,QAAQC,0CAIPC,eAAgBC,cACnClB,mBAAUC,OAAO,qDAAsD,CAC1EkB,eAAgBF,eAChBG,QAAmB,IAAVF"}
@@ -0,0 +1,3 @@
define("mod_assign/bulkactions/grading/extend",["exports","core/bulkactions/bulk_action","core/templates","core/str","core/modal_save_cancel","core/modal_events"],(function(_exports,_bulk_action,_templates,_str,_modal_save_cancel,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_events=_interopRequireDefault(_modal_events);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';var _cmid=new WeakMap,_sesskey=new WeakMap;class _default extends _bulk_action.default{constructor(cmid,sesskey){super(),_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_sesskey,sesskey)}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="grantextension"]'}async triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value)),modal=await _modal_save_cancel.default.create({title:await(0,_str.getString)("grantextension","mod_assign"),buttons:{save:await(0,_str.getString)("batchoperationgrantextension","mod_assign")},body:_templates.default.render("mod_assign/bulkactions/grading/bulk_action_modal_body",{text:await(0,_str.getString)("batchoperationconfirmgrantextension","mod_assign"),operation:"grantextension",cmid:_classPrivateFieldGet(this,_cmid),selectedusers:selectedUsers.join(","),sesskey:_classPrivateFieldGet(this,_sesskey)}),show:!0,removeOnClose:!0});modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),modal.getRoot().find("form").submit()}))}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_extend_trigger",{showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=extend.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"extend.min.js","sources":["../../../src/bulkactions/grading/extend.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Class that defines the bulk action to extend the assignment deadline in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/extend\n * @copyright 2024 Shamim Rezaie <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport ModalEvents from 'core/modal_events';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n /** @type {number} The course module ID. */\n #cmid;\n\n /** @type {string} The session key. */\n #sesskey;\n\n /**\n * The class constructor.\n *\n * @param {number} cmid The course module ID.\n * @param {string} sesskey The session key.\n */\n constructor(cmid, sesskey) {\n super();\n this.#cmid = cmid;\n this.#sesskey = sesskey;\n }\n\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"grantextension\"]';\n }\n\n async triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n\n const modal = await SaveCancelModal.create({\n title: await getString('grantextension', 'mod_assign'),\n buttons: {\n save: await getString('batchoperationgrantextension', 'mod_assign'),\n },\n body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {\n text: await getString('batchoperationconfirmgrantextension', 'mod_assign'),\n operation: 'grantextension',\n cmid: this.#cmid,\n selectedusers: selectedUsers.join(','),\n sesskey: this.#sesskey\n }),\n show: true,\n removeOnClose: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n modal.getRoot().find('form').submit();\n });\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_extend_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","constructor","cmid","sesskey","getBulkActionTriggerSelector","selectedUsers","document","querySelectorAll","map","checkbox","value","modal","SaveCancelModal","create","title","buttons","save","body","Templates","render","text","operation","this","selectedusers","join","show","removeOnClose","getRoot","on","ModalEvents","e","preventDefault","find","submit","showInDropdown","index","showindropdown","isfirst"],"mappings":"+nDA6BMA,iCACsB,yHAGCC,qBAazBC,YAAYC,KAAMC,yLAEDD,0CACGC,SAGpBC,qCACW,2FAIDC,cAAgB,IAAIC,SAASC,iBAAiBR,mCAAmCS,KAAIC,UAAYA,SAASC,QAE1GC,YAAcC,2BAAgBC,OAAO,CACvCC,YAAa,kBAAU,iBAAkB,cACzCC,QAAS,CACLC,WAAY,kBAAU,+BAAgC,eAE1DC,KAAMC,mBAAUC,OAAO,wDAAyD,CAC5EC,WAAY,kBAAU,sCAAuC,cAC7DC,UAAW,iBACXnB,2BAAMoB,YACNC,cAAelB,cAAcmB,KAAK,KAClCrB,8BAASmB,iBAEbG,MAAM,EACNC,eAAe,IAInBf,MAAMgB,UAAUC,GAAGC,sBAAYb,MAAOc,IAClCA,EAAEC,iBACFpB,MAAMgB,UAAUK,KAAK,QAAQC,0CAIPC,eAAgBC,cACnCjB,mBAAUC,OAAO,qDAAsD,CAC1EiB,eAAgBF,eAChBG,QAAmB,IAAVF"}
@@ -0,0 +1,3 @@
define("mod_assign/bulkactions/grading/general_action",["exports","core/bulkactions/bulk_action","core/templates","core/modal_save_cancel","core/modal_events"],(function(_exports,_bulk_action,_templates,_modal_save_cancel,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_events=_interopRequireDefault(_modal_events);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';var _cmid=new WeakMap,_buttonIcon=new WeakMap,_buttonLabel=new WeakMap,_confirmationTitle=new WeakMap,_confirmationQuestion=new WeakMap,_confirmationYes=new WeakMap,_sesskey=new WeakMap;class _default extends _bulk_action.default{constructor(cmid,sesskey,actionKey,buttonLabel,buttonIcon,confirmationTitle,confirmationQuestion,confirmationYes){var obj,key,value;super(),value=void 0,(key="actionKey")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_buttonIcon,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_buttonLabel,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_confirmationTitle,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_confirmationQuestion,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_confirmationYes,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_sesskey,sesskey),this.actionKey=actionKey,_classPrivateFieldSet(this,_buttonLabel,buttonLabel),_classPrivateFieldSet(this,_buttonIcon,buttonIcon),_classPrivateFieldSet(this,_confirmationTitle,confirmationTitle),_classPrivateFieldSet(this,_confirmationQuestion,confirmationQuestion),_classPrivateFieldSet(this,_confirmationYes,confirmationYes)}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="'.concat(this.actionKey,'"]')}async triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value)),modal=await _modal_save_cancel.default.create({title:await _classPrivateFieldGet(this,_confirmationTitle),buttons:{save:await _classPrivateFieldGet(this,_confirmationYes)},body:_templates.default.render("mod_assign/bulkactions/grading/bulk_action_modal_body",{text:await _classPrivateFieldGet(this,_confirmationQuestion),operation:this.actionKey,cmid:_classPrivateFieldGet(this,_cmid),selectedusers:selectedUsers.join(","),sesskey:_classPrivateFieldGet(this,_sesskey)}),show:!0,removeOnClose:!0});modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),modal.getRoot().find("form").submit()}))}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_general_action_trigger",{action:this.actionKey,title:await _classPrivateFieldGet(this,_buttonLabel),icon:await _classPrivateFieldGet(this,_buttonIcon),showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=general_action.min.js.map
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
define("mod_assign/bulkactions/grading/message",["exports","core/bulkactions/bulk_action","core/templates","core_message/message_send_bulk"],(function(_exports,_bulk_action,_templates,_message_send_bulk){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Bulk action for messaging users in the assignment grading page.
*
* @module mod_assign/bulkactions/grading/message
* @copyright 2024 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';class _default extends _bulk_action.default{getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="message"]'}triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value));(0,_message_send_bulk.showModal)(selectedUsers)}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_message_trigger",{showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=message.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"message.min.js","sources":["../../../src/bulkactions/grading/message.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Bulk action for messaging users in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/message\n * @copyright 2024 Shamim Rezaie <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport {showModal as showMessageModal} from 'core_message/message_send_bulk';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"message\"]';\n }\n\n triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n showMessageModal(selectedUsers);\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_message_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","getBulkActionTriggerSelector","triggerBulkAction","selectedUsers","document","querySelectorAll","map","checkbox","value","showInDropdown","index","Templates","render","showindropdown","isfirst"],"mappings":";;;;;;;2LA2BMA,iCACsB,8EAGCC,qBACzBC,qCACW,oDAGXC,0BACUC,cAAgB,IAAIC,SAASC,iBAAiBN,mCAAmCO,KAAIC,UAAYA,SAASC,yCAC/FL,6CAGSM,eAAgBC,cACnCC,mBAAUC,OAAO,sDAAuD,CAC3EC,eAAgBJ,eAChBK,QAAmB,IAAVJ"}
@@ -0,0 +1,3 @@
define("mod_assign/bulkactions/grading/setmarkingallocation",["exports","core/bulkactions/bulk_action","core/templates","core/str","core/modal_save_cancel","core/modal_events"],(function(_exports,_bulk_action,_templates,_str,_modal_save_cancel,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_events=_interopRequireDefault(_modal_events);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';var _cmid=new WeakMap,_sesskey=new WeakMap;class _default extends _bulk_action.default{constructor(cmid,sesskey){super(),_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_sesskey,sesskey)}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="setmarkingallocation"]'}async triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value)),modal=await _modal_save_cancel.default.create({title:await(0,_str.getString)("setmarkingallocation","mod_assign"),buttons:{save:await(0,_str.getString)("batchoperationsetmarkingallocation","mod_assign")},body:_templates.default.render("mod_assign/bulkactions/grading/bulk_action_modal_body",{text:await(0,_str.getString)("batchoperationconfirmsetmarkingallocation","mod_assign"),operation:"setmarkingallocation",cmid:_classPrivateFieldGet(this,_cmid),selectedusers:selectedUsers.join(","),sesskey:_classPrivateFieldGet(this,_sesskey)}),show:!0,removeOnClose:!0});modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),modal.getRoot().find("form").submit()}))}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_setmarkingallocation_trigger",{showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=setmarkingallocation.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"setmarkingallocation.min.js","sources":["../../../src/bulkactions/grading/setmarkingallocation.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Bulk action for allocating markers in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/setmarkingallocation\n * @copyright 2024 Shamim Rezaie <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport ModalEvents from 'core/modal_events';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n /** @type {number} The course module ID. */\n #cmid;\n\n /** @type {string} The session key. */\n #sesskey;\n\n /**\n * The class constructor.\n *\n * @param {number} cmid The course module ID.\n * @param {string} sesskey The session key.\n */\n constructor(cmid, sesskey) {\n super();\n this.#cmid = cmid;\n this.#sesskey = sesskey;\n }\n\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"setmarkingallocation\"]';\n }\n\n async triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n\n const modal = await SaveCancelModal.create({\n title: await getString('setmarkingallocation', 'mod_assign'),\n buttons: {\n save: await getString('batchoperationsetmarkingallocation', 'mod_assign'),\n },\n body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {\n text: await getString('batchoperationconfirmsetmarkingallocation', 'mod_assign'),\n operation: 'setmarkingallocation',\n cmid: this.#cmid,\n selectedusers: selectedUsers.join(','),\n sesskey: this.#sesskey\n }),\n show: true,\n removeOnClose: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n modal.getRoot().find('form').submit();\n });\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_setmarkingallocation_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","constructor","cmid","sesskey","getBulkActionTriggerSelector","selectedUsers","document","querySelectorAll","map","checkbox","value","modal","SaveCancelModal","create","title","buttons","save","body","Templates","render","text","operation","this","selectedusers","join","show","removeOnClose","getRoot","on","ModalEvents","e","preventDefault","find","submit","showInDropdown","index","showindropdown","isfirst"],"mappings":"6oDA6BMA,iCACsB,yHAGCC,qBAazBC,YAAYC,KAAMC,yLAEDD,0CACGC,SAGpBC,qCACW,iGAIDC,cAAgB,IAAIC,SAASC,iBAAiBR,mCAAmCS,KAAIC,UAAYA,SAASC,QAE1GC,YAAcC,2BAAgBC,OAAO,CACvCC,YAAa,kBAAU,uBAAwB,cAC/CC,QAAS,CACLC,WAAY,kBAAU,qCAAsC,eAEhEC,KAAMC,mBAAUC,OAAO,wDAAyD,CAC5EC,WAAY,kBAAU,4CAA6C,cACnEC,UAAW,uBACXnB,2BAAMoB,YACNC,cAAelB,cAAcmB,KAAK,KAClCrB,8BAASmB,iBAEbG,MAAM,EACNC,eAAe,IAInBf,MAAMgB,UAAUC,GAAGC,sBAAYb,MAAOc,IAClCA,EAAEC,iBACFpB,MAAMgB,UAAUK,KAAK,QAAQC,0CAIPC,eAAgBC,cACnCjB,mBAAUC,OAAO,mEAAoE,CACxFiB,eAAgBF,eAChBG,QAAmB,IAAVF"}
@@ -0,0 +1,3 @@
define("mod_assign/bulkactions/grading/setmarkingworkflowstate",["exports","core/bulkactions/bulk_action","core/templates","core/str","core/modal_save_cancel","core/modal_events"],(function(_exports,_bulk_action,_templates,_str,_modal_save_cancel,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_events=_interopRequireDefault(_modal_events);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';var _cmid=new WeakMap,_sesskey=new WeakMap;class _default extends _bulk_action.default{constructor(cmid,sesskey){super(),_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_sesskey,sesskey)}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="setmarkingworkflowstate"]'}async triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value)),modal=await _modal_save_cancel.default.create({title:await(0,_str.getString)("setmarkingworkflowstate","mod_assign"),buttons:{save:await(0,_str.getString)("batchoperationsetmarkingworkflowstate","mod_assign")},body:_templates.default.render("mod_assign/bulkactions/grading/bulk_action_modal_body",{text:await(0,_str.getString)("batchoperationconfirmsetmarkingworkflowstate","mod_assign"),operation:"setmarkingworkflowstate",cmid:_classPrivateFieldGet(this,_cmid),selectedusers:selectedUsers.join(","),sesskey:_classPrivateFieldGet(this,_sesskey)}),show:!0,removeOnClose:!0});modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),modal.getRoot().find("form").submit()}))}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_setmarkingworkflowstate_trigger",{showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=setmarkingworkflowstate.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"setmarkingworkflowstate.min.js","sources":["../../../src/bulkactions/grading/setmarkingworkflowstate.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Class that defines the bulk action for setting marking workflow state in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/setmarkingworkflowstate\n * @copyright 2024 Shamim Rezaie <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport ModalEvents from 'core/modal_events';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n /** @type {number} The course module ID. */\n #cmid;\n\n /** @type {string} The session key. */\n #sesskey;\n\n /**\n * The class constructor.\n *\n * @param {number} cmid The course module ID.\n * @param {string} sesskey The session key.\n */\n constructor(cmid, sesskey) {\n super();\n this.#cmid = cmid;\n this.#sesskey = sesskey;\n }\n\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"setmarkingworkflowstate\"]';\n }\n\n async triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n\n const modal = await SaveCancelModal.create({\n title: await getString('setmarkingworkflowstate', 'mod_assign'),\n buttons: {\n save: await getString('batchoperationsetmarkingworkflowstate', 'mod_assign'),\n },\n body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {\n text: await getString('batchoperationconfirmsetmarkingworkflowstate', 'mod_assign'),\n operation: 'setmarkingworkflowstate',\n cmid: this.#cmid,\n selectedusers: selectedUsers.join(','),\n sesskey: this.#sesskey\n }),\n show: true,\n removeOnClose: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n modal.getRoot().find('form').submit();\n });\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_setmarkingworkflowstate_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","constructor","cmid","sesskey","getBulkActionTriggerSelector","selectedUsers","document","querySelectorAll","map","checkbox","value","modal","SaveCancelModal","create","title","buttons","save","body","Templates","render","text","operation","this","selectedusers","join","show","removeOnClose","getRoot","on","ModalEvents","e","preventDefault","find","submit","showInDropdown","index","showindropdown","isfirst"],"mappings":"gpDA6BMA,iCACsB,yHAGCC,qBAazBC,YAAYC,KAAMC,yLAEDD,0CACGC,SAGpBC,qCACW,oGAIDC,cAAgB,IAAIC,SAASC,iBAAiBR,mCAAmCS,KAAIC,UAAYA,SAASC,QAE1GC,YAAcC,2BAAgBC,OAAO,CACvCC,YAAa,kBAAU,0BAA2B,cAClDC,QAAS,CACLC,WAAY,kBAAU,wCAAyC,eAEnEC,KAAMC,mBAAUC,OAAO,wDAAyD,CAC5EC,WAAY,kBAAU,+CAAgD,cACtEC,UAAW,0BACXnB,2BAAMoB,YACNC,cAAelB,cAAcmB,KAAK,KAClCrB,8BAASmB,iBAEbG,MAAM,EACNC,eAAe,IAInBf,MAAMgB,UAAUC,GAAGC,sBAAYb,MAAOc,IAClCA,EAAEC,iBACFpB,MAAMgB,UAAUK,KAAK,QAAQC,0CAIPC,eAAgBC,cACnCjB,mBAAUC,OAAO,sEAAuE,CAC3FiB,eAAgBF,eAChBG,QAAmB,IAAVF"}
+10
View File
@@ -0,0 +1,10 @@
define("mod_assign/quick_grading",["exports","core_form/submit"],(function(_exports,formSubmit){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)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,formSubmit=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}
/**
* Module for the quick grading functionality on the submissions page.
*
* @module mod_assign/quick_grading
* @copyright 2024 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/(formSubmit);const Selectors_quickGradingSaveRegion='[data-region="quick-grading-save"]',Selectors_notifyStudentsCheckbox='input[type="checkbox"][name="sendstudentnotifications"]',Selectors_notifyStudentsHidden='input[type="hidden"][name="sendstudentnotifications"]',Selectors_saveButton='button[type="submit"]';_exports.init=()=>{const quickGradingSaveRegion=document.querySelector(Selectors_quickGradingSaveRegion);if(quickGradingSaveRegion){const quickGradingSaveButton=quickGradingSaveRegion.querySelector(Selectors_saveButton);formSubmit.init(quickGradingSaveButton),quickGradingSaveRegion.addEventListener("change",(e=>{const notifyStudentsCheckbox=e.target.closest(Selectors_notifyStudentsCheckbox);if(notifyStudentsCheckbox){notifyStudentsCheckbox.parentNode.querySelector(Selectors_notifyStudentsHidden).disabled=notifyStudentsCheckbox.checked}}))}}}));
//# sourceMappingURL=quick_grading.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"quick_grading.min.js","sources":["../src/quick_grading.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 <http://www.gnu.org/licenses/>.\n\nimport * as formSubmit from 'core_form/submit';\n\n/**\n * Module for the quick grading functionality on the submissions page.\n *\n * @module mod_assign/quick_grading\n * @copyright 2024 Mihail Geshoski <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n quickGradingSaveRegion: '[data-region=\"quick-grading-save\"]',\n notifyStudentsCheckbox: 'input[type=\"checkbox\"][name=\"sendstudentnotifications\"]',\n notifyStudentsHidden: 'input[type=\"hidden\"][name=\"sendstudentnotifications\"]',\n saveButton: 'button[type=\"submit\"]'\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const quickGradingSaveRegion = document.querySelector(Selectors.quickGradingSaveRegion);\n if (quickGradingSaveRegion) {\n const quickGradingSaveButton = quickGradingSaveRegion.querySelector(Selectors.saveButton);\n // Initialize the submit button.\n formSubmit.init(quickGradingSaveButton);\n // Add 'change' event listener to the quick grading save region.\n quickGradingSaveRegion.addEventListener('change', e => {\n const notifyStudentsCheckbox = e.target.closest(Selectors.notifyStudentsCheckbox);\n // The target is the 'Notify student' checkbox.\n if (notifyStudentsCheckbox) {\n // The 'Notify student' option uses a hidden input and a checkbox. The hidden input is used to submit '0'\n // as a workaround when the checkbox is unchecked since unchecked checkboxes are not submitted with the\n // form. Therefore, we need to enable or disable the hidden input based on the checkbox state.\n const notifyStudentsHidden = notifyStudentsCheckbox.parentNode.querySelector(Selectors.notifyStudentsHidden);\n notifyStudentsHidden.disabled = notifyStudentsCheckbox.checked;\n }\n });\n }\n};\n"],"names":["Selectors","quickGradingSaveRegion","document","querySelector","quickGradingSaveButton","formSubmit","init","addEventListener","e","notifyStudentsCheckbox","target","closest","parentNode","disabled","checked"],"mappings":";;;;;;;wBA0BMA,iCACsB,qCADtBA,iCAEsB,0DAFtBA,+BAGoB,wDAHpBA,qBAIU,sCAMI,WACVC,uBAAyBC,SAASC,cAAcH,qCAClDC,uBAAwB,OAClBG,uBAAyBH,uBAAuBE,cAAcH,sBAEpEK,WAAWC,KAAKF,wBAEhBH,uBAAuBM,iBAAiB,UAAUC,UACxCC,uBAAyBD,EAAEE,OAAOC,QAAQX,qCAE5CS,uBAAwB,CAIKA,uBAAuBG,WAAWT,cAAcH,gCACxDa,SAAWJ,uBAAuBK"}
@@ -0,0 +1,238 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class for defining the bulk actions area in the assignment grading page.
*
* @module mod_assign/bulkactions/grading/bulk_actions
* @copyright 2024 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import BulkActions from 'core/bulkactions/bulk_actions';
import GeneralAction from './general_action';
import DeleteAction from './delete';
import ExtendAction from './extend';
import MessageAction from './message';
import SetMarkingAllocationAction from './setmarkingallocation';
import SetMarkingWorkflowStateAction from './setmarkingworkflowstate';
import Templates from 'core/templates';
import {getString} from 'core/str';
const Selectors = {
selectBulkItemCheckbox: 'input[type="checkbox"][name="selectedusers"]',
selectBulkItemTrigger: 'input[type="checkbox"][name="selectedusers"], input[type="checkbox"][name="selectall"]',
};
export default class extends BulkActions {
/** @type {number} The course module ID. */
#cmid;
/** @type {boolean} Whether to show the extend action. */
#extend;
/** @type {boolean} Whether to show the grant extension action. */
#grantAttempt;
/** @type {boolean} Whether to show the set marking allocation action. */
#markingAllocation;
/** @type {string} Whether to show the message action. */
#message;
/** @type {Array} The list of plugin operations. */
#pluginOperations;
/** @type {boolean} Whether to show the remove submission action. */
#removeSubmission;
/** @type {string} The session key. */
#sesskey;
/** @type {boolean} Whether to show the revert to draft action. */
#submissionDrafts;
/** @type {boolean} Whether to show the set workflow state action. */
#workflowState;
/**
* Returns the instance of the class.
*
* @param {Object} options - The options object.
* @param {number} options.cmid - The course module ID.
* @param {string} options.message - Whether to show the message action.
* @param {boolean} options.submissiondrafts - Whether to show the revert to draft action.
* @param {boolean} options.removesubmission - Whether to show the remove submission action.
* @param {boolean} options.extend - Whether to show the grant extension action.
* @param {boolean} options.grantattempt - Whether to show the grant attempt action.
* @param {boolean} options.workflowstate - Whether to show the set workflow state action.
* @param {boolean} options.markingallocation - Whether to show the set marking allocation action.
* @param {Array} options.pluginoperations - The list of plugin operations.
* @param {string} options.sesskey - The session key.
* @returns {this} An instance of the anonymous class extending BulkActions.
*/
static init(options) {
return new this(options);
}
/**
* The class constructor
*
* @param {Object} options - The options object.
* @param {number} options.cmid - The course module ID.
* @param {string} options.message - Whether to show the message action.
* @param {boolean} options.submissiondrafts - Whether to show the revert to draft action.
* @param {boolean} options.removesubmission - Whether to show the remove submission action.
* @param {boolean} options.extend - Whether to show the grant extension action.
* @param {boolean} options.grantattempt - Whether to show the grant attempt action.
* @param {boolean} options.workflowstate - Whether to show the set workflow state action.
* @param {boolean} options.markingallocation - Whether to show the set marking allocation action.
* @param {Array} options.pluginoperations - The list of plugin operations.
* @param {string} options.sesskey - The session key.
*/
constructor({
cmid, message, submissiondrafts, removesubmission, extend,
grantattempt, workflowstate, markingallocation, pluginoperations, sesskey
}) {
super();
this.#cmid = cmid;
this.#message = message;
this.#submissionDrafts = submissiondrafts;
this.#removeSubmission = removesubmission;
this.#extend = extend;
this.#grantAttempt = grantattempt;
this.#workflowState = workflowstate;
this.#markingAllocation = markingallocation;
this.#sesskey = sesskey;
this.#pluginOperations = pluginoperations;
}
getBulkActions() {
const actions = [
new GeneralAction(
this.#cmid,
this.#sesskey,
'lock',
getString('batchoperationlock', 'mod_assign'),
Templates.renderPix('i/lock', 'core'),
getString('locksubmissions', 'mod_assign'),
getString('batchoperationconfirmlock', 'mod_assign'),
getString('batchoperationlock', 'mod_assign'),
),
new GeneralAction(
this.#cmid,
this.#sesskey,
'unlock',
getString('batchoperationunlock', 'mod_assign'),
Templates.renderPix('i/unlock', 'core'),
getString('unlocksubmissions', 'mod_assign'),
getString('batchoperationconfirmunlock', 'mod_assign'),
getString('batchoperationunlock', 'mod_assign'),
),
new GeneralAction(
this.#cmid,
this.#sesskey,
'downloadselected',
getString('batchoperationdownloadselected', 'mod_assign'),
Templates.renderPix('t/download', 'core'),
getString('downloadselectedsubmissions', 'mod_assign'),
getString('batchoperationconfirmdownloadselected', 'mod_assign'),
getString('batchoperationdownloadselected', 'mod_assign'),
),
];
if (this.#removeSubmission) {
actions.push(new DeleteAction(this.#cmid, this.#sesskey));
}
if (this.#extend) {
actions.push(new ExtendAction(this.#cmid, this.#sesskey));
}
if (this.#grantAttempt) {
actions.push(
new GeneralAction(
this.#cmid,
this.#sesskey,
'addattempt',
getString('batchoperationaddattempt', 'mod_assign'),
Templates.renderPix('t/add', 'core'),
getString('addattempt', 'mod_assign'),
getString('batchoperationconfirmaddattempt', 'mod_assign'),
getString('batchoperationaddattempt', 'mod_assign'),
)
);
}
if (this.#workflowState) {
actions.push(new SetMarkingWorkflowStateAction(this.#cmid, this.#sesskey));
}
if (this.#markingAllocation) {
actions.push(new SetMarkingAllocationAction(this.#cmid, this.#sesskey));
}
if (this.#submissionDrafts) {
actions.push(
new GeneralAction(
this.#cmid,
this.#sesskey,
'reverttodraft',
getString('batchoperationreverttodraft', 'mod_assign'),
Templates.renderPix('e/undo', 'core'),
getString('reverttodraft', 'mod_assign'),
getString('batchoperationconfirmreverttodraft', 'mod_assign'),
getString('batchoperationreverttodraft', 'mod_assign'),
)
);
}
if (this.#message) {
actions.push(new MessageAction());
}
for (const operation of this.#pluginOperations) {
actions.push(
new GeneralAction(
this.#cmid,
this.#sesskey,
operation.key,
operation.label,
operation.icon,
operation.confirmationtitle,
operation.confirmationquestion,
)
);
}
return actions;
}
getSelectedItems() {
return document.querySelectorAll(`${Selectors.selectBulkItemCheckbox}:checked`);
}
registerItemSelectChangeEvent(eventHandler) {
const itemSelectCheckboxes = document.querySelectorAll(Selectors.selectBulkItemTrigger);
itemSelectCheckboxes.forEach((checkbox) => {
checkbox.addEventListener('change', eventHandler.bind(this));
});
}
deselectItem(selectedItem) {
selectedItem.checked = false;
selectedItem.closest('tr').classList.replace('selectedrow', 'unselectedrow');
}
}
@@ -0,0 +1,89 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class that defines the bulk action for removing submissions in the assignment grading page.
*
* @module mod_assign/bulkactions/grading/delete
* @copyright 2024 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import BulkAction from 'core/bulkactions/bulk_action';
import Templates from 'core/templates';
import {getString} from 'core/str';
import DeleteCancelModal from 'core/modal_delete_cancel';
import ModalEvents from 'core/modal_events';
const Selectors = {
selectBulkItemCheckbox: 'input[type="checkbox"][name="selectedusers"]:checked',
};
export default class extends BulkAction {
/** @type {number} The course module ID. */
#cmid;
/** @type {string} The session key. */
#sesskey;
/**
* The class constructor.
*
* @param {number} cmid The course module ID.
* @param {string} sesskey The session key.
*/
constructor(cmid, sesskey) {
super();
this.#cmid = cmid;
this.#sesskey = sesskey;
}
getBulkActionTriggerSelector() {
return '[data-type="bulkactions"] [data-action="removesubmission"]';
}
async triggerBulkAction() {
const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);
const modal = await DeleteCancelModal.create({
title: await getString('removesubmission', 'mod_assign'),
buttons: {
save: await getString('batchoperationremovesubmission', 'mod_assign'),
},
body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {
text: await getString('batchoperationconfirmremovesubmission', 'mod_assign'),
operation: 'removesubmission',
cmid: this.#cmid,
selectedusers: selectedUsers.join(','),
sesskey: this.#sesskey
}),
show: true,
removeOnClose: true,
});
// Handle delete event.
modal.getRoot().on(ModalEvents.delete, (e) => {
e.preventDefault();
modal.getRoot().find('form').submit();
});
}
async renderBulkActionTrigger(showInDropdown, index) {
return Templates.render('mod_assign/bulkactions/grading/bulk_delete_trigger', {
showindropdown: showInDropdown,
isfirst: index === 0,
});
}
}
@@ -0,0 +1,89 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class that defines the bulk action to extend the assignment deadline in the assignment grading page.
*
* @module mod_assign/bulkactions/grading/extend
* @copyright 2024 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import BulkAction from 'core/bulkactions/bulk_action';
import Templates from 'core/templates';
import {getString} from 'core/str';
import SaveCancelModal from 'core/modal_save_cancel';
import ModalEvents from 'core/modal_events';
const Selectors = {
selectBulkItemCheckbox: 'input[type="checkbox"][name="selectedusers"]:checked',
};
export default class extends BulkAction {
/** @type {number} The course module ID. */
#cmid;
/** @type {string} The session key. */
#sesskey;
/**
* The class constructor.
*
* @param {number} cmid The course module ID.
* @param {string} sesskey The session key.
*/
constructor(cmid, sesskey) {
super();
this.#cmid = cmid;
this.#sesskey = sesskey;
}
getBulkActionTriggerSelector() {
return '[data-type="bulkactions"] [data-action="grantextension"]';
}
async triggerBulkAction() {
const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);
const modal = await SaveCancelModal.create({
title: await getString('grantextension', 'mod_assign'),
buttons: {
save: await getString('batchoperationgrantextension', 'mod_assign'),
},
body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {
text: await getString('batchoperationconfirmgrantextension', 'mod_assign'),
operation: 'grantextension',
cmid: this.#cmid,
selectedusers: selectedUsers.join(','),
sesskey: this.#sesskey
}),
show: true,
removeOnClose: true,
});
// Handle save event.
modal.getRoot().on(ModalEvents.save, (e) => {
e.preventDefault();
modal.getRoot().find('form').submit();
});
}
async renderBulkActionTrigger(showInDropdown, index) {
return Templates.render('mod_assign/bulkactions/grading/bulk_extend_trigger', {
showindropdown: showInDropdown,
isfirst: index === 0,
});
}
}
@@ -0,0 +1,122 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class that defines the bulk action for general actions in the assignment grading page.
*
* @module mod_assign/bulkactions/grading/general_action
* @copyright 2024 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import BulkAction from 'core/bulkactions/bulk_action';
import Templates from 'core/templates';
import SaveCancelModal from 'core/modal_save_cancel';
import ModalEvents from 'core/modal_events';
const Selectors = {
selectBulkItemCheckbox: 'input[type="checkbox"][name="selectedusers"]:checked',
};
export default class extends BulkAction {
/** @type {string} The action key. */
actionKey;
/** @type {number} The course module ID. */
#cmid;
/** @type {Promise<string>} The action button's icon. */
#buttonIcon;
/** @type {Promise<string>} The action button's label. */
#buttonLabel;
/** @type {Promise<string>} Title of the confirmation dialog. */
#confirmationTitle;
/** @type {Promise<string>} Question of the confirmation dialog. */
#confirmationQuestion;
/** @type {Promise<string>} Text for the confirmation yes button. */
#confirmationYes;
/** @type {string} The session key. */
#sesskey;
/**
* The class constructor.
*
* @param {int} cmid The course module ID.
* @param {string} sesskey The session key.
* @param {string} actionKey The action key.
* @param {Promise<string>} buttonLabel The action button's label.
* @param {Promise<string>} buttonIcon The action button's icon.
* @param {Promise<string>} confirmationTitle Title of the confirmation dialog.
* @param {Promise<string>} confirmationQuestion Question of the confirmation dialog.
* @param {Promise<string>} confirmationYes Text for the confirmation yes button.
*/
constructor(cmid, sesskey, actionKey, buttonLabel, buttonIcon, confirmationTitle, confirmationQuestion, confirmationYes) {
super();
this.#cmid = cmid;
this.#sesskey = sesskey;
this.actionKey = actionKey;
this.#buttonLabel = buttonLabel;
this.#buttonIcon = buttonIcon;
this.#confirmationTitle = confirmationTitle;
this.#confirmationQuestion = confirmationQuestion;
this.#confirmationYes = confirmationYes;
}
getBulkActionTriggerSelector() {
return `[data-type="bulkactions"] [data-action="${this.actionKey}"]`;
}
async triggerBulkAction() {
const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);
const modal = await SaveCancelModal.create({
title: await this.#confirmationTitle,
buttons: {
save: await this.#confirmationYes,
},
body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {
text: await this.#confirmationQuestion,
operation: this.actionKey,
cmid: this.#cmid,
selectedusers: selectedUsers.join(','),
sesskey: this.#sesskey
}),
show: true,
removeOnClose: true,
});
// Handle save event.
modal.getRoot().on(ModalEvents.save, (e) => {
e.preventDefault();
modal.getRoot().find('form').submit();
});
}
async renderBulkActionTrigger(showInDropdown, index) {
return Templates.render('mod_assign/bulkactions/grading/bulk_general_action_trigger', {
action: this.actionKey,
title: await this.#buttonLabel,
icon: await this.#buttonIcon,
showindropdown: showInDropdown,
isfirst: index === 0,
});
}
}
@@ -0,0 +1,48 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Bulk action for messaging users in the assignment grading page.
*
* @module mod_assign/bulkactions/grading/message
* @copyright 2024 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import BulkAction from 'core/bulkactions/bulk_action';
import Templates from 'core/templates';
import {showModal as showMessageModal} from 'core_message/message_send_bulk';
const Selectors = {
selectBulkItemCheckbox: 'input[type="checkbox"][name="selectedusers"]:checked',
};
export default class extends BulkAction {
getBulkActionTriggerSelector() {
return '[data-type="bulkactions"] [data-action="message"]';
}
triggerBulkAction() {
const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);
showMessageModal(selectedUsers);
}
async renderBulkActionTrigger(showInDropdown, index) {
return Templates.render('mod_assign/bulkactions/grading/bulk_message_trigger', {
showindropdown: showInDropdown,
isfirst: index === 0,
});
}
}
@@ -0,0 +1,89 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Bulk action for allocating markers in the assignment grading page.
*
* @module mod_assign/bulkactions/grading/setmarkingallocation
* @copyright 2024 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import BulkAction from 'core/bulkactions/bulk_action';
import Templates from 'core/templates';
import {getString} from 'core/str';
import SaveCancelModal from 'core/modal_save_cancel';
import ModalEvents from 'core/modal_events';
const Selectors = {
selectBulkItemCheckbox: 'input[type="checkbox"][name="selectedusers"]:checked',
};
export default class extends BulkAction {
/** @type {number} The course module ID. */
#cmid;
/** @type {string} The session key. */
#sesskey;
/**
* The class constructor.
*
* @param {number} cmid The course module ID.
* @param {string} sesskey The session key.
*/
constructor(cmid, sesskey) {
super();
this.#cmid = cmid;
this.#sesskey = sesskey;
}
getBulkActionTriggerSelector() {
return '[data-type="bulkactions"] [data-action="setmarkingallocation"]';
}
async triggerBulkAction() {
const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);
const modal = await SaveCancelModal.create({
title: await getString('setmarkingallocation', 'mod_assign'),
buttons: {
save: await getString('batchoperationsetmarkingallocation', 'mod_assign'),
},
body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {
text: await getString('batchoperationconfirmsetmarkingallocation', 'mod_assign'),
operation: 'setmarkingallocation',
cmid: this.#cmid,
selectedusers: selectedUsers.join(','),
sesskey: this.#sesskey
}),
show: true,
removeOnClose: true,
});
// Handle save event.
modal.getRoot().on(ModalEvents.save, (e) => {
e.preventDefault();
modal.getRoot().find('form').submit();
});
}
async renderBulkActionTrigger(showInDropdown, index) {
return Templates.render('mod_assign/bulkactions/grading/bulk_setmarkingallocation_trigger', {
showindropdown: showInDropdown,
isfirst: index === 0,
});
}
}
@@ -0,0 +1,89 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class that defines the bulk action for setting marking workflow state in the assignment grading page.
*
* @module mod_assign/bulkactions/grading/setmarkingworkflowstate
* @copyright 2024 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import BulkAction from 'core/bulkactions/bulk_action';
import Templates from 'core/templates';
import {getString} from 'core/str';
import SaveCancelModal from 'core/modal_save_cancel';
import ModalEvents from 'core/modal_events';
const Selectors = {
selectBulkItemCheckbox: 'input[type="checkbox"][name="selectedusers"]:checked',
};
export default class extends BulkAction {
/** @type {number} The course module ID. */
#cmid;
/** @type {string} The session key. */
#sesskey;
/**
* The class constructor.
*
* @param {number} cmid The course module ID.
* @param {string} sesskey The session key.
*/
constructor(cmid, sesskey) {
super();
this.#cmid = cmid;
this.#sesskey = sesskey;
}
getBulkActionTriggerSelector() {
return '[data-type="bulkactions"] [data-action="setmarkingworkflowstate"]';
}
async triggerBulkAction() {
const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);
const modal = await SaveCancelModal.create({
title: await getString('setmarkingworkflowstate', 'mod_assign'),
buttons: {
save: await getString('batchoperationsetmarkingworkflowstate', 'mod_assign'),
},
body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {
text: await getString('batchoperationconfirmsetmarkingworkflowstate', 'mod_assign'),
operation: 'setmarkingworkflowstate',
cmid: this.#cmid,
selectedusers: selectedUsers.join(','),
sesskey: this.#sesskey
}),
show: true,
removeOnClose: true,
});
// Handle save event.
modal.getRoot().on(ModalEvents.save, (e) => {
e.preventDefault();
modal.getRoot().find('form').submit();
});
}
async renderBulkActionTrigger(showInDropdown, index) {
return Templates.render('mod_assign/bulkactions/grading/bulk_setmarkingworkflowstate_trigger', {
showindropdown: showInDropdown,
isfirst: index === 0,
});
}
}
+56
View File
@@ -0,0 +1,56 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
import * as formSubmit from 'core_form/submit';
/**
* Module for the quick grading functionality on the submissions page.
*
* @module mod_assign/quick_grading
* @copyright 2024 Mihail Geshoski <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/** @constant {Object} The object containing the relevant selectors. */
const Selectors = {
quickGradingSaveRegion: '[data-region="quick-grading-save"]',
notifyStudentsCheckbox: 'input[type="checkbox"][name="sendstudentnotifications"]',
notifyStudentsHidden: 'input[type="hidden"][name="sendstudentnotifications"]',
saveButton: 'button[type="submit"]'
};
/**
* Initialize module.
*/
export const init = () => {
const quickGradingSaveRegion = document.querySelector(Selectors.quickGradingSaveRegion);
if (quickGradingSaveRegion) {
const quickGradingSaveButton = quickGradingSaveRegion.querySelector(Selectors.saveButton);
// Initialize the submit button.
formSubmit.init(quickGradingSaveButton);
// Add 'change' event listener to the quick grading save region.
quickGradingSaveRegion.addEventListener('change', e => {
const notifyStudentsCheckbox = e.target.closest(Selectors.notifyStudentsCheckbox);
// The target is the 'Notify student' checkbox.
if (notifyStudentsCheckbox) {
// The 'Notify student' option uses a hidden input and a checkbox. The hidden input is used to submit '0'
// as a workaround when the checkbox is unchecked since unchecked checkboxes are not submitted with the
// form. Therefore, we need to enable or disable the hidden input based on the checkbox state.
const notifyStudentsHidden = notifyStudentsCheckbox.parentNode.querySelector(Selectors.notifyStudentsHidden);
notifyStudentsHidden.disabled = notifyStudentsCheckbox.checked;
}
});
}
};
@@ -51,18 +51,6 @@ class grading_options_temp_form extends \moodleform {
$mform->disable_form_change_checker();
$mform->addElement('header', 'general', get_string('gradingoptions', 'assign'));
// Visible elements.
$options = array(10 => '10', 20 => '20', 50 => '50', 100 => '100', -1 => get_string('all'));
$maxperpage = get_config('assign', 'maxperpage');
if (isset($maxperpage) && $maxperpage != -1) {
unset($options[-1]);
foreach ($options as $val) {
if ($val > $maxperpage) {
unset($options[$val]);
}
}
}
$mform->addElement('select', 'perpage', get_string('assignmentsperpage', 'assign'), $options);
if (!empty($instance['markingallocationopt'])) {
$markingfilter = get_string('markerfilter', 'assign');
$mform->addElement('select', 'markerfilter', $markingfilter, $instance['markingallocationopt']);
-17
View File
@@ -1131,23 +1131,6 @@ class renderer extends \plugin_renderer_base {
$o .= $this->output->box_start('boxaligncenter gradingtable position-relative');
$this->page->requires->js_init_call('M.mod_assign.init_grading_table', array());
$this->page->requires->string_for_js('nousersselected', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmgrantextension', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmlock', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmremovesubmission', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmreverttodraft', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmunlock', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmaddattempt', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmdownloadselected', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmsetmarkingworkflowstate', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmsetmarkingallocation', 'assign');
$this->page->requires->string_for_js('editaction', 'assign');
foreach ($table->plugingradingbatchoperations as $plugin => $operations) {
foreach ($operations as $operation => $description) {
$this->page->requires->string_for_js('batchoperationconfirm' . $operation,
'assignfeedback_' . $plugin);
}
}
$o .= $this->flexible_table($table, $table->get_rows_per_page(), true);
$o .= $this->output->box_end();
@@ -29,12 +29,12 @@ Feature: In an assignment, teachers can provide feedback comments on student sub
And I navigate to "Submissions" in current page administration
Then I click on "Quick grading" "checkbox"
And I set the field "Feedback comments" to "Feedback from teacher."
And I press "Save all quick grading changes"
And I click on "Save" "button" in the "sticky-footer" "region"
And I should see "The grade changes were saved"
And I press "Continue"
And I should see "Feedback from teacher."
And I set the field "Feedback comments" to ""
And I press "Save all quick grading changes"
And I click on "Save" "button" in the "sticky-footer" "region"
And I should see "The grade changes were saved"
And I press "Continue"
And I should not see "Feedback from teacher."
@@ -23,6 +23,7 @@
*/
$string['batchoperationconfirmuploadfiles'] = 'Upload one or more feedback files for all selected users?';
$string['batchoperationuploadfiles'] = 'Upload feedback';
$string['batchuploadfiles'] = 'Upload feedback files for multiple users';
$string['batchuploadfilesforusers'] = 'Send feedback files to {$a} selected user(s).';
$string['configmaxbytes'] = 'Maximum file size';
+12 -8
View File
@@ -433,14 +433,18 @@ class assign_feedback_file extends assign_feedback_plugin {
return true;
}
/**
* Return a list of the batch grading operations performed by this plugin.
* This plugin supports batch upload files and upload zip.
*
* @return array The list of batch grading operations
*/
public function get_grading_batch_operations() {
return array('uploadfiles'=>get_string('uploadfiles', 'assignfeedback_file'));
public function get_grading_batch_operation_details() {
global $OUTPUT;
return [
(object) [
'key' => 'uploadfiles',
'label' => get_string('batchoperationuploadfiles', 'assignfeedback_file'),
'icon' => $OUTPUT->pix_icon('i/upload', ''),
'confirmationtitle' => get_string('uploadfiles', 'assignfeedback_file'),
'confirmationquestion' => get_string('batchoperationconfirmuploadfiles', 'assignfeedback_file'),
],
];
}
/**
+20 -1
View File
@@ -158,14 +158,33 @@ abstract class assign_feedback_plugin extends assign_plugin {
return false;
}
/**
* Return a list of detailed batch grading operations supported by this plugin.
*
* @return array - An array of objects containing batch operation details. Each object should contain:
* - 'key': the action identifier (string)
* - 'label': the button label (string)
* - 'icon': the button icon (string)
* - 'confirmationtitle': the title for the confirmation modal (string)
* - 'confirmationquestion': the question for the confirmation modal (string)
*/
public function get_grading_batch_operation_details() {
return [];
}
/**
* Return a list of the batch grading operations supported by this plugin.
*
* @return array - An array of action and description strings.
* The action will be passed to grading_batch_operation.
* @deprecated since 4.5, use get_grading_batch_operation_details() instead.
* @todo Final deprecation in Moodle 6.0. See MDL-82856.
*/
#[\core\attribute\deprecated('get_grading_batch_operation_details', since: '4.5', mdl: 'MDL-80750')]
public function get_grading_batch_operations() {
return array();
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
return array_column(static::get_grading_batch_operation_details(), 'confirmationtitle', 'key');
}
/**
+18 -1
View File
@@ -33,12 +33,30 @@ require_once($CFG->dirroot . '/mod/assign/locallib.php');
* @package mod_assign
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @deprecated since 4.5
* @todo Final deprecation in Moodle 6.0. See MDL-82869.
*/
#[\core\attribute\deprecated(
replacement: null,
since: '4.5',
reason: 'It is no longer used.',
mdl: 'MDL-80750',
)]
class mod_assign_grading_batch_operations_form extends moodleform {
/**
* Define this form - called by the parent constructor.
*
* @deprecated since 4.5
*/
#[\core\attribute\deprecated(
replacement: null,
since: '4.5',
reason: 'It is no longer used.',
mdl: 'MDL-80750'
)]
public function definition() {
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
global $CFG;
$mform = $this->_form;
$instance = $this->_customdata;
@@ -100,4 +118,3 @@ class mod_assign_grading_batch_operations_form extends moodleform {
$mform->addElement('group', 'actionsgrp', $batchdescription, $objs, ' ', false);
}
}
+120 -12
View File
@@ -40,6 +40,8 @@ class assign_grading_table extends table_sql implements renderable {
private $assignment = null;
/** @var int $perpage */
private $perpage = 10;
/** @var int[] $pagingoptions Available pagination options */
private $pagingoptions = [10, 20, 50, 100];
/** @var int $rownum (global index of current row in table) */
private $rownum = -1;
/** @var renderer_base for getting output */
@@ -58,8 +60,6 @@ class assign_grading_table extends table_sql implements renderable {
private $groupsubmissions = array();
/** @var array $submissiongroups - A static cache of submission groups */
private $submissiongroups = array();
/** @var string $plugingradingbatchoperations - List of plugin supported batch operations */
public $plugingradingbatchoperations = array();
/** @var array $plugincache - A cache of plugin lookups to match a column name to a plugin efficiently */
private $plugincache = array();
/** @var array $scale - A list of the keys and descriptions for the custom scale */
@@ -100,16 +100,6 @@ class assign_grading_table extends table_sql implements renderable {
$this->hasviewblind = has_capability('mod/assign:viewblinddetails',
$this->assignment->get_context());
foreach ($assignment->get_feedback_plugins() as $plugin) {
if ($plugin->is_visible() && $plugin->is_enabled()) {
foreach ($plugin->get_grading_batch_operations() as $action => $description) {
if (empty($this->plugingradingbatchoperations)) {
$this->plugingradingbatchoperations[$plugin->get_type()] = array();
}
$this->plugingradingbatchoperations[$plugin->get_type()][$action] = $description;
}
}
}
$this->perpage = $perpage;
$this->quickgrading = $quickgrading && $this->hasgrade;
$this->output = $PAGE->get_renderer('mod_assign');
@@ -929,6 +919,7 @@ class assign_grading_table extends table_sql implements renderable {
$selectcol .= get_string('selectuser', 'assign', $this->assignment->fullname($row));
$selectcol .= '</label>';
$selectcol .= '<input type="checkbox"
class="ignoredirty"
id="selectuser_' . $row->userid . '"
name="selectedusers"
value="' . $row->userid . '"/>';
@@ -1824,4 +1815,121 @@ class assign_grading_table extends table_sql implements renderable {
}
parent::setup();
}
/**
* Returns the html for the paging bar.
*
* @return string
*/
public function get_paging_bar(): string {
global $OUTPUT;
if ($this->use_pages) {
$pagingbar = new paging_bar($this->totalrows, $this->currpage, $this->pagesize, $this->baseurl);
$pagingbar->pagevar = $this->request[TABLE_VAR_PAGE];
return $OUTPUT->render($pagingbar);
}
return '';
}
/**
* Returns the html for the paging selector.
*
* @return string
*/
public function get_paging_selector(): string {
global $OUTPUT;
if ($this->use_pages) {
$pagingoptions = [...$this->pagingoptions, $this->perpage]; // To make sure the actual page size is within the options.
$pagingoptions = array_unique($pagingoptions);
sort($pagingoptions);
$pagingoptions = array_combine($pagingoptions, $pagingoptions);
$maxperpage = get_config('assign', 'maxperpage');
if (isset($maxperpage) && $maxperpage != -1) {
// Remove any options that are greater than the maxperpage.
$pagingoptions = array_filter($pagingoptions, fn($value) => $value <= $maxperpage);
} else {
$pagingoptions[-1] = get_string('all');
}
$data = [
'baseurl' => $this->baseurl->out(false),
'options' => array_map(fn($key, $name): array => [
'name' => $name,
'value' => $key,
'selected' => $key == $this->perpage,
], array_keys($pagingoptions), $pagingoptions),
];
return $OUTPUT->render_from_template('mod_assign/grading_paging_selector', $data);
}
return '';
}
/**
* Finish the HTML output.
* This function is essentially a copy of the parent function except the paging bar not being rendered.
*
* @return void
*/
public function finish_html(): void {
if (!$this->started_output) {
// No data has been added to the table.
$this->print_nothing_to_display();
} else {
// Print empty rows to fill the table to the current pagesize.
// This is done so the header aria-controls attributes do not point to
// non-existent elements.
$emptyrow = array_fill(0, count($this->columns), '');
while ($this->currentrow < $this->pagesize) {
$this->print_row($emptyrow, 'emptyrow');
}
echo html_writer::end_tag('tbody');
echo html_writer::end_tag('table');
if ($this->responsive) {
echo html_writer::end_tag('div');
}
$this->wrap_html_finish();
if (in_array(TABLE_P_BOTTOM, $this->showdownloadbuttonsat)) {
echo $this->download_buttons();
}
// Render the dynamic table footer.
echo $this->get_dynamic_table_html_end();
}
}
/**
* Start the HTML output.
* This function is essentially a copy of the parent function except the paging bar not being rendered.
*
* @return void
*/
public function start_html(): void {
// Render the dynamic table header.
echo $this->get_dynamic_table_html_start();
// Render button to allow user to reset table preferences.
echo $this->render_reset_button();
// Do we need to print initial bars?
$this->print_initials_bar();
if (in_array(TABLE_P_TOP, $this->showdownloadbuttonsat)) {
echo $this->download_buttons();
}
$this->wrap_html_start();
// Start of main data table.
if ($this->responsive) {
echo html_writer::start_tag('div', ['class' => 'no-overflow']);
}
echo html_writer::start_tag('table', $this->attributes) . $this->render_caption();
}
}
+12 -5
View File
@@ -122,6 +122,7 @@ $string['attemptreopenmethod_untilpass_help'] = 'After each attempt, the next wi
$string['availability'] = 'Availability';
$string['back'] = 'Back';
$string['backtoassignment'] = 'Back to assignment';
$string['batchoperationaddattempt'] = 'Grant attempt';
$string['batchoperationsdescription'] = 'With selected...';
$string['batchoperationconfirmlock'] = 'Lock all selected submissions?';
$string['batchoperationconfirmgrantextension'] = 'Grant an extension to all selected submissions?';
@@ -132,9 +133,14 @@ $string['batchoperationconfirmaddattempt'] = 'Allow another attempt for selected
$string['batchoperationconfirmsetmarkingworkflowstate'] = 'Set marking workflow state for all selected submissions?';
$string['batchoperationconfirmsetmarkingallocation'] = 'Set marking allocation for all selected submissions?';
$string['batchoperationconfirmdownloadselected'] = 'Download selected submissions?';
$string['batchoperationlock'] = 'lock submissions';
$string['batchoperationunlock'] = 'unlock submissions';
$string['batchoperationreverttodraft'] = 'revert submissions to draft';
$string['batchoperationdownloadselected'] = 'Download';
$string['batchoperationgrantextension'] = 'Extend';
$string['batchoperationlock'] = 'Lock';
$string['batchoperationremovesubmission'] = 'Delete';
$string['batchoperationunlock'] = 'Unlock';
$string['batchoperationreverttodraft'] = 'Revert to draft';
$string['batchoperationsetmarkingallocation'] = 'Allocate marker';
$string['batchoperationsetmarkingworkflowstate'] = 'Change marking state';
$string['batchsetallocatedmarker'] = 'Set allocated marker for {$a} selected user(s).';
$string['batchsetmarkingworkflowstateforusers'] = 'Set marking workflow state for {$a} selected user(s).';
$string['beginassignment'] = 'Begin assignment';
@@ -209,7 +215,6 @@ $string['editsubmission'] = 'Edit submission';
$string['editsubmissionother'] = 'Edit submission for {$a}';
$string['editsubmission_help'] = 'You can still make changes to your submission.';
$string['editingstatus'] = 'Editing status';
$string['editaction'] = 'Actions...';
$string['enabled'] = 'Enabled';
$string['enabletimelimit'] = 'Enable timed assignments';
$string['enabletimelimit_help'] = 'If enabled, you can set a time limit on assignment settings page.';
@@ -434,6 +439,7 @@ $string['noteamgrader'] = 'Not a member of any group, so unable to make submissi
$string['notgraded'] = 'Not graded';
$string['notgradedyet'] = 'Not graded yet';
$string['notifications'] = 'Notifications';
$string['notifystudents'] = 'Notify students';
$string['nousersselected'] = 'No users selected';
$string['nousers'] = 'No users';
$string['numberofdraftsubmissions'] = 'Drafts';
@@ -525,7 +531,6 @@ $string['reverttodraft'] = 'Revert the submission to draft status';
$string['reverttodraftshort'] = 'Revert the submission to draft';
$string['reviewed'] = 'Reviewed';
$string['save'] = 'Save';
$string['saveallquickgradingchanges'] = 'Save all quick grading changes';
$string['saveandcontinue'] = 'Save and continue';
$string['savechanges'] = 'Save changes';
$string['savegradingresult'] = 'Grade';
@@ -702,7 +707,9 @@ $string['xofy'] = '{$a->x} of {$a->y}';
$string['attemptreopenmethod_none'] = 'Never';
$string['choosegradingaction'] = 'Grading action';
$string['downloadasfolders_help'] = 'Assignment submissions may be downloaded in folders. Each submission is then put in a separate folder, with the folder structure kept for any subfolders, and files are not renamed.';
$string['editaction'] = 'Actions...';
$string['groupoverridesdeleted'] = 'Group overrides deleted';
$string['saveallquickgradingchanges'] = 'Save all quick grading changes';
$string['updategrade'] = 'Update grade';
$string['useroverridesdeleted'] = 'User overrides deleted';
$string['viewgrader'] = 'View Grader';
+2
View File
@@ -5,3 +5,5 @@ choosegradingaction,mod_assign
updategrade,mod_assign
viewgrader,mod_assign
downloadasfolders_help,mod_assign
editaction,mod_assign
saveallquickgradingchanges,mod_assign
+130 -134
View File
@@ -594,7 +594,7 @@ class assign {
$action = 'viewsubmitforgradingerror';
}
} else if ($action == 'gradingbatchoperation') {
$action = $this->process_grading_batch_operation($mform);
$action = $this->process_grading_batch_operation();
if ($action == 'grading') {
$action = 'redirect';
$nextpageparams['action'] = 'grading';
@@ -699,20 +699,20 @@ class assign {
} else if ($action == 'grantextension') {
$o .= $this->view_grant_extension($mform);
} else if ($action == 'revealidentities') {
$o .= $this->view_reveal_identities_confirm($mform);
$o .= $this->view_reveal_identities_confirm();
} else if ($action == 'removesubmissionconfirm') {
$PAGE->add_body_class('limitedwidth');
$o .= $this->view_remove_submission_confirm();
} else if ($action == 'plugingradingbatchoperation') {
$o .= $this->view_plugin_grading_batch_operation($mform);
$o .= $this->view_plugin_grading_batch_operation();
} else if ($action == 'viewpluginpage') {
$o .= $this->view_plugin_page();
} else if ($action == 'viewcourseindex') {
$o .= $this->view_course_index();
} else if ($action == 'viewbatchsetmarkingworkflowstate') {
$o .= $this->view_batch_set_workflow_state($mform);
$o .= $this->view_batch_set_workflow_state();
} else if ($action == 'viewbatchmarkingallocation') {
$o .= $this->view_batch_markingallocation($mform);
$o .= $this->view_batch_markingallocation();
} else if ($action == 'viewsubmitforgradingerror') {
$o .= $this->view_error_page(get_string('submitforgrading', 'assign'), $notices);
} else if ($action == 'fixrescalednullgrades') {
@@ -4478,12 +4478,11 @@ class assign {
* @return string
*/
protected function view_grading_table() {
global $USER, $CFG, $SESSION, $PAGE;
global $USER, $CFG, $SESSION, $PAGE, $OUTPUT;
// Include grading options form.
require_once($CFG->dirroot . '/mod/assign/gradingoptionsform.php');
require_once($CFG->dirroot . '/mod/assign/quickgradingform.php');
require_once($CFG->dirroot . '/mod/assign/gradingbatchoperationsform.php');
$submittedfilter = optional_param('status', null, PARAM_ALPHA);
if (isset($submittedfilter)) {
@@ -4504,6 +4503,11 @@ class assign {
set_user_preference('assign_downloadasfolders', $submitteddownloadasfolders);
}
$submittedperpage = optional_param('perpage', null, PARAM_INT);
if (isset($submittedperpage)) {
set_user_preference('assign_perpage', $submittedperpage);
}
$o = '';
$cmid = $this->get_course_module()->id;
@@ -4565,28 +4569,7 @@ class assign {
'',
$classoptions);
$batchformparams = array('cm'=>$cmid,
'submissiondrafts'=>$this->get_instance()->submissiondrafts,
'duedate'=>$this->get_instance()->duedate,
'maxattempts' => $this->get_instance()->maxattempts,
'attemptreopenmethod'=>$this->get_instance()->attemptreopenmethod,
'feedbackplugins'=>$this->get_feedback_plugins(),
'context'=>$this->get_context(),
'markingworkflow'=>$markingworkflow,
'markingallocation'=>$markingallocation);
$classoptions = [
'class' => 'gradingbatchoperationsform',
'data-double-submit-protection' => 'off',
];
$gradingbatchoperationsform = new mod_assign_grading_batch_operations_form(null,
$batchformparams,
'post',
'',
$classoptions);
$gradingoptionsdata = new stdClass();
$gradingoptionsdata->perpage = $perpage;
$gradingoptionsdata->markerfilter = $markerfilter;
$gradingoptionsform->set_data($gradingoptionsdata);
@@ -4619,42 +4602,86 @@ class assign {
}
// Load and print the table of submissions.
if ($showquickgrading && $quickgrading) {
$gradingtable = new assign_grading_table($this, $perpage, $filter, 0, true);
$gradingtable->responsive = false;
$table = $this->get_renderer()->render($gradingtable);
$usequickgrading = $showquickgrading && $quickgrading;
$gradingtable = new assign_grading_table($this, $perpage, $filter, 0, $usequickgrading);
$gradingtable->responsive = false;
$table = $this->get_renderer()->render($gradingtable);
$footerdata = [
'perpage' => $gradingtable->get_paging_selector(),
'pagingbar' => $gradingtable->get_paging_bar(),
'hassubmit' => $usequickgrading,
'sendstudentnotifications' => $this->get_instance()->sendstudentnotifications,
];
$footer = new core\output\sticky_footer($OUTPUT->render_from_template('mod_assign/grading_sticky_footer', $footerdata));
if ($usequickgrading) {
$page = optional_param('page', null, PARAM_INT);
$quickformparams = array('cm'=>$this->get_course_module()->id,
'gradingtable'=>$table,
'sendstudentnotifications' => $this->get_instance()->sendstudentnotifications,
'page' => $page);
$PAGE->requires->js_call_amd('mod_assign/quick_grading', 'init', []);
$quickformparams = [
'cm' => $this->get_course_module()->id,
'gradingtable' => $table,
'page' => $page,
'footer' => $this->get_renderer()->render($footer),
];
$quickgradingform = new mod_assign_quick_grading_form(null, $quickformparams);
$o .= $this->get_renderer()->render(new assign_form('quickgradingform', $quickgradingform));
} else {
$gradingtable = new assign_grading_table($this, $perpage, $filter, 0, false);
$gradingtable->responsive = false;
$o .= $this->get_renderer()->render($gradingtable);
$o .= $table;
$o .= $this->get_renderer()->render($footer);
}
if ($this->can_grade()) {
// We need to store the order of uses in the table as the person may wish to grade them.
// This is done based on the row number of the user.
// Pagination has be added before this because calling get_column_data will reset the pagination.
$useridlist = $gradingtable->get_column_data('userid');
$SESSION->mod_assign_useridlist[$this->get_useridlist_key()] = $useridlist;
}
$currentgroup = groups_get_activity_group($this->get_course_module(), true);
$users = array_keys($this->list_participants($currentgroup, true));
if (count($users) != 0 && $this->can_grade()) {
// If no enrolled user in a course then don't display the batch operations feature.
$assignform = new assign_form('gradingbatchoperationsform', $gradingbatchoperationsform);
$o .= $this->get_renderer()->render($assignform);
}
$assignform = new assign_form('gradingoptionsform',
$gradingoptionsform,
'M.mod_assign.init_grading_options');
$o .= $this->get_renderer()->render($assignform);
$currentgroup = groups_get_activity_group($this->get_course_module(), true);
$users = array_keys($this->list_participants($currentgroup, true));
if (count($users) != 0 && $this->can_grade()) {
$jsparams = [];
$jsparams['message'] = !empty($CFG->messaging)
&& has_all_capabilities(['moodle/site:sendmessage', 'moodle/course:bulkmessaging'], $this->context);
$jsparams['submissiondrafts'] = !empty($this->get_instance()->submissiondrafts);
$jsparams['removesubmission'] = has_capability('mod/assign:editothersubmission', $this->context);
$jsparams['extend'] = $this->get_instance()->duedate && has_capability('mod/assign:grantextension', $this->context);
$multipleattemptsallowed = $this->get_instance()->maxattempts > 1
|| $this->get_instance()->maxattempts == ASSIGN_UNLIMITED_ATTEMPTS;
$jsparams['grantattempt'] =
$multipleattemptsallowed && $this->get_instance()->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL;
$jsparams['pluginoperations'] = [];
foreach ($this->get_feedback_plugins() as $plugin) {
if ($plugin->is_visible() && $plugin->is_enabled()) {
foreach ($plugin->get_grading_batch_operation_details() as $operation) {
$jsparams['pluginoperations'][] = [
'key' => 'plugingradingbatchoperation_' . $plugin->get_type() . '_' . $operation->key,
'label' => $operation->label,
'icon' => $operation->icon,
'confirmationtitle' => $operation->confirmationtitle,
'confirmationquestion' => $operation->confirmationquestion,
];
}
}
}
$jsparams['workflowstate'] = !empty($this->get_instance()->markingworkflow);
$jsparams['markingallocation'] = !empty($this->get_instance()->markingallocation);
$jsparams['cmid'] = $this->get_course_module()->id;
$jsparams['sesskey'] = sesskey();
$PAGE->requires->js_call_amd('mod_assign/bulkactions/grading/bulk_actions', 'init', [$jsparams]);
}
return $o;
}
@@ -5006,112 +5033,84 @@ class assign {
/**
* Allows the plugin to show a batch grading operation page.
* You should confirm sesskey before calling this function.
*
* @param moodleform $mform
* @return none
*/
protected function view_plugin_grading_batch_operation($mform) {
protected function view_plugin_grading_batch_operation() {
require_capability('mod/assign:grade', $this->context);
$prefix = 'plugingradingbatchoperation_';
if ($data = $mform->get_data()) {
$tail = substr($data->operation, strlen($prefix));
list($plugintype, $action) = explode('_', $tail, 2);
$operation = required_param('operation', PARAM_ALPHAEXT);
$plugin = $this->get_feedback_plugin_by_type($plugintype);
if ($plugin) {
$users = $data->selectedusers;
$userlist = explode(',', $users);
echo $plugin->grading_batch_operation($action, $userlist);
return;
}
$tail = substr($operation, strlen($prefix));
list($plugintype, $action) = explode('_', $tail, 2);
$plugin = $this->get_feedback_plugin_by_type($plugintype);
if ($plugin) {
$users = required_param('selectedusers', PARAM_SEQUENCE);
$userlist = explode(',', $users);
echo $plugin->grading_batch_operation($action, $userlist);
return;
}
throw new \moodle_exception('invalidformdata', '');
}
/**
* Ask the user to confirm they want to perform this batch operation
*
* @param moodleform $mform Set to a grading batch operations form
* @return string - the page to view after processing these actions
*/
protected function process_grading_batch_operation(& $mform) {
global $CFG;
require_once($CFG->dirroot . '/mod/assign/gradingbatchoperationsform.php');
protected function process_grading_batch_operation() {
require_sesskey();
$markingallocation = $this->get_instance()->markingworkflow &&
$this->get_instance()->markingallocation &&
has_capability('mod/assign:manageallocations', $this->context);
$operation = required_param('operation', PARAM_ALPHAEXT);
$selectedusers = required_param('selectedusers', PARAM_SEQUENCE);
$batchformparams = array('cm'=>$this->get_course_module()->id,
'submissiondrafts'=>$this->get_instance()->submissiondrafts,
'duedate'=>$this->get_instance()->duedate,
'maxattempts' => $this->get_instance()->maxattempts,
'attemptreopenmethod'=>$this->get_instance()->attemptreopenmethod,
'feedbackplugins'=>$this->get_feedback_plugins(),
'context'=>$this->get_context(),
'markingworkflow'=>$this->get_instance()->markingworkflow,
'markingallocation'=>$markingallocation);
$formclasses = [
'class' => 'gradingbatchoperationsform',
'data-double-submit-protection' => 'off'
];
// Get the list of users.
$userlist = explode(',', $selectedusers);
$mform = new mod_assign_grading_batch_operations_form(null,
$batchformparams,
'post',
'',
$formclasses);
$prefix = 'plugingradingbatchoperation_';
if ($data = $mform->get_data()) {
// Get the list of users.
$users = $data->selectedusers;
$userlist = explode(',', $users);
if ($operation == 'grantextension') {
return 'grantextension';
} else if ($operation == 'setmarkingworkflowstate') {
return 'viewbatchsetmarkingworkflowstate';
} else if ($operation == 'setmarkingallocation') {
return 'viewbatchmarkingallocation';
} else if (strpos($operation, $prefix) === 0) {
$tail = substr($operation, strlen($prefix));
list($plugintype, $action) = explode('_', $tail, 2);
$prefix = 'plugingradingbatchoperation_';
if ($data->operation == 'grantextension') {
// Reset the form so the grant extension page will create the extension form.
$mform = null;
return 'grantextension';
} else if ($data->operation == 'setmarkingworkflowstate') {
return 'viewbatchsetmarkingworkflowstate';
} else if ($data->operation == 'setmarkingallocation') {
return 'viewbatchmarkingallocation';
} else if (strpos($data->operation, $prefix) === 0) {
$tail = substr($data->operation, strlen($prefix));
list($plugintype, $action) = explode('_', $tail, 2);
$plugin = $this->get_feedback_plugin_by_type($plugintype);
if ($plugin) {
return 'plugingradingbatchoperation';
}
$plugin = $this->get_feedback_plugin_by_type($plugintype);
if ($plugin) {
return 'plugingradingbatchoperation';
}
}
if ($data->operation == 'downloadselected') {
$this->download_submissions($userlist);
} else {
foreach ($userlist as $userid) {
if ($data->operation == 'lock') {
$this->process_lock_submission($userid);
} else if ($data->operation == 'unlock') {
$this->process_unlock_submission($userid);
} else if ($data->operation == 'reverttodraft') {
$this->process_revert_to_draft($userid);
} else if ($data->operation == 'removesubmission') {
$this->process_remove_submission($userid);
} else if ($data->operation == 'addattempt') {
if (!$this->get_instance()->teamsubmission) {
$this->process_add_attempt($userid);
}
if ($operation == 'downloadselected') {
$this->download_submissions($userlist);
} else {
foreach ($userlist as $userid) {
if ($operation == 'lock') {
$this->process_lock_submission($userid);
} else if ($operation == 'unlock') {
$this->process_unlock_submission($userid);
} else if ($operation == 'reverttodraft') {
$this->process_revert_to_draft($userid);
} else if ($operation == 'removesubmission') {
$this->process_remove_submission($userid);
} else if ($operation == 'addattempt') {
if (!$this->get_instance()->teamsubmission) {
$this->process_add_attempt($userid);
}
}
}
if ($this->get_instance()->teamsubmission && $data->operation == 'addattempt') {
// This needs to be handled separately so that each team submission is only re-opened one time.
$this->process_add_attempt_group($userlist);
}
}
if ($this->get_instance()->teamsubmission && $operation == 'addattempt') {
// This needs to be handled separately so that each team submission is only re-opened one time.
$this->process_add_attempt_group($userlist);
}
return 'grading';
@@ -5119,19 +5118,18 @@ class assign {
/**
* Shows a form that allows the workflow state for selected submissions to be changed.
* You should confirm sesskey before calling this function.
*
* @param moodleform $mform Set to a grading batch operations form
* @return string - the page to view after processing these actions
*/
protected function view_batch_set_workflow_state($mform) {
protected function view_batch_set_workflow_state() {
global $CFG, $DB;
require_once($CFG->dirroot . '/mod/assign/batchsetmarkingworkflowstateform.php');
$o = '';
$submitteddata = $mform->get_data();
$users = $submitteddata->selectedusers;
$users = required_param('selectedusers', PARAM_SEQUENCE);
$userlist = explode(',', $users);
$formdata = array('id' => $this->get_course_module()->id,
@@ -5184,19 +5182,18 @@ class assign {
/**
* Shows a form that allows the allocated marker for selected submissions to be changed.
* You should confirm sesskey before calling this function.
*
* @param moodleform $mform Set to a grading batch operations form
* @return string - the page to view after processing these actions
*/
public function view_batch_markingallocation($mform) {
public function view_batch_markingallocation() {
global $CFG, $DB;
require_once($CFG->dirroot . '/mod/assign/batchsetallocatedmarkerform.php');
$o = '';
$submitteddata = $mform->get_data();
$users = $submitteddata->selectedusers;
$users = required_param('selectedusers', PARAM_SEQUENCE);
$userlist = explode(',', $users);
$formdata = array('id' => $this->get_course_module()->id,
@@ -7426,7 +7423,6 @@ class assign {
];
$mform = new mod_assign\form\grading_options_temp_form(null, $gradingoptionsparams);
if ($formdata = $mform->get_data()) {
set_user_preference('assign_perpage', $formdata->perpage);
if (isset($formdata->markerfilter)) {
set_user_preference('assign_markerfilter', $formdata->markerfilter);
}
-51
View File
@@ -69,53 +69,6 @@ M.mod_assign.init_grading_table = function(Y) {
});
}
const batchform = Y.one('form.gradingbatchoperationsform');
if (batchform) {
batchform.on('submit', function(e) {
M.util.js_pending('mod_assign/module.js:batch:submit');
let selectedusers = [];
checkboxes.each(function(node) {
if (node.get('checked')) {
selectedusers.push(node.get('value'));
}
});
const operation = Y.one('#id_operation');
const usersinput = Y.one('input.selectedusers');
usersinput.set('value', selectedusers.join(','));
if (selectedusers.length === 0) {
alert(M.util.get_string('nousersselected', 'assign'));
e.preventDefault();
} else {
let action = operation.get('value');
const prefix = 'plugingradingbatchoperation_';
let confirmmessage = false;
if (action.indexOf(prefix) === 0) {
const pluginaction = action.slice(prefix.length);
const plugin = pluginaction.split('_')[0];
action = pluginaction.slice(plugin.length + 1);
confirmmessage = M.util.get_string('batchoperationconfirm' + action, 'assignfeedback_' + plugin);
} else if (action === 'message') {
e.preventDefault();
require(['core_message/message_send_bulk'], function(BulkSender) {
BulkSender.showModal(selectedusers, function() {
document.getElementById('page-header').scrollIntoView();
});
});
} else {
confirmmessage = M.util.get_string('batchoperationconfirm' + operation.get('value'), 'assign');
}
// Complete here the action (js_complete event) when we send a bulk message, or we have a confirmation message.
// When the confirmation dialogue is completed, the event is fired.
if (action === 'message' || confirmmessage !== false && !confirm(confirmmessage)) {
M.util.js_complete('mod_assign/module.js:batch:submit');
e.preventDefault();
}
// Note: Do not js_complete. The page being reloaded will empty it.
}
});
}
var quickgrade = Y.all('.gradingtable .quickgrade');
quickgrade.each(function(quick) {
quick.on('change', function(e) {
@@ -127,10 +80,6 @@ M.mod_assign.init_grading_table = function(Y) {
M.mod_assign.init_grading_options = function(Y) {
Y.use('node', function(Y) {
var paginationelement = Y.one('#id_perpage');
paginationelement.on('change', function(e) {
Y.one('form.gradingoptionsform').submit();
});
var markerfilterelement = Y.one('#id_markerfilter');
if (markerfilterelement) {
markerfilterelement.on('change', function(e) {
+3 -7
View File
@@ -54,13 +54,9 @@ class mod_assign_quick_grading_form extends moodleform {
$mform->addElement('hidden', 'lastpage', $instance['page']);
$mform->setType('lastpage', PARAM_INT);
// Skip notifications option.
$mform->addElement('selectyesno', 'sendstudentnotifications', get_string('sendstudentnotifications', 'assign'));
$mform->setDefault('sendstudentnotifications', $instance['sendstudentnotifications']);
// Buttons.
$savemessage = get_string('saveallquickgradingchanges', 'assign');
$mform->addElement('submit', 'savequickgrades', $savemessage);
// No need to add a submit button.
// The submit button is in the footer.
$mform->addElement('html', $instance['footer']);
}
}
-8
View File
@@ -135,14 +135,6 @@
display: table-cell;
}
.path-mod-assign .gradingbatchoperationsform {
display: none;
}
.path-mod-assign.jsenabled .gradingbatchoperationsform {
display: block;
}
.path-mod-assign .gradingtable table {
border-collapse: separate;
border-spacing: 0;
@@ -0,0 +1,46 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/bulkactions/grading/bulk_action_modal_body
Renders the body of the bulk action confirmation modal.
Context variables required for this template:
* text - The bulk action confirmation text.
* operation - The name of the bulk operation.
* cmid - The course module ID.
* selectedusers - The string containing the IDs of the selected users.
* sesskey - The session key.
Example context (json):
{
"text": "Lock all selected submissions?",
"operation": "lock",
"cmid": 2,
"selectedusers": "3,5",
"sesskey": "abc123"
}
}}
<p>{{text}}</p>
<form method="post" action="view.php">
<input type="hidden" name="action" value="gradingbatchoperation">
<input type="hidden" name="operation" value="{{operation}}">
<input type="hidden" name="id" value="{{cmid}}">
<input type="hidden" name="selectedusers" value="{{selectedusers}}">
<input type="hidden" name="sesskey" value="{{{sesskey}}}">
<input type="hidden" name="returnaction" value="grading">
</form>
@@ -0,0 +1,36 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/bulkactions/grading/bulk_delete_trigger
Renders the bulk delete trigger element in assignment grading page.
Context variables required for this template:
* showindropdown - Whether the action is displayed under a 'More' dropdown or as a separate button.
* isfirst - Whether the action is the first one in the list.
Example context (json):
{
"showindropdown": false,
"isfirst": true
}
}}
{{< core/bulkactions/bulk_action_trigger }}
{{$action}}removesubmission{{/action}}
{{$title}}{{#str}} batchoperationremovesubmission, mod_assign {{/str}}{{/title}}
{{$icon}}{{#pix}} i/delete, core {{/pix}}{{/icon}}
{{/ core/bulkactions/bulk_action_trigger }}
@@ -0,0 +1,36 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/bulkactions/grading/bulk_extend_trigger
Renders the bulk extend trigger element in assignment grading page.
Context variables required for this template:
* showindropdown - Whether the action is displayed under a 'More' dropdown or as a separate button.
* isfirst - Whether the action is the first one in the list.
Example context (json):
{
"showindropdown": false,
"isfirst": true
}
}}
{{< core/bulkactions/bulk_action_trigger }}
{{$action}}grantextension{{/action}}
{{$title}}{{#str}} batchoperationgrantextension, mod_assign {{/str}}{{/title}}
{{$icon}}{{#pix}} i/duration, core {{/pix}}{{/icon}}
{{/ core/bulkactions/bulk_action_trigger }}
@@ -0,0 +1,42 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/bulkactions/grading/bulk_general_action_trigger
Renders the general bulk action trigger element in assignment grading page.
Context variables required for this template:
* action: The action to be performed.
* title: The title of the action button.
* icon: The icon of the action button.
* showindropdown - Whether the action is displayed under a 'More' dropdown or as a separate button.
* isfirst - Whether the action is the first one in the list.
Example context (json):
{
"action": "lock",
"title": "Lock",
"icon": "<i class=\"icon fa fa-lock fa-fw\"></i>",
"showindropdown": false,
"isfirst": true
}
}}
{{< core/bulkactions/bulk_action_trigger }}
{{$action}}{{action}}{{/action}}
{{$title}}{{title}}{{/title}}
{{$icon}}{{{icon}}}{{/icon}}
{{/ core/bulkactions/bulk_action_trigger }}
@@ -0,0 +1,36 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/bulkactions/grading/bulk_message_trigger
Renders the bulk message trigger element in assignment grading page.
Context variables required for this template:
* showindropdown - Whether the action is displayed under a 'More' dropdown or as a separate button.
* isfirst - Whether the action is the first one in the list.
Example context (json):
{
"showindropdown": false,
"isfirst": true
}
}}
{{< core/bulkactions/bulk_action_trigger }}
{{$action}}message{{/action}}
{{$title}}{{#str}} message, core {{/str}}{{/title}}
{{$icon}}{{#pix}} t/message, core {{/pix}}{{/icon}}
{{/ core/bulkactions/bulk_action_trigger }}
@@ -0,0 +1,36 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/bulkactions/grading/bulk_setmarkingallocation_trigger
Renders the bulk set allocated marker trigger element in assignment grading page.
Context variables required for this template:
* showindropdown - Whether the action is displayed under a 'More' dropdown or as a separate button.
* isfirst - Whether the action is the first one in the list.
Example context (json):
{
"showindropdown": false,
"isfirst": true
}
}}
{{< core/bulkactions/bulk_action_trigger }}
{{$action}}setmarkingallocation{{/action}}
{{$title}}{{#str}} batchoperationsetmarkingallocation, mod_assign {{/str}}{{/title}}
{{$icon}}<i class="icon fa fa-pen-to-square fa-fw"></i>{{/icon}}
{{/ core/bulkactions/bulk_action_trigger }}
@@ -0,0 +1,36 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/bulkactions/grading/bulk_setmarkingworkflowstate_trigger
Renders the bulk set marking workflow state trigger element in assignment grading page.
Context variables required for this template:
* showindropdown - Whether the action is displayed under a 'More' dropdown or as a separate button.
* isfirst - Whether the action is the first one in the list.
Example context (json):
{
"showindropdown": false,
"isfirst": true
}
}}
{{< core/bulkactions/bulk_action_trigger }}
{{$action}}setmarkingworkflowstate{{/action}}
{{$title}}{{#str}} batchoperationsetmarkingworkflowstate, mod_assign {{/str}}{{/title}}
{{$icon}}<i class="icon fa fa-diagram-project fa-fw"></i>{{/icon}}
{{/ core/bulkactions/bulk_action_trigger }}
@@ -0,0 +1,48 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/grading_paging_selector
Template for pagination dropdown on the assignment grading page.
Example context (json):
{
"baseurl": "http://example.com/grading.php?id=1",
"options": [
{"name": "10", "value": "10", "selected": false},
{"name": "20", "value": "20", "selected": false},
{"name": "50", "value": "50", "selected": true},
{"name": "All", "value": "-1", "selected": false}
]
}
}}
<label>
{{#str}}show{{/str}}
<select name="perpage" class="mt-1 custom-select ignoredirty" id="{{uniqid}}-perpage">
{{#options}}
<option value="{{value}}" {{#selected}}selected{{/selected}}>{{name}}</option>
{{/options}}
</select>
</label>
{{#js}}
document.getElementById('{{uniqid}}-perpage').addEventListener('change', function(e) {
var url = new URL('{{{baseurl}}}');
url.searchParams.set('perpage', e.target.value);
window.location.href = url;
});
{{/js}}
@@ -0,0 +1,53 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/grading_sticky_footer
Sticky footer template for the assignment grading page.
Example context (json):
{
"perpage": "<label>Show<select name='perpage' class='custom-select'><option value='-1'>All</select></label>",
"pagingbar": "<nav class='pagination pagination-centered justify-content-center'><ul class='mt-1 pagination '><li class='page-item active'><a href='#' class='page-link'><span>1</span></a></li></ul></nav>",
"hassubmit": true,
"sendstudentnotifications": true
}
}}
<div class="col-auto">
{{#perpage}}
{{{.}}}
{{/perpage}}
</div>
<div class="col">
{{#pagingbar}}
{{{.}}}
{{/pagingbar}}
</div>
<div class="col-auto">
{{#hassubmit}}
<div class="d-flex align-items-center" data-region="quick-grading-save">
<div class="me-4">
<input type="hidden" name="sendstudentnotifications" value="0" {{#sendstudentnotifications}}disabled{{/sendstudentnotifications}}>
<input type="checkbox" id="send-student-notifications-{{uniqid}}" name="sendstudentnotifications" {{#sendstudentnotifications}}checked="checked"{{/sendstudentnotifications}}>
<label for="send-student-notifications-{{uniqid}}" class="m-0">{{#str}} notifystudents, mod_assign {{/str}}</label>
</div>
<button type="submit" class="btn btn-primary">
{{#str}}save{{/str}}
</button>
</div>
{{/hassubmit}}
</div>
@@ -107,15 +107,13 @@ Feature: In an assignment, students start a new attempt based on their previous
And "Student 3" row "Status" column of "generaltable" table should contain "No submission"
And "Student 4" row "Status" column of "generaltable" table should contain "No submission"
And I click on "Quick grading" "checkbox"
And I click on "Student 1" "checkbox"
And I set the field "User grade" to "60.0"
And I press "Save all quick grading changes"
And I click on "Save" "button" in the "sticky-footer" "region"
And I should see "The grade changes were saved"
And I press "Continue"
And I click on "Student 1" "checkbox"
And I set the following fields to these values:
| operation | Allow another attempt |
And I click on "Go" "button" confirming the dialogue
And I click on "Grant attempt" "button" in the "sticky-footer" "region"
And I click on "Grant attempt" "button" in the "Allow another attempt" "dialogue"
And I should not see "The grades were not saved because someone has modified one or more records more recently than when you loaded the page."
And I log out
And I am on the "Test assignment name" Activity page logged in as student3
@@ -67,8 +67,8 @@ Feature: Bulk released grades should not be sent to gradebook while submissions
@javascript @_alert
Scenario: Grades are released in bulk before student identities are revealed.
When I set the field "selectall" to "1"
And I set the field "operation" to "Set marking workflow state"
And I click on "Go" "button" confirming the dialogue
And I click on "Change marking state" "button" in the "sticky-footer" "region"
And I click on "Change marking state" "button" in the "Set marking workflow state" "dialogue"
Then I should not see "Student 1 (student1@example.com)"
And I should not see "Student 2 (student2@example.com)"
And I set the field "Marking workflow state" to "Released"
@@ -102,8 +102,8 @@ Feature: Bulk released grades should not be sent to gradebook while submissions
When I choose the "Reveal student identities" item in the "Actions" action menu
And I press "Continue"
When I set the field "selectall" to "1"
And I set the field "operation" to "Set marking workflow state"
And I click on "Go" "button" confirming the dialogue
And I click on "Change marking state" "button" in the "sticky-footer" "region"
And I click on "Change marking state" "button" in the "Set marking workflow state" "dialogue"
Then I should see "Student 1 (student1@example.com)"
And I should see "Student 2 (student2@example.com)"
And I set the field "Marking workflow state" to "Released"
@@ -128,8 +128,8 @@ Feature: Bulk released grades should not be sent to gradebook while submissions
And I press "Save and display"
And I navigate to "Submissions" in current page administration
When I set the field "selectall" to "1"
And I set the field "operation" to "Set marking workflow state"
And I click on "Go" "button" confirming the dialogue
And I click on "Change marking state" "button" in the "sticky-footer" "region"
And I click on "Change marking state" "button" in the "Set marking workflow state" "dialogue"
Then I should not see "Student 1 (student1@example.com)"
And I should not see "Student 2 (student2@example.com)"
And I set the field "Marking workflow state" to "Released"
@@ -45,8 +45,8 @@ Feature: Bulk remove submissions
And I should see "I'm the student1 submission"
And I should see "I'm the student2 submission"
And I set the field "selectall" to "1"
When I set the field "operation" to "Remove submission"
And I click on "Go" "button" confirming the dialogue
And I click on "Delete" "button" in the "sticky-footer" "region"
And I click on "Delete" "button" in the "Remove submission" "dialogue"
Then I should not see "I'm the student1 submission"
And I should not see "I'm the student2 submission"
And I log out
@@ -78,7 +78,7 @@ Feature: Bulk remove submissions
And I should see "I'm the student1 submission"
And I should see "I'm the student2 submission"
And I set the field "selectall" to "1"
Then I should not see "Remove submission" in the "Choose operation" "select"
Then I should not see "Delete" in the "sticky-footer" "region"
@javascript @skip_chrome_zerosize
Scenario: Bulk remove submission when shared group users are added to the bulk
@@ -111,8 +111,8 @@ Feature: Bulk remove submissions
And I should see "I'm the student2 submission"
And I should not see "I'm the student3 submission"
And I set the field "selectall" to "1"
When I set the field "operation" to "Remove submission"
And I click on "Go" "button" confirming the dialogue
When I click on "Delete" "button" in the "sticky-footer" "region"
And I click on "Delete" "button" in the "Remove submission" "dialogue"
Then I should not see "I'm the student1 submission"
Then I should not see "I'm the student2 submission"
@@ -147,8 +147,8 @@ Feature: Bulk remove submissions
And I should see "I'm the student2 submission"
And I should see "I'm the student3 submission"
And I set the field "selectall" to "1"
When I set the field "operation" to "Remove submission"
And I click on "Go" "button" confirming the dialogue
When I click on "Delete" "button" in the "sticky-footer" "region"
And I click on "Delete" "button" in the "Remove submission" "dialogue"
Then I should not see "I'm the student1 submission"
And I should not see "I'm the student2 submission"
And I should not see "I'm the student3 submission"
@@ -53,8 +53,8 @@ Feature: Grant an extension to an offline student
And I am on the "Test assignment name" Activity page logged in as teacher1
When I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
And I set the field "operation" to "Grant extension"
And I click on "Go" "button" confirming the dialogue
And I click on "Extend" "button" in the "sticky-footer" "region"
And I click on "Extend" "button" in the "Grant extension" "dialogue"
And I should see "Student 1 (student1@example.com)"
And I should see "Student 2 (student2@example.com)"
And I should see "Student 3 (student3@example.com)"
@@ -102,8 +102,8 @@ Feature: Grant an extension to an offline student
And I am on the "Test assignment name" Activity page logged in as teacher1
When I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
And I set the field "operation" to "Grant extension"
And I click on "Go" "button" confirming the dialogue
And I click on "Extend" "button" in the "sticky-footer" "region"
And I click on "Extend" "button" in the "Grant extension" "dialogue"
And I should see "Student 1 (student1@example.com)"
And I should see "Student 2 (student2@example.com)"
And I should see "Student 3 (student3@example.com)"
@@ -80,7 +80,8 @@ Feature: Prevent or allow assignment submission changes
And I am on the "Test assignment name" Activity page logged in as teacher1
When I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
And I click on "Go" "button" confirming the dialogue
And I click on "Lock" "button" in the "sticky-footer" "region"
And I click on "Lock" "button" in the "Lock submissions" "dialogue"
Then I should see "Submission changes not allowed" in the "Student 1" "table_row"
And I should see "Submission changes not allowed" in the "Student 2" "table_row"
And I log out
@@ -92,8 +93,8 @@ Feature: Prevent or allow assignment submission changes
And I am on the "Test assignment name" Activity page logged in as teacher1
And I navigate to "Submissions" in current page administration
And I set the field "selectall" to "1"
And I set the field "id_operation" to "Unlock submissions"
And I click on "Go" "button" confirming the dialogue
And I click on "Unlock" "button" in the "sticky-footer" "region"
And I click on "Unlock" "button" in the "Unlock submissions" "dialogue"
And I should not see "Submission changes not allowed" in the "Student 1" "table_row"
And I should not see "Submission changes not allowed" in the "Student 2" "table_row"
And I log out
+2 -2
View File
@@ -95,7 +95,7 @@ Feature: In an assignment, teachers grade multiple students on one page
And I follow "View all submissions"
Then I click on "Quick grading" "checkbox"
And I set the field "User grade" to "60.0"
And I press "Save all quick grading changes"
And I click on "Save" "button" in the "sticky-footer" "region"
And I should see "The grade changes were saved"
And I press "Continue"
And I am on the "Test assignment name" "assign activity" page logged in as student1
@@ -123,7 +123,7 @@ Feature: In an assignment, teachers grade multiple students on one page
And I click on "Hide Feedback comments" "link"
And I click on "Hide Final grade" "link"
And I click on "Hide Outcomes" "link"
And I press "Save all quick grading changes"
And I click on "Save" "button" in the "sticky-footer" "region"
And I should see "The grade changes were saved"
And I press "Continue"
And I am on the "Test assignment name" "assign activity" page logged in as student1
@@ -42,7 +42,7 @@ Feature: Submissions are unlocked when a new attempt is given
And I should see "Submission changes not allowed"
And I click on "Quick grading" "checkbox"
And I set the field "User grade" to "49.0"
And I press "Save all quick grading changes"
And I click on "Save" "button" in the "sticky-footer" "region"
And I should see "The grade changes were saved"
And I press "Continue"
Then I should see "Reopened"
+4 -33
View File
@@ -117,44 +117,15 @@ class mod_assign_testable_assign extends assign {
public function testable_view_batch_set_workflow_state($selectedusers) {
global $PAGE;
$PAGE->set_url('/mod/assign/view.php');
$mform = $this->testable_grading_batch_operations_form('setmarkingworkflowstate', $selectedusers);
return parent::view_batch_set_workflow_state($mform);
$_POST['selectedusers'] = $selectedusers;
return parent::view_batch_set_workflow_state();
}
public function testable_view_batch_markingallocation($selectedusers) {
global $PAGE;
$PAGE->set_url('/mod/assign/view.php');
$mform = $this->testable_grading_batch_operations_form('setmarkingallocation', $selectedusers);
return parent::view_batch_markingallocation($mform);
}
public function testable_grading_batch_operations_form($operation, $selectedusers) {
global $CFG;
require_once($CFG->dirroot . '/mod/assign/gradingbatchoperationsform.php');
// Mock submit the grading operations form.
$data = array();
$data['id'] = $this->get_course_module()->id;
$data['selectedusers'] = $selectedusers;
$data['returnaction'] = 'grading';
$data['operation'] = $operation;
mod_assign_grading_batch_operations_form::mock_submit($data);
// Set required variables in the form.
$formparams = array();
$formparams['submissiondrafts'] = 1;
$formparams['duedate'] = 1;
$formparams['maxattempts'] = ASSIGN_UNLIMITED_ATTEMPTS;
$formparams['attemptreopenmethod'] = ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL;
$formparams['feedbackplugins'] = array();
$formparams['markingworkflow'] = 1;
$formparams['markingallocation'] = 1;
$formparams['cm'] = $this->get_course_module()->id;
$formparams['context'] = $this->get_context();
$mform = new mod_assign_grading_batch_operations_form(null, $formparams);
return $mform;
$_POST['selectedusers'] = $selectedusers;
return parent::view_batch_markingallocation();
}
public function testable_update_activity_completion_records($teamsubmission,
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+56 -29
View File
@@ -107,7 +107,7 @@ const dropdownFix = () => {
// Search for menu items by finding the first item that has
// text starting with the typed character (case insensitive).
document.addEventListener('keypress', e => {
if (e.target.matches('.dropdown [role="menu"] [role="menuitem"]')) {
if (e.target.matches('[role="menu"] [role="menuitem"]')) {
const menu = e.target.closest('[role="menu"]');
if (!menu) {
return;
@@ -139,7 +139,7 @@ const dropdownFix = () => {
handleMenuButton(e);
}
if (e.target.matches('.dropdown [role="menu"] [role="menuitem"]')) {
if (e.target.matches('[role="menu"] [role="menuitem"]')) {
const trigger = e.key;
let next = false;
const menu = e.target.closest('[role="menu"]');
@@ -193,8 +193,9 @@ const dropdownFix = () => {
}
});
$('.dropdown').on('shown.bs.dropdown', e => {
const dialog = e.target.querySelector(`#${e.relatedTarget.getAttribute('aria-controls')}[role="dialog"]`);
// Trap focus if the dropdown is a dialog.
$(document).on('shown.bs.dropdown', e => {
const dialog = e.target.querySelector('.dropdown-menu[role="dialog"]');
if (dialog) {
// Use setTimeout to make sure the dialog is positioned correctly to prevent random scrolling.
setTimeout(() => {
@@ -203,8 +204,9 @@ const dropdownFix = () => {
}
});
$('.dropdown').on('hidden.bs.dropdown', e => {
const dialog = e.target.querySelector(`#${e.relatedTarget.getAttribute('aria-controls')}[role="dialog"]`);
// Untrap focus when the dialog dropdown is closed.
$(document).on('hidden.bs.dropdown', e => {
const dialog = e.target.querySelector('.dropdown-menu[role="dialog"]');
if (dialog) {
FocusLockManager.untrapFocus();
}
@@ -414,47 +416,50 @@ const autoFocus = () => {
};
/**
* Changes the focus to the correct tab based on the key that is pressed.
* @param {KeyboardEvent} e
* Changes the focus to the correct element based on the key that is pressed.
* @param {NodeList} elements A NodeList of focusable elements to navigate between.
* @param {KeyboardEvent} e The keyboard event that triggers the roving focus.
* @param {boolean} vertical Whether the navigation is vertical.
* @param {boolean} updateTabIndex Whether to update the tabIndex of the elements.
*/
const updateTabFocus = e => {
const tabList = e.target.closest('[role="tablist"]');
const vertical = tabList.getAttribute('aria-orientation') == 'vertical';
const rovingFocus = (elements, e, vertical, updateTabIndex) => {
const rtl = window.right_to_left();
const arrowNext = vertical ? 'ArrowDown' : (rtl ? 'ArrowLeft' : 'ArrowRight');
const arrowPrevious = vertical ? 'ArrowUp' : (rtl ? 'ArrowRight' : 'ArrowLeft');
const tabs = Array.prototype.filter.call(
tabList.querySelectorAll('[role="tab"]'),
tab => !!tab.offsetHeight); // We only work with the visible tabs.
const keys = [arrowNext, arrowPrevious, 'Home', 'End'];
for (let i = 0; i < tabs.length; i++) {
tabs[i].index = i;
if (!keys.includes(e.key)) {
return;
}
const focusElement = index => {
elements[index].focus();
if (updateTabIndex) {
elements.forEach((element, i) => element.setAttribute('tabindex', i === index ? '0' : '-1'));
}
};
const currentIndex = Array.prototype.indexOf.call(elements, e.target);
let nextIndex;
switch (e.key) {
case arrowNext:
e.preventDefault();
if (e.target.index !== undefined && tabs[e.target.index + 1]) {
tabs[e.target.index + 1].focus();
} else {
tabs[0].focus();
}
nextIndex = (currentIndex + 1 < elements.length) ? currentIndex + 1 : 0;
focusElement(nextIndex);
break;
case arrowPrevious:
e.preventDefault();
if (e.target.index !== undefined && tabs[e.target.index - 1]) {
tabs[e.target.index - 1].focus();
} else {
tabs[tabs.length - 1].focus();
}
nextIndex = (currentIndex - 1 >= 0) ? currentIndex - 1 : elements.length - 1;
focusElement(nextIndex);
break;
case 'Home':
e.preventDefault();
tabs[0].focus();
focusElement(0);
break;
case 'End':
e.preventDefault();
tabs[tabs.length - 1].focus();
focusElement(elements.length - 1);
}
};
@@ -465,7 +470,14 @@ const tabElementFix = () => {
document.addEventListener('keydown', e => {
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) {
if (e.target.matches('[role="tablist"] [role="tab"]')) {
updateTabFocus(e);
const tabList = e.target.closest('[role="tablist"]');
const tabs = Array.prototype.filter.call(
tabList.querySelectorAll('[role="tab"]'),
tab => !!tab.offsetHeight
); // We only work with the visible tabs.
const vertical = tabList.getAttribute('aria-orientation') == 'vertical';
rovingFocus(tabs, e, vertical, false);
}
}
});
@@ -500,10 +512,25 @@ const collapseFix = () => {
});
};
/**
* Fix accessibility issues
*/
const toolbarFix = () => {
document.addEventListener('keydown', e => {
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) {
if (e.target.matches('[role="toolbar"] button')) {
const buttons = e.target.closest('[role="toolbar"]').querySelectorAll('button');
rovingFocus(buttons, e, false, true);
}
}
});
};
export const init = () => {
dropdownFix();
comboboxFix();
autoFocus();
tabElementFix();
collapseFix();
toolbarFix();
};
@@ -15,7 +15,6 @@ body {
bottom: calc(#{$stickyfooter-height} * -1);
transition: bottom .5s;
z-index: $zindex-fixed;
overflow: hidden;
box-shadow: 0 0 1rem rgba($black, .15);
font-size: calc(#{$font-size-base} * 1.10);
// Adjust sticky footer width to the main content area.
-1
View File
@@ -36632,7 +36632,6 @@ body {
bottom: calc(max(80px, 0.9375rem * 3) * -1);
transition: bottom 0.5s;
z-index: 1030;
overflow: hidden;
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.15);
font-size: calc(0.9375rem * 1.10);
}
-1
View File
@@ -36566,7 +36566,6 @@ body {
bottom: calc(max(80px, 0.9375rem * 3) * -1);
transition: bottom 0.5s;
z-index: 1030;
overflow: hidden;
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.15);
font-size: calc(0.9375rem * 1.10);
}