Merge branch 'MDL-65132-master' of git://github.com/cescobedo/moodle

This commit is contained in:
Andrew Nicols
2019-05-07 16:24:40 +08:00
committed by Adrian Greeve
19 changed files with 626 additions and 32 deletions
+2
View File
@@ -55,6 +55,8 @@ $string['deleteallconfirm'] = "Are you sure you would like to delete this entire
$string['deleteallmessages'] = "Delete all messages";
$string['deleteallselfconfirm'] = "Are you sure you would like to delete this entire personal conversation?";
$string['deleteconversation'] = "Delete conversation";
$string['deleteforeveryone'] = "Delete for me and everyone";
$string['deleteforeveryoneselectedmessagesconfirm'] = 'Are you sure you would like to delete the selected messages?';
$string['deleteselectedmessages'] = 'Delete selected messages';
$string['deleteselectedmessagesconfirm'] = 'Are you sure you would like to delete the selected messages? This will not delete them for other conversation participants.';
$string['deleteselectedmessagesconfirmselfconversation'] = 'Are you sure you would like to delete the selected personal messages?';
+10
View File
@@ -1407,6 +1407,16 @@ $functions = array(
'ajax' => true,
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_message_delete_message_for_all_users' => array(
'classname' => 'core_message_external',
'methodname' => 'delete_message_for_all_users',
'classpath' => 'message/externallib.php',
'description' => 'Deletes a message for all users.',
'type' => 'write',
'capabilities' => 'moodle/site:deleteanymessage',
'ajax' => true,
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_notes_create_notes' => array(
'classname' => 'core_notes_external',
'methodname' => 'create_notes',
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
File diff suppressed because one or more lines are too long
@@ -350,6 +350,7 @@ function(
newState = StateManager.setIsFavourite(newState, conversation.isfavourite);
newState = StateManager.setIsMuted(newState, conversation.ismuted);
newState = StateManager.addMessages(newState, conversation.messages);
newState = StateManager.setCanDeleteMessagesForAllUsers(newState, conversation.candeletemessagesforallusers);
return newState;
};
@@ -886,6 +887,10 @@ function(
var newState = StateManager.setLoadingConfirmAction(viewState, true);
return render(newState)
.then(function() {
if (newState.deleteMessagesForAllUsers) {
return Repository.deleteMessagesForAllUsers(viewState.loggedInUserId, messageIds);
}
return Repository.deleteMessages(viewState.loggedInUserId, messageIds);
})
.then(function() {
@@ -893,6 +898,7 @@ function(
newState = StateManager.removePendingDeleteMessagesById(newState, messageIds);
newState = StateManager.removeSelectedMessagesById(newState, messageIds);
newState = StateManager.setLoadingConfirmAction(newState, false);
newState = StateManager.setDeleteMessagesForAllUsers(newState, false);
var prevLastMessage = viewState.messages[viewState.messages.length - 1];
var newLastMessage = newState.messages.length ? newState.messages[newState.messages.length - 1] : null;
@@ -959,6 +965,7 @@ function(
newState = StateManager.removePendingBlockUsersById(newState, [userId]);
newState = StateManager.removePendingDeleteMessagesById(newState, pendingDeleteMessageIds);
newState = StateManager.setPendingDeleteConversation(newState, false);
newState = StateManager.setDeleteMessagesForAllUsers(newState, false);
return render(newState);
};
@@ -1037,6 +1044,7 @@ function(
isSendingMessage = true;
var newState = StateManager.setSendingMessage(viewState, true);
var newConversationId = null;
var newCanDeleteMessagesForAllUsers = false;
return render(newState)
.then(function() {
if (!conversationId && (viewState.type != CONVERSATION_TYPES.PUBLIC)) {
@@ -1046,6 +1054,7 @@ function(
return Repository.sendMessageToUser(otherUserId, text)
.then(function(message) {
newConversationId = parseInt(message.conversationid, 10);
newCanDeleteMessagesForAllUsers = message.candeletemessagesforallusers;
return message;
});
} else {
@@ -1064,6 +1073,7 @@ function(
conversation.id = newConversationId;
resetMessagePollTimer(newConversationId);
PubSub.publish(MessageDrawerEvents.CONVERSATION_CREATED, conversation);
newState = StateManager.setCanDeleteMessagesForAllUsers(newState, newCanDeleteMessagesForAllUsers);
}
return render(newState)
@@ -1298,6 +1308,18 @@ function(
data.originalEvent.preventDefault();
};
/**
* Handle clicking on the checkbox that toggles deleting messages for
* all users.
*
* @param {Object} e Element this event handler is called on.
*/
var handleDeleteMessagesForAllUsersToggle = function(e) {
var newValue = $(e.target).prop('checked');
var newState = StateManager.setDeleteMessagesForAllUsers(viewState, newValue);
render(newState);
};
/**
* Show the view contact page.
*
@@ -1358,7 +1380,8 @@ function(
[SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],
[SELECTORS.ACTION_ACCEPT_CONTACT_REQUEST, generateConfirmActionHandler(acceptContactRequest)],
[SELECTORS.ACTION_DECLINE_CONTACT_REQUEST, generateConfirmActionHandler(declineContactRequest)],
[SELECTORS.MESSAGE, handleSelectMessage]
[SELECTORS.MESSAGE, handleSelectMessage],
[SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE, handleDeleteMessagesForAllUsersToggle]
];
var footerActivateHandlers = [
[SELECTORS.SEND_MESSAGE_BUTTON, handleSendMessage],
@@ -61,6 +61,8 @@ define([], function() {
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"]',
DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE: '[data-region="delete-messages-for-all-users-toggle"]',
DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER: '[data-region="delete-messages-for-all-users-toggle-container"]',
FAVOURITE_ICON_CONTAINER: '[data-region="favourite-icon-container"]',
FOOTER_CONTAINER: '[data-region="content-messages-footer-container"]',
HEADER: '[data-region="header-content"]',
@@ -562,13 +562,22 @@ function(
*
* @param {Object} state The current state.
* @param {Object} newState The new state.
* @return {int|Null} The conversation type of the messages to be deleted.
* @return {Object|Null} The conversation type and if the user can delete the messages for all users.
*/
var buildConfirmDeleteSelectedMessages = function(state, newState) {
if (newState.pendingDeleteMessageIds.length) {
return newState.type;
} else if (state.pendingDeleteMessageIds.length) {
return false;
var oldPendingCount = state.pendingDeleteMessageIds.length;
var newPendingCount = newState.pendingDeleteMessageIds.length;
if (newPendingCount && !oldPendingCount) {
return {
show: true,
type: newState.type,
canDeleteMessagesForAllUsers: newState.canDeleteMessagesForAllUsers
};
} else if (oldPendingCount && !newPendingCount) {
return {
show: false
};
}
return null;
@@ -1026,6 +1026,7 @@ function(
var text = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_TEXT);
var dialogueHeader = dialogue.find(SELECTORS.CONFIRM_DIALOGUE_HEADER);
hideCheckDeleteDialogue(body);
hideConfirmDialogueContainer(body);
hideConfirmDialogueContainer(footer);
hideConfirmDialogueContainer(header);
@@ -1143,20 +1144,25 @@ function(
* @param {Object} header The header container element.
* @param {Object} body The body container element.
* @param {Object} footer The footer container element.
* @param {int|Null} type The messages conversation type to be removed.
* @param {Object} data If the dialogue should show and checkbox shows to delete message for all users.
* @return {Object} jQuery promise
*/
var renderConfirmDeleteSelectedMessages = function(header, body, footer, type) {
var renderConfirmDeleteSelectedMessages = function(header, body, footer, data) {
var showmessage = null;
if (type == CONVERSATION_TYPES.SELF) {
if (data.type == CONVERSATION_TYPES.SELF) {
// Message displayed to self-conversations is slighly different.
showmessage = 'deleteselectedmessagesconfirmselfconversation';
} else if (type) {
} else {
// This other message should be displayed.
showmessage = 'deleteselectedmessagesconfirm';
if (data.canDeleteMessagesForAllUsers) {
showCheckDeleteDialogue(body);
showmessage = 'deleteforeveryoneselectedmessagesconfirm';
} else {
showmessage = 'deleteselectedmessagesconfirm';
}
}
if (showmessage) {
if (data.show) {
return Str.get_string(showmessage, 'core_message')
.then(function(string) {
return showConfirmDialogue(
@@ -1237,6 +1243,30 @@ function(
}
};
/**
* Show the checkbox to allow delete message for all.
*
* @param {Object} body The body container element.
*/
var showCheckDeleteDialogue = function(body) {
var dialogue = getConfirmDialogueContainer(body);
var checkboxRegion = dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER);
checkboxRegion.removeClass('hidden');
};
/**
* Hide the checkbox to allow delete message for all.
*
* @param {Object} body The body container element.
*/
var hideCheckDeleteDialogue = function(body) {
var dialogue = getConfirmDialogueContainer(body);
var checkboxRegion = dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER);
var checkbox = dialogue.find(SELECTORS.DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE);
checkbox.prop('checked', false);
checkboxRegion.addClass('hidden');
};
/**
* Show or hide the block / unblock option in the header dropdown menu.
*
@@ -40,18 +40,11 @@ define(['jquery'], function($) {
* @return {Object} newstate A copy of the state to clone.
*/
var cloneState = function(state) {
var newState = $.extend({}, state);
newState.messages = state.messages.map(function(message) {
return $.extend({}, message);
});
newState.members = Object.keys(state.members).reduce(function(carry, id) {
carry[id] = $.extend({}, state.members[id]);
carry[id].contactrequests = state.members[id].contactrequests.map(function(request) {
return $.extend({}, request);
});
return carry;
}, {});
return newState;
// Do a deep extend to make sure we recursively copy objects and
// arrays so that the new state doesn't contain any references to
// the old state, e.g. adding a value to an array in the new state
// shouldn't also add it to the old state.
return $.extend(true, {}, state);
};
/**
@@ -122,6 +115,8 @@ define(['jquery'], function($) {
imageUrl: null,
isFavourite: null,
isMuted: null,
canDeleteMessagesForAllUsers: false,
deleteMessagesForAllUsers: false,
members: {},
messages: [],
hasTriedToLoadMessages: false,
@@ -658,6 +653,32 @@ define(['jquery'], function($) {
return newState;
};
/**
* Set wheter the message of the conversation can delete for all users.
*
* @param {Object} state Current state.
* @param {Bool} value If it can delete for all users.
* @return {Object} New state.
*/
var setCanDeleteMessagesForAllUsers = function(state, value) {
var newState = cloneState(state);
newState.canDeleteMessagesForAllUsers = value;
return newState;
};
/**
* Set wheter the messages of the conversation delete for all users.
*
* @param {Object} state Current state.
* @param {Bool} value Delete messages for all users.
* @return {Object} New state.
*/
var setDeleteMessagesForAllUsers = function(state, value) {
var newState = cloneState(state);
newState.deleteMessagesForAllUsers = value;
return newState;
};
return {
buildInitialState: buildInitialState,
addMessages: addMessages,
@@ -674,6 +695,8 @@ define(['jquery'], function($) {
setType: setType,
setIsFavourite: setIsFavourite,
setIsMuted: setIsMuted,
setCanDeleteMessagesForAllUsers: setCanDeleteMessagesForAllUsers,
setDeleteMessagesForAllUsers: setDeleteMessagesForAllUsers,
setTotalMemberCount: setTotalMemberCount,
setImageUrl: setImageUrl,
setLoadingConfirmAction: setLoadingConfirmAction,
+22 -1
View File
@@ -431,7 +431,8 @@ define(
text: result.text,
timecreated: result.timecreated,
useridfrom: result.useridfrom,
conversationid: result.conversationid
conversationid: result.conversationid,
candeletemessagesforallusers: result.candeletemessagesforallusers
};
});
});
@@ -542,6 +543,25 @@ define(
})));
};
/**
* Delete a list of messages for all users.
*
* @param {int} userId The user to delete messages for
* @param {int[]} messageIds List of message ids to delete
* @return {object} jQuery promise
*/
var deleteMessagesForAllUsers = function(userId, messageIds) {
return $.when.apply(null, Ajax.call(messageIds.map(function(messageId) {
return {
methodname: 'core_message_delete_message_for_all_users',
args: {
messageid: messageId,
userid: userId
}
};
})));
};
/**
* Delete a conversation between two users.
*
@@ -1138,6 +1158,7 @@ define(
savePreferences: savePreferences,
getPreferences: getPreferences,
deleteMessages: deleteMessages,
deleteMessagesForAllUsers: deleteMessagesForAllUsers,
deleteConversation: deleteConversation,
getContactRequests: getContactRequests,
acceptContactRequest: acceptContactRequest,
+66 -1
View File
@@ -802,8 +802,11 @@ class api {
// If not set, the context is always context_user.
if (is_null($conversation->contextid)) {
$convcontext = \context_user::instance($userid);
// We'll need to check the capability to delete messages for all users in context system when contextid is null.
$contexttodeletemessageforall = \context_system::instance();
} else {
$convcontext = \context::instance_by_id($conversation->contextid);
$contexttodeletemessageforall = $convcontext;
}
$conv->name = format_string($conversation->conversationname, true, ['context' => $convcontext]);
@@ -819,6 +822,8 @@ class api {
// Add the most recent message information.
$conv->messages = [];
// Add if the user has to allow delete messages for all users in the conversation.
$conv->candeletemessagesforallusers = has_capability('moodle/site:deleteanymessage', $contexttodeletemessageforall);
if ($conversation->smallmessage) {
$msg = new \stdClass();
$msg->id = $conversation->messageid;
@@ -983,6 +988,9 @@ class api {
$ismuted = true;
}
// Get the context of the conversation. This will be used to check if the user can delete all messages in the conversation.
$deleteallcontext = empty($conversation->contextid) ? $systemcontext : \context::instance_by_id($conversation->contextid);
return (object) [
'id' => $conversation->id,
'name' => $conversation->name,
@@ -995,7 +1003,8 @@ class api {
'unreadcount' => $unreadcount,
'ismuted' => $ismuted,
'members' => $members,
'messages' => $messages['messages']
'messages' => $messages['messages'],
'candeletemessagesforallusers' => has_capability('moodle/site:deleteanymessage', $deleteallcontext)
];
}
@@ -3331,4 +3340,60 @@ class api {
$service = \core_favourites\service_factory::get_service_for_component('core_message');
$service->delete_favourites_by_type('message_conversations', $convcontext);
}
/**
* Checks if a user can delete a message for all users.
*
* @param int $userid the user id of who we want to delete the message for all users
* @param int $messageid The message id
* @return bool Returns true if a user can delete the message for all users, false otherwise.
*/
public static function can_delete_message_for_all_users(int $userid, int $messageid) : bool {
global $DB;
$sql = "SELECT mc.id, mc.contextid
FROM {message_conversations} mc
INNER JOIN {messages} m
ON mc.id = m.conversationid
WHERE m.id = :messageid";
$conversation = $DB->get_record_sql($sql, ['messageid' => $messageid]);
if (!empty($conversation->contextid)) {
return has_capability('moodle/site:deleteanymessage',
\context::instance_by_id($conversation->contextid), $userid);
}
return has_capability('moodle/site:deleteanymessage', \context_system::instance(), $userid);
}
/**
* Delete a message for all users.
*
* This function does not verify any permissions.
*
* @param int $messageid The message id
* @return void
*/
public static function delete_message_for_all_users(int $messageid) {
global $DB, $USER;
if (!$DB->record_exists('messages', ['id' => $messageid])) {
return false;
}
// Get all members in the conversation where the message belongs.
$membersql = "SELECT mcm.id, mcm.userid
FROM {message_conversation_members} mcm
INNER JOIN {messages} m
ON mcm.conversationid = m.conversationid
WHERE m.id = :messageid";
$params = [
'messageid' => $messageid
];
$members = $DB->get_records_sql($membersql, $params);
if ($members) {
foreach ($members as $member) {
self::delete_message($member->userid, $messageid);
}
}
}
}
+72
View File
@@ -159,6 +159,9 @@ class core_message_external extends external_api {
self::validate_context($context);
require_capability('moodle/site:sendmessage', $context);
// Ensure the current user is allowed to delete message for everyone.
$candeletemessagesforallusers = has_capability('moodle/site:deleteanymessage', $context);
$params = self::validate_parameters(self::send_instant_messages_parameters(), array('messages' => $messages));
//retrieve all tousers of the messages
@@ -205,6 +208,7 @@ class core_message_external extends external_api {
if ($success) {
$resultmsg['msgid'] = $success;
$resultmsg['timecreated'] = time();
$resultmsg['candeletemessagesforallusers'] = $candeletemessagesforallusers;
$messageids[] = $success;
} else {
// WARNINGS: for backward compatibility we return this errormessage.
@@ -257,6 +261,8 @@ 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),
'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
)
)
);
@@ -1260,6 +1266,8 @@ class core_message_external extends external_api {
'messages' => new external_multiple_structure(
self::get_conversation_message_structure()
),
'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
)
);
}
@@ -4806,4 +4814,68 @@ class core_message_external extends external_api {
]
);
}
/**
* Returns description of method parameters
*
* @return external_function_parameters
* @since 3.7
*/
public static function delete_message_for_all_users_parameters() {
return new external_function_parameters(
array(
'messageid' => new external_value(PARAM_INT, 'The message id'),
'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for all users')
)
);
}
/**
* Deletes a message for all users
*
* @param int $messageid the message id
* @param int $userid the user id of who we want to delete the message for all users
* @return external_description
* @throws moodle_exception
* @since 3.7
*/
public static function delete_message_for_all_users(int $messageid, int $userid) {
global $CFG;
// Check if private messaging between users is allowed.
if (empty($CFG->messaging)) {
throw new moodle_exception('disabled', 'message');
}
// Validate params.
$params = array(
'messageid' => $messageid,
'userid' => $userid
);
$params = self::validate_parameters(self::delete_message_for_all_users_parameters(), $params);
// Validate context.
$context = context_system::instance();
self::validate_context($context);
$user = core_user::get_user($params['userid'], '*', MUST_EXIST);
core_user::require_active_user($user);
// Checks if a user can delete a message for all users.
if (core_message\api::can_delete_message_for_all_users($user->id, $params['messageid'])) {
\core_message\api::delete_message_for_all_users($params['messageid']);
} else {
throw new moodle_exception('You do not have permission to delete this message for everyone.');
}
return [];
}
/**
* Returns description of method result value
*
* @return external_description
* @since 3.7
*/
public static function delete_message_for_all_users_returns() {
return new external_warnings();
}
}
@@ -38,6 +38,12 @@
<div class="p-3 bg-white" data-region="confirm-dialogue" role="alert">
<h3 class="h6 hidden" data-region="dialogue-header"></h3>
<p class="text-muted" data-region="dialogue-text"></p>
<div class="mb-2 hidden" data-region="delete-messages-for-all-users-toggle-container">
<label class="custom-control-label ml-2 text-muted">
<input type="checkbox" data-region="delete-messages-for-all-users-toggle">
{{#str}} deleteforeveryone, core_message {{/str}}
</label>
</div>
<button type="button" class="btn btn-primary btn-block hidden" data-action="confirm-block">
<span data-region="dialogue-button-text">{{#str}} blockuserconfirmbutton, core_message {{/str}}</span>
<span class="hidden" data-region="loading-icon-container">{{> core/loading }}</span>
+171
View File
@@ -6967,6 +6967,177 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
$coursecontext1));
}
/**
* Tests the user can delete message for all users as a teacher.
*/
public function test_can_delete_message_for_all_users_teacher() {
global $DB;
$this->resetAfterTest(true);
// Create fake data to test it.
list($teacher, $student1, $student2, $convgroup, $convindividual) = $this->create_delete_message_test_data();
// Allow Teacher can delete messages for all.
$editingteacher = $DB->get_record('role', ['shortname' => 'editingteacher']);
assign_capability('moodle/site:deleteanymessage', CAP_ALLOW, $editingteacher->id, context_system::instance());
// Set as the first user.
$this->setUser($teacher);
// Send a message to private conversation and in a group conversation.
$messageidind = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convindividual->id);
$messageidgrp = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convgroup->id);
// Teacher cannot delete message for everyone in a private conversation.
$this->assertFalse(\core_message\api::can_delete_message_for_all_users($teacher->id, $messageidind));
// Teacher can delete message for everyone in a group conversation.
$this->assertTrue(\core_message\api::can_delete_message_for_all_users($teacher->id, $messageidgrp));
}
/**
* Tests the user can delete message for all users as a student.
*/
public function test_can_delete_message_for_all_users_student() {
$this->resetAfterTest(true);
// Create fake data to test it.
list($teacher, $student1, $student2, $convgroup, $convindividual) = $this->create_delete_message_test_data();
// Set as the first user.
$this->setUser($student1);
// Send a message to private conversation and in a group conversation.
$messageidind = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convindividual->id);
$messageidgrp = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convgroup->id);
// Student1 cannot delete message for everyone in a private conversation.
$this->assertFalse(\core_message\api::can_delete_message_for_all_users($student1->id, $messageidind));
// Student1 cannot delete message for everyone in a group conversation.
$this->assertFalse(\core_message\api::can_delete_message_for_all_users($student1->id, $messageidgrp));
}
/**
* Tests tdelete message for all users in group conversation.
*/
public function test_delete_message_for_all_users_group_conversation() {
global $DB;
$this->resetAfterTest(true);
// Create fake data to test it.
list($teacher, $student1, $student2, $convgroup, $convindividual) = $this->create_delete_message_test_data();
// Send 3 messages to a group conversation.
$mgid1 = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convgroup->id);
$mgid2 = \core_message\tests\helper::send_fake_message_to_conversation($student1, $convgroup->id);
$mgid3 = \core_message\tests\helper::send_fake_message_to_conversation($student2, $convgroup->id);
// Delete message 1 for all users.
\core_message\api::delete_message_for_all_users($mgid1);
// Get the messages to check if the message 1 was deleted for teacher.
$convmessages1 = \core_message\api::get_conversation_messages($teacher->id, $convgroup->id);
// Only has to remains 2 messages.
$this->assertCount(2, $convmessages1['messages']);
// Check if no one of the two messages is message 1.
foreach ($convmessages1['messages'] as $message) {
$this->assertNotEquals($mgid1, $message->id);
}
// Get the messages to check if the message 1 was deleted for student1.
$convmessages2 = \core_message\api::get_conversation_messages($student1->id, $convgroup->id);
// Only has to remains 2 messages.
$this->assertCount(2, $convmessages2['messages']);
// Check if no one of the two messages is message 1.
foreach ($convmessages2['messages'] as $message) {
$this->assertNotEquals($mgid1, $message->id);
}
// Get the messages to check if the message 1 was deleted for student2.
$convmessages3 = \core_message\api::get_conversation_messages($student2->id, $convgroup->id);
// Only has to remains 2 messages.
$this->assertCount(2, $convmessages3['messages']);
// Check if no one of the two messages is message 1.
foreach ($convmessages3['messages'] as $message) {
$this->assertNotEquals($mgid1, $message->id);
}
}
/**
* Tests delete message for all users in private conversation.
*/
public function test_delete_message_for_all_users_individual_conversation() {
global $DB;
$this->resetAfterTest(true);
// Create fake data to test it.
list($teacher, $student1, $student2, $convgroup, $convindividual) = $this->create_delete_message_test_data();
// Send 2 messages in a individual conversation.
$mid1 = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convindividual->id);
$mid2 = \core_message\tests\helper::send_fake_message_to_conversation($student1, $convindividual->id);
// Delete the first message for all users.
\core_message\api::delete_message_for_all_users($mid1);
// Get the messages to check if the message 1 was deleted for teacher.
$convmessages1 = \core_message\api::get_conversation_messages($teacher->id, $convindividual->id);
// Only has to remains 1 messages for teacher.
$this->assertCount(1, $convmessages1['messages']);
// Check the one messages remains not is the first message.
$this->assertNotEquals($mid1, $convmessages1['messages'][0]->id);
// Get the messages to check if the message 1 was deleted for student1.
$convmessages2 = \core_message\api::get_conversation_messages($student1->id, $convindividual->id);
// Only has to remains 1 messages for student1.
$this->assertCount(1, $convmessages2['messages']);
// Check the one messages remains not is the first message.
$this->assertNotEquals($mid1, $convmessages2['messages'][0]->id);
}
/**
* Helper to seed the database with initial state with data.
*/
protected function create_delete_message_test_data() {
// Create some users.
$teacher = self::getDataGenerator()->create_user();
$student1 = self::getDataGenerator()->create_user();
$student2 = self::getDataGenerator()->create_user();
// Create a course and enrol the users.
$course = $this->getDataGenerator()->create_course();
$coursecontext = context_course::instance($course->id);
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
$this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student');
$this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student');
// Create a group and added the users into.
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
groups_add_member($group1->id, $teacher->id);
groups_add_member($group1->id, $student1->id);
groups_add_member($group1->id, $student2->id);
// Create a group conversation linked with the course.
$convgroup = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
[$teacher->id, $student1->id, $student2->id],
'Group test delete for everyone', \core_message\api::MESSAGE_CONVERSATION_ENABLED,
'core_group',
'groups',
$group1->id,
context_course::instance($course->id)->id
);
// Create and individual conversation.
$convindividual = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$teacher->id, $student1->id]
);
return [$teacher, $student1, $student2, $convgroup, $convindividual];
}
/**
* Comparison function for sorting contacts.
*
+160
View File
@@ -7129,4 +7129,164 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
$this->assertEquals($expectedunreadcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF],
$counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF]);
}
/**
* Test delete_message for all users.
*/
public function test_delete_message_for_all_users() {
global $DB;
$this->resetAfterTest(true);
// Create fake data to test it.
list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
// Send message as user1 to group conversation.
$messageid1 = testhelper::send_fake_message_to_conversation($user1, $convgroup->id);
$messageid2 = testhelper::send_fake_message_to_conversation($user2, $convgroup->id);
// User1 deletes the first message for all users of group conversation.
// First, we have to allow user1 (Teacher) can delete messages for all users.
$editingteacher = $DB->get_record('role', ['shortname' => 'editingteacher']);
assign_capability('moodle/site:deleteanymessage', CAP_ALLOW, $editingteacher->id, context_system::instance());
$this->setUser($user1);
// Now, user1 deletes message for all users.
$return = core_message_external::delete_message_for_all_users($messageid1, $user1->id);
$return = external_api::clean_returnvalue(core_message_external::delete_message_for_all_users_returns(), $return);
// Check if everything is ok.
$this->assertEquals(array(), $return);
// Check we have 3 records on message_user_actions with the mark MESSAGE_ACTION_DELETED.
$muas = $DB->get_records('message_user_actions', array('messageid' => $messageid1), 'userid ASC');
$this->assertCount(3, $muas);
$mua1 = array_shift($muas);
$mua2 = array_shift($muas);
$mua3 = array_shift($muas);
$this->assertEquals($user1->id, $mua1->userid);
$this->assertEquals($messageid1, $mua1->messageid);
$this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua1->action);
$this->assertEquals($user2->id, $mua2->userid);
$this->assertEquals($messageid1, $mua2->messageid);
$this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua2->action);
$this->assertEquals($user3->id, $mua3->userid);
$this->assertEquals($messageid1, $mua3->messageid);
$this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua3->action);
}
/**
* Test delete_message for all users with messaging disabled.
*/
public function test_delete_message_for_all_users_messaging_disabled() {
global $CFG;
$this->resetAfterTest();
// Create fake data to test it.
list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
// Send message as user1 to group conversation.
$messageid = testhelper::send_fake_message_to_conversation($user1, $convgroup->id);
$this->setUser($user1);
// Disable messaging.
$CFG->messaging = 0;
// Ensure an exception is thrown.
$this->expectException('moodle_exception');
core_message_external::delete_message_for_all_users($messageid, $user1->id);
}
/**
* Test delete_message for all users with no permission.
*/
public function test_delete_message_for_all_users_no_permission() {
$this->resetAfterTest();
// Create fake data to test it.
list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
// Send message as user1 to group conversation.
$messageid = testhelper::send_fake_message_to_conversation($user1, $convgroup->id);
$this->setUser($user2);
// Try as user2 to delete a message for all users without permission to do it.
$this->expectException('moodle_exception');
$this->expectExceptionMessage('You do not have permission to delete this message for everyone.');
core_message_external::delete_message_for_all_users($messageid, $user2->id);
}
/**
* Test delete_message for all users in a private conversation.
*/
public function test_delete_message_for_all_users_private_conversation() {
global $DB;
$this->resetAfterTest();
// Create fake data to test it.
list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
// Send message as user1 to private conversation.
$messageid = testhelper::send_fake_message_to_conversation($user1, $convindividual->id);
// First, we have to allow user1 (Teacher) can delete messages for all users.
$editingteacher = $DB->get_record('role', ['shortname' => 'editingteacher']);
assign_capability('moodle/site:deleteanymessage', CAP_ALLOW, $editingteacher->id, context_system::instance());
$this->setUser($user1);
// Try as user1 to delete a private message for all users on individual conversation.
// User1 should not delete message for all users in a private conversations despite being a teacher.
// Because is a teacher in a course and not in a system context.
$this->expectException('moodle_exception');
$this->expectExceptionMessage('You do not have permission to delete this message for everyone.');
core_message_external::delete_message_for_all_users($messageid, $user1->id);
}
/**
* Helper to seed the database with initial state with data.
*/
protected function create_delete_message_test_data() {
// Create some users.
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
// Create a course and enrol the users.
$course = $this->getDataGenerator()->create_course();
$coursecontext = context_course::instance($course->id);
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'editingteacher');
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
$this->getDataGenerator()->enrol_user($user3->id, $course->id, 'student');
// Create a group and added the users into.
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
groups_add_member($group1->id, $user1->id);
groups_add_member($group1->id, $user2->id);
groups_add_member($group1->id, $user3->id);
// Create a group conversation linked with the course.
$convgroup = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
[$user1->id, $user2->id, $user3->id],
'Group test delete for everyone', \core_message\api::MESSAGE_CONVERSATION_ENABLED,
'core_group',
'groups',
$group1->id,
context_course::instance($course->id)->id
);
// Create and individual conversation.
$convindividual = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$user1->id, $user2->id]
);
return [$user1, $user2, $user3, $convgroup, $convindividual];
}
}