Merge branch 'MDL-68463-master-3' of git://github.com/andrewnicols/moodle
This commit is contained in:
+3
-1
@@ -1524,7 +1524,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';
|
||||
@@ -2291,3 +2290,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}';
|
||||
|
||||
@@ -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';
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
File diff suppressed because one or more lines are too long
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* 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();
|
||||
};
|
||||
@@ -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 '</div>'."\n";
|
||||
}
|
||||
|
||||
@@ -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
|
||||
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.\n\n/**\n * Module to handle AJAX interactions.\n *\n * @module core_user/repository\n * @copyright 2020 Andrew Nicols <[email protected]>\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"}
|
||||
{"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 <http://www.gnu.org/licenses/>.\n\n/**\n * Module to handle AJAX interactions.\n *\n * @module core_user/repository\n * @copyright 2020 Andrew Nicols <[email protected]>\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"}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Bulk actions for lists of participants.
|
||||
*
|
||||
* @module core_user/local/participants/bulkactions
|
||||
* @package core_user
|
||||
* @copyright 2020 Andrew Nicols <[email protected]>
|
||||
* @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);
|
||||
};
|
||||
+182
-285
@@ -22,310 +22,207 @@
|
||||
* @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 * 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 jQuery from 'jquery';
|
||||
import {showAddNote, showSendMessage} from 'core_user/local/participants/bulkactions';
|
||||
|
||||
const Selectors = {
|
||||
bulkActionSelect: "#formactionid",
|
||||
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}"]`,
|
||||
};
|
||||
|
||||
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 = () => {
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
});
|
||||
|
||||
root.addEventListener('click', e => {
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
|
||||
$(SELECTORS.CHECKALLNOSBUTTON).on('click', function() {
|
||||
$(SELECTORS.BULKUSERNOSCHECKBOXES).prop('checked', true);
|
||||
// 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');
|
||||
|
||||
if (checkCountButton) {
|
||||
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;
|
||||
|
||||
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',
|
||||
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;
|
||||
|
||||
if (checkCountButton) {
|
||||
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) {
|
||||
checkCountButton.value = selectCountString;
|
||||
}
|
||||
|
||||
return;
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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();
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
+50
-26
@@ -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);
|
||||
|
||||
@@ -286,48 +284,73 @@ $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 '<form action="action_redir.php" method="post" id="participantsform">';
|
||||
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,
|
||||
'data-table-default-per-page' => ($perpage < DEFAULT_PAGE_SIZE) ? $perpage : DEFAULT_PAGE_SIZE,
|
||||
]);
|
||||
echo '<div>';
|
||||
echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
|
||||
echo '<input type="hidden" name="returnto" value="'.s($PAGE->url->out(false)).'" />';
|
||||
}
|
||||
|
||||
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');
|
||||
$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)), array(), '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)),
|
||||
array(), '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 '<br /><div class="buttons"><div class="form-inline">';
|
||||
|
||||
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();
|
||||
@@ -392,13 +415,14 @@ if ($bulkoperations) {
|
||||
echo html_writer::tag('div', $label . $select);
|
||||
|
||||
echo '<input type="hidden" name="id" value="' . $course->id . '" />';
|
||||
echo '<div class="d-none" data-region="state-help-icon">' . $OUTPUT->help_icon('publishstate', 'notes') . '</div>';
|
||||
echo '</div></div></div>';
|
||||
echo '</form>';
|
||||
|
||||
$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]);
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user