Merge branch 'MDL-64773_master' of git://github.com/markn86/moodle

This commit is contained in:
Jake Dallimore 2019-03-13 11:03:52 +08:00
commit a6646dfd92
39 changed files with 1093 additions and 21 deletions

View File

@ -107,6 +107,7 @@ $string['messagepreferences'] = 'Message preferences';
$string['messages'] = 'Messages';
$string['messagesselected:'] = 'Messages selected:';
$string['messagingdatahasnotbeenmigrated'] = 'Your messages are temporarily unavailable due to upgrades in the messaging infrastructure. Please wait for them to be migrated.';
$string['muteconversation'] = 'Mute';
$string['newonlymsg'] = 'Show only new';
$string['newmessage'] = 'New message';
$string['newmessagesearch'] = 'Select or search for a contact to send a new message.';
@ -162,6 +163,11 @@ $string['privacy:metadata:message_contact_requests'] = 'The list of contact requ
$string['privacy:metadata:message_contact_requests:requesteduserid'] = 'The ID of the user who received the contact request';
$string['privacy:metadata:message_contact_requests:timecreated'] = 'The time when the contact request was created';
$string['privacy:metadata:message_contact_requests:userid'] = 'The ID of the user who sent the contact request';
$string['privacy:metadata:message_conversation_actions'] = 'The list of conversation user actions';
$string['privacy:metadata:message_conversation_actions:action'] = 'The action that was performed';
$string['privacy:metadata:message_conversation_actions:conversationid'] = 'The ID of the conversation this action belongs to';
$string['privacy:metadata:message_conversation_actions:timecreated'] = 'The time when the action was created';
$string['privacy:metadata:message_conversation_actions:userid'] = 'The ID of the user who performed this action';
$string['privacy:metadata:message_conversation_members'] = 'The list of users in a conversation';
$string['privacy:metadata:message_conversation_members:conversationid'] = 'The ID of the conversation';
$string['privacy:metadata:message_conversation_members:timecreated'] = 'The time when the member was created';
@ -233,6 +239,7 @@ $string['unblockcontact'] = 'Unblock contact';
$string['unblockuser'] = 'Unblock user';
$string['unblockuserconfirm'] = 'Are you sure you want to unblock {$a}?';
$string['unknownuser'] = 'Unknown user';
$string['unmuteconversation'] = 'Unmute';
$string['unreadnotification'] = 'Unread notification: {$a}';
$string['unreadnewgroupconversationmessage'] = 'New message from {$a->name} in {$a->conversationname}';
$string['unreadnewmessage'] = 'New message from {$a}';

View File

