diff --git a/lang/en/message.php b/lang/en/message.php
index daec24fcc98..b13d5a44d40 100644
--- a/lang/en/message.php
+++ b/lang/en/message.php
@@ -238,6 +238,7 @@ $string['sendingviaenabled'] = 'Sending "{$a->provider}" via "{$a->processor}" e
$string['sendingvialocked'] = 'Sending "{$a->provider}" via "{$a->processor}" locked status';
$string['sendmessage'] = 'Send message';
$string['sendbulkmessage'] = 'Send message to {$a} people';
+$string['sendbulkmessagesentwitherrors'] = 'Message sent to {$a->sent} of {$a->total} people';
$string['sendbulkmessagesingle'] = 'Send message to 1 person';
$string['sendbulkmessagesent'] = 'Message sent to {$a} people';
$string['sendbulkmessagesentsingle'] = 'Message sent to 1 person';
@@ -270,7 +271,8 @@ $string['unreadnewgroupconversationmessage'] = 'New message from {$a->name} in {
$string['unreadnewmessage'] = 'New message from {$a}';
$string['unsentmessagenotification'] = 'You have an unsent message. It will be lost if you leave this page.';
$string['useentertosend'] = 'Use enter to send';
-$string['usercantbemessaged'] = 'You can\'t message this user due to their message preferences. Try adding them as a contact.';
+$string['usercantbemessaged'] = 'You can\'t message {$a} due to their message preferences. Try adding them as a contact.';
+$string['usercantbemessagedbulk'] = 'You can\'t message the following users due to their message preferences. Try adding them as a contact.
{$a}';
$string['userwouldliketocontactyou'] = '{$a} would like to contact you';
$string['viewfullnotification'] = 'View full notification';
$string['viewmessageswith'] = 'View messages with {$a}';
diff --git a/message/externallib.php b/message/externallib.php
index 027adcc45d3..7ac6cbe5df0 100644
--- a/message/externallib.php
+++ b/message/externallib.php
@@ -195,7 +195,7 @@ class core_message_external extends external_api {
// We are going to do some checking.
// Code should match /messages/index.php checks.
$success = true;
-
+ $cantsendtouser = '';
// Check the user exists.
if (empty($tousers[$message['touserid']])) {
$success = false;
@@ -212,7 +212,14 @@ class core_message_external extends external_api {
// Check if the recipient can be messaged by the sender.
if ($success && !\core_message\api::can_send_message($tousers[$message['touserid']]->id, $USER->id)) {
$success = false;
- $errormessage = get_string('usercantbemessaged', 'message');
+ $fullname = fullname(\core_user::get_user($message['touserid']));
+ $errormessage = get_string(
+ 'usercantbemessaged',
+ 'message',
+ $fullname
+ );
+ // Keep track of the user the message could not be sent to.
+ $cantsendtouser = $fullname;
}
// Now we can send the message (at least try).
@@ -239,6 +246,7 @@ class core_message_external extends external_api {
$errormessage = get_string('messageundeliveredbynotificationsettings', 'error');
}
$resultmsg['errormessage'] = $errormessage;
+ $resultmsg['cantsendtouser'] = $cantsendtouser;
}
$resultmessages[] = $resultmsg;
@@ -252,6 +260,9 @@ class core_message_external extends external_api {
'',
'id, conversationid, smallmessage, fullmessageformat, fullmessagetrust');
$resultmessages = array_map(function($resultmessage) use ($messagerecords, $USER) {
+ if (!empty($resultmessage['errormessage'])) {
+ return $resultmessage;
+ }
$id = $resultmessage['msgid'];
$resultmessage['conversationid'] = isset($messagerecords[$id]) ? $messagerecords[$id]->conversationid : null;
$resultmessage['useridfrom'] = $USER->id;
@@ -284,6 +295,7 @@ class core_message_external extends external_api {
'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message', VALUE_OPTIONAL),
'conversationid' => new external_value(PARAM_INT, 'The conversation id for this message', VALUE_OPTIONAL),
'useridfrom' => new external_value(PARAM_INT, 'The user id who sent the message', VALUE_OPTIONAL),
+ 'cantsendtouser' => new external_value(PARAM_TEXT, 'The user that could not be sent to', VALUE_OPTIONAL),
'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
)
diff --git a/message/tests/externallib_test.php b/message/tests/externallib_test.php
index f1c904229af..9c6a79654f9 100644
--- a/message/tests/externallib_test.php
+++ b/message/tests/externallib_test.php
@@ -129,7 +129,7 @@ final class externallib_test extends externallib_advanced_testcase {
$sentmessages = core_message_external::send_instant_messages($messages);
$sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
$this->assertEquals(
- get_string('usercantbemessaged', 'message'),
+ get_string('usercantbemessaged', 'message', fullname($user2)),
array_pop($sentmessages)['errormessage']
);
@@ -225,8 +225,10 @@ final class externallib_test extends externallib_advanced_testcase {
$sentmessage = reset($sentmessages);
- $this->assertEquals(get_string('usercantbemessaged', 'message'), $sentmessage['errormessage']);
-
+ $this->assertEquals(
+ get_string('usercantbemessaged', 'message', fullname($user2)),
+ $sentmessage['errormessage']
+ );
$this->assertEquals(0, $DB->count_records('messages'));
}
@@ -261,8 +263,10 @@ final class externallib_test extends externallib_advanced_testcase {
$sentmessage = reset($sentmessages);
- $this->assertEquals(get_string('usercantbemessaged', 'message'), $sentmessage['errormessage']);
-
+ $this->assertEquals(
+ get_string('usercantbemessaged', 'message', fullname($user2)),
+ $sentmessage['errormessage']
+ );
$this->assertEquals(0, $DB->count_records('messages'));
}
diff --git a/user/amd/build/local/participants/bulkactions.min.js b/user/amd/build/local/participants/bulkactions.min.js
index 66f248815c3..8be4d74f7e8 100644
--- a/user/amd/build/local/participants/bulkactions.min.js
+++ b/user/amd/build/local/participants/bulkactions.min.js
@@ -5,6 +5,6 @@ define("core_user/local/participants/bulkactions",["exports","core_user/reposito
* @module core_user/local/participants/bulkactions
* @copyright 2020 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showSendMessage=_exports.showAddNote=void 0,Repository=_interopRequireWildcard(Repository),Str=_interopRequireWildcard(Str),_modal_events=_interopRequireDefault(_modal_events),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_notification=_interopRequireDefault(_notification),_templates=_interopRequireDefault(_templates);_exports.showAddNote=(courseid,users,noteStateNames,stateHelpIcon)=>{if(!users.length)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]})}const context={stateNames:states,stateHelpIcon:stateHelpIcon.innerHTML};let titlePromise=null;return titlePromise=1===users.length?Str.get_string("addbulknotesingle","core_notes"):Str.get_string("addbulknote","core_notes",users.length),_modal_save_cancel.default.create({body:_templates.default.render("core_user/add_bulk_note",context),title:titlePromise,buttons:{save:titlePromise},removeOnClose:!0,show:!0}).then((modal=>(modal.getRoot().on(_modal_events.default.save,(()=>submitAddNote(courseid,users,modal))),modal)))};const submitAddNote=(courseid,users,modal)=>{const text=modal.getRoot().find("form textarea").val(),publishstate=modal.getRoot().find("form select").val(),notes=users.map((userid=>({userid:userid,text:text,courseid:courseid,publishstate:publishstate})));return Repository.createNotesForUsers(notes).then((noteIds=>1===noteIds.length?Str.get_string("addbulknotedonesingle","core_notes"):Str.get_string("addbulknotedone","core_notes",noteIds.length))).then((msg=>(0,_toast.add)(msg))).catch(_notification.default.exception)};_exports.showSendMessage=users=>{if(!users.length)return Promise.resolve();let titlePromise;return titlePromise=1===users.length?Str.get_string("sendbulkmessagesingle","core_message"):Str.get_string("sendbulkmessage","core_message",users.length),_modal_save_cancel.default.create({body:_templates.default.render("core_user/send_bulk_message",{}),title:titlePromise,buttons:{save:titlePromise},removeOnClose:!0,show:!0}).then((modal=>(modal.getRoot().on(_modal_events.default.save,(e=>{const text=modal.getRoot().find("form textarea").val();if(""===text.trim())return modal.getRoot().find('[data-role="messagetextrequired"]').removeAttr("hidden"),void e.preventDefault();submitSendMessage(modal,users,text)})),modal)))};const submitSendMessage=(modal,users,text)=>{const messages=users.map((touserid=>({touserid:touserid,text:text})));return Repository.sendMessagesToUsers(messages).then((messageIds=>1==messageIds.length?Str.get_string("sendbulkmessagesentsingle","core_message"):Str.get_string("sendbulkmessagesent","core_message",messageIds.length))).then((msg=>(0,_toast.add)(msg))).catch(_notification.default.exception)}}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showSendMessage=_exports.showAddNote=void 0,Repository=_interopRequireWildcard(Repository),Str=_interopRequireWildcard(Str),_modal_events=_interopRequireDefault(_modal_events),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_notification=_interopRequireDefault(_notification),_templates=_interopRequireDefault(_templates);_exports.showAddNote=(courseid,users,noteStateNames,stateHelpIcon)=>{if(!users.length)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]})}const context={stateNames:states,stateHelpIcon:stateHelpIcon.innerHTML};let titlePromise=null;return titlePromise=1===users.length?Str.get_string("addbulknotesingle","core_notes"):Str.get_string("addbulknote","core_notes",users.length),_modal_save_cancel.default.create({body:_templates.default.render("core_user/add_bulk_note",context),title:titlePromise,buttons:{save:titlePromise},removeOnClose:!0,show:!0}).then((modal=>(modal.getRoot().on(_modal_events.default.save,(()=>submitAddNote(courseid,users,modal))),modal)))};const submitAddNote=(courseid,users,modal)=>{const text=modal.getRoot().find("form textarea").val(),publishstate=modal.getRoot().find("form select").val(),notes=users.map((userid=>({userid:userid,text:text,courseid:courseid,publishstate:publishstate})));return Repository.createNotesForUsers(notes).then((noteIds=>1===noteIds.length?Str.get_string("addbulknotedonesingle","core_notes"):Str.get_string("addbulknotedone","core_notes",noteIds.length))).then((msg=>(0,_toast.add)(msg))).catch(_notification.default.exception)};_exports.showSendMessage=users=>{if(!users.length)return Promise.resolve();let titlePromise;return titlePromise=1===users.length?Str.get_string("sendbulkmessagesingle","core_message"):Str.get_string("sendbulkmessage","core_message",users.length),_modal_save_cancel.default.create({body:_templates.default.render("core_user/send_bulk_message",{}),title:titlePromise,buttons:{save:titlePromise},removeOnClose:!0,show:!0}).then((modal=>(modal.getRoot().on(_modal_events.default.save,(e=>{const text=modal.getRoot().find("form textarea").val();if(""===text.trim())return modal.getRoot().find('[data-role="messagetextrequired"]').removeAttr("hidden"),void e.preventDefault();submitSendMessage(modal,users,text)})),modal)))};const submitSendMessage=(modal,users,text)=>{const messages=users.map((touserid=>({touserid:touserid,text:text})));return Repository.sendMessagesToUsers(messages).then((messageIds=>{const cantSendToUsers=messageIds.filter((msg=>msg.cantsendtouser&&""!==msg.cantsendtouser));if(cantSendToUsers.length>1){const users=cantSendToUsers.map((user=>user.cantsendtouser)).join(", ");Str.get_string("usercantbemessagedbulk","core_message",users).done((msg=>{_notification.default.addNotification({message:msg,type:"error"})}))}else 1===cantSendToUsers.length&&_notification.default.addNotification({message:cantSendToUsers[0].errormessage,type:"error"});messageIds.filter((msg=>msg.errormessage&&(!msg.cantsendtouser||""===msg.cantsendtouser))).forEach((error=>{_notification.default.addNotification({message:error.errormessage,type:"error"})}));let toastMessage="";const errorMessages=messageIds.filter((msg=>msg.errormessage)),successCount=messageIds.length-errorMessages.length;return toastMessage=1==successCount&&0==errorMessages.length?Str.get_string("sendbulkmessagesentsingle","core_message"):successCount>1&&0==errorMessages.length?Str.get_string("sendbulkmessagesent","core_message",messageIds.length):Str.get_string("sendbulkmessagesentwitherrors","core_message",{sent:successCount,total:messageIds.length}),{message:toastMessage,errors:errorMessages.length}})).then((_ref=>{let{message:message,errors:errors}=_ref;if(errors>0){const config={type:"warning",closeButton:!0,autohide:!1};(0,_toast.add)(message,config)}else(0,_toast.add)(message)})).catch(_notification.default.exception)}}));
//# sourceMappingURL=bulkactions.min.js.map
\ No newline at end of file
diff --git a/user/amd/build/local/participants/bulkactions.min.js.map b/user/amd/build/local/participants/bulkactions.min.js.map
index e13b5c9fdd9..e818ee24a16 100644
--- a/user/amd/build/local/participants/bulkactions.min.js.map
+++ b/user/amd/build/local/participants/bulkactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"bulkactions.min.js","sources":["../../../src/local/participants/bulkactions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Bulk actions for lists of participants.\n *\n * @module core_user/local/participants/bulkactions\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 SaveCancelModal from 'core/modal_save_cancel';\nimport Notification from 'core/notification';\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 SaveCancelModal.create({\n body: Templates.render('core_user/add_bulk_note', context),\n title: titlePromise,\n buttons: {\n save: titlePromise,\n },\n removeOnClose: true,\n show: true,\n })\n .then(modal => {\n modal.getRoot().on(ModalEvents.save, () => submitAddNote(courseid, users, modal));\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 SaveCancelModal.create({\n body: Templates.render('core_user/send_bulk_message', {}),\n title: titlePromise,\n buttons: {\n save: titlePromise,\n },\n removeOnClose: true,\n show: true,\n })\n .then(modal => {\n modal.getRoot().on(ModalEvents.save, (e) => {\n const text = modal.getRoot().find('form textarea').val();\n if (text.trim() === '') {\n modal.getRoot().find('[data-role=\"messagetextrequired\"]').removeAttr('hidden');\n e.preventDefault();\n return;\n }\n\n submitSendMessage(modal, users, text);\n });\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 * @param {String} text\n * @return {Promise}\n */\nconst submitSendMessage = (modal, users, text) => {\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"],"names":["courseid","users","noteStateNames","stateHelpIcon","length","Promise","resolve","states","key","push","value","label","selected","context","stateNames","innerHTML","titlePromise","Str","get_string","SaveCancelModal","create","body","Templates","render","title","buttons","save","removeOnClose","show","then","modal","getRoot","on","ModalEvents","submitAddNote","text","find","val","publishstate","notes","map","userid","Repository","createNotesForUsers","noteIds","msg","catch","Notification","exception","e","trim","removeAttr","preventDefault","submitSendMessage","messages","touserid","sendMessagesToUsers","messageIds"],"mappings":";;;;;;;2aAwC2B,CAACA,SAAUC,MAAOC,eAAgBC,qBACpDF,MAAMG,cAEAC,QAAQC,gBAGbC,OAAS,OACV,IAAIC,OAAON,sBACJM,SACC,QACDD,OAAOE,KAAK,CAACC,MAAO,WAAYC,MAAOT,eAAeM,iBAErD,SACDD,OAAOE,KAAK,CAACC,MAAO,SAAUC,MAAOT,eAAeM,KAAMI,SAAU,cAEnE,OACDL,OAAOE,KAAK,CAACC,MAAOF,IAAKG,MAAOT,eAAeM,aAKrDK,QAAU,CACZC,WAAYP,OACZJ,cAAeA,cAAcY,eAG7BC,aAAe,YAEfA,aADiB,IAAjBf,MAAMG,OACSa,IAAIC,WAAW,oBAAqB,cAEpCD,IAAIC,WAAW,cAAe,aAAcjB,MAAMG,QAG9De,2BAAgBC,OAAO,CAC1BC,KAAMC,mBAAUC,OAAO,0BAA2BV,SAClDW,MAAOR,aACPS,QAAS,CACLC,KAAMV,cAEVW,eAAe,EACfC,MAAM,IAETC,MAAKC,QACFA,MAAMC,UAAUC,GAAGC,sBAAYP,MAAM,IAAMQ,cAAclC,SAAUC,MAAO6B,SACnEA,gBAYTI,cAAgB,CAAClC,SAAUC,MAAO6B,eAC9BK,KAAOL,MAAMC,UAAUK,KAAK,iBAAiBC,MAC7CC,aAAeR,MAAMC,UAAUK,KAAK,eAAeC,MAEnDE,MAAQtC,MAAMuC,KAAIC,SACb,CACHA,OAAAA,OACAN,KAAAA,KACAnC,SAAAA,SACAsC,aAAAA,wBAIDI,WAAWC,oBAAoBJ,OACrCV,MAAKe,SACqB,IAAnBA,QAAQxC,OACDa,IAAIC,WAAW,wBAAyB,cAExCD,IAAIC,WAAW,kBAAmB,aAAc0B,QAAQxC,UAGtEyB,MAAKgB,MAAO,cAAWA,OACvBC,MAAMC,sBAAaC,qCASO/C,YACtBA,MAAMG,cAEAC,QAAQC,cAGfU,oBAEAA,aADiB,IAAjBf,MAAMG,OACSa,IAAIC,WAAW,wBAAyB,gBAExCD,IAAIC,WAAW,kBAAmB,eAAgBjB,MAAMG,QAGpEe,2BAAgBC,OAAO,CAC1BC,KAAMC,mBAAUC,OAAO,8BAA+B,IACtDC,MAAOR,aACPS,QAAS,CACLC,KAAMV,cAEVW,eAAe,EACfC,MAAM,IAETC,MAAKC,QACFA,MAAMC,UAAUC,GAAGC,sBAAYP,MAAOuB,UAC5Bd,KAAOL,MAAMC,UAAUK,KAAK,iBAAiBC,SAC/B,KAAhBF,KAAKe,cACLpB,MAAMC,UAAUK,KAAK,qCAAqCe,WAAW,eACrEF,EAAEG,iBAINC,kBAAkBvB,MAAO7B,MAAOkC,SAG7BL,gBAYTuB,kBAAoB,CAACvB,MAAO7B,MAAOkC,cAC/BmB,SAAWrD,MAAMuC,KAAIe,WAChB,CACHA,SAAAA,SACApB,KAAAA,gBAIDO,WAAWc,oBAAoBF,UACrCzB,MAAK4B,YACuB,GAArBA,WAAWrD,OACJa,IAAIC,WAAW,4BAA6B,gBAE5CD,IAAIC,WAAW,sBAAuB,eAAgBuC,WAAWrD,UAG/EyB,MAAKgB,MAAO,cAAWA,OACvBC,MAAMC,sBAAaC"}
\ No newline at end of file
+{"version":3,"file":"bulkactions.min.js","sources":["../../../src/local/participants/bulkactions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Bulk actions for lists of participants.\n *\n * @module core_user/local/participants/bulkactions\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 SaveCancelModal from 'core/modal_save_cancel';\nimport Notification from 'core/notification';\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 SaveCancelModal.create({\n body: Templates.render('core_user/add_bulk_note', context),\n title: titlePromise,\n buttons: {\n save: titlePromise,\n },\n removeOnClose: true,\n show: true,\n })\n .then(modal => {\n modal.getRoot().on(ModalEvents.save, () => submitAddNote(courseid, users, modal));\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 SaveCancelModal.create({\n body: Templates.render('core_user/send_bulk_message', {}),\n title: titlePromise,\n buttons: {\n save: titlePromise,\n },\n removeOnClose: true,\n show: true,\n })\n .then(modal => {\n modal.getRoot().on(ModalEvents.save, (e) => {\n const text = modal.getRoot().find('form textarea').val();\n if (text.trim() === '') {\n modal.getRoot().find('[data-role=\"messagetextrequired\"]').removeAttr('hidden');\n e.preventDefault();\n return;\n }\n\n submitSendMessage(modal, users, text);\n });\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 * @param {String} text\n * @return {Promise}\n */\nconst submitSendMessage = (modal, users, text) => {\n const messages = users.map(touserid => {\n return {\n touserid,\n text,\n };\n });\n return Repository.sendMessagesToUsers(messages)\n .then(messageIds => {\n // To help teachers know which users could not be sent to, let's build a notification.\n const cantSendToUsers = messageIds.filter(msg => msg.cantsendtouser && msg.cantsendtouser !== '');\n if (cantSendToUsers.length > 1) {\n // If there are multiple users who can't be sent to, extract their names and build an error message.\n const users = cantSendToUsers.map(user => user.cantsendtouser).join(', ');\n const stringPromise = Str.get_string('usercantbemessagedbulk', 'core_message', users);\n stringPromise.done(msg => {\n Notification.addNotification({\n message: msg,\n type: 'error',\n });\n });\n } else if (cantSendToUsers.length === 1) {\n // If there was only one error, just notify with the singular 'usercantbemessaged' string (already built in PHP).\n Notification.addNotification({\n message: cantSendToUsers[0].errormessage,\n type: 'error',\n });\n }\n // Always handle other errors independently.\n const otherErrors = messageIds.filter(msg => msg.errormessage && (!msg.cantsendtouser || msg.cantsendtouser === ''));\n otherErrors.forEach(error => {\n Notification.addNotification({\n message: error.errormessage,\n type: 'error',\n });\n });\n\n // Determine appropriate success/error toast message based on send results.\n let toastMessage = '';\n const errorMessages = messageIds.filter(msg => msg.errormessage);\n const successCount = messageIds.length - errorMessages.length;\n if (successCount == 1 && errorMessages.length == 0) {\n toastMessage = Str.get_string('sendbulkmessagesentsingle', 'core_message');\n } else if (successCount > 1 && errorMessages.length == 0) {\n toastMessage = Str.get_string('sendbulkmessagesent', 'core_message', messageIds.length);\n } else {\n toastMessage = Str.get_string(\n 'sendbulkmessagesentwitherrors',\n 'core_message',\n {\n sent: successCount,\n total: messageIds.length\n }\n );\n }\n return {\n message: toastMessage,\n errors: errorMessages.length,\n };\n })\n .then(({message, errors}) => {\n if (errors > 0) {\n // Customise the toast message to indicate there was an issue with sending.\n const config = {\n type: 'warning',\n closeButton: true,\n autohide: false,\n };\n notifyUser(message, config);\n } else {\n notifyUser(message);\n }\n return;\n })\n .catch(Notification.exception);\n};\n"],"names":["courseid","users","noteStateNames","stateHelpIcon","length","Promise","resolve","states","key","push","value","label","selected","context","stateNames","innerHTML","titlePromise","Str","get_string","SaveCancelModal","create","body","Templates","render","title","buttons","save","removeOnClose","show","then","modal","getRoot","on","ModalEvents","submitAddNote","text","find","val","publishstate","notes","map","userid","Repository","createNotesForUsers","noteIds","msg","catch","Notification","exception","e","trim","removeAttr","preventDefault","submitSendMessage","messages","touserid","sendMessagesToUsers","messageIds","cantSendToUsers","filter","cantsendtouser","user","join","done","addNotification","message","type","errormessage","forEach","error","toastMessage","errorMessages","successCount","sent","total","errors","_ref","config","closeButton","autohide"],"mappings":";;;;;;;2aAwC2B,CAACA,SAAUC,MAAOC,eAAgBC,qBACpDF,MAAMG,cAEAC,QAAQC,gBAGbC,OAAS,OACV,IAAIC,OAAON,sBACJM,SACC,QACDD,OAAOE,KAAK,CAACC,MAAO,WAAYC,MAAOT,eAAeM,iBAErD,SACDD,OAAOE,KAAK,CAACC,MAAO,SAAUC,MAAOT,eAAeM,KAAMI,SAAU,cAEnE,OACDL,OAAOE,KAAK,CAACC,MAAOF,IAAKG,MAAOT,eAAeM,aAKrDK,QAAU,CACZC,WAAYP,OACZJ,cAAeA,cAAcY,eAG7BC,aAAe,YAEfA,aADiB,IAAjBf,MAAMG,OACSa,IAAIC,WAAW,oBAAqB,cAEpCD,IAAIC,WAAW,cAAe,aAAcjB,MAAMG,QAG9De,2BAAgBC,OAAO,CAC1BC,KAAMC,mBAAUC,OAAO,0BAA2BV,SAClDW,MAAOR,aACPS,QAAS,CACLC,KAAMV,cAEVW,eAAe,EACfC,MAAM,IAETC,MAAKC,QACFA,MAAMC,UAAUC,GAAGC,sBAAYP,MAAM,IAAMQ,cAAclC,SAAUC,MAAO6B,SACnEA,gBAYTI,cAAgB,CAAClC,SAAUC,MAAO6B,eAC9BK,KAAOL,MAAMC,UAAUK,KAAK,iBAAiBC,MAC7CC,aAAeR,MAAMC,UAAUK,KAAK,eAAeC,MAEnDE,MAAQtC,MAAMuC,KAAIC,SACb,CACHA,OAAAA,OACAN,KAAAA,KACAnC,SAAAA,SACAsC,aAAAA,wBAIDI,WAAWC,oBAAoBJ,OACrCV,MAAKe,SACqB,IAAnBA,QAAQxC,OACDa,IAAIC,WAAW,wBAAyB,cAExCD,IAAIC,WAAW,kBAAmB,aAAc0B,QAAQxC,UAGtEyB,MAAKgB,MAAO,cAAWA,OACvBC,MAAMC,sBAAaC,qCASO/C,YACtBA,MAAMG,cAEAC,QAAQC,cAGfU,oBAEAA,aADiB,IAAjBf,MAAMG,OACSa,IAAIC,WAAW,wBAAyB,gBAExCD,IAAIC,WAAW,kBAAmB,eAAgBjB,MAAMG,QAGpEe,2BAAgBC,OAAO,CAC1BC,KAAMC,mBAAUC,OAAO,8BAA+B,IACtDC,MAAOR,aACPS,QAAS,CACLC,KAAMV,cAEVW,eAAe,EACfC,MAAM,IAETC,MAAKC,QACFA,MAAMC,UAAUC,GAAGC,sBAAYP,MAAOuB,UAC5Bd,KAAOL,MAAMC,UAAUK,KAAK,iBAAiBC,SAC/B,KAAhBF,KAAKe,cACLpB,MAAMC,UAAUK,KAAK,qCAAqCe,WAAW,eACrEF,EAAEG,iBAINC,kBAAkBvB,MAAO7B,MAAOkC,SAG7BL,gBAYTuB,kBAAoB,CAACvB,MAAO7B,MAAOkC,cAC/BmB,SAAWrD,MAAMuC,KAAIe,WAChB,CACHA,SAAAA,SACApB,KAAAA,gBAGDO,WAAWc,oBAAoBF,UACrCzB,MAAK4B,mBAEIC,gBAAkBD,WAAWE,QAAOd,KAAOA,IAAIe,gBAAyC,KAAvBf,IAAIe,oBACvEF,gBAAgBtD,OAAS,EAAG,OAEtBH,MAAQyD,gBAAgBlB,KAAIqB,MAAQA,KAAKD,iBAAgBE,KAAK,MAC9C7C,IAAIC,WAAW,yBAA0B,eAAgBjB,OACjE8D,MAAKlB,4BACFmB,gBAAgB,CACzBC,QAASpB,IACTqB,KAAM,kBAGoB,IAA3BR,gBAAgBtD,8BAEV4D,gBAAgB,CACzBC,QAASP,gBAAgB,GAAGS,aAC5BD,KAAM,UAIMT,WAAWE,QAAOd,KAAOA,IAAIsB,gBAAkBtB,IAAIe,gBAAyC,KAAvBf,IAAIe,kBACjFQ,SAAQC,8BACHL,gBAAgB,CACzBC,QAASI,MAAMF,aACfD,KAAM,iBAKVI,aAAe,SACbC,cAAgBd,WAAWE,QAAOd,KAAOA,IAAIsB,eAC7CK,aAAef,WAAWrD,OAASmE,cAAcnE,cAEnDkE,aADgB,GAAhBE,cAA6C,GAAxBD,cAAcnE,OACpBa,IAAIC,WAAW,4BAA6B,gBACpDsD,aAAe,GAA6B,GAAxBD,cAAcnE,OAC1Ba,IAAIC,WAAW,sBAAuB,eAAgBuC,WAAWrD,QAEjEa,IAAIC,WACf,gCACA,eACA,CACIuD,KAAMD,aACNE,MAAOjB,WAAWrD,SAIvB,CACH6D,QAASK,aACTK,OAAQJ,cAAcnE,WAG7ByB,MAAK+C,WAACX,QAACA,QAADU,OAAUA,gBACTA,OAAS,EAAG,OAENE,OAAS,CACXX,KAAM,UACNY,aAAa,EACbC,UAAU,kBAEHd,QAASY,2BAETZ,YAIlBnB,MAAMC,sBAAaC"}
\ No newline at end of file
diff --git a/user/amd/src/local/participants/bulkactions.js b/user/amd/src/local/participants/bulkactions.js
index 7072573a71a..57b8e503aba 100644
--- a/user/amd/src/local/participants/bulkactions.js
+++ b/user/amd/src/local/participants/bulkactions.js
@@ -178,15 +178,72 @@ const submitSendMessage = (modal, users, text) => {
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);
+ // To help teachers know which users could not be sent to, let's build a notification.
+ const cantSendToUsers = messageIds.filter(msg => msg.cantsendtouser && msg.cantsendtouser !== '');
+ if (cantSendToUsers.length > 1) {
+ // If there are multiple users who can't be sent to, extract their names and build an error message.
+ const users = cantSendToUsers.map(user => user.cantsendtouser).join(', ');
+ const stringPromise = Str.get_string('usercantbemessagedbulk', 'core_message', users);
+ stringPromise.done(msg => {
+ Notification.addNotification({
+ message: msg,
+ type: 'error',
+ });
+ });
+ } else if (cantSendToUsers.length === 1) {
+ // If there was only one error, just notify with the singular 'usercantbemessaged' string (already built in PHP).
+ Notification.addNotification({
+ message: cantSendToUsers[0].errormessage,
+ type: 'error',
+ });
}
+ // Always handle other errors independently.
+ const otherErrors = messageIds.filter(msg => msg.errormessage && (!msg.cantsendtouser || msg.cantsendtouser === ''));
+ otherErrors.forEach(error => {
+ Notification.addNotification({
+ message: error.errormessage,
+ type: 'error',
+ });
+ });
+
+ // Determine appropriate success/error toast message based on send results.
+ let toastMessage = '';
+ const errorMessages = messageIds.filter(msg => msg.errormessage);
+ const successCount = messageIds.length - errorMessages.length;
+ if (successCount == 1 && errorMessages.length == 0) {
+ toastMessage = Str.get_string('sendbulkmessagesentsingle', 'core_message');
+ } else if (successCount > 1 && errorMessages.length == 0) {
+ toastMessage = Str.get_string('sendbulkmessagesent', 'core_message', messageIds.length);
+ } else {
+ toastMessage = Str.get_string(
+ 'sendbulkmessagesentwitherrors',
+ 'core_message',
+ {
+ sent: successCount,
+ total: messageIds.length
+ }
+ );
+ }
+ return {
+ message: toastMessage,
+ errors: errorMessages.length,
+ };
+ })
+ .then(({message, errors}) => {
+ if (errors > 0) {
+ // Customise the toast message to indicate there was an issue with sending.
+ const config = {
+ type: 'warning',
+ closeButton: true,
+ autohide: false,
+ };
+ notifyUser(message, config);
+ } else {
+ notifyUser(message);
+ }
+ return;
})
- .then(msg => notifyUser(msg))
.catch(Notification.exception);
};