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":"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"} \ 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); };