From bae72dd06daedb9f3f5054634f7d25305063149f Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Fri, 24 Apr 2020 09:40:20 +0800 Subject: [PATCH 1/6] MDL-68463 user: Rewrite participant bulk actions in ES --- .../local/participants/bulkactions.min.js | 2 + .../local/participants/bulkactions.min.js.map | 1 + user/amd/build/participants.min.js | 2 +- user/amd/build/participants.min.js.map | 2 +- user/amd/build/repository.min.js | 2 +- user/amd/build/repository.min.js.map | 2 +- .../amd/src/local/participants/bulkactions.js | 191 ++++++++++ user/amd/src/participants.js | 355 ++++-------------- user/amd/src/repository.js | 16 + user/index.php | 17 +- 10 files changed, 296 insertions(+), 294 deletions(-) create mode 100644 user/amd/build/local/participants/bulkactions.min.js create mode 100644 user/amd/build/local/participants/bulkactions.min.js.map create mode 100644 user/amd/src/local/participants/bulkactions.js diff --git a/user/amd/build/local/participants/bulkactions.min.js b/user/amd/build/local/participants/bulkactions.min.js new file mode 100644 index 00000000000..90f87b3499a --- /dev/null +++ b/user/amd/build/local/participants/bulkactions.min.js @@ -0,0 +1,2 @@ +function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_user/local/participants/bulkactions",["exports","core_user/repository","core/str","core/modal_events","core/modal_factory","core/templates","core/toast"],function(a,b,c,d,e,f,g){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.showSendMessage=a.showAddNote=void 0;b=j(b);c=j(c);d=h(d);e=h(e);f=h(f);function h(a){return a&&a.__esModule?a:{default:a}}function i(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;i=function(){return a};return a}function j(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=i();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}var k=function(a,b,g,h){if(!b.length){return Promise.resolve()}var i=[];for(var m in g){switch(m){case"draft":i.push({value:"personal",label:g[m]});break;case"public":i.push({value:"course",label:g[m],selected:1});break;case"site":i.push({value:m,label:g[m]});break;}}var j={stateNames:i,stateHelpIcon:h.innerHTML},k=null;if(1===b.length){k=c.get_string("addbulknotesingle","core_notes")}else{k=c.get_string("addbulknote","core_notes",b.length)}return e.default.create({type:e.default.types.SAVE_CANCEL,body:f.default.render("core_user/add_bulk_note",j),title:k,buttons:{save:k},removeOnClose:!0}).then(function(c){c.getRoot().on(d.default.save,function(){return l(a,b,c)});c.show();return c})};a.showAddNote=k;var l=function(a,d,e){var f=e.getRoot().find("form textarea").val(),h=e.getRoot().find("form select").val(),i=d.map(function(b){return{userid:b,text:f,courseid:a,publishstate:h}});return b.createNotesForUsers(i).then(function(a){if(1===a.length){return c.get_string("addbulknotedonesingle","core_notes")}else{return c.get_string("addbulknotedone","core_notes",a.length)}}).then(function(a){return(0,g.add)(a)}).catch(Notification.exception)},m=function(a){if(!a.length){return Promise.resolve()}var b;if(1===a.length){b=c.get_string("sendbulkmessagesingle","core_message")}else{b=c.get_string("sendbulkmessage","core_message",a.length)}return e.default.create({type:e.default.types.SAVE_CANCEL,body:f.default.render("core_user/send_bulk_message",{}),title:b,buttons:{save:b},removeOnClose:!0}).then(function(b){b.getRoot().on(d.default.save,function(){n(b,a)});b.show();return b})};a.showSendMessage=m;var n=function(a,d){var e=a.getRoot().find("form textarea").val(),f=d.map(function(a){return{touserid:a,text:e}});return b.sendMessagesToUsers(f).then(function(a){if(1==a.length){return c.get_string("sendbulkmessagesentsingle","core_message")}else{return c.get_string("sendbulkmessagesent","core_message",a.length)}}).then(function(a){return(0,g.add)(a)}).catch(Notification.exception)}}); +//# sourceMappingURL=bulkactions.min.js.map diff --git a/user/amd/build/local/participants/bulkactions.min.js.map b/user/amd/build/local/participants/bulkactions.min.js.map new file mode 100644 index 00000000000..c09406ff260 --- /dev/null +++ b/user/amd/build/local/participants/bulkactions.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/local/participants/bulkactions.js"],"names":["showAddNote","courseid","users","noteStateNames","stateHelpIcon","length","Promise","resolve","states","key","push","value","label","selected","context","stateNames","innerHTML","titlePromise","Str","get_string","ModalFactory","create","type","types","SAVE_CANCEL","body","Templates","render","title","buttons","save","removeOnClose","then","modal","getRoot","on","ModalEvents","submitAddNote","show","text","find","val","publishstate","notes","map","userid","Repository","createNotesForUsers","noteIds","msg","catch","Notification","exception","showSendMessage","submitSendMessage","messages","touserid","sendMessagesToUsers","messageIds"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Bulk actions for lists of participants.\n *\n * @module core_user/local/participants/bulkactions\n * @package core_user\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as Repository from 'core_user/repository';\nimport * as Str from 'core/str';\nimport ModalEvents from 'core/modal_events';\nimport ModalFactory from 'core/modal_factory';\nimport Templates from 'core/templates';\nimport {add as notifyUser} from 'core/toast';\n\n/**\n * Show the add note popup\n *\n * @param {Number} courseid\n * @param {Number[]} users\n * @param {String[]} noteStateNames\n * @param {HTMLElement} stateHelpIcon\n * @return {Promise}\n */\nexport const showAddNote = (courseid, users, noteStateNames, stateHelpIcon) => {\n if (!users.length) {\n // No users were selected.\n return Promise.resolve();\n }\n\n const states = [];\n for (let key in noteStateNames) {\n switch (key) {\n case 'draft':\n states.push({value: 'personal', label: noteStateNames[key]});\n break;\n case 'public':\n states.push({value: 'course', label: noteStateNames[key], selected: 1});\n break;\n case 'site':\n states.push({value: key, label: noteStateNames[key]});\n break;\n }\n }\n\n const context = {\n stateNames: states,\n stateHelpIcon: stateHelpIcon.innerHTML,\n };\n\n let titlePromise = null;\n if (users.length === 1) {\n titlePromise = Str.get_string('addbulknotesingle', 'core_notes');\n } else {\n titlePromise = Str.get_string('addbulknote', 'core_notes', users.length);\n }\n\n return ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n body: Templates.render('core_user/add_bulk_note', context),\n title: titlePromise,\n buttons: {\n save: titlePromise,\n },\n removeOnClose: true,\n })\n .then(modal => {\n modal.getRoot().on(ModalEvents.save, () => submitAddNote(courseid, users, modal));\n\n modal.show();\n\n return modal;\n });\n};\n\n/**\n * Add a note to this list of users.\n *\n * @param {Number} courseid\n * @param {Number[]} users\n * @param {Modal} modal\n * @return {Promise}\n */\nconst submitAddNote = (courseid, users, modal) => {\n const text = modal.getRoot().find('form textarea').val();\n const publishstate = modal.getRoot().find('form select').val();\n\n const notes = users.map(userid => {\n return {\n userid,\n text,\n courseid,\n publishstate,\n };\n });\n\n return Repository.createNotesForUsers(notes)\n .then(noteIds => {\n if (noteIds.length === 1) {\n return Str.get_string('addbulknotedonesingle', 'core_notes');\n } else {\n return Str.get_string('addbulknotedone', 'core_notes', noteIds.length);\n }\n })\n .then(msg => notifyUser(msg))\n .catch(Notification.exception);\n};\n\n/**\n * Show the send message popup.\n *\n * @param {Number[]} users\n * @return {Promise}\n */\nexport const showSendMessage = users => {\n if (!users.length) {\n // Nothing to do.\n return Promise.resolve();\n }\n\n let titlePromise;\n if (users.length === 1) {\n titlePromise = Str.get_string('sendbulkmessagesingle', 'core_message');\n } else {\n titlePromise = Str.get_string('sendbulkmessage', 'core_message', users.length);\n }\n\n return ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n body: Templates.render('core_user/send_bulk_message', {}),\n title: titlePromise,\n buttons: {\n save: titlePromise,\n },\n removeOnClose: true,\n })\n .then(modal => {\n modal.getRoot().on(ModalEvents.save, () => {\n submitSendMessage(modal, users);\n });\n\n modal.show();\n\n return modal;\n });\n};\n\n/**\n * Send a message to these users.\n *\n * @param {Modal} modal\n * @param {Number[]} users\n * @return {Promise}\n */\nconst submitSendMessage = (modal, users) => {\n const text = modal.getRoot().find('form textarea').val();\n\n const messages = users.map(touserid => {\n return {\n touserid,\n text,\n };\n });\n\n return Repository.sendMessagesToUsers(messages)\n .then(messageIds => {\n if (messageIds.length == 1) {\n return Str.get_string('sendbulkmessagesentsingle', 'core_message');\n } else {\n return Str.get_string('sendbulkmessagesent', 'core_message', messageIds.length);\n }\n })\n .then(msg => notifyUser(msg))\n .catch(Notification.exception);\n};\n"],"file":"bulkactions.min.js"} \ No newline at end of file diff --git a/user/amd/build/participants.min.js b/user/amd/build/participants.min.js index 56be84f842b..f433003ac95 100644 --- a/user/amd/build/participants.min.js +++ b/user/amd/build/participants.min.js @@ -1,2 +1,2 @@ -define ("core_user/participants",["jquery","core/str","core/modal_factory","core/modal_events","core/templates","core/notification","core/ajax","core/custom_interaction_events"],function(a,b,c,d,e,f,g,h){var i={BULKACTIONSELECT:"#formactionid",BULKUSERCHECKBOXES:"input.usercheckbox",BULKUSERNOSCHECKBOXES:"input.usercheckbox[value='0']",BULKUSERSELECTEDCHECKBOXES:"input.usercheckbox:checked",BULKACTIONFORM:"#participantsform",CHECKALLBUTTON:"#checkall",CHECKALLNOSBUTTON:"#checkallnos"},j=function(a){this.courseId=a.courseid;this.noteStateNames=a.noteStateNames;this.stateHelpIcon=a.stateHelpIcon;this.attachEventListeners()};j.prototype.modal=null;j.prototype.courseId=-1;j.prototype.noteStateNames={};j.prototype.stateHelpIcon="";j.prototype.attachEventListeners=function(){h.define(i.BULKACTIONSELECT,[h.events.accessibleChange]);a(i.BULKACTIONSELECT).on(h.events.accessibleChange,function(b){var c=a(b.target).val();if(-1!==c.indexOf("#")){b.preventDefault();var d=[];a(i.BULKUSERSELECTEDCHECKBOXES).each(function(b,c){var e=a(c).attr("name"),f=e.replace("user","");d.push(f)});if("#messageselect"==c){this.showSendMessage(d).fail(f.exception)}else if("#addgroupnote"==c){this.showAddNote(d).fail(f.exception)}a(i.BULKACTIONSELECT+" option[value=\"\"]").prop("selected","selected")}else if(""!==c){if(0.\n\n/**\n * Some UI stuff for participants page.\n * This is also used by the report/participants/index.php because it has the same functionality.\n *\n * @module core_user/participants\n * @package core_user\n * @copyright 2017 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/str', 'core/modal_factory', 'core/modal_events', 'core/templates', 'core/notification', 'core/ajax',\n 'core/custom_interaction_events'],\n function($, Str, ModalFactory, ModalEvents, Templates, Notification, Ajax, CustomEvents) {\n\n var SELECTORS = {\n BULKACTIONSELECT: \"#formactionid\",\n BULKUSERCHECKBOXES: \"input.usercheckbox\",\n BULKUSERNOSCHECKBOXES: \"input.usercheckbox[value='0']\",\n BULKUSERSELECTEDCHECKBOXES: \"input.usercheckbox:checked\",\n BULKACTIONFORM: \"#participantsform\",\n CHECKALLBUTTON: \"#checkall\",\n CHECKALLNOSBUTTON: \"#checkallnos\"\n };\n\n /**\n * Constructor\n *\n * @param {Object} options Object containing options. Contextid is required.\n * Each call to templates.render gets it's own instance of this class.\n */\n var Participants = function(options) {\n\n this.courseId = options.courseid;\n this.noteStateNames = options.noteStateNames;\n this.stateHelpIcon = options.stateHelpIcon;\n\n this.attachEventListeners();\n };\n // Class variables and functions.\n\n /**\n * @var {Modal} modal\n * @private\n */\n Participants.prototype.modal = null;\n\n /**\n * @var {int} courseId\n * @private\n */\n Participants.prototype.courseId = -1;\n\n /**\n * @var {Object} noteStateNames\n * @private\n */\n Participants.prototype.noteStateNames = {};\n\n /**\n * @var {String} stateHelpIcon\n * @private\n */\n Participants.prototype.stateHelpIcon = \"\";\n\n /**\n * Private method\n *\n * @method attachEventListeners\n * @private\n */\n Participants.prototype.attachEventListeners = function() {\n CustomEvents.define(SELECTORS.BULKACTIONSELECT, [CustomEvents.events.accessibleChange]);\n $(SELECTORS.BULKACTIONSELECT).on(CustomEvents.events.accessibleChange, function(e) {\n var action = $(e.target).val();\n if (action.indexOf('#') !== -1) {\n e.preventDefault();\n\n var ids = [];\n $(SELECTORS.BULKUSERSELECTEDCHECKBOXES).each(function(index, ele) {\n var name = $(ele).attr('name');\n var id = name.replace('user', '');\n ids.push(id);\n });\n\n if (action == '#messageselect') {\n this.showSendMessage(ids).fail(Notification.exception);\n } else if (action == '#addgroupnote') {\n this.showAddNote(ids).fail(Notification.exception);\n }\n $(SELECTORS.BULKACTIONSELECT + ' option[value=\"\"]').prop('selected', 'selected');\n } else if (action !== '') {\n if ($(SELECTORS.BULKUSERSELECTEDCHECKBOXES).length > 0) {\n $(SELECTORS.BULKACTIONFORM).submit();\n } else {\n $(SELECTORS.BULKACTIONSELECT + ' option[value=\"\"]').prop('selected', 'selected');\n }\n }\n }.bind(this));\n\n $(SELECTORS.CHECKALLBUTTON).on('click', function() {\n var showallink = $(this).data('showallink');\n if (showallink) {\n window.location = showallink;\n }\n });\n\n $(SELECTORS.CHECKALLNOSBUTTON).on('click', function() {\n $(SELECTORS.BULKUSERNOSCHECKBOXES).prop('checked', true);\n });\n };\n\n /**\n * Show the add note popup\n *\n * @method showAddNote\n * @private\n * @param {int[]} users\n * @return {Promise}\n */\n Participants.prototype.showAddNote = function(users) {\n\n if (users.length == 0) {\n // Nothing to do.\n return $.Deferred().resolve().promise();\n }\n\n var states = [];\n for (var key in this.noteStateNames) {\n switch (key) {\n case 'draft':\n states.push({value: 'personal', label: this.noteStateNames[key]});\n break;\n case 'public':\n states.push({value: 'course', label: this.noteStateNames[key], selected: 1});\n break;\n case 'site':\n states.push({value: key, label: this.noteStateNames[key]});\n break;\n }\n }\n\n var context = {stateNames: states, stateHelpIcon: this.stateHelpIcon};\n var titlePromise = null;\n if (users.length == 1) {\n titlePromise = Str.get_string('addbulknotesingle', 'core_notes');\n } else {\n titlePromise = Str.get_string('addbulknote', 'core_notes', users.length);\n }\n\n return $.when(\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n body: Templates.render('core_user/add_bulk_note', context)\n }),\n titlePromise\n ).then(function(modal, title) {\n // Keep a reference to the modal.\n this.modal = modal;\n this.modal.setTitle(title);\n this.modal.setSaveButtonText(title);\n\n // We want to focus on the action select when the dialog is closed.\n this.modal.getRoot().on(ModalEvents.hidden, function() {\n var notification = $('#user-notifications [role=alert]');\n if (notification.length) {\n notification.focus();\n } else {\n $(SELECTORS.BULKACTIONSELECT).focus();\n }\n this.modal.getRoot().remove();\n }.bind(this));\n\n this.modal.getRoot().on(ModalEvents.save, this.submitAddNote.bind(this, users));\n\n this.modal.show();\n\n return this.modal;\n }.bind(this));\n };\n\n /**\n * Add a note to this list of users.\n *\n * @method submitAddNote\n * @private\n * @param {int[]} users\n * @return {Promise}\n */\n Participants.prototype.submitAddNote = function(users) {\n var noteText = this.modal.getRoot().find('form textarea').val();\n var publishState = this.modal.getRoot().find('form select').val();\n var notes = [],\n i = 0;\n\n for (i = 0; i < users.length; i++) {\n notes.push({userid: users[i], text: noteText, courseid: this.courseId, publishstate: publishState});\n }\n\n return Ajax.call([{\n methodname: 'core_notes_create_notes',\n args: {notes: notes}\n }])[0].then(function(noteIds) {\n if (noteIds.length == 1) {\n return Str.get_string('addbulknotedonesingle', 'core_notes');\n } else {\n return Str.get_string('addbulknotedone', 'core_notes', noteIds.length);\n }\n }).then(function(msg) {\n Notification.addNotification({\n message: msg,\n type: \"success\"\n });\n return true;\n }).catch(Notification.exception);\n };\n\n /**\n * Show the send message popup.\n *\n * @method showSendMessage\n * @private\n * @param {int[]} users\n * @return {Promise}\n */\n Participants.prototype.showSendMessage = function(users) {\n\n if (users.length == 0) {\n // Nothing to do.\n return $.Deferred().resolve().promise();\n }\n var titlePromise = null;\n if (users.length == 1) {\n titlePromise = Str.get_string('sendbulkmessagesingle', 'core_message');\n } else {\n titlePromise = Str.get_string('sendbulkmessage', 'core_message', users.length);\n }\n\n return $.when(\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n body: Templates.render('core_user/send_bulk_message', {})\n }),\n titlePromise\n ).then(function(modal, title) {\n // Keep a reference to the modal.\n this.modal = modal;\n\n this.modal.setTitle(title);\n this.modal.setSaveButtonText(title);\n\n // We want to focus on the action select when the dialog is closed.\n this.modal.getRoot().on(ModalEvents.hidden, function() {\n $(SELECTORS.BULKACTIONSELECT).focus();\n this.modal.getRoot().remove();\n }.bind(this));\n\n this.modal.getRoot().on(ModalEvents.save, this.submitSendMessage.bind(this, users));\n\n this.modal.show();\n\n return this.modal;\n }.bind(this));\n };\n\n /**\n * Send a message to these users.\n *\n * @method submitSendMessage\n * @private\n * @param {int[]} users\n * @param {Event} e Form submission event.\n * @return {Promise}\n */\n Participants.prototype.submitSendMessage = function(users) {\n\n var messageText = this.modal.getRoot().find('form textarea').val();\n\n var messages = [],\n i = 0;\n\n for (i = 0; i < users.length; i++) {\n messages.push({touserid: users[i], text: messageText});\n }\n\n return Ajax.call([{\n methodname: 'core_message_send_instant_messages',\n args: {messages: messages}\n }])[0].then(function(messageIds) {\n if (messageIds.length == 1) {\n return Str.get_string('sendbulkmessagesentsingle', 'core_message');\n } else {\n return Str.get_string('sendbulkmessagesent', 'core_message', messageIds.length);\n }\n }).then(function(msg) {\n Notification.addNotification({\n message: msg,\n type: \"success\"\n });\n return true;\n }).catch(Notification.exception);\n };\n\n return /** @alias module:core_user/participants */ {\n // Public variables and functions.\n\n /**\n * Initialise the unified user filter.\n *\n * @method init\n * @param {Object} options - List of options.\n * @return {Participants}\n */\n 'init': function(options) {\n return new Participants(options);\n }\n };\n});\n"],"file":"participants.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/participants.js"],"names":["Selectors","bulkActionSelect","bulkUserCheckBoxes","bulkUserSelectedCheckBoxes","checkAllButton","stateHelpIcon","tableForm","uniqueId","init","uniqueid","noteStateNames","root","document","querySelector","getTableFromUniqueId","DynamicTableSelectors","main","fromRegionId","registerEventListeners","addEventListener","CustomEvents","events","accessibleChange","e","action","target","value","tableRoot","checkboxes","querySelectorAll","indexOf","preventDefault","ids","forEach","checkbox","push","getAttribute","replace","bulkAction","dataset","courseId","then","modal","getRoot","on","ModalEvents","hidden","bulkActionSelector","focus","catch","Notification","exception","length","form","submit","resetBulkAction","closest","showAllLink","showalllink","window","location"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Some UI stuff for participants page.\n * This is also used by the report/participants/index.php because it has the same functionality.\n *\n * @module core_user/participants\n * @package core_user\n * @copyright 2017 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DynamicTableSelectors from 'core_table/local/dynamic/selectors';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {showAddNote, showSendMessage} from 'core_user/local/participants/bulkactions';\n\nconst Selectors = {\n bulkActionSelect: \"#formactionid\",\n bulkUserCheckBoxes: \"input.usercheckbox\",\n bulkUserSelectedCheckBoxes: \"input.usercheckbox:checked\",\n checkAllButton: \"#checkall\",\n stateHelpIcon: '[data-region=\"state-help-icon\"]',\n tableForm: uniqueId => `form[data-table-unique-id=\"${uniqueId}\"]`,\n};\n\nexport const init = ({\n uniqueid,\n noteStateNames = {},\n}) => {\n const root = document.querySelector(Selectors.tableForm(uniqueid));\n const getTableFromUniqueId = uniqueId => root.querySelector(DynamicTableSelectors.main.fromRegionId(uniqueId));\n\n /**\n * Private method.\n *\n * @method registerEventListeners\n * @private\n */\n const registerEventListeners = () => {\n root.querySelector(Selectors.bulkActionSelect).addEventListener(CustomEvents.events.accessibleChange, e => {\n const action = e.target.value;\n const tableRoot = getTableFromUniqueId(uniqueid);\n const checkboxes = tableRoot.querySelectorAll(Selectors.bulkUserSelectedCheckBoxes);\n\n if (action.indexOf('#') !== -1) {\n e.preventDefault();\n\n const ids = [];\n checkboxes.forEach(checkbox => {\n ids.push(checkbox.getAttribute('name').replace('user', ''));\n });\n\n let bulkAction;\n if (action === '#messageselect') {\n bulkAction = showSendMessage(ids);\n } else if (action === '#addgroupnote') {\n bulkAction = showAddNote(\n root.dataset.courseId,\n ids,\n noteStateNames,\n root.querySelector(Selectors.stateHelpIcon)\n );\n }\n\n if (bulkAction) {\n bulkAction\n .then(modal => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Focus on the action select when the dialog is closed.\n const bulkActionSelector = root.querySelector(Selectors.bulkActionSelect);\n bulkActionSelector.focus();\n });\n\n return modal;\n })\n .catch(Notification.exception);\n }\n } else if (action !== '' && checkboxes.length) {\n e.target.form.submit();\n }\n\n resetBulkAction(e.target);\n });\n\n root.addEventListener('click', e => {\n const checkAllButton = e.target.closest(Selectors.checkAllButton);\n if (checkAllButton) {\n const showAllLink = checkAllButton.dataset.showalllink;\n if (showAllLink) {\n window.location = showAllLink;\n }\n }\n });\n };\n\n const resetBulkAction = bulkActionSelect => {\n bulkActionSelect.value = '';\n };\n\n registerEventListeners();\n};\n"],"file":"participants.min.js"} \ No newline at end of file diff --git a/user/amd/build/repository.min.js b/user/amd/build/repository.min.js index 5ff57f0f070..fec9ab0154c 100644 --- a/user/amd/build/repository.min.js +++ b/user/amd/build/repository.min.js @@ -1,2 +1,2 @@ -define ("core_user/repository",["exports","core/ajax"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.submitUserEnrolmentForm=a.unenrolUser=void 0;a.unenrolUser=function unenrolUser(a){return(0,b.call)([{methodname:"core_enrol_unenrol_user_enrolment",args:{ueid:a}}])[0]};a.submitUserEnrolmentForm=function submitUserEnrolmentForm(a){return(0,b.call)([{methodname:"core_enrol_submit_user_enrolment_form",args:{formdata:a}}])[0]}}); +define ("core_user/repository",["exports","core/ajax"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.sendMessagesToUsers=a.createNotesForUsers=a.submitUserEnrolmentForm=a.unenrolUser=void 0;a.unenrolUser=function unenrolUser(a){return(0,b.call)([{methodname:"core_enrol_unenrol_user_enrolment",args:{ueid:a}}])[0]};a.submitUserEnrolmentForm=function submitUserEnrolmentForm(a){return(0,b.call)([{methodname:"core_enrol_submit_user_enrolment_form",args:{formdata:a}}])[0]};a.createNotesForUsers=function createNotesForUsers(a){return(0,b.call)([{methodname:"core_notes_create_notes",args:{notes:a}}])[0]};a.sendMessagesToUsers=function sendMessagesToUsers(a){return(0,b.call)([{methodname:"core_message_send_instant_messages",args:{messages:a}}])[0]}}); //# sourceMappingURL=repository.min.js.map diff --git a/user/amd/build/repository.min.js.map b/user/amd/build/repository.min.js.map index 7d60e609a70..b117500cf86 100644 --- a/user/amd/build/repository.min.js.map +++ b/user/amd/build/repository.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/repository.js"],"names":["unenrolUser","userEnrolmentId","methodname","args","ueid","submitUserEnrolmentForm","formdata"],"mappings":"gMA8B2B,QAAdA,CAAAA,WAAc,CAAAC,CAAe,CAAI,CAC1C,MAAO,WAAU,CAAC,CACdC,UAAU,CAAE,mCADE,CAEdC,IAAI,CAAE,CACFC,IAAI,CAAEH,CADJ,CAFQ,CAAD,CAAV,EAKH,CALG,CAMV,C,2BAQsC,QAA1BI,CAAAA,uBAA0B,CAAAC,CAAQ,CAAI,CAC/C,MAAO,WAAU,CAAC,CACdJ,UAAU,CAAE,uCADE,CAEdC,IAAI,CAAE,CACFG,QAAQ,CAARA,CADE,CAFQ,CAAD,CAAV,EAKH,CALG,CAMV,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module to handle AJAX interactions.\n *\n * @module core_user/repository\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {call as fetchMany} from 'core/ajax';\n\n/**\n * Unenrol the user with the specified user enrolmentid ID.\n *\n * @param {Number} userEnrolmentId\n * @return {Promise}\n */\nexport const unenrolUser = userEnrolmentId => {\n return fetchMany([{\n methodname: 'core_enrol_unenrol_user_enrolment',\n args: {\n ueid: userEnrolmentId,\n },\n }])[0];\n};\n\n/**\n * Submit the user enrolment form with the specified form data.\n *\n * @param {String} formdata\n * @return {Promise}\n */\nexport const submitUserEnrolmentForm = formdata => {\n return fetchMany([{\n methodname: 'core_enrol_submit_user_enrolment_form',\n args: {\n formdata,\n },\n }])[0];\n};\n"],"file":"repository.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/repository.js"],"names":["unenrolUser","userEnrolmentId","methodname","args","ueid","submitUserEnrolmentForm","formdata","createNotesForUsers","notes","sendMessagesToUsers","messages"],"mappings":"4OA8B2B,QAAdA,CAAAA,WAAc,CAAAC,CAAe,CAAI,CAC1C,MAAO,WAAU,CAAC,CACdC,UAAU,CAAE,mCADE,CAEdC,IAAI,CAAE,CACFC,IAAI,CAAEH,CADJ,CAFQ,CAAD,CAAV,EAKH,CALG,CAMV,C,2BAQsC,QAA1BI,CAAAA,uBAA0B,CAAAC,CAAQ,CAAI,CAC/C,MAAO,WAAU,CAAC,CACdJ,UAAU,CAAE,uCADE,CAEdC,IAAI,CAAE,CACFG,QAAQ,CAARA,CADE,CAFQ,CAAD,CAAV,EAKH,CALG,CAMV,C,uBAEkC,QAAtBC,CAAAA,mBAAsB,CAAAC,CAAK,CAAI,CACxC,MAAO,WAAU,CAAC,CACdN,UAAU,CAAE,yBADE,CAEdC,IAAI,CAAE,CACFK,KAAK,CAALA,CADE,CAFQ,CAAD,CAAV,EAKH,CALG,CAMV,C,uBAEkC,QAAtBC,CAAAA,mBAAsB,CAAAC,CAAQ,CAAI,CAC3C,MAAO,WAAU,CAAC,CACdR,UAAU,CAAE,oCADE,CAEdC,IAAI,CAAE,CAACO,QAAQ,CAARA,CAAD,CAFQ,CAAD,CAAV,EAGH,CAHG,CAIV,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module to handle AJAX interactions.\n *\n * @module core_user/repository\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {call as fetchMany} from 'core/ajax';\n\n/**\n * Unenrol the user with the specified user enrolmentid ID.\n *\n * @param {Number} userEnrolmentId\n * @return {Promise}\n */\nexport const unenrolUser = userEnrolmentId => {\n return fetchMany([{\n methodname: 'core_enrol_unenrol_user_enrolment',\n args: {\n ueid: userEnrolmentId,\n },\n }])[0];\n};\n\n/**\n * Submit the user enrolment form with the specified form data.\n *\n * @param {String} formdata\n * @return {Promise}\n */\nexport const submitUserEnrolmentForm = formdata => {\n return fetchMany([{\n methodname: 'core_enrol_submit_user_enrolment_form',\n args: {\n formdata,\n },\n }])[0];\n};\n\nexport const createNotesForUsers = notes => {\n return fetchMany([{\n methodname: 'core_notes_create_notes',\n args: {\n notes\n }\n }])[0];\n};\n\nexport const sendMessagesToUsers = messages => {\n return fetchMany([{\n methodname: 'core_message_send_instant_messages',\n args: {messages}\n }])[0];\n};\n"],"file":"repository.min.js"} \ No newline at end of file diff --git a/user/amd/src/local/participants/bulkactions.js b/user/amd/src/local/participants/bulkactions.js new file mode 100644 index 00000000000..3872f71ef5a --- /dev/null +++ b/user/amd/src/local/participants/bulkactions.js @@ -0,0 +1,191 @@ +// 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 . + +/** + * Bulk actions for lists of participants. + * + * @module core_user/local/participants/bulkactions + * @package core_user + * @copyright 2020 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import * as Repository from 'core_user/repository'; +import * as Str from 'core/str'; +import ModalEvents from 'core/modal_events'; +import ModalFactory from 'core/modal_factory'; +import Templates from 'core/templates'; +import {add as notifyUser} from 'core/toast'; + +/** + * Show the add note popup + * + * @param {Number} courseid + * @param {Number[]} users + * @param {String[]} noteStateNames + * @param {HTMLElement} stateHelpIcon + * @return {Promise} + */ +export const showAddNote = (courseid, users, noteStateNames, stateHelpIcon) => { + if (!users.length) { + // No users were selected. + return Promise.resolve(); + } + + const states = []; + for (let key in noteStateNames) { + switch (key) { + case 'draft': + states.push({value: 'personal', label: noteStateNames[key]}); + break; + case 'public': + states.push({value: 'course', label: noteStateNames[key], selected: 1}); + break; + case 'site': + states.push({value: key, label: noteStateNames[key]}); + break; + } + } + + const context = { + stateNames: states, + stateHelpIcon: stateHelpIcon.innerHTML, + }; + + let titlePromise = null; + if (users.length === 1) { + titlePromise = Str.get_string('addbulknotesingle', 'core_notes'); + } else { + titlePromise = Str.get_string('addbulknote', 'core_notes', users.length); + } + + return ModalFactory.create({ + type: ModalFactory.types.SAVE_CANCEL, + body: Templates.render('core_user/add_bulk_note', context), + title: titlePromise, + buttons: { + save: titlePromise, + }, + removeOnClose: true, + }) + .then(modal => { + modal.getRoot().on(ModalEvents.save, () => submitAddNote(courseid, users, modal)); + + modal.show(); + + return modal; + }); +}; + +/** + * Add a note to this list of users. + * + * @param {Number} courseid + * @param {Number[]} users + * @param {Modal} modal + * @return {Promise} + */ +const submitAddNote = (courseid, users, modal) => { + const text = modal.getRoot().find('form textarea').val(); + const publishstate = modal.getRoot().find('form select').val(); + + const notes = users.map(userid => { + return { + userid, + text, + courseid, + publishstate, + }; + }); + + return Repository.createNotesForUsers(notes) + .then(noteIds => { + if (noteIds.length === 1) { + return Str.get_string('addbulknotedonesingle', 'core_notes'); + } else { + return Str.get_string('addbulknotedone', 'core_notes', noteIds.length); + } + }) + .then(msg => notifyUser(msg)) + .catch(Notification.exception); +}; + +/** + * Show the send message popup. + * + * @param {Number[]} users + * @return {Promise} + */ +export const showSendMessage = users => { + if (!users.length) { + // Nothing to do. + return Promise.resolve(); + } + + let titlePromise; + if (users.length === 1) { + titlePromise = Str.get_string('sendbulkmessagesingle', 'core_message'); + } else { + titlePromise = Str.get_string('sendbulkmessage', 'core_message', users.length); + } + + return ModalFactory.create({ + type: ModalFactory.types.SAVE_CANCEL, + body: Templates.render('core_user/send_bulk_message', {}), + title: titlePromise, + buttons: { + save: titlePromise, + }, + removeOnClose: true, + }) + .then(modal => { + modal.getRoot().on(ModalEvents.save, () => { + submitSendMessage(modal, users); + }); + + modal.show(); + + return modal; + }); +}; + +/** + * Send a message to these users. + * + * @param {Modal} modal + * @param {Number[]} users + * @return {Promise} + */ +const submitSendMessage = (modal, users) => { + const text = modal.getRoot().find('form textarea').val(); + + const messages = users.map(touserid => { + return { + touserid, + text, + }; + }); + + return Repository.sendMessagesToUsers(messages) + .then(messageIds => { + if (messageIds.length == 1) { + return Str.get_string('sendbulkmessagesentsingle', 'core_message'); + } else { + return Str.get_string('sendbulkmessagesent', 'core_message', messageIds.length); + } + }) + .then(msg => notifyUser(msg)) + .catch(Notification.exception); +}; diff --git a/user/amd/src/participants.js b/user/amd/src/participants.js index d2156d54c8b..eba586aae00 100644 --- a/user/amd/src/participants.js +++ b/user/amd/src/participants.js @@ -22,310 +22,95 @@ * @copyright 2017 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define(['jquery', 'core/str', 'core/modal_factory', 'core/modal_events', 'core/templates', 'core/notification', 'core/ajax', - 'core/custom_interaction_events'], - function($, Str, ModalFactory, ModalEvents, Templates, Notification, Ajax, CustomEvents) { - var SELECTORS = { - BULKACTIONSELECT: "#formactionid", - BULKUSERCHECKBOXES: "input.usercheckbox", - BULKUSERNOSCHECKBOXES: "input.usercheckbox[value='0']", - BULKUSERSELECTEDCHECKBOXES: "input.usercheckbox:checked", - BULKACTIONFORM: "#participantsform", - CHECKALLBUTTON: "#checkall", - CHECKALLNOSBUTTON: "#checkallnos" - }; +import DynamicTableSelectors from 'core_table/local/dynamic/selectors'; +import ModalEvents from 'core/modal_events'; +import Notification from 'core/notification'; +import CustomEvents from 'core/custom_interaction_events'; +import {showAddNote, showSendMessage} from 'core_user/local/participants/bulkactions'; + +const Selectors = { + bulkActionSelect: "#formactionid", + bulkUserCheckBoxes: "input.usercheckbox", + bulkUserSelectedCheckBoxes: "input.usercheckbox:checked", + checkAllButton: "#checkall", + stateHelpIcon: '[data-region="state-help-icon"]', + tableForm: uniqueId => `form[data-table-unique-id="${uniqueId}"]`, +}; + +export const init = ({ + uniqueid, + noteStateNames = {}, +}) => { + const root = document.querySelector(Selectors.tableForm(uniqueid)); + const getTableFromUniqueId = uniqueId => root.querySelector(DynamicTableSelectors.main.fromRegionId(uniqueId)); /** - * Constructor + * Private method. * - * @param {Object} options Object containing options. Contextid is required. - * Each call to templates.render gets it's own instance of this class. - */ - var Participants = function(options) { - - this.courseId = options.courseid; - this.noteStateNames = options.noteStateNames; - this.stateHelpIcon = options.stateHelpIcon; - - this.attachEventListeners(); - }; - // Class variables and functions. - - /** - * @var {Modal} modal + * @method registerEventListeners * @private */ - Participants.prototype.modal = null; + const registerEventListeners = () => { + root.querySelector(Selectors.bulkActionSelect).addEventListener(CustomEvents.events.accessibleChange, e => { + const action = e.target.value; + const tableRoot = getTableFromUniqueId(uniqueid); + const checkboxes = tableRoot.querySelectorAll(Selectors.bulkUserSelectedCheckBoxes); - /** - * @var {int} courseId - * @private - */ - Participants.prototype.courseId = -1; - - /** - * @var {Object} noteStateNames - * @private - */ - Participants.prototype.noteStateNames = {}; - - /** - * @var {String} stateHelpIcon - * @private - */ - Participants.prototype.stateHelpIcon = ""; - - /** - * Private method - * - * @method attachEventListeners - * @private - */ - Participants.prototype.attachEventListeners = function() { - CustomEvents.define(SELECTORS.BULKACTIONSELECT, [CustomEvents.events.accessibleChange]); - $(SELECTORS.BULKACTIONSELECT).on(CustomEvents.events.accessibleChange, function(e) { - var action = $(e.target).val(); if (action.indexOf('#') !== -1) { e.preventDefault(); - var ids = []; - $(SELECTORS.BULKUSERSELECTEDCHECKBOXES).each(function(index, ele) { - var name = $(ele).attr('name'); - var id = name.replace('user', ''); - ids.push(id); + const ids = []; + checkboxes.forEach(checkbox => { + ids.push(checkbox.getAttribute('name').replace('user', '')); }); - if (action == '#messageselect') { - this.showSendMessage(ids).fail(Notification.exception); - } else if (action == '#addgroupnote') { - this.showAddNote(ids).fail(Notification.exception); + let bulkAction; + if (action === '#messageselect') { + bulkAction = showSendMessage(ids); + } else if (action === '#addgroupnote') { + bulkAction = showAddNote( + root.dataset.courseId, + ids, + noteStateNames, + root.querySelector(Selectors.stateHelpIcon) + ); } - $(SELECTORS.BULKACTIONSELECT + ' option[value=""]').prop('selected', 'selected'); - } else if (action !== '') { - if ($(SELECTORS.BULKUSERSELECTEDCHECKBOXES).length > 0) { - $(SELECTORS.BULKACTIONFORM).submit(); - } else { - $(SELECTORS.BULKACTIONSELECT + ' option[value=""]').prop('selected', 'selected'); - } - } - }.bind(this)); - $(SELECTORS.CHECKALLBUTTON).on('click', function() { - var showallink = $(this).data('showallink'); - if (showallink) { - window.location = showallink; + if (bulkAction) { + bulkAction + .then(modal => { + modal.getRoot().on(ModalEvents.hidden, () => { + // Focus on the action select when the dialog is closed. + const bulkActionSelector = root.querySelector(Selectors.bulkActionSelect); + bulkActionSelector.focus(); + }); + + return modal; + }) + .catch(Notification.exception); + } + } else if (action !== '' && checkboxes.length) { + e.target.form.submit(); } + + resetBulkAction(e.target); }); - $(SELECTORS.CHECKALLNOSBUTTON).on('click', function() { - $(SELECTORS.BULKUSERNOSCHECKBOXES).prop('checked', true); + root.addEventListener('click', e => { + const checkAllButton = e.target.closest(Selectors.checkAllButton); + if (checkAllButton) { + const showAllLink = checkAllButton.dataset.showalllink; + if (showAllLink) { + window.location = showAllLink; + } + } }); }; - /** - * Show the add note popup - * - * @method showAddNote - * @private - * @param {int[]} users - * @return {Promise} - */ - Participants.prototype.showAddNote = function(users) { - - if (users.length == 0) { - // Nothing to do. - return $.Deferred().resolve().promise(); - } - - var states = []; - for (var key in this.noteStateNames) { - switch (key) { - case 'draft': - states.push({value: 'personal', label: this.noteStateNames[key]}); - break; - case 'public': - states.push({value: 'course', label: this.noteStateNames[key], selected: 1}); - break; - case 'site': - states.push({value: key, label: this.noteStateNames[key]}); - break; - } - } - - var context = {stateNames: states, stateHelpIcon: this.stateHelpIcon}; - var titlePromise = null; - if (users.length == 1) { - titlePromise = Str.get_string('addbulknotesingle', 'core_notes'); - } else { - titlePromise = Str.get_string('addbulknote', 'core_notes', users.length); - } - - return $.when( - ModalFactory.create({ - type: ModalFactory.types.SAVE_CANCEL, - body: Templates.render('core_user/add_bulk_note', context) - }), - titlePromise - ).then(function(modal, title) { - // Keep a reference to the modal. - this.modal = modal; - this.modal.setTitle(title); - this.modal.setSaveButtonText(title); - - // We want to focus on the action select when the dialog is closed. - this.modal.getRoot().on(ModalEvents.hidden, function() { - var notification = $('#user-notifications [role=alert]'); - if (notification.length) { - notification.focus(); - } else { - $(SELECTORS.BULKACTIONSELECT).focus(); - } - this.modal.getRoot().remove(); - }.bind(this)); - - this.modal.getRoot().on(ModalEvents.save, this.submitAddNote.bind(this, users)); - - this.modal.show(); - - return this.modal; - }.bind(this)); + const resetBulkAction = bulkActionSelect => { + bulkActionSelect.value = ''; }; - /** - * Add a note to this list of users. - * - * @method submitAddNote - * @private - * @param {int[]} users - * @return {Promise} - */ - Participants.prototype.submitAddNote = function(users) { - var noteText = this.modal.getRoot().find('form textarea').val(); - var publishState = this.modal.getRoot().find('form select').val(); - var notes = [], - i = 0; - - for (i = 0; i < users.length; i++) { - notes.push({userid: users[i], text: noteText, courseid: this.courseId, publishstate: publishState}); - } - - return Ajax.call([{ - methodname: 'core_notes_create_notes', - args: {notes: notes} - }])[0].then(function(noteIds) { - if (noteIds.length == 1) { - return Str.get_string('addbulknotedonesingle', 'core_notes'); - } else { - return Str.get_string('addbulknotedone', 'core_notes', noteIds.length); - } - }).then(function(msg) { - Notification.addNotification({ - message: msg, - type: "success" - }); - return true; - }).catch(Notification.exception); - }; - - /** - * Show the send message popup. - * - * @method showSendMessage - * @private - * @param {int[]} users - * @return {Promise} - */ - Participants.prototype.showSendMessage = function(users) { - - if (users.length == 0) { - // Nothing to do. - return $.Deferred().resolve().promise(); - } - var titlePromise = null; - if (users.length == 1) { - titlePromise = Str.get_string('sendbulkmessagesingle', 'core_message'); - } else { - titlePromise = Str.get_string('sendbulkmessage', 'core_message', users.length); - } - - return $.when( - ModalFactory.create({ - type: ModalFactory.types.SAVE_CANCEL, - body: Templates.render('core_user/send_bulk_message', {}) - }), - titlePromise - ).then(function(modal, title) { - // Keep a reference to the modal. - this.modal = modal; - - this.modal.setTitle(title); - this.modal.setSaveButtonText(title); - - // We want to focus on the action select when the dialog is closed. - this.modal.getRoot().on(ModalEvents.hidden, function() { - $(SELECTORS.BULKACTIONSELECT).focus(); - this.modal.getRoot().remove(); - }.bind(this)); - - this.modal.getRoot().on(ModalEvents.save, this.submitSendMessage.bind(this, users)); - - this.modal.show(); - - return this.modal; - }.bind(this)); - }; - - /** - * Send a message to these users. - * - * @method submitSendMessage - * @private - * @param {int[]} users - * @param {Event} e Form submission event. - * @return {Promise} - */ - Participants.prototype.submitSendMessage = function(users) { - - var messageText = this.modal.getRoot().find('form textarea').val(); - - var messages = [], - i = 0; - - for (i = 0; i < users.length; i++) { - messages.push({touserid: users[i], text: messageText}); - } - - return Ajax.call([{ - methodname: 'core_message_send_instant_messages', - args: {messages: messages} - }])[0].then(function(messageIds) { - if (messageIds.length == 1) { - return Str.get_string('sendbulkmessagesentsingle', 'core_message'); - } else { - return Str.get_string('sendbulkmessagesent', 'core_message', messageIds.length); - } - }).then(function(msg) { - Notification.addNotification({ - message: msg, - type: "success" - }); - return true; - }).catch(Notification.exception); - }; - - return /** @alias module:core_user/participants */ { - // Public variables and functions. - - /** - * Initialise the unified user filter. - * - * @method init - * @param {Object} options - List of options. - * @return {Participants} - */ - 'init': function(options) { - return new Participants(options); - } - }; -}); + registerEventListeners(); +}; diff --git a/user/amd/src/repository.js b/user/amd/src/repository.js index 7be9378875f..c719bc12046 100644 --- a/user/amd/src/repository.js +++ b/user/amd/src/repository.js @@ -51,3 +51,19 @@ export const submitUserEnrolmentForm = formdata => { }, }])[0]; }; + +export const createNotesForUsers = notes => { + return fetchMany([{ + methodname: 'core_notes_create_notes', + args: { + notes + } + }])[0]; +}; + +export const sendMessagesToUsers = messages => { + return fetchMany([{ + methodname: 'core_message_send_instant_messages', + args: {messages} + }])[0]; +}; diff --git a/user/index.php b/user/index.php index 92f81e2bfdb..4aa5fcb647f 100644 --- a/user/index.php +++ b/user/index.php @@ -289,7 +289,13 @@ ob_end_clean(); echo html_writer::tag('p', get_string('participantscount', 'moodle', $participanttable->totalrows)); if ($bulkoperations) { - echo '
'; + echo html_writer::start_tag('form', [ + 'action' => 'action_redir.php', + 'method' => 'post', + 'id' => 'participantsform', + 'data-course-id' => $course->id, + 'data-table-unique-id' => $participanttable->uniqueid, + ]); echo '
'; echo ''; echo ''; @@ -392,13 +398,14 @@ if ($bulkoperations) { echo html_writer::tag('div', $label . $select); echo ''; + echo '
' . $OUTPUT->help_icon('publishstate', 'notes') . '
'; echo '
'; echo '
'; - $options = new stdClass(); - $options->courseid = $course->id; - $options->noteStateNames = note_get_state_names(); - $options->stateHelpIcon = $OUTPUT->help_icon('publishstate', 'notes'); + $options = (object) [ + 'uniqueid' => $participanttable->uniqueid, + 'noteStateNames' => note_get_state_names(), + ]; $PAGE->requires->js_call_amd('core_user/participants', 'init', [$options]); } From 0d01f2ae561856e52269a30e421e8f9f02efe74e Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 18 May 2020 10:16:55 +0800 Subject: [PATCH 2/6] MDL-68463 report_participants: Use new shared participant actions module --- .../amd/build/participants.min.js | 2 + .../amd/build/participants.min.js.map | 1 + report/participation/amd/src/participants.js | 88 +++++++++++++++++++ report/participation/index.php | 2 +- 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 report/participation/amd/build/participants.min.js create mode 100644 report/participation/amd/build/participants.min.js.map create mode 100644 report/participation/amd/src/participants.js diff --git a/report/participation/amd/build/participants.min.js b/report/participation/amd/build/participants.min.js new file mode 100644 index 00000000000..fa9364b470d --- /dev/null +++ b/report/participation/amd/build/participants.min.js @@ -0,0 +1,2 @@ +define ("report_participation/participants",["exports","jquery","core/custom_interaction_events","core/modal_events","core/notification","core_user/local/participants/bulkactions"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=g(b);c=g(c);d=g(d);e=g(e);function g(a){return a&&a.__esModule?a:{default:a}}var h={bulkActionSelect:"#formactionid",bulkUserSelectedCheckBoxes:"input[data-togglegroup^='participants-table']:checked",participantsForm:"#participantsform"};a.init=function init(){var a=document.querySelector(h.participantsForm),g=function(){c.default.define(h.bulkActionSelect,[c.default.events.accessibleChange]);(0,b.default)(h.bulkActionSelect).on(c.default.events.accessibleChange,function(b){var c=b.target.value,g=a.querySelectorAll(h.bulkUserSelectedCheckBoxes);if(-1!==c.indexOf("#")){b.preventDefault();var j=[];g.forEach(function(a){j.push(a.getAttribute("name").replace("user",""))});if("#messageselect"===c){(0,f.showSendMessage)(j).then(function(b){b.getRoot().on(d.default.hidden,function(){var b=a.querySelector(h.bulkActionSelect);i(b);b.focus()});return b}).catch(e.default.exception)}}else if(""!==c&&g.length){b.target.form().submit()}i(b.target)})},i=function(a){a.value=""};g()}}); +//# sourceMappingURL=participants.min.js.map diff --git a/report/participation/amd/build/participants.min.js.map b/report/participation/amd/build/participants.min.js.map new file mode 100644 index 00000000000..23f6cb6b2df --- /dev/null +++ b/report/participation/amd/build/participants.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/participants.js"],"names":["Selectors","bulkActionSelect","bulkUserSelectedCheckBoxes","participantsForm","init","root","document","querySelector","registerEventListeners","CustomEvents","define","events","accessibleChange","on","e","action","target","value","checkboxes","querySelectorAll","indexOf","preventDefault","ids","forEach","checkbox","push","getAttribute","replace","then","modal","getRoot","ModalEvents","hidden","bulkActionSelector","resetBulkAction","focus","catch","Notification","exception","length","form","submit"],"mappings":"uRAyBA,OACA,OACA,OACA,O,sDAGMA,CAAAA,CAAS,CAAG,CACdC,gBAAgB,CAAE,eADJ,CAEdC,0BAA0B,CAAE,uDAFd,CAGdC,gBAAgB,CAAE,mBAHJ,C,QAME,QAAPC,CAAAA,IAAO,EAAM,IAChBC,CAAAA,CAAI,CAAGC,QAAQ,CAACC,aAAT,CAAuBP,CAAS,CAACG,gBAAjC,CADS,CAShBK,CAAsB,CAAG,UAAM,CACjCC,UAAaC,MAAb,CAAoBV,CAAS,CAACC,gBAA9B,CAAgD,CAACQ,UAAaE,MAAb,CAAoBC,gBAArB,CAAhD,EACA,cAAOZ,CAAS,CAACC,gBAAjB,EAAmCY,EAAnC,CAAsCJ,UAAaE,MAAb,CAAoBC,gBAA1D,CAA4E,SAAAE,CAAC,CAAI,IACvEC,CAAAA,CAAM,CAAGD,CAAC,CAACE,MAAF,CAASC,KADqD,CAEvEC,CAAU,CAAGb,CAAI,CAACc,gBAAL,CAAsBnB,CAAS,CAACE,0BAAhC,CAF0D,CAI7E,GAA4B,CAAC,CAAzB,GAAAa,CAAM,CAACK,OAAP,CAAe,GAAf,CAAJ,CAAgC,CAC5BN,CAAC,CAACO,cAAF,GAEA,GAAMC,CAAAA,CAAG,CAAG,EAAZ,CACAJ,CAAU,CAACK,OAAX,CAAmB,SAAAC,CAAQ,CAAI,CAC3BF,CAAG,CAACG,IAAJ,CAASD,CAAQ,CAACE,YAAT,CAAsB,MAAtB,EAA8BC,OAA9B,CAAsC,MAAtC,CAA8C,EAA9C,CAAT,CACH,CAFD,EAIA,GAAe,gBAAX,GAAAZ,CAAJ,CAAiC,CAC7B,sBAAgBO,CAAhB,EACCM,IADD,CACM,SAAAC,CAAK,CAAI,CACXA,CAAK,CAACC,OAAN,GAAgBjB,EAAhB,CAAmBkB,UAAYC,MAA/B,CAAuC,UAAM,CAEzC,GAAMC,CAAAA,CAAkB,CAAG5B,CAAI,CAACE,aAAL,CAAmBP,CAAS,CAACC,gBAA7B,CAA3B,CACAiC,CAAe,CAACD,CAAD,CAAf,CACAA,CAAkB,CAACE,KAAnB,EACH,CALD,EAOA,MAAON,CAAAA,CACV,CAVD,EAWCO,KAXD,CAWOC,UAAaC,SAXpB,CAYH,CACJ,CAtBD,IAsBO,IAAe,EAAX,GAAAvB,CAAM,EAAWG,CAAU,CAACqB,MAAhC,CAAwC,CAC3CzB,CAAC,CAACE,MAAF,CAASwB,IAAT,GAAgBC,MAAhB,EACH,CAEDP,CAAe,CAACpB,CAAC,CAACE,MAAH,CAClB,CA/BD,CAgCH,CA3CqB,CA6ChBkB,CAAe,CAAG,SAAAjC,CAAgB,CAAI,CACxCA,CAAgB,CAACgB,KAAjB,CAAyB,EAC5B,CA/CqB,CAiDtBT,CAAsB,EACzB,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Some UI stuff for participants page.\n * This is also used by the report/participants/index.php because it has the same functionality.\n *\n * @module core_user/participants\n * @package core_user\n * @copyright 2017 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport jQuery from 'jquery';\nimport CustomEvents from 'core/custom_interaction_events';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport {showSendMessage} from 'core_user/local/participants/bulkactions';\n\nconst Selectors = {\n bulkActionSelect: \"#formactionid\",\n bulkUserSelectedCheckBoxes: \"input[data-togglegroup^='participants-table']:checked\",\n participantsForm: '#participantsform',\n};\n\nexport const init = () => {\n const root = document.querySelector(Selectors.participantsForm);\n\n /**\n * Private method.\n *\n * @method registerEventListeners\n * @private\n */\n const registerEventListeners = () => {\n CustomEvents.define(Selectors.bulkActionSelect, [CustomEvents.events.accessibleChange]);\n jQuery(Selectors.bulkActionSelect).on(CustomEvents.events.accessibleChange, e => {\n const action = e.target.value;\n const checkboxes = root.querySelectorAll(Selectors.bulkUserSelectedCheckBoxes);\n\n if (action.indexOf('#') !== -1) {\n e.preventDefault();\n\n const ids = [];\n checkboxes.forEach(checkbox => {\n ids.push(checkbox.getAttribute('name').replace('user', ''));\n });\n\n if (action === '#messageselect') {\n showSendMessage(ids)\n .then(modal => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Focus on the action select when the dialog is closed.\n const bulkActionSelector = root.querySelector(Selectors.bulkActionSelect);\n resetBulkAction(bulkActionSelector);\n bulkActionSelector.focus();\n });\n\n return modal;\n })\n .catch(Notification.exception);\n }\n } else if (action !== '' && checkboxes.length) {\n e.target.form().submit();\n }\n\n resetBulkAction(e.target);\n });\n };\n\n const resetBulkAction = bulkActionSelect => {\n bulkActionSelect.value = '';\n };\n\n registerEventListeners();\n};\n"],"file":"participants.min.js"} \ No newline at end of file diff --git a/report/participation/amd/src/participants.js b/report/participation/amd/src/participants.js new file mode 100644 index 00000000000..7af7b60b847 --- /dev/null +++ b/report/participation/amd/src/participants.js @@ -0,0 +1,88 @@ +// 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 . + +/** + * Some UI stuff for participants page. + * This is also used by the report/participants/index.php because it has the same functionality. + * + * @module core_user/participants + * @package core_user + * @copyright 2017 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import jQuery from 'jquery'; +import CustomEvents from 'core/custom_interaction_events'; +import ModalEvents from 'core/modal_events'; +import Notification from 'core/notification'; +import {showSendMessage} from 'core_user/local/participants/bulkactions'; + +const Selectors = { + bulkActionSelect: "#formactionid", + bulkUserSelectedCheckBoxes: "input[data-togglegroup^='participants-table']:checked", + participantsForm: '#participantsform', +}; + +export const init = () => { + const root = document.querySelector(Selectors.participantsForm); + + /** + * Private method. + * + * @method registerEventListeners + * @private + */ + const registerEventListeners = () => { + CustomEvents.define(Selectors.bulkActionSelect, [CustomEvents.events.accessibleChange]); + jQuery(Selectors.bulkActionSelect).on(CustomEvents.events.accessibleChange, e => { + const action = e.target.value; + const checkboxes = root.querySelectorAll(Selectors.bulkUserSelectedCheckBoxes); + + if (action.indexOf('#') !== -1) { + e.preventDefault(); + + const ids = []; + checkboxes.forEach(checkbox => { + ids.push(checkbox.getAttribute('name').replace('user', '')); + }); + + if (action === '#messageselect') { + showSendMessage(ids) + .then(modal => { + modal.getRoot().on(ModalEvents.hidden, () => { + // Focus on the action select when the dialog is closed. + const bulkActionSelector = root.querySelector(Selectors.bulkActionSelect); + resetBulkAction(bulkActionSelector); + bulkActionSelector.focus(); + }); + + return modal; + }) + .catch(Notification.exception); + } + } else if (action !== '' && checkboxes.length) { + e.target.form().submit(); + } + + resetBulkAction(e.target); + }); + }; + + const resetBulkAction = bulkActionSelect => { + bulkActionSelect.value = ''; + }; + + registerEventListeners(); +}; diff --git a/report/participation/index.php b/report/participation/index.php index 41019471579..ef7a6172273 100644 --- a/report/participation/index.php +++ b/report/participation/index.php @@ -385,7 +385,7 @@ if (!empty($instanceid) && !empty($roleid)) { $options->courseid = $course->id; $options->noteStateNames = note_get_state_names(); $options->stateHelpIcon = $OUTPUT->help_icon('publishstate', 'notes'); - $PAGE->requires->js_call_amd('core_user/participants', 'init', [$options]); + $PAGE->requires->js_call_amd('report_participation/participants', 'init', [$options]); } echo ''."\n"; } From 814da16778c20c21150d899ec58d7ce5287f8a31 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Wed, 13 May 2020 15:59:23 +0800 Subject: [PATCH 3/6] MDL-68463 core: Add helper functions to toggle checkboxes --- lib/amd/build/checkbox-toggleall.min.js | 2 +- lib/amd/build/checkbox-toggleall.min.js.map | 2 +- lib/amd/src/checkbox-toggleall.js | 50 +++++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/lib/amd/build/checkbox-toggleall.min.js b/lib/amd/build/checkbox-toggleall.min.js index be9c0aebddd..4fd1ed409b3 100644 --- a/lib/amd/build/checkbox-toggleall.min.js +++ b/lib/amd/build/checkbox-toggleall.min.js @@ -1,2 +1,2 @@ -define ("core/checkbox-toggleall",["jquery","core/pubsub"],function(a,b){var c=!1,d={checkboxToggled:"core/checkbox-toggleall:checkboxToggled"},e=function(a,b,c){if(c){return a.find("[data-action=\"toggle\"][data-togglegroup=\""+b+"\"]")}else{return a.find("[data-action=\"toggle\"][data-togglegroup^=\""+b+"\"]")}},f=function(a,b){return e(a,b,!1).filter("[data-toggle=\"slave\"]")},g=function(a,b,c){return e(a,b,c).filter("[data-toggle=\"master\"]")},h=function(a,b){return e(a,b,!0).filter("[data-toggle=\"action\"]")},i=function(c){var e=c.data.root,g=a(c.target),h=g.data("togglegroup"),i;if(g.is(":checkbox")){i=g.is(":checked")}else{i=1===g.data("checkall")}var j=f(e,h),k=j.filter(":checked");l(e,h,i,!1);j.prop("checked",i);j.trigger("change");b.publish(d.checkboxToggled,{root:e,toggleGroupName:h,slaves:j,checkedSlaves:k,anyChecked:i})},j=function(c){var e=c.data.root,g=a(c.target),h=g.data("togglegroup").split(" "),i=[],j="";h.forEach(function(a){j+=" "+a;i.push(j.trim())});i.forEach(function(a){var c=f(e,a),g=c.filter(":checked"),h=c.length===g.length;l(e,a,h,!0);k(e,a,!g.length);b.publish(d.checkboxToggled,{root:e,toggleGroupName:a,slaves:c,checkedSlaves:g,anyChecked:!!g.length})})},k=function(a,b,c){h(a,b).prop("disabled",c)},l=function(b,c,d,e){var f=g(b,c,e);f.prop("checked",d);f.each(function(c,e){e=a(e);var f;if(d){f=e.data("toggle-deselectall")}else{f=e.data("toggle-selectall")}if(e.is(":checkbox")){var g=b.find("[for=\""+e.attr("id")+"\"]");if(g.length){if(g.html()!==f){g.html(f)}}}else{e.text(f);e.data("checkall",d?0:1)}})},m=function(){if(!c){c=!0;var b=a(document.body);b.on("click","[data-action=\"toggle\"][data-toggle=\"master\"]",{root:b},i);b.on("change","[data-action=\"toggle\"][data-toggle=\"slave\"]",{root:b},j)}};return{init:function init(){m()},events:d}}); +define ("core/checkbox-toggleall",["jquery","core/pubsub"],function(a,b){var c=!1,d={checkboxToggled:"core/checkbox-toggleall:checkboxToggled"},e=function(a,b,c){if(c){return a.find("[data-action=\"toggle\"][data-togglegroup=\""+b+"\"]")}else{return a.find("[data-action=\"toggle\"][data-togglegroup^=\""+b+"\"]")}},f=function(a,b){return e(a,b,!1).filter("[data-toggle=\"slave\"]")},g=function(a,b,c){return e(a,b,c).filter("[data-toggle=\"master\"]")},h=function(a,b){return e(a,b,!0).filter("[data-toggle=\"action\"]")},i=function(b){var c=b.data.root,d=a(b.target),e=d.data("togglegroup"),f;if(d.is(":checkbox")){f=d.is(":checked")}else{f=1===d.data("checkall")}j(c,e,f)},j=function(a,c,e){var g=f(a,c),h=g.filter(":checked");m(a,c,e,!1);g.prop("checked",e);g.trigger("change");b.publish(d.checkboxToggled,{root:a,toggleGroupName:c,slaves:g,checkedSlaves:h,anyChecked:e})},k=function(c){var e=c.data.root,g=a(c.target),h=g.data("togglegroup").split(" "),i=[],j="";h.forEach(function(a){j+=" "+a;i.push(j.trim())});i.forEach(function(a){var c=f(e,a),g=c.filter(":checked"),h=c.length===g.length;m(e,a,h,!0);l(e,a,!g.length);b.publish(d.checkboxToggled,{root:e,toggleGroupName:a,slaves:c,checkedSlaves:g,anyChecked:!!g.length})})},l=function(a,b,c){h(a,b).prop("disabled",c)},m=function(b,c,d,e){var f=g(b,c,e);f.prop("checked",d);f.each(function(c,e){e=a(e);var f;if(d){f=e.data("toggle-deselectall")}else{f=e.data("toggle-selectall")}if(e.is(":checkbox")){var g=b.find("[for=\""+e.attr("id")+"\"]");if(g.length){if(g.html()!==f){g.html(f)}}}else{e.text(f);e.data("checkall",d?0:1)}})},n=function(){if(!c){c=!0;var b=a(document.body);b.on("click","[data-action=\"toggle\"][data-toggle=\"master\"]",{root:b},i);b.on("change","[data-action=\"toggle\"][data-toggle=\"slave\"]",{root:b},k)}};return{init:function init(){n()},events:d,setGroupState:function setGroupState(b,c,d){b=a(b);m(b,c,d,!0);j(b,c,d)},updateSlavesFromMasterState:function updateSlavesFromMasterState(b,c){b=a(b);var d=g(b,c,!1),e;if(d.is(":checkbox")){e=d.is(":checked")}else{e=1===d.data("checkall")}j(b,c,e)}}}); //# sourceMappingURL=checkbox-toggleall.min.js.map diff --git a/lib/amd/build/checkbox-toggleall.min.js.map b/lib/amd/build/checkbox-toggleall.min.js.map index 846842132f3..e7a5c400985 100644 --- a/lib/amd/build/checkbox-toggleall.min.js.map +++ b/lib/amd/build/checkbox-toggleall.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/checkbox-toggleall.js"],"names":["define","$","PubSub","registered","events","checkboxToggled","getToggleGroupElements","root","toggleGroup","exactMatch","find","getAllSlaveCheckboxes","filter","getControlCheckboxes","getActionElements","toggleSlavesFromMasters","e","data","target","toggleGroupName","targetState","is","slaves","checkedSlaves","setMasterStates","prop","trigger","publish","anyChecked","toggleMastersFromSlaves","toggleGroups","split","toggleGroupLevels","toggleGroupLevel","forEach","push","trim","length","setActionElementStates","disableActionElements","masters","each","i","masterElement","targetString","masterLabel","attr","html","text","registerListeners","document","body","on","init"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A module to help with toggle select/deselect all.\n *\n * @module core/checkbox-toggleall\n * @copyright 2019 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/pubsub'], function($, PubSub) {\n\n /**\n * Whether event listeners have already been registered.\n *\n * @private\n * @type {boolean}\n */\n var registered = false;\n\n /**\n * List of custom events that this module publishes.\n *\n * @private\n * @type {{checkboxToggled: string}}\n */\n var events = {\n checkboxToggled: 'core/checkbox-toggleall:checkboxToggled',\n };\n\n /**\n * Fetches elements that are member of a given toggle group.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroup The toggle group name that we're searching form.\n * @param {boolean} exactMatch Whether we want an exact match we just want to match toggle groups that start with the given\n * toggle group name.\n * @returns {jQuery} The elements matching the given toggle group.\n */\n var getToggleGroupElements = function(root, toggleGroup, exactMatch) {\n if (exactMatch) {\n return root.find('[data-action=\"toggle\"][data-togglegroup=\"' + toggleGroup + '\"]');\n } else {\n return root.find('[data-action=\"toggle\"][data-togglegroup^=\"' + toggleGroup + '\"]');\n }\n };\n\n /**\n * Fetches the slave checkboxes for a given toggle group.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroup The toggle group name.\n * @returns {jQuery} The slave checkboxes belonging to the toggle group.\n */\n var getAllSlaveCheckboxes = function(root, toggleGroup) {\n return getToggleGroupElements(root, toggleGroup, false).filter('[data-toggle=\"slave\"]');\n };\n\n /**\n * Fetches the master elements (checkboxes or buttons) that control the slave checkboxes in a given toggle group.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroup The toggle group name.\n * @param {boolean} exactMatch\n * @returns {jQuery} The control elements belonging to the toggle group.\n */\n var getControlCheckboxes = function(root, toggleGroup, exactMatch) {\n return getToggleGroupElements(root, toggleGroup, exactMatch).filter('[data-toggle=\"master\"]');\n };\n\n /**\n * Fetches the action elements that perform actions on the selected checkboxes in a given toggle group.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroup The toggle group name.\n * @returns {jQuery} The action elements belonging to the toggle group.\n */\n var getActionElements = function(root, toggleGroup) {\n return getToggleGroupElements(root, toggleGroup, true).filter('[data-toggle=\"action\"]');\n };\n\n /**\n * Toggles the slave checkboxes in a given toggle group when a master element in that toggle group is toggled.\n *\n * @private\n * @param {Object} e The event object.\n */\n var toggleSlavesFromMasters = function(e) {\n var root = e.data.root;\n var target = $(e.target);\n\n var toggleGroupName = target.data('togglegroup');\n var targetState;\n if (target.is(':checkbox')) {\n targetState = target.is(':checked');\n } else {\n targetState = target.data('checkall') === 1;\n }\n\n var slaves = getAllSlaveCheckboxes(root, toggleGroupName);\n var checkedSlaves = slaves.filter(':checked');\n\n setMasterStates(root, toggleGroupName, targetState, false);\n\n // Set the slave checkboxes from the masters.\n slaves.prop('checked', targetState);\n // Trigger 'change' event to toggle other master checkboxes (e.g. parent master checkboxes) and action elements.\n slaves.trigger('change');\n\n PubSub.publish(events.checkboxToggled, {\n root: root,\n toggleGroupName: toggleGroupName,\n slaves: slaves,\n checkedSlaves: checkedSlaves,\n anyChecked: targetState,\n });\n };\n\n /**\n * Toggles the master checkboxes in a given toggle group when all or none of the slave checkboxes in the same toggle group\n * have been selected.\n *\n * @private\n * @param {Object} e The event object.\n */\n var toggleMastersFromSlaves = function(e) {\n var root = e.data.root;\n var target = $(e.target);\n\n var toggleGroups = target.data('togglegroup').split(' ');\n var toggleGroupLevels = [];\n var toggleGroupLevel = '';\n toggleGroups.forEach(function(toggleGroupName) {\n toggleGroupLevel += ' ' + toggleGroupName;\n toggleGroupLevels.push(toggleGroupLevel.trim());\n });\n\n toggleGroupLevels.forEach(function(toggleGroupName) {\n var slaves = getAllSlaveCheckboxes(root, toggleGroupName);\n var checkedSlaves = slaves.filter(':checked');\n var targetState = (slaves.length === checkedSlaves.length);\n\n // Make sure to toggle the exact master checkbox.\n setMasterStates(root, toggleGroupName, targetState, true);\n\n // Enable action elements when there's at least one checkbox checked. Disable otherwise.\n setActionElementStates(root, toggleGroupName, !checkedSlaves.length);\n\n PubSub.publish(events.checkboxToggled, {\n root: root,\n toggleGroupName: toggleGroupName,\n slaves: slaves,\n checkedSlaves: checkedSlaves,\n anyChecked: !!checkedSlaves.length,\n });\n });\n };\n\n /**\n * Enables or disables the action elements.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroupName The toggle group name of the action element(s).\n * @param {boolean} disableActionElements Whether to disable or to enable the action elements.\n */\n var setActionElementStates = function(root, toggleGroupName, disableActionElements) {\n getActionElements(root, toggleGroupName).prop('disabled', disableActionElements);\n };\n\n /**\n * Selects or deselects the master elements.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroupName The toggle group name of the master element(s).\n * @param {boolean} targetState Whether to select (true) or deselect (false).\n * @param {boolean} exactMatch Whether to do an exact match for the toggle group name or not.\n */\n var setMasterStates = function(root, toggleGroupName, targetState, exactMatch) {\n // Set the master checkboxes value and ARIA labels..\n var masters = getControlCheckboxes(root, toggleGroupName, exactMatch);\n masters.prop('checked', targetState);\n masters.each(function(i, masterElement) {\n masterElement = $(masterElement);\n\n var targetString;\n if (targetState) {\n targetString = masterElement.data('toggle-deselectall');\n } else {\n targetString = masterElement.data('toggle-selectall');\n }\n\n if (masterElement.is(':checkbox')) {\n var masterLabel = root.find('[for=\"' + masterElement.attr('id') + '\"]');\n if (masterLabel.length) {\n if (masterLabel.html() !== targetString) {\n masterLabel.html(targetString);\n }\n }\n } else {\n masterElement.text(targetString);\n // Set the checkall data attribute.\n masterElement.data('checkall', targetState ? 0 : 1);\n }\n });\n };\n\n /**\n * Registers the event listeners.\n *\n * @private\n */\n var registerListeners = function() {\n if (!registered) {\n registered = true;\n\n var root = $(document.body);\n root.on('click', '[data-action=\"toggle\"][data-toggle=\"master\"]', {root: root}, toggleSlavesFromMasters);\n root.on('change', '[data-action=\"toggle\"][data-toggle=\"slave\"]', {root: root}, toggleMastersFromSlaves);\n }\n };\n\n return {\n init: function() {\n registerListeners();\n },\n events: events,\n };\n});\n"],"file":"checkbox-toggleall.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/checkbox-toggleall.js"],"names":["define","$","PubSub","registered","events","checkboxToggled","getToggleGroupElements","root","toggleGroup","exactMatch","find","getAllSlaveCheckboxes","filter","getControlCheckboxes","getActionElements","toggleSlavesFromMasters","e","data","target","toggleGroupName","targetState","is","toggleSlavesToState","slaves","checkedSlaves","setMasterStates","prop","trigger","publish","anyChecked","toggleMastersFromSlaves","toggleGroups","split","toggleGroupLevels","toggleGroupLevel","forEach","push","trim","length","setActionElementStates","disableActionElements","masters","each","i","masterElement","targetString","masterLabel","attr","html","text","registerListeners","document","body","on","init","setGroupState","updateSlavesFromMasterState"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A module to help with toggle select/deselect all.\n *\n * @module core/checkbox-toggleall\n * @copyright 2019 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/pubsub'], function($, PubSub) {\n\n /**\n * Whether event listeners have already been registered.\n *\n * @private\n * @type {boolean}\n */\n var registered = false;\n\n /**\n * List of custom events that this module publishes.\n *\n * @private\n * @type {{checkboxToggled: string}}\n */\n var events = {\n checkboxToggled: 'core/checkbox-toggleall:checkboxToggled',\n };\n\n /**\n * Fetches elements that are member of a given toggle group.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroup The toggle group name that we're searching form.\n * @param {boolean} exactMatch Whether we want an exact match we just want to match toggle groups that start with the given\n * toggle group name.\n * @returns {jQuery} The elements matching the given toggle group.\n */\n var getToggleGroupElements = function(root, toggleGroup, exactMatch) {\n if (exactMatch) {\n return root.find('[data-action=\"toggle\"][data-togglegroup=\"' + toggleGroup + '\"]');\n } else {\n return root.find('[data-action=\"toggle\"][data-togglegroup^=\"' + toggleGroup + '\"]');\n }\n };\n\n /**\n * Fetches the slave checkboxes for a given toggle group.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroup The toggle group name.\n * @returns {jQuery} The slave checkboxes belonging to the toggle group.\n */\n var getAllSlaveCheckboxes = function(root, toggleGroup) {\n return getToggleGroupElements(root, toggleGroup, false).filter('[data-toggle=\"slave\"]');\n };\n\n /**\n * Fetches the master elements (checkboxes or buttons) that control the slave checkboxes in a given toggle group.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroup The toggle group name.\n * @param {boolean} exactMatch\n * @returns {jQuery} The control elements belonging to the toggle group.\n */\n var getControlCheckboxes = function(root, toggleGroup, exactMatch) {\n return getToggleGroupElements(root, toggleGroup, exactMatch).filter('[data-toggle=\"master\"]');\n };\n\n /**\n * Fetches the action elements that perform actions on the selected checkboxes in a given toggle group.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroup The toggle group name.\n * @returns {jQuery} The action elements belonging to the toggle group.\n */\n var getActionElements = function(root, toggleGroup) {\n return getToggleGroupElements(root, toggleGroup, true).filter('[data-toggle=\"action\"]');\n };\n\n /**\n * Toggles the slave checkboxes in a given toggle group when a master element in that toggle group is toggled.\n *\n * @private\n * @param {Object} e The event object.\n */\n var toggleSlavesFromMasters = function(e) {\n var root = e.data.root;\n var target = $(e.target);\n\n var toggleGroupName = target.data('togglegroup');\n var targetState;\n if (target.is(':checkbox')) {\n targetState = target.is(':checked');\n } else {\n targetState = target.data('checkall') === 1;\n }\n\n toggleSlavesToState(root, toggleGroupName, targetState);\n };\n\n /**\n * Toggles the slave checkboxes from the masters.\n *\n * @param {HTMLElement} root\n * @param {String} toggleGroupName\n */\n var updateSlavesFromMasterState = function(root, toggleGroupName) {\n // Normalise to jQuery Object.\n root = $(root);\n\n var target = getControlCheckboxes(root, toggleGroupName, false);\n var targetState;\n if (target.is(':checkbox')) {\n targetState = target.is(':checked');\n } else {\n targetState = target.data('checkall') === 1;\n }\n\n toggleSlavesToState(root, toggleGroupName, targetState);\n };\n\n /**\n * Toggles the slave checkboxes to a specific state.\n *\n * @param {HTMLElement} root\n * @param {String} toggleGroupName\n * @param {Bool} targetState\n */\n var toggleSlavesToState = function(root, toggleGroupName, targetState) {\n var slaves = getAllSlaveCheckboxes(root, toggleGroupName);\n var checkedSlaves = slaves.filter(':checked');\n\n setMasterStates(root, toggleGroupName, targetState, false);\n\n // Set the slave checkboxes from the masters.\n slaves.prop('checked', targetState);\n // Trigger 'change' event to toggle other master checkboxes (e.g. parent master checkboxes) and action elements.\n slaves.trigger('change');\n\n PubSub.publish(events.checkboxToggled, {\n root: root,\n toggleGroupName: toggleGroupName,\n slaves: slaves,\n checkedSlaves: checkedSlaves,\n anyChecked: targetState,\n });\n };\n\n /**\n * Set the state for an entire group of checkboxes.\n *\n * @param {HTMLElement} root\n * @param {String} toggleGroupName\n * @param {Bool} targetState\n */\n var setGroupState = function(root, toggleGroupName, targetState) {\n // Normalise to jQuery Object.\n root = $(root);\n\n // Set the master and slaves.\n setMasterStates(root, toggleGroupName, targetState, true);\n toggleSlavesToState(root, toggleGroupName, targetState);\n };\n\n /**\n * Toggles the master checkboxes in a given toggle group when all or none of the slave checkboxes in the same toggle group\n * have been selected.\n *\n * @private\n * @param {Object} e The event object.\n */\n var toggleMastersFromSlaves = function(e) {\n var root = e.data.root;\n var target = $(e.target);\n\n var toggleGroups = target.data('togglegroup').split(' ');\n var toggleGroupLevels = [];\n var toggleGroupLevel = '';\n toggleGroups.forEach(function(toggleGroupName) {\n toggleGroupLevel += ' ' + toggleGroupName;\n toggleGroupLevels.push(toggleGroupLevel.trim());\n });\n\n toggleGroupLevels.forEach(function(toggleGroupName) {\n var slaves = getAllSlaveCheckboxes(root, toggleGroupName);\n var checkedSlaves = slaves.filter(':checked');\n var targetState = (slaves.length === checkedSlaves.length);\n\n // Make sure to toggle the exact master checkbox.\n setMasterStates(root, toggleGroupName, targetState, true);\n\n // Enable action elements when there's at least one checkbox checked. Disable otherwise.\n setActionElementStates(root, toggleGroupName, !checkedSlaves.length);\n\n PubSub.publish(events.checkboxToggled, {\n root: root,\n toggleGroupName: toggleGroupName,\n slaves: slaves,\n checkedSlaves: checkedSlaves,\n anyChecked: !!checkedSlaves.length,\n });\n });\n };\n\n /**\n * Enables or disables the action elements.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroupName The toggle group name of the action element(s).\n * @param {boolean} disableActionElements Whether to disable or to enable the action elements.\n */\n var setActionElementStates = function(root, toggleGroupName, disableActionElements) {\n getActionElements(root, toggleGroupName).prop('disabled', disableActionElements);\n };\n\n /**\n * Selects or deselects the master elements.\n *\n * @private\n * @param {jQuery} root The root jQuery element.\n * @param {string} toggleGroupName The toggle group name of the master element(s).\n * @param {boolean} targetState Whether to select (true) or deselect (false).\n * @param {boolean} exactMatch Whether to do an exact match for the toggle group name or not.\n */\n var setMasterStates = function(root, toggleGroupName, targetState, exactMatch) {\n // Set the master checkboxes value and ARIA labels..\n var masters = getControlCheckboxes(root, toggleGroupName, exactMatch);\n masters.prop('checked', targetState);\n masters.each(function(i, masterElement) {\n masterElement = $(masterElement);\n\n var targetString;\n if (targetState) {\n targetString = masterElement.data('toggle-deselectall');\n } else {\n targetString = masterElement.data('toggle-selectall');\n }\n\n if (masterElement.is(':checkbox')) {\n var masterLabel = root.find('[for=\"' + masterElement.attr('id') + '\"]');\n if (masterLabel.length) {\n if (masterLabel.html() !== targetString) {\n masterLabel.html(targetString);\n }\n }\n } else {\n masterElement.text(targetString);\n // Set the checkall data attribute.\n masterElement.data('checkall', targetState ? 0 : 1);\n }\n });\n };\n\n /**\n * Registers the event listeners.\n *\n * @private\n */\n var registerListeners = function() {\n if (!registered) {\n registered = true;\n\n var root = $(document.body);\n root.on('click', '[data-action=\"toggle\"][data-toggle=\"master\"]', {root: root}, toggleSlavesFromMasters);\n root.on('change', '[data-action=\"toggle\"][data-toggle=\"slave\"]', {root: root}, toggleMastersFromSlaves);\n }\n };\n\n return {\n init: function() {\n registerListeners();\n },\n events: events,\n setGroupState: setGroupState,\n updateSlavesFromMasterState: updateSlavesFromMasterState,\n };\n});\n"],"file":"checkbox-toggleall.min.js"} \ No newline at end of file diff --git a/lib/amd/src/checkbox-toggleall.js b/lib/amd/src/checkbox-toggleall.js index 345db8ffaa4..473a514ad9c 100644 --- a/lib/amd/src/checkbox-toggleall.js +++ b/lib/amd/src/checkbox-toggleall.js @@ -113,6 +113,38 @@ define(['jquery', 'core/pubsub'], function($, PubSub) { targetState = target.data('checkall') === 1; } + toggleSlavesToState(root, toggleGroupName, targetState); + }; + + /** + * Toggles the slave checkboxes from the masters. + * + * @param {HTMLElement} root + * @param {String} toggleGroupName + */ + var updateSlavesFromMasterState = function(root, toggleGroupName) { + // Normalise to jQuery Object. + root = $(root); + + var target = getControlCheckboxes(root, toggleGroupName, false); + var targetState; + if (target.is(':checkbox')) { + targetState = target.is(':checked'); + } else { + targetState = target.data('checkall') === 1; + } + + toggleSlavesToState(root, toggleGroupName, targetState); + }; + + /** + * Toggles the slave checkboxes to a specific state. + * + * @param {HTMLElement} root + * @param {String} toggleGroupName + * @param {Bool} targetState + */ + var toggleSlavesToState = function(root, toggleGroupName, targetState) { var slaves = getAllSlaveCheckboxes(root, toggleGroupName); var checkedSlaves = slaves.filter(':checked'); @@ -132,6 +164,22 @@ define(['jquery', 'core/pubsub'], function($, PubSub) { }); }; + /** + * Set the state for an entire group of checkboxes. + * + * @param {HTMLElement} root + * @param {String} toggleGroupName + * @param {Bool} targetState + */ + var setGroupState = function(root, toggleGroupName, targetState) { + // Normalise to jQuery Object. + root = $(root); + + // Set the master and slaves. + setMasterStates(root, toggleGroupName, targetState, true); + toggleSlavesToState(root, toggleGroupName, targetState); + }; + /** * Toggles the master checkboxes in a given toggle group when all or none of the slave checkboxes in the same toggle group * have been selected. @@ -242,5 +290,7 @@ define(['jquery', 'core/pubsub'], function($, PubSub) { registerListeners(); }, events: events, + setGroupState: setGroupState, + updateSlavesFromMasterState: updateSlavesFromMasterState, }; }); From ed23725bc64b1fa8f661af8cd5f2d9dc3c1b8c0a Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Fri, 24 Apr 2020 12:33:36 +0800 Subject: [PATCH 4/6] MDL-68463 user: Update select/show [count] links dynamically --- lang/en/moodle.php | 4 +- lang/en/user.php | 1 + lib/tablelib.php | 1 + user/amd/build/participants.min.js | 2 +- user/amd/build/participants.min.js.map | 2 +- user/amd/src/participants.js | 126 ++++++++++++++++-- user/index.php | 53 +++++--- .../behat/filter_participants_showall.feature | 4 +- 8 files changed, 159 insertions(+), 34 deletions(-) diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 4885cc4598e..484eb5264e3 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1523,7 +1523,6 @@ $string['parentcategory'] = 'Parent category'; $string['parentcoursenotfound'] = 'Parent course not found!'; $string['parentfolder'] = 'Parent folder'; $string['participants'] = 'Participants'; -$string['participantscount'] = 'Number of participants: {$a}'; $string['participantslist'] = 'Participants list'; $string['participationratio'] = 'Participation ratio'; $string['participationreport'] = 'Participation report'; @@ -2290,3 +2289,6 @@ $string['messagedselectedcountusersfailed'] = 'A problem occurred and {$a} messa $string['messagedselecteduserfailed'] = 'The message was not sent to user {$a->fullname}.'; $string['previewhtml'] = 'HTML format preview'; $string['sitemessage'] = 'Message users'; + +// Deprecated since Moodle 3.9. +$string['participantscount'] = 'Number of participants: {$a}'; diff --git a/lang/en/user.php b/lang/en/user.php index 61600142b70..e010e0948e1 100644 --- a/lang/en/user.php +++ b/lang/en/user.php @@ -22,6 +22,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +$string['countparticipantsfound'] = '{$a} participants found'; $string['privacy:courserequestpath'] = 'Requested courses'; $string['privacy:descriptionpath'] = 'Profile description'; $string['privacy:devicespath'] = 'User devices'; diff --git a/lib/tablelib.php b/lib/tablelib.php index 2c46b2fd84a..997a96520b3 100644 --- a/lib/tablelib.php +++ b/lib/tablelib.php @@ -1658,6 +1658,7 @@ class flexible_table { 'data-table-page-number' => $this->currpage + 1, 'data-table-page-size' => $this->pagesize, 'data-table-hidden-columns' => json_encode(array_keys($this->prefs['collapse'])), + 'data-table-total-rows' => $this->totalrows, ]); } diff --git a/user/amd/build/participants.min.js b/user/amd/build/participants.min.js index f433003ac95..1d61489ed05 100644 --- a/user/amd/build/participants.min.js +++ b/user/amd/build/participants.min.js @@ -1,2 +1,2 @@ -define ("core_user/participants",["exports","core_table/local/dynamic/selectors","core/modal_events","core/notification","core/custom_interaction_events","core_user/local/participants/bulkactions"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=g(b);c=g(c);d=g(d);e=g(e);function g(a){return a&&a.__esModule?a:{default:a}}var h={bulkActionSelect:"#formactionid",bulkUserCheckBoxes:"input.usercheckbox",bulkUserSelectedCheckBoxes:"input.usercheckbox:checked",checkAllButton:"#checkall",stateHelpIcon:"[data-region=\"state-help-icon\"]",tableForm:function tableForm(a){return"form[data-table-unique-id=\"".concat(a,"\"]")}};a.init=function init(a){var g=a.uniqueid,i=a.noteStateNames,j=void 0===i?{}:i,k=document.querySelector(h.tableForm(g)),l=function(a){return k.querySelector(b.default.main.fromRegionId(a))},m=function(){k.querySelector(h.bulkActionSelect).addEventListener(e.default.events.accessibleChange,function(a){var b=a.target.value,e=l(g),i=e.querySelectorAll(h.bulkUserSelectedCheckBoxes);if(-1!==b.indexOf("#")){a.preventDefault();var m=[];i.forEach(function(a){m.push(a.getAttribute("name").replace("user",""))});var o;if("#messageselect"===b){o=(0,f.showSendMessage)(m)}else if("#addgroupnote"===b){o=(0,f.showAddNote)(k.dataset.courseId,m,j,k.querySelector(h.stateHelpIcon))}if(o){o.then(function(a){a.getRoot().on(c.default.hidden,function(){var a=k.querySelector(h.bulkActionSelect);a.focus()});return a}).catch(d.default.exception)}}else if(""!==b&&i.length){a.target.form.submit()}n(a.target)});k.addEventListener("click",function(a){var b=a.target.closest(h.checkAllButton);if(b){var c=b.dataset.showalllink;if(c){window.location=c}}})},n=function(a){a.value=""};m()}}); +function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_user/participants",["exports","core_table/dynamic","core/str","core/checkbox-toggleall","core/custom_interaction_events","core_table/local/dynamic/selectors","core/modal_events","core/notification","jquery","core_user/local/participants/bulkactions"],function(a,b,c,d,e,f,g,h,i,j){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=m(b);c=m(c);d=k(d);e=k(e);f=k(f);g=k(g);h=k(h);i=k(i);function k(a){return a&&a.__esModule?a:{default:a}}function l(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;l=function(){return a};return a}function m(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=l();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function n(a,b){return s(a)||r(a,b)||p(a,b)||o()}function o(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function p(a,b){if(!a)return;if("string"==typeof a)return q(a,b);var c=Object.prototype.toString.call(a).slice(8,-1);if("Object"===c&&a.constructor)c=a.constructor.name;if("Map"===c||"Set"===c)return Array.from(c);if("Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c))return q(a,b)}function q(a,b){if(null==b||b>a.length)b=a.length;for(var c=0,d=Array(b);c.\n\n/**\n * Some UI stuff for participants page.\n * This is also used by the report/participants/index.php because it has the same functionality.\n *\n * @module core_user/participants\n * @package core_user\n * @copyright 2017 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DynamicTableSelectors from 'core_table/local/dynamic/selectors';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {showAddNote, showSendMessage} from 'core_user/local/participants/bulkactions';\n\nconst Selectors = {\n bulkActionSelect: \"#formactionid\",\n bulkUserCheckBoxes: \"input.usercheckbox\",\n bulkUserSelectedCheckBoxes: \"input.usercheckbox:checked\",\n checkAllButton: \"#checkall\",\n stateHelpIcon: '[data-region=\"state-help-icon\"]',\n tableForm: uniqueId => `form[data-table-unique-id=\"${uniqueId}\"]`,\n};\n\nexport const init = ({\n uniqueid,\n noteStateNames = {},\n}) => {\n const root = document.querySelector(Selectors.tableForm(uniqueid));\n const getTableFromUniqueId = uniqueId => root.querySelector(DynamicTableSelectors.main.fromRegionId(uniqueId));\n\n /**\n * Private method.\n *\n * @method registerEventListeners\n * @private\n */\n const registerEventListeners = () => {\n root.querySelector(Selectors.bulkActionSelect).addEventListener(CustomEvents.events.accessibleChange, e => {\n const action = e.target.value;\n const tableRoot = getTableFromUniqueId(uniqueid);\n const checkboxes = tableRoot.querySelectorAll(Selectors.bulkUserSelectedCheckBoxes);\n\n if (action.indexOf('#') !== -1) {\n e.preventDefault();\n\n const ids = [];\n checkboxes.forEach(checkbox => {\n ids.push(checkbox.getAttribute('name').replace('user', ''));\n });\n\n let bulkAction;\n if (action === '#messageselect') {\n bulkAction = showSendMessage(ids);\n } else if (action === '#addgroupnote') {\n bulkAction = showAddNote(\n root.dataset.courseId,\n ids,\n noteStateNames,\n root.querySelector(Selectors.stateHelpIcon)\n );\n }\n\n if (bulkAction) {\n bulkAction\n .then(modal => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Focus on the action select when the dialog is closed.\n const bulkActionSelector = root.querySelector(Selectors.bulkActionSelect);\n bulkActionSelector.focus();\n });\n\n return modal;\n })\n .catch(Notification.exception);\n }\n } else if (action !== '' && checkboxes.length) {\n e.target.form.submit();\n }\n\n resetBulkAction(e.target);\n });\n\n root.addEventListener('click', e => {\n const checkAllButton = e.target.closest(Selectors.checkAllButton);\n if (checkAllButton) {\n const showAllLink = checkAllButton.dataset.showalllink;\n if (showAllLink) {\n window.location = showAllLink;\n }\n }\n });\n };\n\n const resetBulkAction = bulkActionSelect => {\n bulkActionSelect.value = '';\n };\n\n registerEventListeners();\n};\n"],"file":"participants.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/participants.js"],"names":["Selectors","bulkActionSelect","bulkUserSelectedCheckBoxes","checkCountButton","showCountText","showCountToggle","stateHelpIcon","tableForm","uniqueId","init","uniqueid","noteStateNames","root","document","querySelector","getTableFromUniqueId","DynamicTableSelectors","main","fromRegionId","registerEventListeners","CustomEvents","define","events","accessibleChange","on","e","action","target","value","tableRoot","checkboxes","querySelectorAll","indexOf","preventDefault","ids","forEach","checkbox","push","getAttribute","replace","bulkAction","dataset","courseId","then","modal","getRoot","ModalEvents","hidden","bulkActionSelector","focus","catch","Notification","exception","length","form","submit","resetBulkAction","addEventListener","showCountLink","showCountLinkClicked","contains","checkCountButtonClicked","DynamicTable","setPageSize","targetPageSize","CheckboxToggleAll","setGroupState","Events","tableContentRefreshed","defaultPageSize","parseInt","tableDefaultPerPage","currentPageSize","tablePageSize","totalRowCount","tableTotalRows","updateSlavesFromMasterState","pageCountStrings","key","component","param","classList","add","remove","Str","get_strings","showingParticipantCountString","showCountString","selectCountString","showingParticipantCount","innerHTML"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Some UI stuff for participants page.\n * This is also used by the report/participants/index.php because it has the same functionality.\n *\n * @module core_user/participants\n * @package core_user\n * @copyright 2017 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as DynamicTable from 'core_table/dynamic';\nimport * as Str from 'core/str';\nimport CheckboxToggleAll from 'core/checkbox-toggleall';\nimport CustomEvents from 'core/custom_interaction_events';\nimport DynamicTableSelectors from 'core_table/local/dynamic/selectors';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport jQuery from 'jquery';\nimport {showAddNote, showSendMessage} from 'core_user/local/participants/bulkactions';\n\nconst Selectors = {\n bulkActionSelect: \"#formactionid\",\n bulkUserSelectedCheckBoxes: \"input[data-togglegroup='participants-table']:checked\",\n checkCountButton: \"#checkall\",\n showCountText: '[data-region=\"participant-count\"]',\n showCountToggle: '[data-action=\"showcount\"]',\n stateHelpIcon: '[data-region=\"state-help-icon\"]',\n tableForm: uniqueId => `form[data-table-unique-id=\"${uniqueId}\"]`,\n};\n\nexport const init = ({\n uniqueid,\n noteStateNames = {},\n}) => {\n const root = document.querySelector(Selectors.tableForm(uniqueid));\n const getTableFromUniqueId = uniqueId => root.querySelector(DynamicTableSelectors.main.fromRegionId(uniqueId));\n\n /**\n * Private method.\n *\n * @method registerEventListeners\n * @private\n */\n const registerEventListeners = () => {\n CustomEvents.define(Selectors.bulkActionSelect, [CustomEvents.events.accessibleChange]);\n jQuery(Selectors.bulkActionSelect).on(CustomEvents.events.accessibleChange, e => {\n const action = e.target.value;\n const tableRoot = getTableFromUniqueId(uniqueid);\n const checkboxes = tableRoot.querySelectorAll(Selectors.bulkUserSelectedCheckBoxes);\n\n if (action.indexOf('#') !== -1) {\n e.preventDefault();\n\n const ids = [];\n checkboxes.forEach(checkbox => {\n ids.push(checkbox.getAttribute('name').replace('user', ''));\n });\n\n let bulkAction;\n if (action === '#messageselect') {\n bulkAction = showSendMessage(ids);\n } else if (action === '#addgroupnote') {\n bulkAction = showAddNote(\n root.dataset.courseId,\n ids,\n noteStateNames,\n root.querySelector(Selectors.stateHelpIcon)\n );\n }\n\n if (bulkAction) {\n bulkAction\n .then(modal => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Focus on the action select when the dialog is closed.\n const bulkActionSelector = root.querySelector(Selectors.bulkActionSelect);\n bulkActionSelector.focus();\n });\n\n return modal;\n })\n .catch(Notification.exception);\n }\n } else if (action !== '' && checkboxes.length) {\n e.target.form.submit();\n }\n\n resetBulkAction(e.target);\n });\n\n root.addEventListener('click', e => {\n // Handle clicking of the \"Show [all|count]\" and \"Select all\" actions.\n const showCountLink = root.querySelector(Selectors.showCountToggle);\n const checkCountButton = root.querySelector(Selectors.checkCountButton);\n\n const showCountLinkClicked = showCountLink && showCountLink.contains(e.target);\n const checkCountButtonClicked = checkCountButton && checkCountButton.contains(e.target);\n\n if (showCountLinkClicked || checkCountButtonClicked) {\n e.preventDefault();\n\n const tableRoot = getTableFromUniqueId(uniqueid);\n\n DynamicTable.setPageSize(tableRoot, showCountLink.dataset.targetPageSize)\n .then(tableRoot => {\n // Always update the toggle state.\n // This ensures that the bulk actions are disabled after changing the page size.\n CheckboxToggleAll.setGroupState(tableRoot, 'participants-table', checkCountButtonClicked);\n\n return tableRoot;\n })\n .catch(Notification.exception);\n }\n });\n\n // When the content is refreshed, update the row counts in various places.\n root.addEventListener(DynamicTable.Events.tableContentRefreshed, e => {\n const showCountLink = root.querySelector(Selectors.showCountToggle);\n const checkCountButton = root.querySelector(Selectors.checkCountButton);\n\n const tableRoot = e.target;\n\n const defaultPageSize = parseInt(root.dataset.tableDefaultPerPage, 10);\n const currentPageSize = parseInt(tableRoot.dataset.tablePageSize, 10);\n const totalRowCount = parseInt(tableRoot.dataset.tableTotalRows, 10);\n\n CheckboxToggleAll.updateSlavesFromMasterState(tableRoot, 'participants-table');\n\n const pageCountStrings = [\n {\n key: 'countparticipantsfound',\n component: 'core_user',\n param: totalRowCount,\n },\n ];\n\n\n if (totalRowCount <= defaultPageSize) {\n // There are fewer than the default page count numbers of rows.\n showCountLink.classList.add('hidden');\n checkCountButton.classList.add('hidden');\n } else if (totalRowCount <= currentPageSize) {\n // The are fewer than the current page size.\n pageCountStrings.push({\n key: 'showperpage',\n component: 'core',\n param: defaultPageSize,\n });\n\n pageCountStrings.push({\n key: 'selectalluserswithcount',\n component: 'core',\n param: defaultPageSize,\n });\n\n // Show the 'Show [x]' link.\n showCountLink.classList.remove('hidden');\n showCountLink.dataset.targetPageSize = defaultPageSize;\n\n // The 'Check all [x]' button is only visible when there are values to set.\n checkCountButton.classList.add('hidden');\n } else {\n pageCountStrings.push({\n key: 'showall',\n component: 'core',\n param: totalRowCount,\n });\n\n pageCountStrings.push({\n key: 'selectalluserswithcount',\n component: 'core',\n param: totalRowCount,\n });\n\n // Show both the 'Show [x]' link, and the 'Check all [x]' button.\n showCountLink.classList.remove('hidden');\n showCountLink.dataset.targetPageSize = totalRowCount;\n checkCountButton.classList.remove('hidden');\n }\n\n Str.get_strings(pageCountStrings)\n .then(([showingParticipantCountString, showCountString, selectCountString]) => {\n const showingParticipantCount = root.querySelector(Selectors.showCountText);\n showingParticipantCount.innerHTML = showingParticipantCountString;\n\n if (showCountString) {\n showCountLink.innerHTML = showCountString;\n }\n\n if (selectCountString) {\n checkCountButton.value = selectCountString;\n }\n\n return;\n })\n .catch(Notification.exception);\n });\n };\n\n const resetBulkAction = bulkActionSelect => {\n bulkActionSelect.value = '';\n };\n\n registerEventListeners();\n};\n"],"file":"participants.min.js"} \ No newline at end of file diff --git a/user/amd/src/participants.js b/user/amd/src/participants.js index eba586aae00..a5e27045a6d 100644 --- a/user/amd/src/participants.js +++ b/user/amd/src/participants.js @@ -23,17 +23,22 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +import * as DynamicTable from 'core_table/dynamic'; +import * as Str from 'core/str'; +import CheckboxToggleAll from 'core/checkbox-toggleall'; +import CustomEvents from 'core/custom_interaction_events'; import DynamicTableSelectors from 'core_table/local/dynamic/selectors'; import ModalEvents from 'core/modal_events'; import Notification from 'core/notification'; -import CustomEvents from 'core/custom_interaction_events'; +import jQuery from 'jquery'; import {showAddNote, showSendMessage} from 'core_user/local/participants/bulkactions'; const Selectors = { bulkActionSelect: "#formactionid", - bulkUserCheckBoxes: "input.usercheckbox", - bulkUserSelectedCheckBoxes: "input.usercheckbox:checked", - checkAllButton: "#checkall", + bulkUserSelectedCheckBoxes: "input[data-togglegroup='participants-table']:checked", + checkCountButton: "#checkall", + showCountText: '[data-region="participant-count"]', + showCountToggle: '[data-action="showcount"]', stateHelpIcon: '[data-region="state-help-icon"]', tableForm: uniqueId => `form[data-table-unique-id="${uniqueId}"]`, }; @@ -52,7 +57,8 @@ export const init = ({ * @private */ const registerEventListeners = () => { - root.querySelector(Selectors.bulkActionSelect).addEventListener(CustomEvents.events.accessibleChange, e => { + CustomEvents.define(Selectors.bulkActionSelect, [CustomEvents.events.accessibleChange]); + jQuery(Selectors.bulkActionSelect).on(CustomEvents.events.accessibleChange, e => { const action = e.target.value; const tableRoot = getTableFromUniqueId(uniqueid); const checkboxes = tableRoot.querySelectorAll(Selectors.bulkUserSelectedCheckBoxes); @@ -98,14 +104,112 @@ export const init = ({ }); root.addEventListener('click', e => { - const checkAllButton = e.target.closest(Selectors.checkAllButton); - if (checkAllButton) { - const showAllLink = checkAllButton.dataset.showalllink; - if (showAllLink) { - window.location = showAllLink; - } + // Handle clicking of the "Show [all|count]" and "Select all" actions. + const showCountLink = root.querySelector(Selectors.showCountToggle); + const checkCountButton = root.querySelector(Selectors.checkCountButton); + + const showCountLinkClicked = showCountLink && showCountLink.contains(e.target); + const checkCountButtonClicked = checkCountButton && checkCountButton.contains(e.target); + + if (showCountLinkClicked || checkCountButtonClicked) { + e.preventDefault(); + + const tableRoot = getTableFromUniqueId(uniqueid); + + DynamicTable.setPageSize(tableRoot, showCountLink.dataset.targetPageSize) + .then(tableRoot => { + // Always update the toggle state. + // This ensures that the bulk actions are disabled after changing the page size. + CheckboxToggleAll.setGroupState(tableRoot, 'participants-table', checkCountButtonClicked); + + return tableRoot; + }) + .catch(Notification.exception); } }); + + // When the content is refreshed, update the row counts in various places. + root.addEventListener(DynamicTable.Events.tableContentRefreshed, e => { + const showCountLink = root.querySelector(Selectors.showCountToggle); + const checkCountButton = root.querySelector(Selectors.checkCountButton); + + const tableRoot = e.target; + + const defaultPageSize = parseInt(root.dataset.tableDefaultPerPage, 10); + const currentPageSize = parseInt(tableRoot.dataset.tablePageSize, 10); + const totalRowCount = parseInt(tableRoot.dataset.tableTotalRows, 10); + + CheckboxToggleAll.updateSlavesFromMasterState(tableRoot, 'participants-table'); + + const pageCountStrings = [ + { + key: 'countparticipantsfound', + component: 'core_user', + param: totalRowCount, + }, + ]; + + + if (totalRowCount <= defaultPageSize) { + // There are fewer than the default page count numbers of rows. + showCountLink.classList.add('hidden'); + checkCountButton.classList.add('hidden'); + } else if (totalRowCount <= currentPageSize) { + // The are fewer than the current page size. + pageCountStrings.push({ + key: 'showperpage', + component: 'core', + param: defaultPageSize, + }); + + pageCountStrings.push({ + key: 'selectalluserswithcount', + component: 'core', + param: defaultPageSize, + }); + + // Show the 'Show [x]' link. + showCountLink.classList.remove('hidden'); + showCountLink.dataset.targetPageSize = defaultPageSize; + + // The 'Check all [x]' button is only visible when there are values to set. + checkCountButton.classList.add('hidden'); + } else { + pageCountStrings.push({ + key: 'showall', + component: 'core', + param: totalRowCount, + }); + + pageCountStrings.push({ + key: 'selectalluserswithcount', + component: 'core', + param: totalRowCount, + }); + + // Show both the 'Show [x]' link, and the 'Check all [x]' button. + showCountLink.classList.remove('hidden'); + showCountLink.dataset.targetPageSize = totalRowCount; + checkCountButton.classList.remove('hidden'); + } + + Str.get_strings(pageCountStrings) + .then(([showingParticipantCountString, showCountString, selectCountString]) => { + const showingParticipantCount = root.querySelector(Selectors.showCountText); + showingParticipantCount.innerHTML = showingParticipantCountString; + + if (showCountString) { + showCountLink.innerHTML = showCountString; + } + + if (selectCountString) { + checkCountButton.value = selectCountString; + } + + return; + }) + .catch(Notification.exception); + }); }; const resetBulkAction = bulkActionSelect => { diff --git a/user/index.php b/user/index.php index 4aa5fcb647f..b542c1a4227 100644 --- a/user/index.php +++ b/user/index.php @@ -286,8 +286,6 @@ $participanttable->out($perpage, true); $participanttablehtml = ob_get_contents(); ob_end_clean(); -echo html_writer::tag('p', get_string('participantscount', 'moodle', $participanttable->totalrows)); - if ($bulkoperations) { echo html_writer::start_tag('form', [ 'action' => 'action_redir.php', @@ -295,45 +293,64 @@ if ($bulkoperations) { 'id' => 'participantsform', 'data-course-id' => $course->id, 'data-table-unique-id' => $participanttable->uniqueid, + 'data-table-default-per-page' => DEFAULT_PAGE_SIZE, ]); echo '
'; echo ''; echo ''; } +echo html_writer::tag( + 'p', + get_string('countparticipantsfound', 'core_user', $participanttable->totalrows), + [ + 'data-region' => 'participant-count', + ] +); + echo $participanttablehtml; $perpageurl = clone($baseurl); $perpageurl->remove_params('perpage'); if ($perpage == SHOW_ALL_PAGE_SIZE && $participanttable->totalrows > DEFAULT_PAGE_SIZE) { $perpageurl->param('perpage', DEFAULT_PAGE_SIZE); - echo $OUTPUT->container(html_writer::link($perpageurl, get_string('showperpage', '', DEFAULT_PAGE_SIZE)), array(), 'showall'); + echo $OUTPUT->container(html_writer::link( + $perpageurl, + get_string('showperpage', '', DEFAULT_PAGE_SIZE), + [ + 'data-action' => 'showcount', + 'data-target-page-size' => DEFAULT_PAGE_SIZE, + ] + ), [], 'showall'); } else if ($participanttable->get_page_size() < $participanttable->totalrows) { $perpageurl->param('perpage', SHOW_ALL_PAGE_SIZE); - echo $OUTPUT->container(html_writer::link($perpageurl, get_string('showall', '', $participanttable->totalrows)), - array(), 'showall'); + echo $OUTPUT->container(html_writer::link( + $perpageurl, + get_string('showall', '', $participanttable->totalrows), + [ + 'data-action' => 'showcount', + 'data-target-page-size' => SHOW_ALL_PAGE_SIZE, + ] + ), + [], 'showall'); } if ($bulkoperations) { echo '
'; - if ($participanttable->get_page_size() < $participanttable->totalrows) { - $perpageurl = clone($baseurl); - $perpageurl->remove_params('perpage'); - $perpageurl->param('perpage', SHOW_ALL_PAGE_SIZE); - $perpageurl->param('selectall', true); - $showalllink = $perpageurl; - } else { - $showalllink = false; - } - echo html_writer::start_tag('div', array('class' => 'btn-group')); + if ($participanttable->get_page_size() < $participanttable->totalrows) { - // Select all users, refresh page showing all users and mark them all selected. + // Select all users, refresh table showing all users and mark them all selected. $label = get_string('selectalluserswithcount', 'moodle', $participanttable->totalrows); - echo html_writer::empty_tag('input', array('type' => 'button', 'id' => 'checkall', 'class' => 'btn btn-secondary', - 'value' => $label, 'data-showallink' => $showalllink)); + echo html_writer::empty_tag('input', [ + 'type' => 'button', + 'id' => 'checkall', + 'class' => 'btn btn-secondary', + 'value' => $label, + 'data-target-page-size' => $participanttable->totalrows, + ]); } echo html_writer::end_tag('div'); $displaylist = array(); diff --git a/user/tests/behat/filter_participants_showall.feature b/user/tests/behat/filter_participants_showall.feature index 6cec23e74d2..d50851f2186 100644 --- a/user/tests/behat/filter_participants_showall.feature +++ b/user/tests/behat/filter_participants_showall.feature @@ -86,7 +86,7 @@ Feature: Course participants can be filtered to display all the users And I click on "Role: Student" item in the autocomplete list And I click on "Show all 24" "link" Then I should see "Role: Student" - And I should see "Number of participants: 24" in the "//div[@class='userlist']" "xpath_element" + And I should see "24 participants found" And I should see "Show 20 per page" @javascript @@ -101,7 +101,7 @@ Feature: Course participants can be filtered to display all the users And I click on "Show all 23" "link" Then I should see "Role: Student" And I should see "Status: Active" - And I should see "Number of participants: 23" in the "//div[@class='userlist']" "xpath_element" + And I should see "23 participants found" And I should see "Student 1" And I should not see "Student 24" And I should see "Show 20 per page" From 07c9106476ae7dd1f56e338bc09c0a03b8fb0265 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Fri, 24 Apr 2020 14:50:30 +0800 Subject: [PATCH 5/6] MDL-68463 user: Remove old selectall attribute --- user/classes/table/participants.php | 22 +++------------------- user/index.php | 4 +--- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/user/classes/table/participants.php b/user/classes/table/participants.php index c1058cd15dd..04d47f56fb7 100644 --- a/user/classes/table/participants.php +++ b/user/classes/table/participants.php @@ -54,11 +54,6 @@ class participants extends \table_sql implements dynamic_table { */ protected $courseid; - /** - * @var bool $selectall Has the user selected all users on the page? - */ - protected $selectall; - /** * @var string[] The list of countries. */ @@ -134,10 +129,10 @@ class participants extends \table_sql implements dynamic_table { $mastercheckbox = new \core\output\checkbox_toggleall('participants-table', true, [ 'id' => 'select-all-participants', 'name' => 'select-all-participants', - 'label' => $this->selectall ? get_string('deselectall') : get_string('selectall'), + 'label' => get_string('selectall'), 'labelclasses' => 'sr-only', 'classes' => 'm-1', - 'checked' => $this->selectall + 'checked' => false, ]); $headers[] = $OUTPUT->render($mastercheckbox); $columns[] = 'select'; @@ -236,7 +231,7 @@ class participants extends \table_sql implements dynamic_table { 'classes' => 'usercheckbox m-1', 'id' => 'user' . $data->id, 'name' => 'user' . $data->id, - 'checked' => $this->selectall, + 'checked' => false, 'label' => get_string('selectitem', 'moodle', fullname($data)), 'labelclasses' => 'accesshide', ]); @@ -453,17 +448,6 @@ class participants extends \table_sql implements dynamic_table { return ''; } - /** - * Set the value for selectall. - * - * Note: This will be removed later in the 3.9 development cycle. - * - * @param bool $selectall - */ - public function set_selectall(bool $selectall): void { - $this->selectall = $selectall; - } - /** * Set filters and build table structure. * diff --git a/user/index.php b/user/index.php index b542c1a4227..579a04cd6d9 100644 --- a/user/index.php +++ b/user/index.php @@ -42,7 +42,6 @@ $perpage = optional_param('perpage', DEFAULT_PAGE_SIZE, PARAM_INT); // How $contextid = optional_param('contextid', 0, PARAM_INT); // One of this or. $courseid = optional_param('id', 0, PARAM_INT); // This are required. $newcourse = optional_param('newcourse', false, PARAM_BOOL); -$selectall = optional_param('selectall', false, PARAM_BOOL); // When rendering checkboxes against users mark them all checked. $roleid = optional_param('roleid', 0, PARAM_INT); $groupparam = optional_param('group', 0, PARAM_INT); @@ -276,7 +275,6 @@ if (count($keywordfilter)) { } $participanttable = new \core_user\table\participants("user-index-participants-{$course->id}"); -$participanttable->set_selectall($selectall); $participanttable->set_filterset($filterset); $participanttable->define_baseurl($baseurl); @@ -293,7 +291,7 @@ if ($bulkoperations) { 'id' => 'participantsform', 'data-course-id' => $course->id, 'data-table-unique-id' => $participanttable->uniqueid, - 'data-table-default-per-page' => DEFAULT_PAGE_SIZE, + 'data-table-default-per-page' => ($perpage < DEFAULT_PAGE_SIZE) ? $perpage : DEFAULT_PAGE_SIZE, ]); echo '
'; echo ''; From 50ba817fda0a5b629709dfdc153c4ee3cfe0553a Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 19 May 2020 12:18:15 +0800 Subject: [PATCH 6/6] MDL-68463 user: Show count link should always exist in DOM --- user/amd/build/participants.min.js | 2 +- user/amd/build/participants.min.js.map | 2 +- user/amd/src/participants.js | 18 ++++++++---- user/index.php | 38 ++++++++++++++------------ 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/user/amd/build/participants.min.js b/user/amd/build/participants.min.js index 1d61489ed05..b963d28dbbf 100644 --- a/user/amd/build/participants.min.js +++ b/user/amd/build/participants.min.js @@ -1,2 +1,2 @@ -function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_user/participants",["exports","core_table/dynamic","core/str","core/checkbox-toggleall","core/custom_interaction_events","core_table/local/dynamic/selectors","core/modal_events","core/notification","jquery","core_user/local/participants/bulkactions"],function(a,b,c,d,e,f,g,h,i,j){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=m(b);c=m(c);d=k(d);e=k(e);f=k(f);g=k(g);h=k(h);i=k(i);function k(a){return a&&a.__esModule?a:{default:a}}function l(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;l=function(){return a};return a}function m(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=l();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function n(a,b){return s(a)||r(a,b)||p(a,b)||o()}function o(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function p(a,b){if(!a)return;if("string"==typeof a)return q(a,b);var c=Object.prototype.toString.call(a).slice(8,-1);if("Object"===c&&a.constructor)c=a.constructor.name;if("Map"===c||"Set"===c)return Array.from(c);if("Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c))return q(a,b)}function q(a,b){if(null==b||b>a.length)b=a.length;for(var c=0,d=Array(b);ca.length)b=a.length;for(var c=0,d=Array(b);c.\n\n/**\n * Some UI stuff for participants page.\n * This is also used by the report/participants/index.php because it has the same functionality.\n *\n * @module core_user/participants\n * @package core_user\n * @copyright 2017 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as DynamicTable from 'core_table/dynamic';\nimport * as Str from 'core/str';\nimport CheckboxToggleAll from 'core/checkbox-toggleall';\nimport CustomEvents from 'core/custom_interaction_events';\nimport DynamicTableSelectors from 'core_table/local/dynamic/selectors';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport jQuery from 'jquery';\nimport {showAddNote, showSendMessage} from 'core_user/local/participants/bulkactions';\n\nconst Selectors = {\n bulkActionSelect: \"#formactionid\",\n bulkUserSelectedCheckBoxes: \"input[data-togglegroup='participants-table']:checked\",\n checkCountButton: \"#checkall\",\n showCountText: '[data-region=\"participant-count\"]',\n showCountToggle: '[data-action=\"showcount\"]',\n stateHelpIcon: '[data-region=\"state-help-icon\"]',\n tableForm: uniqueId => `form[data-table-unique-id=\"${uniqueId}\"]`,\n};\n\nexport const init = ({\n uniqueid,\n noteStateNames = {},\n}) => {\n const root = document.querySelector(Selectors.tableForm(uniqueid));\n const getTableFromUniqueId = uniqueId => root.querySelector(DynamicTableSelectors.main.fromRegionId(uniqueId));\n\n /**\n * Private method.\n *\n * @method registerEventListeners\n * @private\n */\n const registerEventListeners = () => {\n CustomEvents.define(Selectors.bulkActionSelect, [CustomEvents.events.accessibleChange]);\n jQuery(Selectors.bulkActionSelect).on(CustomEvents.events.accessibleChange, e => {\n const action = e.target.value;\n const tableRoot = getTableFromUniqueId(uniqueid);\n const checkboxes = tableRoot.querySelectorAll(Selectors.bulkUserSelectedCheckBoxes);\n\n if (action.indexOf('#') !== -1) {\n e.preventDefault();\n\n const ids = [];\n checkboxes.forEach(checkbox => {\n ids.push(checkbox.getAttribute('name').replace('user', ''));\n });\n\n let bulkAction;\n if (action === '#messageselect') {\n bulkAction = showSendMessage(ids);\n } else if (action === '#addgroupnote') {\n bulkAction = showAddNote(\n root.dataset.courseId,\n ids,\n noteStateNames,\n root.querySelector(Selectors.stateHelpIcon)\n );\n }\n\n if (bulkAction) {\n bulkAction\n .then(modal => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Focus on the action select when the dialog is closed.\n const bulkActionSelector = root.querySelector(Selectors.bulkActionSelect);\n bulkActionSelector.focus();\n });\n\n return modal;\n })\n .catch(Notification.exception);\n }\n } else if (action !== '' && checkboxes.length) {\n e.target.form.submit();\n }\n\n resetBulkAction(e.target);\n });\n\n root.addEventListener('click', e => {\n // Handle clicking of the \"Show [all|count]\" and \"Select all\" actions.\n const showCountLink = root.querySelector(Selectors.showCountToggle);\n const checkCountButton = root.querySelector(Selectors.checkCountButton);\n\n const showCountLinkClicked = showCountLink && showCountLink.contains(e.target);\n const checkCountButtonClicked = checkCountButton && checkCountButton.contains(e.target);\n\n if (showCountLinkClicked || checkCountButtonClicked) {\n e.preventDefault();\n\n const tableRoot = getTableFromUniqueId(uniqueid);\n\n DynamicTable.setPageSize(tableRoot, showCountLink.dataset.targetPageSize)\n .then(tableRoot => {\n // Always update the toggle state.\n // This ensures that the bulk actions are disabled after changing the page size.\n CheckboxToggleAll.setGroupState(tableRoot, 'participants-table', checkCountButtonClicked);\n\n return tableRoot;\n })\n .catch(Notification.exception);\n }\n });\n\n // When the content is refreshed, update the row counts in various places.\n root.addEventListener(DynamicTable.Events.tableContentRefreshed, e => {\n const showCountLink = root.querySelector(Selectors.showCountToggle);\n const checkCountButton = root.querySelector(Selectors.checkCountButton);\n\n const tableRoot = e.target;\n\n const defaultPageSize = parseInt(root.dataset.tableDefaultPerPage, 10);\n const currentPageSize = parseInt(tableRoot.dataset.tablePageSize, 10);\n const totalRowCount = parseInt(tableRoot.dataset.tableTotalRows, 10);\n\n CheckboxToggleAll.updateSlavesFromMasterState(tableRoot, 'participants-table');\n\n const pageCountStrings = [\n {\n key: 'countparticipantsfound',\n component: 'core_user',\n param: totalRowCount,\n },\n ];\n\n\n if (totalRowCount <= defaultPageSize) {\n // There are fewer than the default page count numbers of rows.\n showCountLink.classList.add('hidden');\n checkCountButton.classList.add('hidden');\n } else if (totalRowCount <= currentPageSize) {\n // The are fewer than the current page size.\n pageCountStrings.push({\n key: 'showperpage',\n component: 'core',\n param: defaultPageSize,\n });\n\n pageCountStrings.push({\n key: 'selectalluserswithcount',\n component: 'core',\n param: defaultPageSize,\n });\n\n // Show the 'Show [x]' link.\n showCountLink.classList.remove('hidden');\n showCountLink.dataset.targetPageSize = defaultPageSize;\n\n // The 'Check all [x]' button is only visible when there are values to set.\n checkCountButton.classList.add('hidden');\n } else {\n pageCountStrings.push({\n key: 'showall',\n component: 'core',\n param: totalRowCount,\n });\n\n pageCountStrings.push({\n key: 'selectalluserswithcount',\n component: 'core',\n param: totalRowCount,\n });\n\n // Show both the 'Show [x]' link, and the 'Check all [x]' button.\n showCountLink.classList.remove('hidden');\n showCountLink.dataset.targetPageSize = totalRowCount;\n checkCountButton.classList.remove('hidden');\n }\n\n Str.get_strings(pageCountStrings)\n .then(([showingParticipantCountString, showCountString, selectCountString]) => {\n const showingParticipantCount = root.querySelector(Selectors.showCountText);\n showingParticipantCount.innerHTML = showingParticipantCountString;\n\n if (showCountString) {\n showCountLink.innerHTML = showCountString;\n }\n\n if (selectCountString) {\n checkCountButton.value = selectCountString;\n }\n\n return;\n })\n .catch(Notification.exception);\n });\n };\n\n const resetBulkAction = bulkActionSelect => {\n bulkActionSelect.value = '';\n };\n\n registerEventListeners();\n};\n"],"file":"participants.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/participants.js"],"names":["Selectors","bulkActionSelect","bulkUserSelectedCheckBoxes","checkCountButton","showCountText","showCountToggle","stateHelpIcon","tableForm","uniqueId","init","uniqueid","noteStateNames","root","document","querySelector","getTableFromUniqueId","DynamicTableSelectors","main","fromRegionId","registerEventListeners","CustomEvents","define","events","accessibleChange","on","e","action","target","value","tableRoot","checkboxes","querySelectorAll","indexOf","preventDefault","ids","forEach","checkbox","push","getAttribute","replace","bulkAction","dataset","courseId","then","modal","getRoot","ModalEvents","hidden","bulkActionSelector","focus","catch","Notification","exception","length","form","submit","resetBulkAction","addEventListener","showCountLink","showCountLinkClicked","contains","checkCountButtonClicked","DynamicTable","setPageSize","targetPageSize","CheckboxToggleAll","setGroupState","Events","tableContentRefreshed","defaultPageSize","parseInt","tableDefaultPerPage","currentPageSize","tablePageSize","totalRowCount","tableTotalRows","updateSlavesFromMasterState","pageCountStrings","key","component","param","classList","add","remove","Str","get_strings","showingParticipantCountString","showCountString","selectCountString","showingParticipantCount","innerHTML"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Some UI stuff for participants page.\n * This is also used by the report/participants/index.php because it has the same functionality.\n *\n * @module core_user/participants\n * @package core_user\n * @copyright 2017 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as DynamicTable from 'core_table/dynamic';\nimport * as Str from 'core/str';\nimport CheckboxToggleAll from 'core/checkbox-toggleall';\nimport CustomEvents from 'core/custom_interaction_events';\nimport DynamicTableSelectors from 'core_table/local/dynamic/selectors';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport jQuery from 'jquery';\nimport {showAddNote, showSendMessage} from 'core_user/local/participants/bulkactions';\n\nconst Selectors = {\n bulkActionSelect: \"#formactionid\",\n bulkUserSelectedCheckBoxes: \"input[data-togglegroup='participants-table']:checked\",\n checkCountButton: \"#checkall\",\n showCountText: '[data-region=\"participant-count\"]',\n showCountToggle: '[data-action=\"showcount\"]',\n stateHelpIcon: '[data-region=\"state-help-icon\"]',\n tableForm: uniqueId => `form[data-table-unique-id=\"${uniqueId}\"]`,\n};\n\nexport const init = ({\n uniqueid,\n noteStateNames = {},\n}) => {\n const root = document.querySelector(Selectors.tableForm(uniqueid));\n const getTableFromUniqueId = uniqueId => root.querySelector(DynamicTableSelectors.main.fromRegionId(uniqueId));\n\n /**\n * Private method.\n *\n * @method registerEventListeners\n * @private\n */\n const registerEventListeners = () => {\n CustomEvents.define(Selectors.bulkActionSelect, [CustomEvents.events.accessibleChange]);\n jQuery(Selectors.bulkActionSelect).on(CustomEvents.events.accessibleChange, e => {\n const action = e.target.value;\n const tableRoot = getTableFromUniqueId(uniqueid);\n const checkboxes = tableRoot.querySelectorAll(Selectors.bulkUserSelectedCheckBoxes);\n\n if (action.indexOf('#') !== -1) {\n e.preventDefault();\n\n const ids = [];\n checkboxes.forEach(checkbox => {\n ids.push(checkbox.getAttribute('name').replace('user', ''));\n });\n\n let bulkAction;\n if (action === '#messageselect') {\n bulkAction = showSendMessage(ids);\n } else if (action === '#addgroupnote') {\n bulkAction = showAddNote(\n root.dataset.courseId,\n ids,\n noteStateNames,\n root.querySelector(Selectors.stateHelpIcon)\n );\n }\n\n if (bulkAction) {\n bulkAction\n .then(modal => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Focus on the action select when the dialog is closed.\n const bulkActionSelector = root.querySelector(Selectors.bulkActionSelect);\n bulkActionSelector.focus();\n });\n\n return modal;\n })\n .catch(Notification.exception);\n }\n } else if (action !== '' && checkboxes.length) {\n e.target.form.submit();\n }\n\n resetBulkAction(e.target);\n });\n\n root.addEventListener('click', e => {\n // Handle clicking of the \"Show [all|count]\" and \"Select all\" actions.\n const showCountLink = root.querySelector(Selectors.showCountToggle);\n const checkCountButton = root.querySelector(Selectors.checkCountButton);\n\n const showCountLinkClicked = showCountLink && showCountLink.contains(e.target);\n const checkCountButtonClicked = checkCountButton && checkCountButton.contains(e.target);\n\n if (showCountLinkClicked || checkCountButtonClicked) {\n e.preventDefault();\n\n const tableRoot = getTableFromUniqueId(uniqueid);\n\n DynamicTable.setPageSize(tableRoot, showCountLink.dataset.targetPageSize)\n .then(tableRoot => {\n // Always update the toggle state.\n // This ensures that the bulk actions are disabled after changing the page size.\n CheckboxToggleAll.setGroupState(tableRoot, 'participants-table', checkCountButtonClicked);\n\n return tableRoot;\n })\n .catch(Notification.exception);\n }\n });\n\n // When the content is refreshed, update the row counts in various places.\n root.addEventListener(DynamicTable.Events.tableContentRefreshed, e => {\n const showCountLink = root.querySelector(Selectors.showCountToggle);\n const checkCountButton = root.querySelector(Selectors.checkCountButton);\n\n const tableRoot = e.target;\n\n const defaultPageSize = parseInt(root.dataset.tableDefaultPerPage, 10);\n const currentPageSize = parseInt(tableRoot.dataset.tablePageSize, 10);\n const totalRowCount = parseInt(tableRoot.dataset.tableTotalRows, 10);\n\n CheckboxToggleAll.updateSlavesFromMasterState(tableRoot, 'participants-table');\n\n const pageCountStrings = [\n {\n key: 'countparticipantsfound',\n component: 'core_user',\n param: totalRowCount,\n },\n ];\n\n\n if (totalRowCount <= defaultPageSize) {\n // There are fewer than the default page count numbers of rows.\n showCountLink.classList.add('hidden');\n\n if (checkCountButton) {\n checkCountButton.classList.add('hidden');\n }\n } else if (totalRowCount <= currentPageSize) {\n // The are fewer than the current page size.\n pageCountStrings.push({\n key: 'showperpage',\n component: 'core',\n param: defaultPageSize,\n });\n\n pageCountStrings.push({\n key: 'selectalluserswithcount',\n component: 'core',\n param: defaultPageSize,\n });\n\n // Show the 'Show [x]' link.\n showCountLink.classList.remove('hidden');\n showCountLink.dataset.targetPageSize = defaultPageSize;\n\n if (checkCountButton) {\n // The 'Check all [x]' button is only visible when there are values to set.\n checkCountButton.classList.add('hidden');\n }\n } else {\n pageCountStrings.push({\n key: 'showall',\n component: 'core',\n param: totalRowCount,\n });\n\n pageCountStrings.push({\n key: 'selectalluserswithcount',\n component: 'core',\n param: totalRowCount,\n });\n\n // Show both the 'Show [x]' link, and the 'Check all [x]' button.\n showCountLink.classList.remove('hidden');\n showCountLink.dataset.targetPageSize = totalRowCount;\n\n if (checkCountButton) {\n checkCountButton.classList.remove('hidden');\n }\n }\n\n Str.get_strings(pageCountStrings)\n .then(([showingParticipantCountString, showCountString, selectCountString]) => {\n const showingParticipantCount = root.querySelector(Selectors.showCountText);\n showingParticipantCount.innerHTML = showingParticipantCountString;\n\n if (showCountString) {\n showCountLink.innerHTML = showCountString;\n }\n\n if (selectCountString && checkCountButton) {\n checkCountButton.value = selectCountString;\n }\n\n return;\n })\n .catch(Notification.exception);\n });\n };\n\n const resetBulkAction = bulkActionSelect => {\n bulkActionSelect.value = '';\n };\n\n registerEventListeners();\n};\n"],"file":"participants.min.js"} \ No newline at end of file diff --git a/user/amd/src/participants.js b/user/amd/src/participants.js index a5e27045a6d..f9b62a670d4 100644 --- a/user/amd/src/participants.js +++ b/user/amd/src/participants.js @@ -153,7 +153,10 @@ export const init = ({ if (totalRowCount <= defaultPageSize) { // There are fewer than the default page count numbers of rows. showCountLink.classList.add('hidden'); - checkCountButton.classList.add('hidden'); + + if (checkCountButton) { + checkCountButton.classList.add('hidden'); + } } else if (totalRowCount <= currentPageSize) { // The are fewer than the current page size. pageCountStrings.push({ @@ -172,8 +175,10 @@ export const init = ({ showCountLink.classList.remove('hidden'); showCountLink.dataset.targetPageSize = defaultPageSize; - // The 'Check all [x]' button is only visible when there are values to set. - checkCountButton.classList.add('hidden'); + if (checkCountButton) { + // The 'Check all [x]' button is only visible when there are values to set. + checkCountButton.classList.add('hidden'); + } } else { pageCountStrings.push({ key: 'showall', @@ -190,7 +195,10 @@ export const init = ({ // Show both the 'Show [x]' link, and the 'Check all [x]' button. showCountLink.classList.remove('hidden'); showCountLink.dataset.targetPageSize = totalRowCount; - checkCountButton.classList.remove('hidden'); + + if (checkCountButton) { + checkCountButton.classList.remove('hidden'); + } } Str.get_strings(pageCountStrings) @@ -202,7 +210,7 @@ export const init = ({ showCountLink.innerHTML = showCountString; } - if (selectCountString) { + if (selectCountString && checkCountButton) { checkCountButton.value = selectCountString; } diff --git a/user/index.php b/user/index.php index 579a04cd6d9..2e9290f39b8 100644 --- a/user/index.php +++ b/user/index.php @@ -310,30 +310,32 @@ echo $participanttablehtml; $perpageurl = clone($baseurl); $perpageurl->remove_params('perpage'); +$perpagesize = DEFAULT_PAGE_SIZE; +$perpagevisible = false; + if ($perpage == SHOW_ALL_PAGE_SIZE && $participanttable->totalrows > DEFAULT_PAGE_SIZE) { $perpageurl->param('perpage', DEFAULT_PAGE_SIZE); - echo $OUTPUT->container(html_writer::link( - $perpageurl, - get_string('showperpage', '', DEFAULT_PAGE_SIZE), - [ - 'data-action' => 'showcount', - 'data-target-page-size' => DEFAULT_PAGE_SIZE, - ] - ), [], 'showall'); - + $perpagevisible = true; } else if ($participanttable->get_page_size() < $participanttable->totalrows) { $perpageurl->param('perpage', SHOW_ALL_PAGE_SIZE); - echo $OUTPUT->container(html_writer::link( - $perpageurl, - get_string('showall', '', $participanttable->totalrows), - [ - 'data-action' => 'showcount', - 'data-target-page-size' => SHOW_ALL_PAGE_SIZE, - ] - ), - [], 'showall'); + $pagesize = SHOW_ALL_PAGE_SIZE; + $perpagevisible = true; } +$perpageclasses = ''; +if (!$perpagevisible) { + $perpageclasses = 'hidden'; +} +echo $OUTPUT->container(html_writer::link( + $perpageurl, + get_string('showperpage', '', DEFAULT_PAGE_SIZE), + [ + 'data-action' => 'showcount', + 'data-target-page-size' => $perpagesize, + 'class' => $perpageclasses, + ] +), [], 'showall'); + if ($bulkoperations) { echo '
';