@ -84,13 +84,16 @@ class manager {
// Get user records for all members of the conversation.
// We must fetch distinct users, because it's possible for a user to message themselves via bulk user actions.
// In such cases, there will be 2 records referring to the same user.
$sql = "SELECT u.*
$sql = "SELECT u.*, mca.id as ismuted
FROM {user} u
LEFT JOIN {message_conversation_actions} mca
ON mca.userid = u.id AND mca.conversationid = ? AND mca.action = ?
WHERE u.id IN (
SELECT mcm.userid FROM {message_conversation_members} mcm
WHERE mcm.conversationid = :convid
WHERE mcm.conversationid = ?
)";
$members = $DB->get_records_sql($sql, ['convid' => $eventdata->convid]);
$members = $DB->get_records_sql($sql, [$eventdata->convid, \core_message\api::CONVERSATION_ACTION_MUTED,
$eventdata->convid]);
if (empty($members)) {
throw new \moodle_exception("Conversation has no members or does not exist.");
}
@ -138,7 +141,10 @@ class manager {
foreach ($otherusers as $recipient) {
// If this message was a legacy (1:1) message, then we use the userto.
if ($legacymessage) {
$ismuted = $recipient->ismuted;
$recipient = $eventdata->userto;
$recipient->ismuted = $ismuted;
}
$usertoisrealuser = (\core_user::is_real_user($recipient->id) != false);
@ -195,8 +201,9 @@ class manager {
// Fill in the array of processors to be used based on default and user preferences.
// This applies only to individual conversations. Messages to group conversations ignore processors.
// Do not process muted conversations.
$processorlist = [];
if ($conv->type == \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL) {
if ($conv->type == \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL && !$recipient->ismuted) {
foreach ($processors as $processor) {
// Skip adding processors for internal user, if processor doesn't support sending message to internal user.
if (!$usertoisrealuser && !$processor->object->can_send_to_any_users()) {

View File

@ -260,6 +260,7 @@ class icon_system_fontawesome extends icon_system_font {
'core:i/moodle_host' => 'fa-graduation-cap',
'core:i/moremenu' => 'fa-ellipsis-h',
'core:i/move_2d' => 'fa-arrows',
'core:i/muted' => 'fa-microphone-slash',
'core:i/navigationitem' => 'fa-fw',
'core:i/ne_red_mark' => 'fa-remove',
'core:i/new' => 'fa-bolt',

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20190122" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20190308" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@ -650,6 +650,20 @@
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="message_conversation_actions" COMMENT="Stores all per-user actions on individual conversations">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="conversationid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="action" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
<KEY NAME="conversationid" TYPE="foreign" FIELDS="conversationid" REFTABLE="message_conversations" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="message_user_actions" COMMENT="Stores all per-user actions on individual messages">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>

View File

@ -874,6 +874,24 @@ $functions = array(
'type' => 'write',
'capabilities' => 'moodle/course:managegroups'
),
'core_message_mute_conversations' => array(
'classname' => 'core_message_external',
'methodname' => 'mute_conversations',
'classpath' => 'message/externallib.php',
'description' => 'Mutes a list of conversations',
'type' => 'write',
'ajax' => true,
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_message_unmute_conversations' => array(
'classname' => 'core_message_external',
'methodname' => 'unmute_conversations',
'classpath' => 'message/externallib.php',
'description' => 'Unmutes a list of conversations',
'type' => 'write',
'ajax' => true,
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_message_block_user' => array(
'classname' => 'core_message_external',
'methodname' => 'block_user',

View File

@ -2780,5 +2780,31 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2019030700.01);
}
if ($oldversion < 2019030800.00) {
// Define table 'message_conversation_actions' to be created.
// Note - I would have preferred 'message_conversation_user_actions' but due to Oracle we can't. Boo.
$table = new xmldb_table('message_conversation_actions');
// Adding fields to table 'message_conversation_actions'.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('conversationid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('action', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
// Adding keys to table 'message_conversation_actions'.
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
$table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
$table->add_key('conversationid', XMLDB_KEY_FOREIGN, ['conversationid'], 'message_conversations', ['id']);
// Conditionally launch create table for 'message_conversation_actions'.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2019030800.00);
}
return true;
}

View File

@ -1118,6 +1118,51 @@ class core_messagelib_testcase extends advanced_testcase {
$this->assertTrue($storedfileexists);
}
public function test_send_message_when_muted() {
$this->preventResetByRollback();
$this->resetAfterTest();
$userfrom = $this->getDataGenerator()->create_user();
$userto = $this->getDataGenerator()->create_user();
// Create a conversation between the users.
$conversation = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[
$userfrom->id,
$userto->id
]
);
$message = new \core\message\message();
$message->courseid = 1;
$message->component = 'moodle';
$message->name = 'instantmessage';
$message->userfrom = $userfrom;
$message->convid = $conversation->id;
$message->subject = 'message subject 1';
$message->fullmessage = 'message body';
$message->fullmessageformat = FORMAT_MARKDOWN;
$message->fullmessagehtml = '<p>message body</p>';
$message->smallmessage = 'small message';
$message->notification = '0';
$sink = $this->redirectEmails();
message_send($message);
$emails = $sink->get_messages();
$this->assertCount(1, $emails);
$sink->clear();
// Mute the conversation.
\core_message\api::mute_conversation($userto->id, $conversation->id);
$sink = $this->redirectEmails();
message_send($message);
$emails = $sink->get_messages();
$this->assertCount(0, $emails);
$sink->clear();
}
/**
* Is a particular message type in the list of message types.
* @param string $component

View File

@ -1 +1 @@
define([],function(){return{CREATE_CONVERSATION_WITH_USER:"message-drawer-create-conversation-with-user",CONTACT_BLOCKED:"message-drawer-contact-blocked",CONTACT_UNBLOCKED:"message-drawer-contact-unblocked",CONTACT_ADDED:"message-drawer-contact-added",CONTACT_REMOVED:"message-drawer-contact-removed",CONTACT_REQUEST_ACCEPTED:"message-drawer-contact-request-accepted",CONTACT_REQUEST_DECLINED:"message-drawer-contact-request-declined",CONVERSATION_CREATED:"message-drawer-conversation-created",CONVERSATION_NEW_LAST_MESSAGE:"message-drawer-conversation-new-last-message",CONVERSATION_DELETED:"message-drawer-conversation-deleted",CONVERSATION_READ:"message-drawer-conversation-read",CONVERSATION_SET_FAVOURITE:"message-drawer-conversation-set-favourite",CONVERSATION_UNSET_FAVOURITE:"message-drawer-conversation-unset-favourite",PREFERENCES_UPDATED:"message-drawer-preferences-updated",ROUTE_CHANGED:"message-drawer-route-change",SHOW:"message-drawer-show",HIDE:"message-drawer-hide",TOGGLE_VISIBILITY:"message-drawer-toggle",SHOW_CONVERSATION:"message-drawer-show-conversation",SHOW_SETTINGS:"message-drawer-show-settings"}});
define([],function(){return{CREATE_CONVERSATION_WITH_USER:"message-drawer-create-conversation-with-user",CONTACT_BLOCKED:"message-drawer-contact-blocked",CONTACT_UNBLOCKED:"message-drawer-contact-unblocked",CONTACT_ADDED:"message-drawer-contact-added",CONTACT_REMOVED:"message-drawer-contact-removed",CONTACT_REQUEST_ACCEPTED:"message-drawer-contact-request-accepted",CONTACT_REQUEST_DECLINED:"message-drawer-contact-request-declined",CONVERSATION_CREATED:"message-drawer-conversation-created",CONVERSATION_NEW_LAST_MESSAGE:"message-drawer-conversation-new-last-message",CONVERSATION_DELETED:"message-drawer-conversation-deleted",CONVERSATION_READ:"message-drawer-conversation-read",CONVERSATION_SET_FAVOURITE:"message-drawer-conversation-set-favourite",CONVERSATION_SET_MUTED:"message-drawer-conversation-set-muted",CONVERSATION_UNSET_FAVOURITE:"message-drawer-conversation-unset-favourite",CONVERSATION_UNSET_MUTED:"message-drawer-conversation-unset-muted",PREFERENCES_UPDATED:"message-drawer-preferences-updated",ROUTE_CHANGED:"message-drawer-route-change",SHOW:"message-drawer-show",HIDE:"message-drawer-hide",TOGGLE_VISIBILITY:"message-drawer-toggle",SHOW_CONVERSATION:"message-drawer-show-conversation",SHOW_SETTINGS:"message-drawer-show-settings"}});

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
define([],function(){var a={ACTION_ACCEPT_CONTACT_REQUEST:'[data-action="accept-contact-request"]',ACTION_CANCEL_CONFIRM:'[data-action="cancel-confirm"]',ACTION_CANCEL_EDIT_MODE:'[data-action="cancel-edit-mode"]',ACTION_CONFIRM_ADD_CONTACT:'[data-action="confirm-add-contact"]',ACTION_CONFIRM_BLOCK:'[data-action="confirm-block"]',ACTION_CONFIRM_DELETE_SELECTED_MESSAGES:'[data-action="confirm-delete-selected-messages"]',ACTION_CONFIRM_DELETE_CONVERSATION:'[data-action="confirm-delete-conversation"]',ACTION_CONFIRM_FAVOURITE:'[data-action="confirm-favourite"]',ACTION_CONFIRM_UNFAVOURITE:'[data-action="confirm-unfavourite"]',ACTION_CONFIRM_REMOVE_CONTACT:'[data-action="confirm-remove-contact"]',ACTION_CONFIRM_UNBLOCK:'[data-action="confirm-unblock"]',ACTION_DECLINE_CONTACT_REQUEST:'[data-action="decline-contact-request"]',ACTION_REQUEST_ADD_CONTACT:'[data-action="request-add-contact"]',ACTION_REQUEST_BLOCK:'[data-action="request-block"]',ACTION_REQUEST_DELETE_CONVERSATION:'[data-action="request-delete-conversation"]',ACTION_REQUEST_DELETE_SELECTED_MESSAGES:'[data-action="delete-selected-messages"]',ACTION_REQUEST_REMOVE_CONTACT:'[data-action="request-remove-contact"]',ACTION_REQUEST_UNBLOCK:'[data-action="request-unblock"]',ACTION_VIEW_CONTACT:'[data-action="view-contact"]',ACTION_VIEW_GROUP_INFO:'[data-action="view-group-info"]',CAN_RECEIVE_FOCUS:'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',CONFIRM_DIALOGUE_BUTTON_TEXT:'[data-region="dialogue-button-text"]',CONFIRM_DIALOGUE_CANCEL_BUTTON:'[data-action="cancel-confirm"]',CONFIRM_DIALOGUE_CONTAINER:'[data-region="confirm-dialogue-container"]',CONFIRM_DIALOGUE_HEADER:'[data-region="dialogue-header"]',CONFIRM_DIALOGUE_TEXT:'[data-region="dialogue-text"]',CONTACT_REQUEST_SENT_MESSAGE_CONTAINER:'[data-region="contact-request-sent-message-container"]',CONTENT_PLACEHOLDER_CONTAINER:'[data-region="content-placeholder"]',CONTENT_CONTAINER:'[data-region="content-container"]',CONTENT_MESSAGES_CONTAINER:'[data-region="content-message-container"]',CONTENT_MESSAGES_FOOTER_CONTAINER:'[data-region="content-messages-footer-container"]',CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER:'[data-region="content-messages-footer-edit-mode-container"]',CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER:'[data-region="content-messages-footer-require-contact-container"]',CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER:'[data-region="content-messages-footer-require-unblock-container"]',CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER:'[data-region="content-messages-footer-unable-to-message"]',DAY_MESSAGES_CONTAINER:'[data-region="day-messages-container"]',FAVOURITE_ICON_CONTAINER:'[data-region="favourite-icon-container"]',FOOTER_CONTAINER:'[data-region="content-messages-footer-container"]',HEADER:'[data-region="header-content"]',HEADER_EDIT_MODE:'[data-region="header-edit-mode"]',HEADER_PLACEHOLDER_CONTAINER:'[data-region="header-placeholder"]',LOADING_ICON_CONTAINER:'[data-region="loading-icon-container"]',MESSAGE:'[data-region="message"]',MESSAGE_NOT_SELECTED:'[data-region="message"][aria-checked="false"]',MESSAGE_NOT_SELECTED_ICON:'[data-region="not-selected-icon"]',MESSAGE_SELECTED_ICON:'[data-region="selected-icon"]',MESSAGES:'[data-region="content-message-container"]',MESSAGES_CONTAINER:'[data-region="content-message-container"]',MESSAGES_SELECTED_COUNT:'[data-region="message-selected-court"]',MESSAGE_TEXT_AREA:'[data-region="send-message-txt"]',MORE_MESSAGES_LOADING_ICON_CONTAINER:'[data-region="more-messages-loading-icon-container"]',PLACEHOLDER_CONTAINER:'[data-region="placeholder-container"]',SEND_MESSAGE_BUTTON:'[data-action="send-message"]',SEND_MESSAGE_ICON_CONTAINER:'[data-region="send-icon-container"]',TEXT:'[data-region="text"]',TITLE:'[data-region="title"]'},b={HEADER_PRIVATE:"core_message/message_drawer_view_conversation_header_content_type_private",HEADER_PRIVATE_NO_CONTROLS:"core_message/message_drawer_view_conversation_header_content_type_private_no_controls",HEADER_PUBLIC:"core_message/message_drawer_view_conversation_header_content_type_public",DAY:"core_message/message_drawer_view_conversation_body_day",MESSAGE:"core_message/message_drawer_view_conversation_body_message",MESSAGES:"core_message/message_drawer_view_conversation_body_messages"},c={PRIVATE:1,PUBLIC:2};return{SELECTORS:a,TEMPLATES:b,CONVERSATION_TYPES:c,NEWEST_MESSAGES_FIRST:!0,LOAD_MESSAGE_LIMIT:100,INITIAL_NEW_MESSAGE_POLL_TIMEOUT:1e3}});
define([],function(){var a={ACTION_ACCEPT_CONTACT_REQUEST:'[data-action="accept-contact-request"]',ACTION_CANCEL_CONFIRM:'[data-action="cancel-confirm"]',ACTION_CANCEL_EDIT_MODE:'[data-action="cancel-edit-mode"]',ACTION_CONFIRM_ADD_CONTACT:'[data-action="confirm-add-contact"]',ACTION_CONFIRM_BLOCK:'[data-action="confirm-block"]',ACTION_CONFIRM_DELETE_SELECTED_MESSAGES:'[data-action="confirm-delete-selected-messages"]',ACTION_CONFIRM_DELETE_CONVERSATION:'[data-action="confirm-delete-conversation"]',ACTION_CONFIRM_FAVOURITE:'[data-action="confirm-favourite"]',ACTION_CONFIRM_MUTE:'[data-action="confirm-mute"]',ACTION_CONFIRM_UNFAVOURITE:'[data-action="confirm-unfavourite"]',ACTION_CONFIRM_REMOVE_CONTACT:'[data-action="confirm-remove-contact"]',ACTION_CONFIRM_UNBLOCK:'[data-action="confirm-unblock"]',ACTION_CONFIRM_UNMUTE:'[data-action="confirm-unmute"]',ACTION_DECLINE_CONTACT_REQUEST:'[data-action="decline-contact-request"]',ACTION_REQUEST_ADD_CONTACT:'[data-action="request-add-contact"]',ACTION_REQUEST_BLOCK:'[data-action="request-block"]',ACTION_REQUEST_DELETE_CONVERSATION:'[data-action="request-delete-conversation"]',ACTION_REQUEST_DELETE_SELECTED_MESSAGES:'[data-action="delete-selected-messages"]',ACTION_REQUEST_REMOVE_CONTACT:'[data-action="request-remove-contact"]',ACTION_REQUEST_UNBLOCK:'[data-action="request-unblock"]',ACTION_VIEW_CONTACT:'[data-action="view-contact"]',ACTION_VIEW_GROUP_INFO:'[data-action="view-group-info"]',CAN_RECEIVE_FOCUS:'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',CONFIRM_DIALOGUE_BUTTON_TEXT:'[data-region="dialogue-button-text"]',CONFIRM_DIALOGUE_CANCEL_BUTTON:'[data-action="cancel-confirm"]',CONFIRM_DIALOGUE_CONTAINER:'[data-region="confirm-dialogue-container"]',CONFIRM_DIALOGUE_HEADER:'[data-region="dialogue-header"]',CONFIRM_DIALOGUE_TEXT:'[data-region="dialogue-text"]',CONTACT_REQUEST_SENT_MESSAGE_CONTAINER:'[data-region="contact-request-sent-message-container"]',CONTENT_PLACEHOLDER_CONTAINER:'[data-region="content-placeholder"]',CONTENT_CONTAINER:'[data-region="content-container"]',CONTENT_MESSAGES_CONTAINER:'[data-region="content-message-container"]',CONTENT_MESSAGES_FOOTER_CONTAINER:'[data-region="content-messages-footer-container"]',CONTENT_MESSAGES_FOOTER_EDIT_MODE_CONTAINER:'[data-region="content-messages-footer-edit-mode-container"]',CONTENT_MESSAGES_FOOTER_REQUIRE_CONTACT_CONTAINER:'[data-region="content-messages-footer-require-contact-container"]',CONTENT_MESSAGES_FOOTER_REQUIRE_UNBLOCK_CONTAINER:'[data-region="content-messages-footer-require-unblock-container"]',CONTENT_MESSAGES_FOOTER_UNABLE_TO_MESSAGE_CONTAINER:'[data-region="content-messages-footer-unable-to-message"]',DAY_MESSAGES_CONTAINER:'[data-region="day-messages-container"]',FAVOURITE_ICON_CONTAINER:'[data-region="favourite-icon-container"]',FOOTER_CONTAINER:'[data-region="content-messages-footer-container"]',HEADER:'[data-region="header-content"]',HEADER_EDIT_MODE:'[data-region="header-edit-mode"]',HEADER_PLACEHOLDER_CONTAINER:'[data-region="header-placeholder"]',LOADING_ICON_CONTAINER:'[data-region="loading-icon-container"]',MESSAGE:'[data-region="message"]',MESSAGE_NOT_SELECTED:'[data-region="message"][aria-checked="false"]',MESSAGE_NOT_SELECTED_ICON:'[data-region="not-selected-icon"]',MESSAGE_SELECTED_ICON:'[data-region="selected-icon"]',MESSAGES:'[data-region="content-message-container"]',MESSAGES_CONTAINER:'[data-region="content-message-container"]',MESSAGES_SELECTED_COUNT:'[data-region="message-selected-court"]',MESSAGE_TEXT_AREA:'[data-region="send-message-txt"]',MORE_MESSAGES_LOADING_ICON_CONTAINER:'[data-region="more-messages-loading-icon-container"]',MUTED_ICON_CONTAINER:'[data-region="muted-icon-container"]',PLACEHOLDER_CONTAINER:'[data-region="placeholder-container"]',SEND_MESSAGE_BUTTON:'[data-action="send-message"]',SEND_MESSAGE_ICON_CONTAINER:'[data-region="send-icon-container"]',TEXT:'[data-region="text"]',TITLE:'[data-region="title"]'},b={HEADER_PRIVATE:"core_message/message_drawer_view_conversation_header_content_type_private",HEADER_PRIVATE_NO_CONTROLS:"core_message/message_drawer_view_conversation_header_content_type_private_no_controls",HEADER_PUBLIC:"core_message/message_drawer_view_conversation_header_content_type_public",DAY:"core_message/message_drawer_view_conversation_body_day",MESSAGE:"core_message/message_drawer_view_conversation_body_message",MESSAGES:"core_message/message_drawer_view_conversation_body_messages"},c={PRIVATE:1,PUBLIC:2};return{SELECTORS:a,TEMPLATES:b,CONVERSATION_TYPES:c,NEWEST_MESSAGES_FIRST:!0,LOAD_MESSAGE_LIMIT:100,INITIAL_NEW_MESSAGE_POLL_TIMEOUT:1e3}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -35,7 +35,9 @@ define([], function() {
CONVERSATION_DELETED: 'message-drawer-conversation-deleted',
CONVERSATION_READ: 'message-drawer-conversation-read',
CONVERSATION_SET_FAVOURITE: 'message-drawer-conversation-set-favourite',
CONVERSATION_SET_MUTED: 'message-drawer-conversation-set-muted',
CONVERSATION_UNSET_FAVOURITE: 'message-drawer-conversation-unset-favourite',
CONVERSATION_UNSET_MUTED: 'message-drawer-conversation-unset-muted',
PREFERENCES_UPDATED: 'message-drawer-preferences-updated',
ROUTE_CHANGED: 'message-drawer-route-change',
SHOW: 'message-drawer-show',

View File

@ -240,6 +240,7 @@ function(
subname: state.subname,
imageUrl: state.imageUrl,
isFavourite: state.isFavourite,
isMuted: state.isMuted,
type: state.type,
totalMemberCount: state.totalMemberCount,
loggedInUserId: state.loggedInUserId,
@ -328,6 +329,7 @@ function(
newState = StateManager.setImageUrl(newState, imageUrl);
newState = StateManager.setTotalMemberCount(newState, conversation.membercount);
newState = StateManager.setIsFavourite(newState, conversation.isfavourite);
newState = StateManager.setIsMuted(newState, conversation.ismuted);
newState = StateManager.addMessages(newState, conversation.messages);
return newState;
};
@ -795,6 +797,50 @@ function(
});
};
/**
* Set the current conversation as a muted conversation.
*
* @return {Promise} Renderer promise.
*/
var setMuted = function() {
var userId = viewState.loggedInUserId;
var conversationId = viewState.id;
return Repository.setMutedConversations(userId, [conversationId])
.then(function() {
var newState = StateManager.setIsMuted(viewState, true);
return render(newState);
})
.then(function() {
return PubSub.publish(
MessageDrawerEvents.CONVERSATION_SET_MUTED,
formatConversationForEvent(viewState)
);
});
};
/**
* Unset the current conversation as a muted conversation.
*
* @return {Promise} Renderer promise.
*/
var unsetMuted = function() {
var userId = viewState.loggedInUserId;
var conversationId = viewState.id;
return Repository.unsetMutedConversations(userId, [conversationId])
.then(function() {
var newState = StateManager.setIsMuted(viewState, false);
return render(newState);
})
.then(function() {
return PubSub.publish(
MessageDrawerEvents.CONVERSATION_UNSET_MUTED,
formatConversationForEvent(viewState)
);
});
};
/**
* Tell the statemanager there is a request to delete the selected messages
* and run the renderer to show confirm delete messages dialogue.
@ -1211,6 +1257,29 @@ function(
/**
* Show the view group info page.
* Set this conversation as muted.
*
* @param {Object} e Element this event handler is called on.
* @param {Object} data Data for this event.
*/
var handleSetMuted = function(e, data) {
setMuted().catch(Notification.exception);
data.originalEvent.preventDefault();
};
/**
* Unset this conversation as muted.
*
* @param {Object} e Element this event handler is called on.
* @param {Object} data Data for this event.
*/
var handleUnsetMuted = function(e, data) {
unsetMuted().catch(Notification.exception);
data.originalEvent.preventDefault();
};
/**
* Show the view contact page.
*
* @param {String} namespace Unique identifier for the Routes
* @return {Function} View group info handler.
@ -1254,7 +1323,9 @@ function(
[SELECTORS.ACTION_VIEW_CONTACT, generateHandleViewContact(namespace)],
[SELECTORS.ACTION_VIEW_GROUP_INFO, generateHandleViewGroupInfo(namespace)],
[SELECTORS.ACTION_CONFIRM_FAVOURITE, handleSetFavourite],
[SELECTORS.ACTION_CONFIRM_MUTE, handleSetMuted],
[SELECTORS.ACTION_CONFIRM_UNFAVOURITE, handleUnsetFavourite],
[SELECTORS.ACTION_CONFIRM_UNMUTE, handleUnsetMuted]
];
var bodyActivateHandlers = [
[SELECTORS.ACTION_CANCEL_CONFIRM, generateConfirmActionHandler(cancelRequest)],

View File

@ -31,9 +31,11 @@ define([], function() {
ACTION_CONFIRM_DELETE_SELECTED_MESSAGES: '[data-action="confirm-delete-selected-messages"]',
ACTION_CONFIRM_DELETE_CONVERSATION: '[data-action="confirm-delete-conversation"]',
ACTION_CONFIRM_FAVOURITE: '[data-action="confirm-favourite"]',
ACTION_CONFIRM_MUTE: '[data-action="confirm-mute"]',
ACTION_CONFIRM_UNFAVOURITE: '[data-action="confirm-unfavourite"]',
ACTION_CONFIRM_REMOVE_CONTACT: '[data-action="confirm-remove-contact"]',
ACTION_CONFIRM_UNBLOCK: '[data-action="confirm-unblock"]',
ACTION_CONFIRM_UNMUTE: '[data-action="confirm-unmute"]',
ACTION_DECLINE_CONTACT_REQUEST: '[data-action="decline-contact-request"]',
ACTION_REQUEST_ADD_CONTACT: '[data-action="request-add-contact"]',
ACTION_REQUEST_BLOCK: '[data-action="request-block"]',
@ -74,6 +76,7 @@ define([], function() {
MESSAGES_SELECTED_COUNT: '[data-region="message-selected-court"]',
MESSAGE_TEXT_AREA: '[data-region="send-message-txt"]',
MORE_MESSAGES_LOADING_ICON_CONTAINER: '[data-region="more-messages-loading-icon-container"]',
MUTED_ICON_CONTAINER: '[data-region="muted-icon-container"]',
PLACEHOLDER_CONTAINER: '[data-region="placeholder-container"]',
SEND_MESSAGE_BUTTON: '[data-action="send-message"]',
SEND_MESSAGE_ICON_CONTAINER: '[data-region="send-icon-container"]',

View File

@ -298,6 +298,7 @@ function(
totalmembercount: newState.totalMemberCount,
imageurl: newState.imageUrl,
isfavourite: newState.isFavourite,
ismuted: newState.isMuted,
// Don't show favouriting if we don't have a conversation.
showfavourite: newState.id !== null,
userid: newOtherUser.id,
@ -336,6 +337,7 @@ function(
totalmembercount: newState.totalMemberCount,
imageurl: newState.imageUrl,
isfavourite: newState.isFavourite,
ismuted: newState.isMuted,
// Don't show favouriting if we don't have a conversation.
showfavourite: newState.id !== null
}
@ -646,6 +648,39 @@ function(
}
};
/**
* Check if there are any changes the conversation muted state.
*
* @param {Object} state The current state.
* @param {Object} newState The new state.
* @return {string|null}
*/
var buildIsMuted = function(state, newState) {
var oldIsMuted = state.isMuted;
var newIsMuted = newState.isMuted;
if (state.id === null && newState.id === null) {
// The conversation isn't yet created so don't change anything.
return null;
} else if (state.id === null && newState.id !== null) {
// The conversation was created so we can show the mute button.
return 'show-mute';
} else if (state.id !== null && newState.id === null) {
// We're changing from a created conversation to a new conversation so hide
// the muting functionality for now.
return 'hide';
} else if (oldIsMuted == newIsMuted) {
// No change.
return null;
} else if (!oldIsMuted && newIsMuted) {
return 'show-unmute';
} else if (oldIsMuted && !newIsMuted) {
return 'show-mute';
} else {
return null;
}
};
/**
* Check if there are any changes in the contact status of the current user
* and other user.
@ -1130,7 +1165,8 @@ function(
confirmDeleteSelectedMessages: buildConfirmDeleteSelectedMessages,
inEditMode: buildInEditMode,
selectedMessages: buildSelectedMessages,
isFavourite: buildIsFavourite
isFavourite: buildIsFavourite,
isMuted: buildIsMuted
}
};
// These build functions are only applicable to private conversations.

View File

@ -1248,6 +1248,39 @@ function(
}
};
/**
* Show or hide the mute / unmute option in the header dropdown menu
* and the muted icon in the header title.
*
* @param {Object} header The header container element.
* @param {Object} body The body container element.
* @param {Object} footer The footer container element.
* @param {string} state The state of the conversation as defined by the patcher.
*/
var renderIsMuted = function(header, body, footer, state) {
var muteIcon = header.find(SELECTORS.MUTED_ICON_CONTAINER);
var setMuted = header.find(SELECTORS.ACTION_CONFIRM_MUTE);
var unsetMuted = header.find(SELECTORS.ACTION_CONFIRM_UNMUTE);
switch (state) {
case 'hide':
muteIcon.addClass('hidden');
setMuted.addClass('hidden');
unsetMuted.addClass('hidden');
break;
case 'show-mute':
muteIcon.addClass('hidden');
setMuted.removeClass('hidden');
unsetMuted.addClass('hidden');
break;
case 'show-unmute':
muteIcon.removeClass('hidden');
setMuted.addClass('hidden');
unsetMuted.removeClass('hidden');
break;
}
};
/**
* Show or hide the add / remove user as contact option in the header dropdown menu.
*
@ -1476,6 +1509,7 @@ function(
isBlocked: renderIsBlocked,
isContact: renderIsContact,
isFavourite: renderIsFavourite,
isMuted: renderIsMuted,
loadingConfirmAction: renderLoadingConfirmAction,
inEditMode: renderInEditMode
},

View File

@ -121,6 +121,7 @@ define(['jquery'], function($) {
totalMemberCount: null,
imageUrl: null,
isFavourite: null,
isMuted: null,
members: {},
messages: [],
hasTriedToLoadMessages: false,
@ -347,6 +348,19 @@ define(['jquery'], function($) {
return newState;
};
/**
* Set whether the conversation is a muted conversation.
*
* @param {Object} state Current state.
* @param {bool} isMuted If it's muted.
* @return {Object} New state.
*/
var setIsMuted = function(state, isMuted) {
var newState = cloneState(state);
newState.isMuted = isMuted;
return newState;
};
/**
* Set the total member count.
*
@ -659,6 +673,7 @@ define(['jquery'], function($) {
setSubname: setSubname,
setType: setType,
setIsFavourite: setIsFavourite,
setIsMuted: setIsMuted,
setTotalMemberCount: setTotalMemberCount,
setImageUrl: setImageUrl,
setLoadingConfirmAction: setLoadingConfirmAction,

View File

@ -58,6 +58,7 @@ function(
BLOCKED_ICON_CONTAINER: '[data-region="contact-icon-blocked"]',
LAST_MESSAGE: '[data-region="last-message"]',
LAST_MESSAGE_DATE: '[data-region="last-message-date"]',
MUTED_ICON_CONTAINER: '[data-region="muted-icon-container"]',
UNREAD_COUNT: '[data-region="unread-count"]',
SECTION_TOTAL_COUNT: '[data-region="section-total-count"]',
SECTION_TOTAL_COUNT_CONTAINER: '[data-region="section-total-count-container"]',
@ -169,6 +170,7 @@ function(
name: conversation.name,
subname: conversation.subname,
unreadcount: conversation.unreadcount,
ismuted: conversation.ismuted,
lastmessagedate: lastMessage ? lastMessage.timecreated : null,
sentfromcurrentuser: lastMessage ? lastMessage.useridfrom == userId : null,
lastmessage: lastMessage ? $(lastMessage.text).text() || lastMessage.text : null
@ -334,6 +336,24 @@ function(
return root.find('[data-user-id="' + userId + '"]');
};
/**
* Show the conversation is muted icon.
*
* @param {Object} conversationElement The conversation element.
*/
var muteConversation = function(conversationElement) {
conversationElement.find(SELECTORS.MUTED_ICON_CONTAINER).removeClass('hidden');
};
/**
* Hide the conversation is muted icon.
*
* @param {Object} conversationElement The conversation element.
*/
var unmuteConversation = function(conversationElement) {
conversationElement.find(SELECTORS.MUTED_ICON_CONTAINER).addClass('hidden');
};
/**
* Show the contact is blocked icon.
*
@ -515,11 +535,28 @@ function(
PubSub.subscribe(MessageDrawerEvents.CONTACT_UNBLOCKED, function(userId) {
var conversationElement = getConversationElementFromUserId(root, userId);
if (conversationElement.length) {
unblockContact(conversationElement);
}
});
PubSub.subscribe(MessageDrawerEvents.CONVERSATION_SET_MUTED, function(conversation) {
var conversationId = conversation.id;
var conversationElement = getConversationElement(root, conversationId);
if (conversationElement.length) {
muteConversation(conversationElement);
}
});
PubSub.subscribe(MessageDrawerEvents.CONVERSATION_UNSET_MUTED, function(conversation) {
var conversationId = conversation.id;
var conversationElement = getConversationElement(root, conversationId);
if (conversationElement.length) {
unmuteConversation(conversationElement);
}
});
PubSub.subscribe(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, function(conversation) {
if (
(type && conversation.type != type) ||

View File

@ -905,6 +905,42 @@ define(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notificat
return Ajax.call([request])[0];
};
/**
* Set a list of conversations to set as muted for the given user.
*
* @param {int} userId The user id
* @param {array} conversationIds List of conversation ids to set as favourite
* @return {object} jQuery promise
*/
var setMutedConversations = function(userId, conversationIds) {
var request = {
methodname: 'core_message_mute_conversations',
args: {
userid: userId,
conversationids: conversationIds
}
};
return Ajax.call([request])[0];
};
/**
* Set a list of conversations to unset as muted for the given user.
*
* @param {int} userId The user id
* @param {array} conversationIds List of conversation ids to unset as favourite
* @return {object} jQuery promise
*/
var unsetMutedConversations = function(userId, conversationIds) {
var request = {
methodname: 'core_message_unmute_conversations',
args: {
userid: userId,
conversationids: conversationIds
}
};
return Ajax.call([request])[0];
};
/**
* Get a list of user's member info.
*
@ -1060,7 +1096,9 @@ define(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notificat
getConversations: getConversations,
getConversationMembers: getConversationMembers,
setFavouriteConversations: setFavouriteConversations,
setMutedConversations: setMutedConversations,
unsetFavouriteConversations: unsetFavouriteConversations,
unsetMutedConversations: unsetMutedConversations,
getMemberInfo: getMemberInfo,
markAllConversationMessagesAsRead: markAllConversationMessagesAsRead,
getUserMessagePreferences: getUserMessagePreferences,

View File

@ -48,6 +48,11 @@ class api {
*/
const MESSAGE_ACTION_DELETED = 2;
/**
* The action for reading a message.
*/
const CONVERSATION_ACTION_MUTED = 1;
/**
* The privacy setting for being messaged by anyone within courses user is member of.
*/
@ -545,7 +550,7 @@ class api {
$sql = "SELECT m.id as messageid, mc.id as id, mc.name as conversationname, mc.type as conversationtype, m.useridfrom,
m.smallmessage, m.fullmessage, m.fullmessageformat, m.fullmessagehtml, m.timecreated, mc.component,
mc.itemtype, mc.itemid, mc.contextid
mc.itemtype, mc.itemid, mc.contextid, mca.action as ismuted
FROM {message_conversations} mc
INNER JOIN {message_conversation_members} mcm
ON (mcm.conversationid = mc.id AND mcm.userid = :userid3)
@ -569,12 +574,15 @@ class api {
ON lastmessage.conversationid = mc.id
LEFT JOIN {messages} m
ON m.id = lastmessage.messageid
LEFT JOIN {message_conversation_actions} mca
ON (mca.conversationid = mc.id AND mca.userid = :userid4 AND mca.action = :convaction)
WHERE mc.id IS NOT NULL
AND mc.enabled = 1 $typesql $favouritesql
ORDER BY (CASE WHEN m.timecreated IS NULL THEN 0 ELSE 1 END) DESC, m.timecreated DESC, id DESC";
$params = array_merge($favouriteparams, ['userid' => $userid, 'action' => self::MESSAGE_ACTION_DELETED,
'userid2' => $userid, 'userid3' => $userid, 'convtype' => $type]);
'userid2' => $userid, 'userid3' => $userid, 'userid4' => $userid, 'convaction' => self::CONVERSATION_ACTION_MUTED,
'convtype' => $type]);
$conversationset = $DB->get_recordset_sql($sql, $params, $limitfrom, $limitnum);
$conversations = [];
@ -790,6 +798,7 @@ class api {
$conv->isfavourite = in_array($conv->id, $favouriteconversationids);
$conv->isread = isset($unreadcounts[$conv->id]) ? false : true;
$conv->unreadcount = isset($unreadcounts[$conv->id]) ? $unreadcounts[$conv->id]->unreadcount : null;
$conv->ismuted = $conversation->ismuted ? true : false;
$conv->members = $members[$conv->id];
// Add the most recent message information.
@ -950,6 +959,12 @@ class api {
$membercount = $DB->count_records('message_conversation_members', ['conversationid' => $conversationid]);
$ismuted = false;
if ($DB->record_exists('message_conversation_actions', ['userid' => $userid,
'conversationid' => $conversationid, 'action' => self::CONVERSATION_ACTION_MUTED])) {
$ismuted = true;
}
return (object) [
'id' => $conversation->id,
'name' => $conversation->name,
@ -960,6 +975,7 @@ class api {
'isfavourite' => $isfavourite,
'isread' => empty($unreadcount),
'unreadcount' => $unreadcount,
'ismuted' => $ismuted,
'members' => $members,
'messages' => $messages['messages']
];
@ -3133,4 +3149,59 @@ class api {
return $counts;
}
/**
* Handles muting a conversation.
*
* @param int $userid The id of the user
* @param int $conversationid The id of the conversation
*/
public static function mute_conversation(int $userid, int $conversationid) : void {
global $DB;
$mutedconversation = new \stdClass();
$mutedconversation->userid = $userid;
$mutedconversation->conversationid = $conversationid;
$mutedconversation->action = self::CONVERSATION_ACTION_MUTED;
$mutedconversation->timecreated = time();
$DB->insert_record('message_conversation_actions', $mutedconversation);
}
/**
* Handles unmuting a conversation.
*
* @param int $userid The id of the user
* @param int $conversationid The id of the conversation
*/
public static function unmute_conversation(int $userid, int $conversationid) : void {
global $DB;
$DB->delete_records('message_conversation_actions',
[
'userid' => $userid,
'conversationid' => $conversationid,
'action' => self::CONVERSATION_ACTION_MUTED
]
);
}
/**
* Checks whether a conversation is muted or not.
*
* @param int $userid The id of the user
* @param int $conversationid The id of the conversation
* @return bool Whether or not the conversation is muted or not
*/
public static function is_conversation_muted(int $userid, int $conversationid) : bool {
global $DB;
return $DB->record_exists('message_conversation_actions',
[
'userid' => $userid,
'conversationid' => $conversationid,
'action' => self::CONVERSATION_ACTION_MUTED
]
);
}
}

View File

@ -99,6 +99,16 @@ class provider implements
'privacy:metadata:message_conversation_members'
);
$items->add_database_table(
'message_conversation_actions',
[
'conversationid' => 'privacy:metadata:message_conversation_actions:conversationid',
'userid' => 'privacy:metadata:message_conversation_actions:userid',
'timecreated' => 'privacy:metadata:message_conversation_actions:timecreated',
],
'privacy:metadata:message_conversation_actions'
);
$items->add_database_table(
'message_contacts',
[
@ -205,6 +215,8 @@ class provider implements
// It is enough to add the user's context as soon as we come to the conclusion that the user has some data.
// Also, the order of checking is sorted by the probability of occurrence (just by guess).
// There is no need to check the message_user_actions table, as there needs to be a message in order to be a message action.
// There is no need to check the message_conversation_actions table, as there needs to be a conversation in order to
// be a conversation action.
// So, checking messages table would suffice.
$hasdata = false;
@ -258,6 +270,8 @@ class provider implements
// It is enough to add the user's context as soon as we come to the conclusion that the user has some data.
// Also, the order of checking is sorted by the probability of occurrence (just by guess).
// There is no need to check the message_user_actions table, as there needs to be a message in order to be a message action.
// There is no need to check the message_conversation_actions table, as there needs to be a conversation in order to
// be a conversation action.
// So, checking messages table would suffice.
$hasdata = false;
@ -568,6 +582,7 @@ class provider implements
// Delete members and conversations.
$DB->delete_records_list('message_conversation_members', 'conversationid', $conversationids);
$DB->delete_records_list('message_conversation_actions', 'conversationid', $conversationids);
$DB->delete_records_list('message_conversations', 'id', $conversationids);
}
}
@ -673,6 +688,9 @@ class provider implements
// Reuse the $params var because it contains the useridparams and the conversationids.
$DB->delete_records_select('message_conversation_members', $sql, $params);
// Delete any conversation actions.
$DB->delete_records_select('message_conversation_actions', $sql, $params);
// Delete the favourite conversations.
$userlist = new \core_privacy\local\request\approved_userlist($context, 'core_message', $userids);
\core_favourites\privacy\provider::delete_favourites_for_userlist(
@ -763,6 +781,9 @@ class provider implements
// Reuse the $params var because it contains the userid and the conversationids.
$DB->delete_records_select('message_conversation_members', $sql, $params);
// Delete any conversation actions.
$DB->delete_records_select('message_conversation_actions', $sql, $params);
// Delete the favourite conversations.
if (empty($contextids) && empty($component) && empty($itemtype) && empty($itemid)) {
// Favourites for individual conversations are stored into the user context.
@ -974,6 +995,20 @@ class provider implements
// If the conversation has been favorited by the user, include it in the export.
writer::with_context($context)->export_related_data($subcontext, 'starred', (object) $conversationfavourite);
}
// Check if the conversation was muted.
$params = [
'userid' => $userid,
'conversationid' => $conversation->id,
'action' => \core_message\api::CONVERSATION_ACTION_MUTED
];
if ($mca = $DB->get_record('message_conversation_actions', $params)) {
$mcatostore = [
'muted' => transform::yesno(true),
'timecreated' => transform::datetime($mca->timecreated),
];
writer::with_context($context)->export_related_data($subcontext, 'muted', (object) $mcatostore);
}
}
}

View File

@ -410,6 +410,125 @@ class core_message_external extends external_api {
return null;
}
/**
* Mute conversations parameters description.
*
* @return external_function_parameters
*/
public static function mute_conversations_parameters() {
return new external_function_parameters(
[
'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
'conversationids' => new external_multiple_structure(
new external_value(PARAM_INT, 'id of the conversation', VALUE_REQUIRED)
),
]
);
}
/**
* Mutes conversations.
*
* @param int $userid The id of the user who is blocking
* @param array $conversationids The list of conversations being muted
* @return external_description
*/
public static function mute_conversations(int $userid, array $conversationids) {
global $CFG, $USER;
// Check if messaging is enabled.
if (empty($CFG->messaging)) {
throw new moodle_exception('disabled', 'message');
}
// Validate context.
$context = context_system::instance();
self::validate_context($context);
$params = ['userid' => $userid, 'conversationids' => $conversationids];
$params = self::validate_parameters(self::mute_conversations_parameters(), $params);
$capability = 'moodle/site:manageallmessaging';
if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
throw new required_capability_exception($context, $capability, 'nopermissions', '');
}
foreach ($params['conversationids'] as $conversationid) {
if (!\core_message\api::is_conversation_muted($params['userid'], $conversationid)) {
\core_message\api::mute_conversation($params['userid'], $conversationid);
}
}
return [];
}
/**
* Mute conversations return description.
*
* @return external_description
*/
public static function mute_conversations_returns() {
return new external_warnings();
}
/**
* Unmute conversations parameters description.
*
* @return external_function_parameters
*/
public static function unmute_conversations_parameters() {
return new external_function_parameters(
[
'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
'conversationids' => new external_multiple_structure(
new external_value(PARAM_INT, 'id of the conversation', VALUE_REQUIRED)
),
]
);
}
/**
* Unmute conversations.
*
* @param int $userid The id of the user who is unblocking
* @param array $conversationids The list of conversations being muted
*/
public static function unmute_conversations(int $userid, array $conversationids) {
global $CFG, $USER;
// Check if messaging is enabled.
if (empty($CFG->messaging)) {
throw new moodle_exception('disabled', 'message');
}
// Validate context.
$context = context_system::instance();
self::validate_context($context);
$params = ['userid' => $userid, 'conversationids' => $conversationids];
$params = self::validate_parameters(self::unmute_conversations_parameters(), $params);
$capability = 'moodle/site:manageallmessaging';
if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
throw new required_capability_exception($context, $capability, 'nopermissions', '');
}
foreach ($params['conversationids'] as $conversationid) {
\core_message\api::unmute_conversation($params['userid'], $conversationid);
}
return [];
}
/**
* Unmute conversations return description.
*
* @return external_description
*/
public static function unmute_conversations_returns() {
return new external_warnings();
}
/**
* Block user parameters description.
*
@ -1123,7 +1242,8 @@ class core_message_external extends external_api {
'imageurl' => new external_value(PARAM_URL, 'A link to the conversation picture, if set', VALUE_DEFAULT, null),
'type' => new external_value(PARAM_INT, 'The type of the conversation (1=individual,2=group)'),
'membercount' => new external_value(PARAM_INT, 'Total number of conversation members'),
'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked conversation this conversation as a favourite'),
'ismuted' => new external_value(PARAM_BOOL, 'If the user muted this conversation'),
'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked this conversation as a favourite'),
'isread' => new external_value(PARAM_BOOL, 'If the user has read all messages in the conversation'),
'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
VALUE_DEFAULT, null),

View File

@ -61,6 +61,9 @@
<span class="{{^isblocked}}hidden{{/isblocked}}" data-region="contact-icon-blocked">
{{#pix}} t/block, core, {{#str}} contactblocked, message {{/str}} {{/pix}}
</span>
<span class="{{^ismuted}}hidden{{/ismuted}}" data-region="muted-icon-container">
{{#pix}} i/muted, core {{/pix}}
</span>
</div>
{{#subname}}
<p class="m-0 text-truncate">{{.}}</p>

View File

@ -60,6 +60,9 @@
<span class="{{^isfavourite}}hidden{{/isfavourite}} ml-1 text-primary" data-region="favourite-icon-container">
{{#pix}} i/star-rating, core {{/pix}}
</span>
<span class="{{^ismuted}}hidden{{/ismuted}} ml-1 text-primary" data-region="muted-icon-container">
{{#pix}} i/muted, core {{/pix}}
</span>
</div>
{{#showonlinestatus}}
<p class="m-0 font-weight-light text-truncate">
@ -90,6 +93,12 @@
<a class="dropdown-item {{^isblocked}}hidden{{/isblocked}}" href="#" data-action="request-unblock">
{{#str}} unblockuser, core_message {{/str}}
</a>
<a class="dropdown-item {{#ismuted}}hidden{{/ismuted}}" href="#" data-action="confirm-mute">
{{#str}} muteconversation, core_message {{/str}}
</a>
<a class="dropdown-item {{^ismuted}}hidden{{/ismuted}}" href="#" data-action="confirm-unmute">
{{#str}} unmuteconversation, core_message {{/str}}
</a>
<a class="dropdown-item" href="#" data-action="request-delete-conversation">
{{#str}} deleteconversation, core_message {{/str}}
</a>

View File

@ -59,6 +59,9 @@
<span class="{{^isfavourite}}hidden{{/isfavourite}} ml-1 text-primary" data-region="favourite-icon-container">
{{#pix}} i/star-rating, core {{/pix}}
</span>
<span class="{{^ismuted}}hidden{{/ismuted}} ml-1 text-primary" data-region="muted-icon-container">
{{#pix}} i/muted, core {{/pix}}
</span>
</div>
{{#showonlinestatus}}
<p class="m-0 font-weight-light text-truncate">

View File

@ -59,6 +59,9 @@
<span class="{{^isfavourite}}hidden{{/isfavourite}} ml-1 text-primary" data-region="favourite-icon-container">
{{#pix}} i/star-rating, core {{/pix}}
</span>
<span class="{{^ismuted}}hidden{{/ismuted}} ml-1 text-primary" data-region="muted-icon-container">
{{#pix}} i/muted, core {{/pix}}
</span>
</div>
<p class="m-0 text-truncate">{{subname}}</p>
</div>
@ -78,6 +81,12 @@
<a class="dropdown-item {{^isfavourite}}hidden{{/isfavourite}}" href="#" data-action="confirm-unfavourite">
{{#str}} removefromfavourites, core_message {{/str}}
</a>
<a class="dropdown-item {{#ismuted}}hidden{{/ismuted}}" href="#" data-action="confirm-mute">
{{#str}} muteconversation, core_message {{/str}}
</a>
<a class="dropdown-item {{^ismuted}}hidden{{/ismuted}}" href="#" data-action="confirm-unmute">
{{#str}} unmuteconversation, core_message {{/str}}
</a>
</div>
</div>
</div>

View File

@ -1302,6 +1302,50 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
$this->assertEquals($expectedmessagetext, $messages[0]->text);
}
/**
* Test verifying get_conversations identifies if a conversation is muted or not.
*/
public function test_get_conversations_some_muted() {
// Create some users.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
$conversation1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]);
testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'Message 1');
testhelper::send_fake_message_to_conversation($user2, $conversation1->id, 'Message 2');
\core_message\api::mute_conversation($user1->id, $conversation1->id);
$conversation2 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user3->id]);
testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'Message 1');
testhelper::send_fake_message_to_conversation($user2, $conversation2->id, 'Message 2');
$conversation3 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
[$user1->id, $user2->id]);
\core_message\api::mute_conversation($user1->id, $conversation3->id);
$conversation4 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
[$user1->id, $user3->id]);
$conversations = \core_message\api::get_conversations($user1->id);
usort($conversations, function($first, $second){
return $first->id > $second->id;
});
$conv1 = array_shift($conversations);
$conv2 = array_shift($conversations);
$conv3 = array_shift($conversations);
$conv4 = array_shift($conversations);
$this->assertTrue($conv1->ismuted);
$this->assertFalse($conv2->ismuted);
$this->assertTrue($conv3->ismuted);
$this->assertFalse($conv4->ismuted);
}
/**
* Tests retrieving conversations with a limit and offset to ensure pagination works correctly.
*/
@ -4912,6 +4956,84 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
$this->assertEquals(0, $DB->count_records('message_users_blocked'));
}
/**
* Test muting a conversation.
*/
public function test_mute_conversation() {
global $DB;
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$conversation = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[
$user1->id,
$user2->id
]
);
$conversationid = $conversation->id;
\core_message\api::mute_conversation($user1->id, $conversationid);
$mutedconversation = $DB->get_records('message_conversation_actions');
$this->assertCount(1, $mutedconversation);
$mutedconversation = reset($mutedconversation);
$this->assertEquals($user1->id, $mutedconversation->userid);
$this->assertEquals($conversationid, $mutedconversation->conversationid);
$this->assertEquals(\core_message\api::CONVERSATION_ACTION_MUTED, $mutedconversation->action);
}
/**
* Test unmuting a conversation.
*/
public function test_unmute_conversation() {
global $DB;
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$conversation = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[
$user1->id,
$user2->id
]
);
$conversationid = $conversation->id;
\core_message\api::mute_conversation($user1->id, $conversationid);
\core_message\api::unmute_conversation($user1->id, $conversationid);
$this->assertEquals(0, $DB->count_records('message_conversation_actions'));
}
/**
* Test if a conversation is muted.
*/
public function test_is_conversation_muted() {
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$conversation = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[
$user1->id,
$user2->id
]
);
$conversationid = $conversation->id;
$this->assertFalse(\core_message\api::is_conversation_muted($user1->id, $conversationid));
\core_message\api::mute_conversation($user1->id, $conversationid);
$this->assertTrue(\core_message\api::is_conversation_muted($user1->id, $conversationid));
}
/**
* Test is contact check.
*/
@ -5772,6 +5894,32 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
\core_message\api::send_message_to_conversation($user2->id, $ic1->id, 'test', FORMAT_MOODLE);
}
/**
* Test the get_conversation() function with a muted conversation.
*/
public function test_get_conversation_with_muted_conversation() {
$this->resetAfterTest();
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$this->setUser($user1);
$conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]);
$conversation = \core_message\api::get_conversation($user1->id, $conversation->id);
$this->assertFalse($conversation->ismuted);
// Now, mute the conversation.
\core_message\api::mute_conversation($user1->id, $conversation->id);
$conversation = \core_message\api::get_conversation($user1->id, $conversation->id);
$this->assertTrue($conversation->ismuted);
}
/**
* Data provider for test_get_conversation_counts().
*/

