Merge branch 'MDL-65060-master' of git://github.com/aanabit/moodle

This commit is contained in:
Sara Arjona 2019-05-24 12:34:23 +02:00
commit d68718a7db
5 changed files with 603 additions and 256 deletions

View File

@ -95,6 +95,7 @@ class behat_partial_named_selector extends \Behat\Mink\Selector\PartialNamedSele
'group_message_member' => 'group_message_member',
'group_message_tab' => 'group_message_tab',
'group_message_list_area' => 'group_message_list_area',
'group_message_message_content' => 'group_message_message_content',
'icon' => 'icon',
'link' => 'link',
'link_or_button' => 'link_or_button',
@ -174,6 +175,9 @@ XPATH
XPATH
, 'group_message_list_area' => <<<XPATH
.//*[@data-region='message-drawer']//*[contains(@data-region, concat('view-overview-', %locator%))]
XPATH
, 'group_message_message_content' => <<<XPATH
.//*[@data-region='message-drawer']//*[@data-region='message' and @data-message-id and contains(., %locator%)]
XPATH
, 'icon' => <<<XPATH
.//*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') and ( contains(normalize-space(@title), %locator%))]

View File

@ -208,6 +208,11 @@ class behat_data_generators extends behat_base {
'required' => array('user', 'contact'),
'switchids' => array('user' => 'userid', 'contact' => 'contactid')
),
'group messages' => array(
'datagenerator' => 'group_messages',
'required' => array('user', 'group', 'message'),
'switchids' => array('user' => 'userid', 'group' => 'groupid')
),
'language customisations' => array(
'datagenerator' => 'customlang',
'required' => array('component', 'stringid', 'value'),
@ -956,6 +961,10 @@ class behat_data_generators extends behat_base {
* @return void
*/
protected function process_private_messages(array $data) {
if (empty($data['format'])) {
$data['format'] = 'FORMAT_PLAIN';
}
if (!$conversationid = \core_message\api::get_conversation_between_users([$data['userid'], $data['contactid']])) {
$conversation = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
@ -963,7 +972,48 @@ class behat_data_generators extends behat_base {
);
$conversationid = $conversation->id;
}
\core_message\api::send_message_to_conversation($data['userid'], $conversationid, $data['message'], FORMAT_PLAIN);
\core_message\api::send_message_to_conversation(
$data['userid'],
$conversationid,
$data['message'],
constant($data['format'])
);
}
/**
* Send a new message from user to a group conversation
*
* @param array $data
* @return void
*/
protected function process_group_messages(array $data) {
global $DB;
if (empty($data['format'])) {
$data['format'] = 'FORMAT_PLAIN';
}
$group = $DB->get_record('groups', ['id' => $data['groupid']]);
$coursecontext = context_course::instance($group->courseid);
if (!$conversation = \core_message\api::get_conversation_by_area('core_group', 'groups', $data['groupid'],
$coursecontext->id)) {
$members = $DB->get_records_menu('groups_members', ['groupid' => $data['groupid']], '', 'userid, id');
$conversation = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
array_keys($members),
$group->name,
\core_message\api::MESSAGE_CONVERSATION_ENABLED,
'core_group',
'groups',
$group->id,
$coursecontext->id);
}
\core_message\api::send_message_to_conversation(
$data['userid'],
$conversation->id,
$data['message'],
constant($data['format'])
);
}
/**

File diff suppressed because one or more lines are too long

View File

@ -97,11 +97,13 @@ function(
var loadedAllMessages = false;
var messagesOffset = 0;
var newMessagesPollTimer = null;
var isRendering = false;
var renderBuffer = [];
// If the UI is currently resetting.
var isResetting = true;
// If the UI is currently sending a message.
var isSendingMessage = false;
// This is the render function which will be generated when this module is
// These functions which will be generated when this module is
// first called. See generateRenderFunction for details.
var render = null;
// The list of renderers that have been registered to render
@ -279,10 +281,9 @@ function(
var conversationType = loggedInUserId == otherUserId ? CONVERSATION_TYPES.SELF : CONVERSATION_TYPES.PRIVATE;
var newState = StateManager.setLoadingMembers(viewState, true);
newState = StateManager.setLoadingMessages(newState, true);
return render(newState)
.then(function() {
return Repository.getMemberInfo(loggedInUserId, [otherUserId], true, true);
})
render(newState);
return Repository.getMemberInfo(loggedInUserId, [otherUserId], true, true)
.then(function(profiles) {
if (profiles.length) {
return profiles[0];
@ -301,10 +302,8 @@ function(
newState = StateManager.setType(newState, conversationType);
newState = StateManager.setImageUrl(newState, profile.profileimageurl);
newState = StateManager.setTotalMemberCount(newState, members.length);
return render(newState)
.then(function() {
return profile;
});
render(newState);
return profile;
})
.catch(function(error) {
var newState = StateManager.setLoadingMembers(viewState, false);
@ -374,20 +373,19 @@ function(
var loggedInUserId = loggedInUserProfile.id;
var newState = StateManager.setLoadingMembers(viewState, true);
newState = StateManager.setLoadingMessages(newState, true);
return render(newState)
.then(function() {
return Repository.getConversation(
loggedInUserId,
conversationId,
true,
true,
0,
0,
messageLimit + 1,
messageOffset,
newestFirst
);
})
render(newState);
return Repository.getConversation(
loggedInUserId,
conversationId,
true,
true,
0,
0,
messageLimit + 1,
messageOffset,
newestFirst
)
.then(function(conversation) {
if (conversation.messages.length > messageLimit) {
conversation.messages = conversation.messages.slice(1);
@ -450,37 +448,30 @@ function(
conversation.members = conversation.members.concat([loggedInUserProfile]);
}
var messageCount = conversation.messages.length;
var hasLoadedEnoughMessages = messageCount >= messageLimit;
var newState = updateStateFromConversation(conversation, loggedInUserProfile.id);
newState = StateManager.setLoadingMembers(newState, false);
newState = StateManager.setLoadingMessages(newState, true);
var messageCount = conversation.messages.length;
return render(newState)
.then(function() {
if (messageCount < messageLimit) {
newState = StateManager.setLoadingMessages(newState, !hasLoadedEnoughMessages);
var renderPromise = render(newState);
return renderPromise.then(function() {
if (!hasLoadedEnoughMessages) {
// We haven't got enough messages so let's load some more.
return loadMessages(conversation.id, messageLimit, messageCount, newestFirst, [])
.then(function(result) {
// Give the list of messages to the next handler.
return result.messages;
});
return loadMessages(conversation.id, messageLimit, messageCount, newestFirst, []);
} else {
// We've got enough messages. No need to load any more for now.
var newState = StateManager.setLoadingMessages(viewState, false);
return render(newState)
.then(function() {
// Give the list of messages to the next handler.
return conversation.messages;
});
return {messages: conversation.messages};
}
})
.then(function(messages) {
.then(function() {
var messages = viewState.messages;
// Update the offset to reflect the number of messages we've loaded.
setMessagesOffset(messages.length);
markConversationAsRead(viewState.id);
return messages;
})
.then(function() {
return markConversationAsRead(conversation.id);
})
.catch(Notification.exception);
};
@ -627,14 +618,12 @@ function(
* Tell the statemanager there is request to block a user and run the renderer
* to show the block user dialogue.
*
* @param {Number} userId User id.
* @return {Promise} Renderer promise.
* @param {Number} userId User id.
*/
var requestBlockUser = function(userId) {
return cancelRequest(userId).then(function() {
var newState = StateManager.addPendingBlockUsersById(viewState, [userId]);
return render(newState);
});
cancelRequest(userId);
var newState = StateManager.addPendingBlockUsersById(viewState, [userId]);
render(newState);
};
/**
@ -646,10 +635,9 @@ function(
*/
var blockUser = function(userId) {
var newState = StateManager.setLoadingConfirmAction(viewState, true);
return render(newState)
.then(function() {
return Repository.blockUser(viewState.loggedInUserId, userId);
})
render(newState);
return Repository.blockUser(viewState.loggedInUserId, userId)
.then(function(profile) {
var newState = StateManager.addMembers(viewState, [profile]);
newState = StateManager.removePendingBlockUsersById(newState, [userId]);
@ -663,14 +651,12 @@ function(
* Tell the statemanager there is a request to unblock a user and run the renderer
* to show the unblock user dialogue.
*
* @param {Number} userId User id of user to unblock.
* @return {Promise} Renderer promise.
* @param {Number} userId User id of user to unblock.
*/
var requestUnblockUser = function(userId) {
return cancelRequest(userId).then(function() {
var newState = StateManager.addPendingUnblockUsersById(viewState, [userId]);
return render(newState);
});
cancelRequest(userId);
var newState = StateManager.addPendingUnblockUsersById(viewState, [userId]);
render(newState);
};
/**
@ -682,10 +668,9 @@ function(
*/
var unblockUser = function(userId) {
var newState = StateManager.setLoadingConfirmAction(viewState, true);
return render(newState)
.then(function() {
return Repository.unblockUser(viewState.loggedInUserId, userId);
})
render(newState);
return Repository.unblockUser(viewState.loggedInUserId, userId)
.then(function(profile) {
var newState = StateManager.addMembers(viewState, [profile]);
newState = StateManager.removePendingUnblockUsersById(newState, [userId]);
@ -699,14 +684,12 @@ function(
* Tell the statemanager there is a request to remove a user from the contact list
* and run the renderer to show the remove user from contacts dialogue.
*
* @param {Number} userId User id of user to remove from contacts.
* @return {Promise} Renderer promise.
* @param {Number} userId User id of user to remove from contacts.
*/
var requestRemoveContact = function(userId) {
return cancelRequest(userId).then(function() {
var newState = StateManager.addPendingRemoveContactsById(viewState, [userId]);
return render(newState);
});
cancelRequest(userId);
var newState = StateManager.addPendingRemoveContactsById(viewState, [userId]);
render(newState);
};
/**
@ -718,10 +701,9 @@ function(
*/
var removeContact = function(userId) {
var newState = StateManager.setLoadingConfirmAction(viewState, true);
return render(newState)
.then(function() {
return Repository.deleteContacts(viewState.loggedInUserId, [userId]);
})
render(newState);
return Repository.deleteContacts(viewState.loggedInUserId, [userId])
.then(function(profiles) {
var newState = StateManager.addMembers(viewState, profiles);
newState = StateManager.removePendingRemoveContactsById(newState, [userId]);
@ -735,14 +717,12 @@ function(
* Tell the statemanager there is a request to add a user to the contact list
* and run the renderer to show the add user to contacts dialogue.
*
* @param {Number} userId User id of user to add to contacts.
* @return {Promise} Renderer promise.
* @param {Number} userId User id of user to add to contacts.
*/
var requestAddContact = function(userId) {
return cancelRequest(userId).then(function() {
var newState = StateManager.addPendingAddContactsById(viewState, [userId]);
return render(newState);
});
cancelRequest(userId);
var newState = StateManager.addPendingAddContactsById(viewState, [userId]);
render(newState);
};
/**
@ -754,10 +734,9 @@ function(
*/
var addContact = function(userId) {
var newState = StateManager.setLoadingConfirmAction(viewState, true);
return render(newState)
.then(function() {
return Repository.createContactRequest(viewState.loggedInUserId, userId);
})
render(newState);
return Repository.createContactRequest(viewState.loggedInUserId, userId)
.then(function(response) {
if (!response.request) {
throw new Error(response.warnings[0].message);
@ -865,15 +844,13 @@ function(
* Tell the statemanager there is a request to delete the selected messages
* and run the renderer to show confirm delete messages dialogue.
*
* @param {Number} userId User id.
* @return {Promise} Renderer promise.
* @param {Number} userId User id.
*/
var requestDeleteSelectedMessages = function(userId) {
var selectedMessageIds = viewState.selectedMessageIds;
return cancelRequest(userId).then(function() {
var newState = StateManager.addPendingDeleteMessagesById(viewState, selectedMessageIds);
return render(newState);
});
cancelRequest(userId);
var newState = StateManager.addPendingDeleteMessagesById(viewState, selectedMessageIds);
render(newState);
};
/**
@ -885,15 +862,18 @@ function(
var deleteSelectedMessages = function() {
var messageIds = viewState.pendingDeleteMessageIds;
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() {
render(newState);
var deleteMessagesPromise = null;
if (newState.deleteMessagesForAllUsers) {
deleteMessagesPromise = Repository.deleteMessagesForAllUsers(viewState.loggedInUserId, messageIds);
} else {
deleteMessagesPromise = Repository.deleteMessages(viewState.loggedInUserId, messageIds);
}
return deleteMessagesPromise.then(function() {
var newState = StateManager.removeMessagesById(viewState, messageIds);
newState = StateManager.removePendingDeleteMessagesById(newState, messageIds);
newState = StateManager.removeSelectedMessagesById(newState, messageIds);
@ -918,14 +898,12 @@ function(
* Tell the statemanager there is a request to delete a conversation
* and run the renderer to show confirm delete conversation dialogue.
*
* @param {Number} userId User id of other user.
* @return {Promise} Renderer promise.
* @param {Number} userId User id of other user.
*/
var requestDeleteConversation = function(userId) {
return cancelRequest(userId).then(function() {
var newState = StateManager.setPendingDeleteConversation(viewState, true);
return render(newState);
});
cancelRequest(userId);
var newState = StateManager.setPendingDeleteConversation(viewState, true);
render(newState);
};
/**
@ -936,10 +914,9 @@ function(
*/
var deleteConversation = function() {
var newState = StateManager.setLoadingConfirmAction(viewState, true);
return render(newState)
.then(function() {
return Repository.deleteConversation(viewState.loggedInUserId, viewState.id);
})
render(newState);
return Repository.deleteConversation(viewState.loggedInUserId, viewState.id)
.then(function() {
var newState = StateManager.removeMessages(viewState, viewState.messages);
newState = StateManager.removeSelectedMessagesById(newState, viewState.selectedMessageIds);
@ -955,7 +932,6 @@ function(
* Tell the statemanager to cancel all pending actions.
*
* @param {Number} userId User id.
* @return {Promise} Renderer promise.
*/
var cancelRequest = function(userId) {
var pendingDeleteMessageIds = viewState.pendingDeleteMessageIds;
@ -966,7 +942,7 @@ function(
newState = StateManager.removePendingDeleteMessagesById(newState, pendingDeleteMessageIds);
newState = StateManager.setPendingDeleteConversation(newState, false);
newState = StateManager.setDeleteMessagesForAllUsers(newState, false);
return render(newState);
render(newState);
};
/**
@ -984,10 +960,9 @@ function(
});
var request = requests[0];
var newState = StateManager.setLoadingConfirmAction(viewState, true);
return render(newState)
.then(function() {
return Repository.acceptContactRequest(userId, loggedInUserId);
})
render(newState);
return Repository.acceptContactRequest(userId, loggedInUserId)
.then(function(profile) {
var newState = StateManager.removeContactRequests(viewState, [request]);
newState = StateManager.addMembers(viewState, [profile]);
@ -1016,10 +991,9 @@ function(
});
var request = requests[0];
var newState = StateManager.setLoadingConfirmAction(viewState, true);
return render(newState)
.then(function() {
return Repository.declineContactRequest(userId, loggedInUserId);
})
render(newState);
return Repository.declineContactRequest(userId, loggedInUserId)
.then(function(profile) {
var newState = StateManager.removeContactRequests(viewState, [request]);
newState = StateManager.addMembers(viewState, [profile]);
@ -1044,24 +1018,26 @@ 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)) {
// If it's a new private conversation then we need to use the old
// web service function to create the conversation.
var otherUserId = getOtherUserId();
return Repository.sendMessageToUser(otherUserId, text)
.then(function(message) {
newConversationId = parseInt(message.conversationid, 10);
newCanDeleteMessagesForAllUsers = message.candeletemessagesforallusers;
return message;
});
} else {
return Repository.sendMessageToConversation(conversationId, text);
}
})
.then(function(message) {
render(newState);
var sendMessagePromise = null;
var newCanDeleteMessagesForAllUsers = null;
if (!conversationId && (viewState.type != CONVERSATION_TYPES.PUBLIC)) {
// If it's a new private conversation then we need to use the old
// web service function to create the conversation.
var otherUserId = getOtherUserId();
sendMessagePromise = Repository.sendMessageToUser(otherUserId, text)
.then(function(message) {
newConversationId = parseInt(message.conversationid, 10);
newCanDeleteMessagesForAllUsers = message.candeletemessagesforallusers;
return message;
});
} else {
sendMessagePromise = Repository.sendMessageToConversation(conversationId, text);
}
sendMessagePromise.then(function(message) {
var newState = StateManager.addMessages(viewState, [message]);
newState = StateManager.setSendingMessage(newState, false);
var conversation = formatConversationForEvent(newState);
@ -1076,12 +1052,10 @@ function(
newState = StateManager.setCanDeleteMessagesForAllUsers(newState, newCanDeleteMessagesForAllUsers);
}
return render(newState)
.then(function() {
isSendingMessage = false;
PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation);
return;
});
render(newState);
isSendingMessage = false;
PubSub.publish(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, conversation);
return;
})
.catch(function(error) {
isSendingMessage = false;
@ -1095,7 +1069,6 @@ function(
* Toggle the selected messages update the statemanager and render the result.
*
* @param {Number} messageId The id of the message to be toggled
* @return {Promise} Renderer promise.
*/
var toggleSelectMessage = function(messageId) {
var newState = viewState;
@ -1106,7 +1079,7 @@ function(
newState = StateManager.addSelectedMessagesById(viewState, [messageId]);
}
return render(newState);
render(newState);
};
/**
@ -1115,10 +1088,45 @@ function(
* @return {Promise} Renderer promise.
*/
var cancelEditMode = function() {
return cancelRequest(getOtherUserId())
cancelRequest(getOtherUserId());
var newState = StateManager.removeSelectedMessagesById(viewState, viewState.selectedMessageIds);
render(newState);
};
/**
* Process the patches in the render buffer one at a time in order until the
* buffer is empty.
*
* @param {Object} header The conversation header container element.
* @param {Object} body The conversation body container element.
* @param {Object} footer The conversation footer container element.
*/
var processRenderBuffer = function(header, body, footer) {
if (isRendering) {
return;
}
if (!renderBuffer.length) {
return;
}
isRendering = true;
var renderable = renderBuffer.shift();
var renderPromises = renderers.map(function(renderFunc) {
return renderFunc(renderable.patch);
});
$.when.apply(null, renderPromises)
.then(function() {
var newState = StateManager.removeSelectedMessagesById(viewState, viewState.selectedMessageIds);
return render(newState);
isRendering = false;
renderable.deferred.resolve(true);
// Keep processing the buffer until it's empty.
processRenderBuffer(header, body, footer);
})
.catch(function(error) {
isRendering = false;
renderable.deferred.reject(error);
Notification.exception(error);
});
};
@ -1148,25 +1156,39 @@ function(
return function(newState) {
var patch = Patcher.buildPatch(viewState, newState);
var deferred = $.Deferred();
// Check if the patch has any data. Ignore empty patches.
if (Object.keys(patch).length) {
// Add the patch to the render buffer which gets processed in order.
renderBuffer.push({
patch: patch,
deferred: deferred
});
} else {
deferred.resolve(true);
}
// This is a great place to add in some console logging if you need
// to debug something. You can log the current state, the next state,
// and the generated patch and see exactly what will be updated.
var renderPromises = renderers.map(function(renderFunc) {
return renderFunc(patch);
});
return $.when.apply(null, renderPromises)
.then(function() {
viewState = newState;
if (newState.id) {
// Only cache created conversations.
stateCache[newState.id] = {
state: newState,
messagesOffset: getMessagesOffset(),
loadedAllMessages: hasLoadedAllMessages()
};
}
return;
});
// Optimistically update the state. We're going to assume that the rendering
// will always succeed. The rendering is asynchronous (annoyingly) so it's buffered
// but it'll reach eventual consistency with the current state.
viewState = newState;
if (newState.id) {
// Only cache created conversations.
stateCache[newState.id] = {
state: newState,
messagesOffset: getMessagesOffset(),
loadedAllMessages: hasLoadedAllMessages()
};
}
// Start processing the buffer.
processRenderBuffer(header, body, footer);
return deferred.promise();
};
};
@ -1179,12 +1201,9 @@ function(
var generateConfirmActionHandler = function(actionCallback) {
return function(e, data) {
if (!viewState.loadingConfirmAction) {
actionCallback(getOtherUserId())
.catch(function(error) {
var newState = StateManager.setLoadingConfirmAction(viewState, false);
render(newState);
Notification.exception(error);
});
actionCallback(getOtherUserId());
var newState = StateManager.setLoadingConfirmAction(viewState, false);
render(newState);
}
data.originalEvent.preventDefault();
};
@ -1232,7 +1251,7 @@ function(
var element = target.closest(SELECTORS.MESSAGE);
var messageId = parseInt(element.attr('data-message-id'), 10);
toggleSelectMessage(messageId).catch(Notification.exception);
toggleSelectMessage(messageId);
data.originalEvent.preventDefault();
};
@ -1244,7 +1263,7 @@ function(
* @param {Object} data Data for this event.
*/
var handleCancelEditMode = function(e, data) {
cancelEditMode().catch(Notification.exception);
cancelEditMode();
data.originalEvent.preventDefault();
};
@ -1413,10 +1432,9 @@ function(
if (!isResetting && !isLoadingMoreMessages && !hasLoadedAllMessages() && hasMembers) {
isLoadingMoreMessages = true;
var newState = StateManager.setLoadingMessages(viewState, true);
render(newState)
.then(function() {
return loadMessages(viewState.id, LOAD_MESSAGE_LIMIT, getMessagesOffset(), NEWEST_FIRST, []);
})
render(newState);
loadMessages(viewState.id, LOAD_MESSAGE_LIMIT, getMessagesOffset(), NEWEST_FIRST, [])
.then(function() {
isLoadingMoreMessages = false;
setMessagesOffset(getMessagesOffset() + LOAD_MESSAGE_LIMIT);
@ -1495,9 +1513,15 @@ function(
* @param {Object} body Conversation body container element.
* @param {Number|null} conversationId The conversation id.
* @param {Object} loggedInUserProfile The logged in user's profile.
* @return {Promise} Renderer promise.
*/
var resetState = function(body, conversationId, loggedInUserProfile) {
// Reset all of the states back to the beginning if we're loading a new
// conversation.
isResetting = true;
isRendering = false;
renderBuffer = [];
isSendingMessage = false;
var loggedInUserId = loggedInUserProfile.id;
var midnight = parseInt(body.attr('data-midnight'), 10);
var initialState = StateManager.buildInitialState(midnight, loggedInUserId, conversationId);
@ -1510,7 +1534,7 @@ function(
newMessagesPollTimer.stop();
}
return render(initialState);
render(initialState);
};
/**
@ -1524,32 +1548,34 @@ function(
var resetNoConversation = function(body, loggedInUserProfile, otherUserId) {
// Always reset the state back to the initial state so that the
// state manager and patcher can work correctly.
return resetState(body, null, loggedInUserProfile)
.then(function() {
if (loggedInUserProfile.id != otherUserId) {
// Private conversation between two different users.
return Repository.getConversationBetweenUsers(
loggedInUserProfile.id,
otherUserId,
true,
true,
0,
0,
LOAD_MESSAGE_LIMIT,
0,
NEWEST_FIRST
);
} else {
// Self conversation.
return Repository.getSelfConversation(
loggedInUserProfile.id,
LOAD_MESSAGE_LIMIT,
0,
NEWEST_FIRST
);
}
})
.then(function(conversation) {
resetState(body, null, loggedInUserProfile);
var resetNoConversationPromise = null;
if (loggedInUserProfile.id != otherUserId) {
// Private conversation between two different users.
resetNoConversationPromise = Repository.getConversationBetweenUsers(
loggedInUserProfile.id,
otherUserId,
true,
true,
0,
0,
LOAD_MESSAGE_LIMIT,
0,
NEWEST_FIRST
);
} else {
// Self conversation.
resetNoConversationPromise = Repository.getSelfConversation(
loggedInUserProfile.id,
LOAD_MESSAGE_LIMIT,
0,
NEWEST_FIRST
);
}
return resetNoConversationPromise.then(function(conversation) {
// Looks like we have a conversation after all! Let's use that.
return resetByConversation(body, conversation, loggedInUserProfile);
})
@ -1575,31 +1601,32 @@ function(
// Always reset the state back to the initial state so that the
// state manager and patcher can work correctly.
return resetState(body, conversationId, loggedInUserProfile)
.then(function() {
if (cache) {
// We've seen this conversation before so there is no need to
// send any network requests.
var newState = cache.state;
// Reset some loading states just in case they were left weirdly.
newState = StateManager.setLoadingMessages(newState, false);
newState = StateManager.setLoadingMembers(newState, false);
setMessagesOffset(cache.messagesOffset);
setLoadedAllMessages(cache.loadedAllMessages);
return render(newState);
} else {
return loadNewConversation(
conversationId,
loggedInUserProfile,
LOAD_MESSAGE_LIMIT,
0,
NEWEST_FIRST
);
}
})
.then(function() {
return resetMessagePollTimer(conversationId);
});
resetState(body, conversationId, loggedInUserProfile);
var promise = $.Deferred().resolve({}).promise();
if (cache) {
// We've seen this conversation before so there is no need to
// send any network requests.
var newState = cache.state;
// Reset some loading states just in case they were left weirdly.
newState = StateManager.setLoadingMessages(newState, false);
newState = StateManager.setLoadingMembers(newState, false);
setMessagesOffset(cache.messagesOffset);
setLoadedAllMessages(cache.loadedAllMessages);
render(newState);
} else {
promise = loadNewConversation(
conversationId,
loggedInUserProfile,
LOAD_MESSAGE_LIMIT,
0,
NEWEST_FIRST
);
}
return promise.then(function() {
return resetMessagePollTimer(conversationId);
});
};
/**
@ -1618,30 +1645,31 @@ function(
// Always reset the state back to the initial state so that the
// state manager and patcher can work correctly.
return resetState(body, conversation.id, loggedInUserProfile)
.then(function() {
if (cache) {
// We've seen this conversation before so there is no need to
// send any network requests.
var newState = cache.state;
// Reset some loading states just in case they were left weirdly.
newState = StateManager.setLoadingMessages(newState, false);
newState = StateManager.setLoadingMembers(newState, false);
setMessagesOffset(cache.messagesOffset);
setLoadedAllMessages(cache.loadedAllMessages);
return render(newState);
} else {
return loadExistingConversation(
conversation,
loggedInUserProfile,
LOAD_MESSAGE_LIMIT,
NEWEST_FIRST
);
}
})
.then(function() {
return resetMessagePollTimer(conversation.id);
});
resetState(body, conversation.id, loggedInUserProfile);
var promise = $.Deferred().resolve({}).promise();
if (cache) {
// We've seen this conversation before so there is no need to
// send any network requests.
var newState = cache.state;
// Reset some loading states just in case they were left weirdly.
newState = StateManager.setLoadingMessages(newState, false);
newState = StateManager.setLoadingMembers(newState, false);
setMessagesOffset(cache.messagesOffset);
setLoadedAllMessages(cache.loadedAllMessages);
render(newState);
} else {
promise = loadExistingConversation(
conversation,
loggedInUserProfile,
LOAD_MESSAGE_LIMIT,
NEWEST_FIRST
);
}
return promise.then(function() {
return resetMessagePollTimer(conversation.id);
});
};
/**
@ -1702,9 +1730,6 @@ function(
}
if (isNewConversation) {
// Reset all of the states back to the beginning if we're loading a new
// conversation.
isResetting = true;
var renderPromise = null;
var loggedInUserProfile = getLoggedInUserProfile(body);
if (conversation) {

View File

@ -0,0 +1,268 @@
@core @core_message @javascript
Feature: Delete messages from conversations
In order to manage a course group in a course
As a user
I need to be able to delete messages from conversations
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | enablemessaging |
| Group 1 | C1 | G1 | 1 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G1 |
And the following "group messages" exist:
| user | group | message |
| student1 | G1 | Hi! |
| student2 | G1 | How are you? |
| student1 | G1 | Can somebody help me? |
And the following "private messages" exist:
| user | contact | message |
| student1 | student2 | Hi! |
| student2 | student1 | Hello! |
| student1 | student2 | Are you free? |
And the following config values are set as admin:
| messaging | 1 |
Scenario: Delete a message sent by the user from a group conversation
Given I log in as "student1"
And I open messaging
And "Group 1" "group_message" should exist
And I select "Group 1" conversation in messaging
And I click on "Hi!" "group_message_message_content"
And I click on "How are you?" "group_message_message_content"
And I click on "Can somebody help me?" "group_message_message_content"
And I should see "3" in the "[data-region='message-selected-court']" "css_element"
# Clicking to unselect
And I click on "How are you?" "group_message_message_content"
And I click on "Can somebody help me?" "group_message_message_content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should see "##today##j F##" in the "Group 1" "group_message_conversation"
And I should see "How are you?" in the "Group 1" "group_message_conversation"
And I should see "Can somebody help me?" in the "Group 1" "group_message_conversation"
And I should not see "Messages selected"
Scenario: Delete two messages from a group conversation; one sent by another user.
Given I log in as "student1"
And I open messaging
And "Group 1" "group_message" should exist
And I select "Group 1" conversation in messaging
And I click on "Hi!" "group_message_message_content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And I click on "How are you?" "group_message_message_content"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should see "##today##j F##" in the "Group 1" "group_message_conversation"
And I should not see "How are you?" in the "Group 1" "group_message_conversation"
And I should see "Can somebody help me?" in the "Group 1" "group_message_conversation"
And I should not see "Messages selected"
# Check messages were not deleted for other users
And I log out
And I log in as "student2"
And I open messaging
And I select "Group 1" conversation in messaging
And I should see "Hi!"
And I should see "How are you?"
And I should see "Can somebody help me?"
Scenario: Cancel deleting two messages from a group conversation
Given I log in as "student1"
And I open messaging
And "Group 1" "group_message" should exist
And I select "Group 1" conversation in messaging
And I click on "Hi!" "group_message_message_content"
And I click on "How are you?" "group_message_message_content"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Canceling deletion, so messages should be there
And I should see "Cancel"
And I click on "//button[@data-action='cancel-confirm']" "xpath_element"
Then I should not see "Cancel"
And I should see "Hi!"
And I should see "How are you?" in the "Group 1" "group_message_conversation"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
Scenario: Delete a message sent by the user from a private conversation
Given I log in as "student1"
And I open messaging
And I should see "Private"
And I open the "Private" conversations list
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "group_message_message_content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should see "##today##j F##" in the "Student 2" "group_message_conversation"
And I should see "Hello!" in the "Student 2" "group_message_conversation"
And I should see "Are you free?" in the "Student 2" "group_message_conversation"
And I should not see "Messages selected"
Scenario: Delete two messages from a private conversation; one sent by another user
Given I log in as "student1"
And I open messaging
And I should see "Private"
And I open the "Private" conversations list
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "group_message_message_content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And I click on "Hello!" "group_message_message_content"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should not see "Hello!" in the "Student 2" "group_message_conversation"
And I should see "##today##j F##" in the "Student 2" "group_message_conversation"
And I should see "Are you free?" in the "Student 2" "group_message_conversation"
And I should not see "Messages selected"
# Check messages were not deleted for the other user
And I log out
And I log in as "student2"
And I open messaging
And I open the "Private" conversations list
And I select "Student 1" conversation in messaging
And I should see "Hi!"
And I should see "Hello!"
And I should see "Are you free?"
Scenario: Cancel deleting two messages from a private conversation
Given I log in as "student1"
And I open messaging
And I should see "Private"
And I open the "Private" conversations list
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "group_message_message_content"
And I click on "Hello!" "group_message_message_content"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Canceling deletion, so messages should be there
And I should see "Cancel"
And I click on "//button[@data-action='cancel-confirm']" "xpath_element"
Then I should not see "Cancel"
And I should see "Hi!"
And I should see "Hello!" in the "Student 2" "group_message_conversation"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
Scenario: Delete a message sent by the user from a favorite conversation
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
And I log in as "student1"
And I open messaging
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "group_message_message_content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should see "##today##j F##" in the "Student 2" "group_message_conversation"
And I should see "Hello!" in the "Student 2" "group_message_conversation"
And I should not see "Messages selected"
Scenario: Delete two messages from a favourite conversation; one sent by another user
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
And I log in as "student1"
And I open messaging
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "group_message_message_content"
And I should see "1" in the "[data-region='message-selected-court']" "css_element"
And I click on "Hello!" "group_message_message_content"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Deleting, so messages should not be there
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
Then I should not see "Delete"
And I should not see "Hi!"
And I should not see "Hello!" in the "Student 2" "group_message_conversation"
And I should see "##today##j F##" in the "Student 2" "group_message_conversation"
And I should see "Are you free?" in the "Student 2" "group_message_conversation"
And I should not see "Messages selected"
Scenario: Cancel deleting two messages from a favourite conversation
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
And I log in as "student1"
And I open messaging
And I should see "Student 2"
And I select "Student 2" conversation in messaging
And I click on "Hi!" "group_message_message_content"
And I click on "Hello!" "group_message_message_content"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
# Canceling deletion, so messages should be there
And I should see "Cancel"
And I click on "//button[@data-action='cancel-confirm']" "xpath_element"
Then I should not see "Cancel"
And I should see "Hi!"
And I should see "Hello!" in the "Student 2" "group_message_conversation"
And I should see "2" in the "[data-region='message-selected-court']" "css_element"
Scenario: Check an empty favourite conversation is still favourite
Given the following "favourite conversations" exist:
| user | contact |
| student1 | student2 |
And I log in as "student1"
And I open messaging
And I should see "Student 2"
And I select "Student 2" conversation in the "favourites" conversations list
And I click on "Hi!" "group_message_message_content"
And I click on "Hello!" "group_message_message_content"
And I click on "Are you free?" "group_message_message_content"
And "Delete selected messages" "button" should exist
When I click on "Delete selected messages" "button"
And I should see "Delete"
And I click on "//button[@data-action='confirm-delete-selected-messages']" "xpath_element"
And I go back in "view-conversation" message drawer
Then I should not see "Student 2" in the "//*[@data-region='message-drawer']//div[@data-region='view-overview-favourites']" "xpath_element"
And I send "Hi!" message to "Student 2" user
And I go back in "view-conversation" message drawer
And I go back in "view-search" message drawer
And I open the "Starred" conversations list
And I should see "Student 2" in the "//*[@data-region='message-drawer']//div[@data-region='view-overview-favourites']" "xpath_element"