View File

@ -1022,6 +1022,168 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
core_message_external::decline_contact_request($user1->id, $user2->id);
}
/**
* Test muting conversations.
*/
public function test_mute_conversations() {
global $DB;
$this->resetAfterTest(true);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]);
$this->setUser($user1);
// Muting a conversation.
$return = core_message_external::mute_conversations($user1->id, [$conversation->id]);
$return = external_api::clean_returnvalue(core_message_external::mute_conversations_returns(), $return);
$this->assertEquals(array(), $return);
// Get list of muted conversations.
$mca = $DB->get_record('message_conversation_actions', []);
$this->assertEquals($user1->id, $mca->userid);
$this->assertEquals($conversation->id, $mca->conversationid);
$this->assertEquals(\core_message\api::CONVERSATION_ACTION_MUTED, $mca->action);
// Muting a conversation that is already muted.
$return = core_message_external::mute_conversations($user1->id, [$conversation->id]);
$return = external_api::clean_returnvalue(core_message_external::mute_conversations_returns(), $return);
$this->assertEquals(array(), $return);
$this->assertEquals(1, $DB->count_records('message_conversation_actions'));
}
/**
* Test muting a conversation with messaging disabled.
*/
public function test_mute_conversations_messaging_disabled() {
global $CFG;
$this->resetAfterTest();
// Create some skeleton data just so we can call the WS.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]);
$this->setUser($user1);
// Disable messaging.
$CFG->messaging = 0;
// Ensure an exception is thrown.
$this->expectException('moodle_exception');
core_message_external::mute_conversations($user1->id, [$conversation->id]);
}
/**
* Test muting a conversation with no permission.
*/
public function test_mute_conversations_no_permission() {
$this->resetAfterTest();
// Create some skeleton data just so we can call the WS.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
$conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]);
$this->setUser($user3);
// Ensure an exception is thrown.
$this->expectException('required_capability_exception');
core_message_external::mute_conversations($user1->id, [$conversation->id]);
}
/**
* Test unmuting conversations.
*/
public function test_unmute_conversations() {
global $DB;
$this->resetAfterTest(true);
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]);
$this->setUser($user1);
// Mute the conversation.
\core_message\api::mute_conversation($user1->id, $conversation->id);
// Unmuting a conversation.
$return = core_message_external::unmute_conversations($user1->id, [$conversation->id]);
$return = external_api::clean_returnvalue(core_message_external::unmute_conversations_returns(), $return);
$this->assertEquals(array(), $return);
$this->assertEquals(0, $DB->count_records('message_conversation_actions'));
// Unmuting a conversation which is already unmuted.
$return = core_message_external::unmute_conversations($user1->id, [$conversation->id]);
$return = external_api::clean_returnvalue(core_message_external::unmute_conversations_returns(), $return);
$this->assertEquals(array(), $return);
$this->assertEquals(0, $DB->count_records('message_conversation_actions'));
}
/**
* Test unmuting a conversation with messaging disabled.
*/
public function test_unmute_conversation_messaging_disabled() {
global $CFG;
$this->resetAfterTest();
// Create some skeleton data just so we can call the WS.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]);
$this->setUser($user1);
// Disable messaging.
$CFG->messaging = 0;
// Ensure an exception is thrown.
$this->expectException('moodle_exception');
core_message_external::unmute_conversations($user1->id, [$user2->id]);
}
/**
* Test unmuting a conversation with no permission.
*/
public function test_unmute_conversation_no_permission() {
$this->resetAfterTest();
// Create some skeleton data just so we can call the WS.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
$conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]);
$this->setUser($user3);
// Ensure an exception is thrown.
$this->expectException('required_capability_exception');
core_message_external::unmute_conversations($user1->id, [$conversation->id]);
}
/**
* Test blocking a user.
*/
@ -5654,6 +5816,55 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
$this->assertNotEmpty($individualmember['contactrequests']);
}
/**
* Test verifying get_conversations identifies if a conversation is muted or not.
*/
public function test_get_conversations_some_muted() {
$this->resetAfterTest();
// Create some users.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
$conversation1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]);
testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'Message 1');
testhelper::send_fake_message_to_conversation($user2, $conversation1->id, 'Message 2');
\core_message\api::mute_conversation($user1->id, $conversation1->id);
$conversation2 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user3->id]);
testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'Message 1');
testhelper::send_fake_message_to_conversation($user2, $conversation2->id, 'Message 2');
$conversation3 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
[$user1->id, $user2->id]);
\core_message\api::mute_conversation($user1->id, $conversation3->id);
$conversation4 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
[$user1->id, $user3->id]);
$this->setUser($user1);
$result = core_message_external::get_conversations($user1->id);
$result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
$conversations = $result['conversations'];
usort($conversations, function($first, $second){
return $first['id'] > $second['id'];
});
$conv1 = array_shift($conversations);
$conv2 = array_shift($conversations);
$conv3 = array_shift($conversations);
$conv4 = array_shift($conversations);
$this->assertTrue($conv1['ismuted']);
$this->assertFalse($conv2['ismuted']);
$this->assertTrue($conv3['ismuted']);
$this->assertFalse($conv4['ismuted']);
}
/**
* Test returning members in a conversation with no contact requests.
*/

View File

@ -47,7 +47,7 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$collection = new collection('core_message');
$newcollection = provider::get_metadata($collection);
$itemcollection = $newcollection->get_collection();
$this->assertCount(9, $itemcollection);
$this->assertCount(10, $itemcollection);
$messagestable = array_shift($itemcollection);
$this->assertEquals('messages', $messagestable->get_name());
@ -58,6 +58,9 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$messageconversationmemberstable = array_shift($itemcollection);
$this->assertEquals('message_conversation_members', $messageconversationmemberstable->get_name());
$messageconversationactions = array_shift($itemcollection);
$this->assertEquals('message_conversation_actions', $messageconversationactions->get_name());
$messagecontacts = array_shift($itemcollection);
$this->assertEquals('message_contacts', $messagecontacts->get_name());
@ -1584,6 +1587,10 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
\core_message\api::set_favourite_conversation($conversation1->id, $user1->id);
\core_message\api::set_favourite_conversation($iconversation1id, $user2->id);
// Mute some conversations.
\core_message\api::mute_conversation($user1->id, $conversation1->id);
\core_message\api::mute_conversation($user2->id, $iconversation1id);
// Send some messages to the conversation.
$m1 = testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'Message 1', $now + 1);
$m2 = testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'Message 2', $now + 2);
@ -1646,6 +1653,15 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$this->assertEquals(transform::datetime($now + 5), $m3->timeread);
$this->assertArrayNotHasKey('timedeleted', (array) $m3);
// Confirm the muted group conversation is correct.
$mutedconversations = (array) $writer->get_related_data([
get_string('messages', 'core_message'),
get_string($conversation1->itemtype, $conversation1->component),
get_string('privacy:export:conversationprefix', 'core_message') . $conversation1->name
], 'muted');
$this->assertCount(2, $mutedconversations);
$this->assertEquals(get_string('yes'), $mutedconversations['muted']);
// Confirm the favourite group conversation is correct.
$favourite = (array) $writer->get_related_data([
get_string('messages', 'core_message'),
@ -1707,6 +1723,14 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$this->assertEquals('-', $m3->timeread);
$this->assertArrayNotHasKey('timedeleted', (array) $m3);
// Confirm the muted group conversation is correct.
$mutedconversations = (array) $writer->get_related_data([
get_string('messages', 'core_message'),
get_string($conversation1->itemtype, $conversation1->component),
$conversation1->name
], 'muted');
$this->assertCount(0, $mutedconversations);
// Confirm there are no favourite group conversation for user2.
$favourite = (array) $writer->get_related_data([
get_string('messages', 'core_message'),
@ -1822,6 +1846,11 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// Mark as read one of the conversation messages.
\core_message\api::mark_message_as_read($user1->id, $dbgm3, $now + 5);
// Mark some conversations as muted by two users.
\core_message\api::mute_conversation($user1->id, $iconversation1id);
\core_message\api::mute_conversation($user1->id, $conversation1->id);
\core_message\api::mute_conversation($user2->id, $conversation1->id);
// There should be 2 contacts.
$this->assertEquals(2, $DB->count_records('message_contacts'));
@ -1837,6 +1866,9 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// There should be 4 user actions - 3 for reading the message, 1 for deleting.
$this->assertEquals(4, $DB->count_records('message_user_actions'));
// There should be 3 muted conversations.
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
// There should be 4 conversations - 2 individual + 2 group.
$this->assertEquals(4, $DB->count_records('message_conversations'));
@ -1861,7 +1893,10 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// There should be still 2 blocked users.
$this->assertEquals(2, $DB->count_records('message_users_blocked'));
// There should be 5 notifications.
// There should be 1 muted conversation.
$this->assertEquals(1, $DB->count_records('message_conversation_actions'));
// There should be 3 notifications.
$this->assertEquals(5, $DB->count_records('notifications'));
// There should be 5 messages - 3 individual - 2 group (course2).
@ -1992,6 +2027,11 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$dbgm3 = $DB->get_record('messages', ['id' => $gm3]);
// Mark some conversations as muted by two users.
\core_message\api::mute_conversation($user1->id, $iconversation1id);
\core_message\api::mute_conversation($user1->id, $conversation1->id);
\core_message\api::mute_conversation($user2->id, $conversation1->id);
// Mark as read one of the conversation messages.
\core_message\api::mark_message_as_read($user1->id, $dbgm3, $now + 5);
@ -2010,6 +2050,9 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// There should be 4 user actions - 3 for reading the message, 1 for deleting.
$this->assertEquals(4, $DB->count_records('message_user_actions'));
// There should be 3 muted conversations.
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
// There should be 4 conversations - 2 individual + 2 group.
$this->assertEquals(4, $DB->count_records('message_conversations'));
@ -2031,6 +2074,7 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$this->assertEquals(2, $DB->count_records('message_users_blocked'));
$this->assertEquals(8, $DB->count_records('messages'));
$this->assertEquals(4, $DB->count_records('message_user_actions'));
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
$this->assertEquals(4, $DB->count_records('message_conversations'));
$this->assertEquals(9, $DB->count_records('message_conversation_members'));
$this->assertEquals(5, $DB->count_records('notifications'));
@ -2045,6 +2089,7 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$this->assertEquals(2, $DB->count_records('message_users_blocked'));
$this->assertEquals(8, $DB->count_records('messages'));
$this->assertEquals(4, $DB->count_records('message_user_actions'));
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
$this->assertEquals(4, $DB->count_records('message_conversations'));
$this->assertEquals(9, $DB->count_records('message_conversation_members'));
$this->assertEquals(5, $DB->count_records('notifications'));
@ -2154,6 +2199,11 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// Mark as read one of the conversation messages.
\core_message\api::mark_message_as_read($user1->id, $dbgm3, $now + 5);
// Mark some of the conversations as muted by two users.
\core_message\api::mute_conversation($user1->id, $iconversation1id);
\core_message\api::mute_conversation($user1->id, $conversation1->id);
\core_message\api::mute_conversation($user2->id, $conversation1->id);
// There should be 2 contacts.
$this->assertEquals(2, $DB->count_records('message_contacts'));
@ -2169,6 +2219,9 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// There should be 4 user actions - 3 for reading the message, 1 for deleting.
$this->assertEquals(4, $DB->count_records('message_user_actions'));
// There should be 3 muted conversations.
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
// There should be 4 conversations - 2 individual + 2 group.
$this->assertEquals(4, $DB->count_records('message_conversations'));
@ -2190,6 +2243,7 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$this->assertEquals(2, $DB->count_records('message_users_blocked'));
$this->assertEquals(8, $DB->count_records('messages'));
$this->assertEquals(4, $DB->count_records('message_user_actions'));
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
$this->assertEquals(4, $DB->count_records('message_conversations'));
$this->assertEquals(9, $DB->count_records('message_conversation_members'));
$this->assertEquals(5, $DB->count_records('notifications'));
@ -2204,6 +2258,7 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$this->assertEquals(2, $DB->count_records('message_users_blocked'));
$this->assertEquals(8, $DB->count_records('messages'));
$this->assertEquals(4, $DB->count_records('message_user_actions'));
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
$this->assertEquals(4, $DB->count_records('message_conversations'));
$this->assertEquals(9, $DB->count_records('message_conversation_members'));
$this->assertEquals(5, $DB->count_records('notifications'));
@ -2301,6 +2356,11 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// Mark as read one of the conversation messages.
\core_message\api::mark_message_as_read($user1->id, $dbm3, $now + 5);
// Mark some of the conversations as muted by two users.
\core_message\api::mute_conversation($user1->id, $iconversation1id);
\core_message\api::mute_conversation($user1->id, $conversation1->id);
\core_message\api::mute_conversation($user2->id, $conversation1->id);
// There should be 2 contacts.
$this->assertEquals(2, $DB->count_records('message_contacts'));
@ -2319,6 +2379,9 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// There should be 4 user actions - 3 for reading the message, one for deleting.
$this->assertEquals(4, $DB->count_records('message_user_actions'));
// There should be 3 users muting a conversation.
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
// There should be 3 conversations - 2 private + 1 group.
$this->assertEquals(3, $DB->count_records('message_conversations'));
@ -2347,7 +2410,10 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// There should be still 2 blocked users.
$this->assertEquals(2, $DB->count_records('message_users_blocked'));
// There should be 5 notifications.
// There should be 2 muted conversation.
$this->assertEquals(2, $DB->count_records('message_conversation_actions'));
// There should be 3 notifications.
$this->assertEquals(5, $DB->count_records('notifications'));
// There should be 4 messages - 3 private + 1 group sent by user2.
@ -2484,6 +2550,11 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// Mark as read one of the conversation messages.
\core_message\api::mark_message_as_read($user1->id, $dbm3, $now + 5);
// Mark some of the conversations as muted by two users.
\core_message\api::mute_conversation($user1->id, $iconversation1id);
\core_message\api::mute_conversation($user1->id, $conversation1->id);
\core_message\api::mute_conversation($user2->id, $conversation1->id);
// There should be 2 contacts.
$this->assertEquals(2, $DB->count_records('message_contacts'));
@ -2502,6 +2573,9 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
// There should be 4 user actions - 3 for reading the message, one for deleting.
$this->assertEquals(4, $DB->count_records('message_user_actions'));
// There should be 3 muted conversation.
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
// There should be 3 conversations - 2 private + 2 group.
$this->assertEquals(4, $DB->count_records('message_conversations'));
@ -2529,6 +2603,7 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$this->assertEquals(5, $DB->count_records('notifications'));
$this->assertEquals(6, $DB->count_records('messages'));
$this->assertEquals(4, $DB->count_records('message_user_actions'));
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
$this->assertEquals(4, $DB->count_records('message_conversations'));
$this->assertEquals(8, $DB->count_records('message_conversation_members'));
$this->assertEquals(3, $DB->count_records('favourite'));
@ -2545,6 +2620,7 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
$this->assertEquals(5, $DB->count_records('notifications'));
$this->assertEquals(6, $DB->count_records('messages'));
$this->assertEquals(4, $DB->count_records('message_user_actions'));
$this->assertEquals(3, $DB->count_records('message_conversation_actions'));
$this->assertEquals(4, $DB->count_records('message_conversations'));
$this->assertEquals(3, $DB->count_records('favourite'));
// There should be 7 conversation members - (2 + 2) private + 3 group.
@ -2583,6 +2659,9 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
}, $useractions);
$this->assertNotContains($gm3, $useractions);
// There should be 1 muted conversation.
$this->assertEquals(1, $DB->count_records('message_conversation_actions'));
// There should be still 4 conversations - 2 private + 2 group.
$this->assertEquals(4, $DB->count_records('message_conversations'));

BIN
pix/i/muted.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

1
pix/i/muted.svg Executable file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>Artboard</title><path d="M6.72,2.22,3.94,5H.75A.75.75,0,0,0,0,5.75v4.5A.75.75,0,0,0,.75,11H3.94l2.78,2.78A.75.75,0,0,0,8,13.25V2.75a.75.75,0,0,0-1.28-.53ZM14.43,8l1.43-1.43a.5.5,0,0,0,0-.71h0l-.71-.71a.5.5,0,0,0-.71,0h0L13,6.57,11.57,5.15a.5.5,0,0,0-.71,0h0l-.71.71a.5.5,0,0,0,0,.71h0L11.57,8,10.15,9.43a.5.5,0,0,0,0,.71h0l.71.71a.5.5,0,0,0,.71,0h0L13,9.43l1.43,1.43a.5.5,0,0,0,.71,0h0l.71-.71a.5.5,0,0,0,0-.71h0Z" fill="#999"/></svg>

After

Width:  |  Height:  |  Size: 501 B

View File

@ -65,6 +65,9 @@
<span class="{{^isfavourite}}hidden{{/isfavourite}} ml-1 text-primary" data-region="favourite-icon-container">
{{#pix}} i/star, core {{/pix}}
</span>
<span class="{{^ismuted}}hidden{{/ismuted}} ml-1 text-primary" data-region="muted-icon-container">
{{#pix}} i/muted, core {{/pix}}
</span>
</div>
{{#showonlinestatus}}
<p class="m-0 font-weight-light text-truncate">
@ -105,6 +108,16 @@
{{#str}} unblockuser, core_message {{/str}}
</a>
</li>
<li class="{{#ismuted}}hidden{{/ismuted}}" data-action="confirm-mute">
<a class="dropdown-item" href="#">
{{#str}} muteconversation, core_message {{/str}}
</a>
</li>
<li class="{{^ismuted}}hidden{{/ismuted}}" data-action="confirm-unmute">
<a class="dropdown-item" href="#">
{{#str}} unmuteconversation, core_message {{/str}}
</a>
</li>
<li data-action="request-delete-conversation">
<a class="dropdown-item" href="#">
{{#str}} deleteconversation, core_message {{/str}}

View File

@ -64,6 +64,9 @@
<span class="{{^isfavourite}}hidden{{/isfavourite}} ml-1 text-primary" data-region="favourite-icon-container">
{{#pix}} i/star, core {{/pix}}
</span>
<span class="{{^ismuted}}hidden{{/ismuted}} ml-1 text-primary" data-region="muted-icon-container">
{{#pix}} i/muted, core {{/pix}}
</span>
</div>
{{#showonlinestatus}}
<p class="m-0 font-weight-light text-truncate">

View File

@ -63,6 +63,9 @@
<span class="{{^isfavourite}}hidden{{/isfavourite}} ml-1 text-primary" data-region="favourite-icon-container">
{{#pix}} i/star, core {{/pix}}
</span>
<span class="{{^ismuted}}hidden{{/ismuted}} ml-1 text-primary" data-region="muted-icon-container">
{{#pix}} i/muted, core {{/pix}}
</span>
</div>
<p class="m-0 text-truncate">{{subname}}</p>
</div>
@ -78,6 +81,16 @@
{{#str}} groupinfo, core_message {{/str}}
</a>
</li>
<li class="{{#ismuted}}hidden{{/ismuted}}" data-action="confirm-mute">
<a class="dropdown-item" href="#">
{{#str}} muteconversation, core_message {{/str}}
</a>
</li>
<li class="{{^ismuted}}hidden{{/ismuted}}" data-action="confirm-unmute">
<a class="dropdown-item" href="#">
{{#str}} unmuteconversation, core_message {{/str}}
</a>
</li>
<li class="{{#isfavourite}}hidden{{/isfavourite}}" data-action="confirm-favourite">
<a class="dropdown-item" href="#">
{{#str}} addtofavourites, core_message {{/str}}

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2019030700.01; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2019030800.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.