Merge branch 'MDL-57370-master' of git://github.com/ryanwyllie/moodle
This commit is contained in:
+5
-3
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="lib/db" VERSION="20161119" COMMENT="XMLDB file for core Moodle tables"
|
||||
<XMLDB PATH="lib/db" VERSION="20170207" COMMENT="XMLDB file for core Moodle tables"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
@@ -545,8 +545,9 @@
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
</KEYS>
|
||||
<INDEXES>
|
||||
<INDEX NAME="useridto" UNIQUE="false" FIELDS="useridto"/>
|
||||
<INDEX NAME="useridfromtodeleted" UNIQUE="false" FIELDS="useridfrom, useridto, timeuserfromdeleted, timeusertodeleted"/>
|
||||
<INDEX NAME="useridfrom_timeuserfromdeleted_notification" UNIQUE="false" FIELDS="useridfrom, timeuserfromdeleted, notification"/>
|
||||
<INDEX NAME="useridto_timeusertodeleted_notification" UNIQUE="false" FIELDS="useridto, timeusertodeleted, notification"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="message_read" COMMENT="Stores all messages that have been read">
|
||||
@@ -573,9 +574,10 @@
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
</KEYS>
|
||||
<INDEXES>
|
||||
<INDEX NAME="useridto" UNIQUE="false" FIELDS="useridto"/>
|
||||
<INDEX NAME="useridfromtodeleted" UNIQUE="false" FIELDS="useridfrom, useridto, timeuserfromdeleted, timeusertodeleted"/>
|
||||
<INDEX NAME="notificationtimeread" UNIQUE="false" FIELDS="notification, timeread"/>
|
||||
<INDEX NAME="useridfrom_timeuserfromdeleted_notification" UNIQUE="false" FIELDS="useridfrom, timeuserfromdeleted, notification"/>
|
||||
<INDEX NAME="useridto_timeusertodeleted_notification" UNIQUE="false" FIELDS="useridto, timeusertodeleted, notification"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="message_contacts" COMMENT="Maintains lists of relationships between users">
|
||||
|
||||
@@ -2465,5 +2465,65 @@ function xmldb_main_upgrade($oldversion) {
|
||||
upgrade_main_savepoint(true, 2016122800.00);
|
||||
}
|
||||
|
||||
if ($oldversion < 2017020200.01) {
|
||||
|
||||
// Define index useridfrom_timeuserfromdeleted_notification (not unique) to be added to message.
|
||||
$table = new xmldb_table('message');
|
||||
$index = new xmldb_index('useridfrom_timeuserfromdeleted_notification', XMLDB_INDEX_NOTUNIQUE, array('useridfrom', 'timeuserfromdeleted', 'notification'));
|
||||
|
||||
// Conditionally launch add index useridfrom_timeuserfromdeleted_notification.
|
||||
if (!$dbman->index_exists($table, $index)) {
|
||||
$dbman->add_index($table, $index);
|
||||
}
|
||||
|
||||
// Define index useridto_timeusertodeleted_notification (not unique) to be added to message.
|
||||
$index = new xmldb_index('useridto_timeusertodeleted_notification', XMLDB_INDEX_NOTUNIQUE, array('useridto', 'timeusertodeleted', 'notification'));
|
||||
|
||||
// Conditionally launch add index useridto_timeusertodeleted_notification.
|
||||
if (!$dbman->index_exists($table, $index)) {
|
||||
$dbman->add_index($table, $index);
|
||||
}
|
||||
|
||||
$index = new xmldb_index('useridto', XMLDB_INDEX_NOTUNIQUE, array('useridto'));
|
||||
|
||||
// Conditionally launch drop index useridto.
|
||||
if ($dbman->index_exists($table, $index)) {
|
||||
$dbman->drop_index($table, $index);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2017020200.01);
|
||||
}
|
||||
|
||||
if ($oldversion < 2017020200.02) {
|
||||
|
||||
// Define index useridfrom_timeuserfromdeleted_notification (not unique) to be added to message_read.
|
||||
$table = new xmldb_table('message_read');
|
||||
$index = new xmldb_index('useridfrom_timeuserfromdeleted_notification', XMLDB_INDEX_NOTUNIQUE, array('useridfrom', 'timeuserfromdeleted', 'notification'));
|
||||
|
||||
// Conditionally launch add index useridfrom_timeuserfromdeleted_notification.
|
||||
if (!$dbman->index_exists($table, $index)) {
|
||||
$dbman->add_index($table, $index);
|
||||
}
|
||||
|
||||
// Define index useridto_timeusertodeleted_notification (not unique) to be added to message_read.
|
||||
$index = new xmldb_index('useridto_timeusertodeleted_notification', XMLDB_INDEX_NOTUNIQUE, array('useridto', 'timeusertodeleted', 'notification'));
|
||||
|
||||
// Conditionally launch add index useridto_timeusertodeleted_notification.
|
||||
if (!$dbman->index_exists($table, $index)) {
|
||||
$dbman->add_index($table, $index);
|
||||
}
|
||||
|
||||
$index = new xmldb_index('useridto', XMLDB_INDEX_NOTUNIQUE, array('useridto'));
|
||||
|
||||
// Conditionally launch drop index useridto.
|
||||
if ($dbman->index_exists($table, $index)) {
|
||||
$dbman->drop_index($table, $index);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2017020200.02);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6385,3 +6385,144 @@ function prevent_form_autofill_password() {
|
||||
debugging('prevent_form_autofill_password has been deprecated and is no longer in use.', DEBUG_DEVELOPER);
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the users recent conversations meaning all the people they've recently
|
||||
* sent or received a message from plus the most recent message sent to or received from each other user
|
||||
*
|
||||
* @deprecated since Moodle 3.3 MDL-57370
|
||||
* @param object|int $userorid the current user or user id
|
||||
* @param int $limitfrom can be used for paging
|
||||
* @param int $limitto can be used for paging
|
||||
* @return array
|
||||
*/
|
||||
function message_get_recent_conversations($userorid, $limitfrom = 0, $limitto = 100) {
|
||||
global $DB;
|
||||
|
||||
debugging('message_get_recent_conversations() is deprecated. Please use \core_message\api::get_conversations() instead.', DEBUG_DEVELOPER);
|
||||
|
||||
if (is_object($userorid)) {
|
||||
$user = $userorid;
|
||||
} else {
|
||||
$userid = $userorid;
|
||||
$user = new stdClass();
|
||||
$user->id = $userid;
|
||||
}
|
||||
|
||||
$userfields = user_picture::fields('otheruser', array('lastaccess'));
|
||||
|
||||
// This query retrieves the most recent message received from or sent to
|
||||
// seach other user.
|
||||
//
|
||||
// If two messages have the same timecreated, we take the one with the
|
||||
// larger id.
|
||||
//
|
||||
// There is a separate query for read and unread messages as they are stored
|
||||
// in different tables. They were originally retrieved in one query but it
|
||||
// was so large that it was difficult to be confident in its correctness.
|
||||
$uniquefield = $DB->sql_concat('message.useridfrom', "'-'", 'message.useridto');
|
||||
$sql = "SELECT $uniquefield, $userfields,
|
||||
message.id as mid, message.notification, message.useridfrom, message.useridto,
|
||||
message.smallmessage, message.fullmessage, message.fullmessagehtml,
|
||||
message.fullmessageformat, message.timecreated,
|
||||
contact.id as contactlistid, contact.blocked
|
||||
FROM {message_read} message
|
||||
JOIN (
|
||||
SELECT MAX(id) AS messageid,
|
||||
matchedmessage.useridto,
|
||||
matchedmessage.useridfrom
|
||||
FROM {message_read} matchedmessage
|
||||
INNER JOIN (
|
||||
SELECT MAX(recentmessages.timecreated) timecreated,
|
||||
recentmessages.useridfrom,
|
||||
recentmessages.useridto
|
||||
FROM {message_read} recentmessages
|
||||
WHERE (
|
||||
(recentmessages.useridfrom = :userid1 AND recentmessages.timeuserfromdeleted = 0) OR
|
||||
(recentmessages.useridto = :userid2 AND recentmessages.timeusertodeleted = 0)
|
||||
)
|
||||
GROUP BY recentmessages.useridfrom, recentmessages.useridto
|
||||
) recent ON matchedmessage.useridto = recent.useridto
|
||||
AND matchedmessage.useridfrom = recent.useridfrom
|
||||
AND matchedmessage.timecreated = recent.timecreated
|
||||
WHERE (
|
||||
(matchedmessage.useridfrom = :userid6 AND matchedmessage.timeuserfromdeleted = 0) OR
|
||||
(matchedmessage.useridto = :userid7 AND matchedmessage.timeusertodeleted = 0)
|
||||
)
|
||||
GROUP BY matchedmessage.useridto, matchedmessage.useridfrom
|
||||
) messagesubset ON messagesubset.messageid = message.id
|
||||
JOIN {user} otheruser ON (message.useridfrom = :userid4 AND message.useridto = otheruser.id)
|
||||
OR (message.useridto = :userid5 AND message.useridfrom = otheruser.id)
|
||||
LEFT JOIN {message_contacts} contact ON contact.userid = :userid3 AND contact.contactid = otheruser.id
|
||||
WHERE otheruser.deleted = 0 AND message.notification = 0
|
||||
ORDER BY message.timecreated DESC";
|
||||
$params = array(
|
||||
'userid1' => $user->id,
|
||||
'userid2' => $user->id,
|
||||
'userid3' => $user->id,
|
||||
'userid4' => $user->id,
|
||||
'userid5' => $user->id,
|
||||
'userid6' => $user->id,
|
||||
'userid7' => $user->id
|
||||
);
|
||||
$read = $DB->get_records_sql($sql, $params, $limitfrom, $limitto);
|
||||
|
||||
// We want to get the messages that have not been read. These are stored in the 'message' table. It is the
|
||||
// exact same query as the one above, except for the table we are querying. So, simply replace references to
|
||||
// the 'message_read' table with the 'message' table.
|
||||
$sql = str_replace('{message_read}', '{message}', $sql);
|
||||
$unread = $DB->get_records_sql($sql, $params, $limitfrom, $limitto);
|
||||
|
||||
$unreadcountssql = 'SELECT useridfrom, count(*) as count
|
||||
FROM {message}
|
||||
WHERE useridto = :userid
|
||||
AND timeusertodeleted = 0
|
||||
AND notification = 0
|
||||
GROUP BY useridfrom';
|
||||
$unreadcounts = $DB->get_records_sql($unreadcountssql, array('userid' => $user->id));
|
||||
|
||||
// Union the 2 result sets together looking for the message with the most
|
||||
// recent timecreated for each other user.
|
||||
// $conversation->id (the array key) is the other user's ID.
|
||||
$conversations = array();
|
||||
$conversation_arrays = array($unread, $read);
|
||||
foreach ($conversation_arrays as $conversation_array) {
|
||||
foreach ($conversation_array as $conversation) {
|
||||
// Only consider it unread if $user has unread messages.
|
||||
if (isset($unreadcounts[$conversation->useridfrom])) {
|
||||
$conversation->isread = 0;
|
||||
$conversation->unreadcount = $unreadcounts[$conversation->useridfrom]->count;
|
||||
} else {
|
||||
$conversation->isread = 1;
|
||||
}
|
||||
|
||||
if (!isset($conversations[$conversation->id])) {
|
||||
$conversations[$conversation->id] = $conversation;
|
||||
} else {
|
||||
$current = $conversations[$conversation->id];
|
||||
// We need to maintain the isread and unreadcount values from existing
|
||||
// parts of the conversation if we're replacing it.
|
||||
$conversation->isread = ($conversation->isread && $current->isread);
|
||||
if (isset($current->unreadcount) && !isset($conversation->unreadcount)) {
|
||||
$conversation->unreadcount = $current->unreadcount;
|
||||
}
|
||||
|
||||
if ($current->timecreated < $conversation->timecreated) {
|
||||
$conversations[$conversation->id] = $conversation;
|
||||
} else if ($current->timecreated == $conversation->timecreated) {
|
||||
if ($current->mid < $conversation->mid) {
|
||||
$conversations[$conversation->id] = $conversation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the conversations by $conversation->timecreated, newest to oldest
|
||||
// There may be multiple conversations with the same timecreated
|
||||
// The conversations array contains both read and unread messages (different tables) so sorting by ID won't work
|
||||
$result = core_collator::asort_objects_by_property($conversations, 'timecreated', core_collator::SORT_NUMERIC);
|
||||
$conversations = array_reverse($conversations);
|
||||
|
||||
return $conversations;
|
||||
}
|
||||
|
||||
+237
-5
@@ -239,18 +239,250 @@ class api {
|
||||
/**
|
||||
* Returns the contacts and their conversation to display in the contacts area.
|
||||
*
|
||||
* ** WARNING **
|
||||
* It is HIGHLY recommended to use a sensible limit when calling this function. Trying
|
||||
* to retrieve too much information in a single call will cause performance problems.
|
||||
* ** WARNING **
|
||||
*
|
||||
* This function has specifically been altered to break each of the data sets it
|
||||
* requires into separate database calls. This is to avoid the performance problems
|
||||
* observed when attempting to join large data sets (e.g. the message tables and
|
||||
* the user table).
|
||||
*
|
||||
* While it is possible to gather the data in a single query, and it may even be
|
||||
* more efficient with a correctly tuned database, we have opted to trade off some of
|
||||
* the benefits of a single query in order to ensure this function will work on
|
||||
* most databases with default tunings and with large data sets.
|
||||
*
|
||||
* @param int $userid The user id
|
||||
* @param int $limitfrom
|
||||
* @param int $limitnum
|
||||
* @return array
|
||||
*/
|
||||
public static function get_conversations($userid, $limitfrom = 0, $limitnum = 0) {
|
||||
$arrconversations = array();
|
||||
if ($conversations = message_get_recent_conversations($userid, $limitfrom, $limitnum)) {
|
||||
foreach ($conversations as $conversation) {
|
||||
$arrconversations[$conversation->id] = helper::create_contact($conversation);
|
||||
public static function get_conversations($userid, $limitfrom = 0, $limitnum = 20) {
|
||||
global $DB;
|
||||
|
||||
// The case statement is used to make sure the same key is generated
|
||||
// whether a user sent or received a message (it's the same conversation).
|
||||
// E.g. If there is a message from user 1 to user 2 and then from user 2 to user 1 the result set
|
||||
// will group those into a single record, since 1 -> 2 and 2 -> 1 is the same conversation.
|
||||
$case1 = $DB->sql_concat('useridfrom', "'-'", 'useridto');
|
||||
$case2 = $DB->sql_concat('useridto', "'-'", 'useridfrom');
|
||||
$convocase = "CASE WHEN useridfrom > useridto
|
||||
THEN $case1
|
||||
ELSE $case2 END";
|
||||
$convosig = "$convocase AS convo_signature";
|
||||
|
||||
// This is a snippet to join the message tables and filter out any messages the user has deleted
|
||||
// and ignore notifications. The fields are specified by name so that the union works on MySQL.
|
||||
$allmessages = "SELECT
|
||||
id, useridfrom, useridto, subject, fullmessage, fullmessageformat,
|
||||
fullmessagehtml, smallmessage, notification, contexturl,
|
||||
contexturlname, timecreated, timeuserfromdeleted, timeusertodeleted,
|
||||
component, eventtype, 0 as timeread
|
||||
FROM {message}
|
||||
WHERE
|
||||
(useridto = ? AND timeusertodeleted = 0 AND notification = 0)
|
||||
OR
|
||||
(useridfrom = ? AND timeuserfromdeleted = 0 AND notification = 0)
|
||||
UNION ALL
|
||||
SELECT
|
||||
id, useridfrom, useridto, subject, fullmessage, fullmessageformat,
|
||||
fullmessagehtml, smallmessage, notification, contexturl,
|
||||
contexturlname, timecreated, timeuserfromdeleted, timeusertodeleted,
|
||||
component, eventtype, timeread
|
||||
FROM {message_read}
|
||||
WHERE
|
||||
(useridto = ? AND timeusertodeleted = 0 AND notification = 0)
|
||||
OR
|
||||
(useridfrom = ? AND timeuserfromdeleted = 0 AND notification = 0)";
|
||||
$allmessagesparams = [$userid, $userid, $userid, $userid];
|
||||
|
||||
// Create a transaction to protect against concurrency issues.
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
|
||||
// First we need to get the list of conversations from the database ordered by the conversation
|
||||
// with the most recent message first.
|
||||
//
|
||||
// This query will join the two message tables and then group the results by the combination
|
||||
// of useridfrom and useridto (the 'convo_signature').
|
||||
$conversationssql = "SELECT $convosig, max(timecreated) as timecreated
|
||||
FROM ($allmessages) x
|
||||
GROUP BY $convocase
|
||||
ORDER BY max(timecreated) DESC, max(id) DESC";
|
||||
$conversationrecords = $DB->get_records_sql($conversationssql, $allmessagesparams, $limitfrom, $limitnum);
|
||||
|
||||
// This user has no conversations so we can return early here.
|
||||
if (empty($conversationrecords)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Next we need to get the max id of the messages sent at the latest time for each conversation.
|
||||
// This needs to be a separate query to above because there is no guarantee that the message with
|
||||
// the highest id will also have the highest timecreated value (in fact that is fairly likely due
|
||||
// to the split between the message tables).
|
||||
//
|
||||
// E.g. if we just added max(id) to the conversation query above and ran it on data like:
|
||||
// id, userfrom, userto, timecreated
|
||||
// 1, 1, 2, 2
|
||||
// 2, 2, 1, 1
|
||||
//
|
||||
// Then the result of the query would be:
|
||||
// convo_signature, timecreated, id
|
||||
// 2-1, 2, 2
|
||||
//
|
||||
// That would be incorrect since the message with id 2 actually has a lower timecreated. Hence why
|
||||
// the two queries need to be split.
|
||||
//
|
||||
// The same result could also be achieved with an inner join in a single query however we're specifically
|
||||
// avoiding multiple joins in the messaging queries because of the size of the messaging tables.
|
||||
$whereclauses = [];
|
||||
$createdtimes = [];
|
||||
foreach ($conversationrecords as $convoid => $record) {
|
||||
$whereclauses[] = "($convocase = '$convoid' AND timecreated = {$record->timecreated})";
|
||||
$createdtimes[] = $record->timecreated;
|
||||
}
|
||||
$messageidwhere = implode(' OR ', $whereclauses);
|
||||
list($timecreatedsql, $timecreatedparams) = $DB->get_in_or_equal($createdtimes);
|
||||
|
||||
$allmessagestimecreated = "SELECT id, useridfrom, useridto, timecreated
|
||||
FROM {message}
|
||||
WHERE
|
||||
(useridto = ? AND timeusertodeleted = 0 AND notification = 0)
|
||||
OR
|
||||
(useridfrom = ? AND timeuserfromdeleted = 0 AND notification = 0)
|
||||
AND timecreated $timecreatedsql
|
||||
UNION ALL
|
||||
SELECT id, useridfrom, useridto, timecreated
|
||||
FROM {message_read}
|
||||
WHERE
|
||||
(useridto = ? AND timeusertodeleted = 0 AND notification = 0)
|
||||
OR
|
||||
(useridfrom = ? AND timeuserfromdeleted = 0 AND notification = 0)
|
||||
AND timecreated $timecreatedsql";
|
||||
$messageidsql = "SELECT max(id)
|
||||
FROM ($allmessagestimecreated) x
|
||||
WHERE $messageidwhere
|
||||
GROUP BY $convocase, timecreated";
|
||||
$messageidparams = array_merge([$userid, $userid], $timecreatedparams, [$userid, $userid], $timecreatedparams);
|
||||
$messageids = array_keys($DB->get_records_sql($messageidsql, $messageidparams));
|
||||
|
||||
// Ok, let's recap. We've pulled a descending ordered list of conversations by latest time created
|
||||
// for the given user. For each of those conversations we've grabbed the max id for messages
|
||||
// created at that time.
|
||||
//
|
||||
// So at this point we have the list of ids for the most recent message in each of the user's most
|
||||
// recent conversations. Now we need to pull all of the message and user data for each message id.
|
||||
list($idsql, $idparams) = $DB->get_in_or_equal($messageids);
|
||||
$messagesql = "SELECT $convosig, m.smallmessage, m.id, m.useridto, m.useridfrom, m.timeread
|
||||
FROM ($allmessages) m
|
||||
WHERE m.id $idsql";
|
||||
$messageparams = array_merge($allmessagesparams, $idparams);
|
||||
|
||||
// We need to handle the case where the $messageids contains two ids from the same conversation
|
||||
// (which can happen because there can be id clashes between the read and unread tables). In
|
||||
// this case we will prioritise the unread message.
|
||||
$messageset = $DB->get_recordset_sql($messagesql, $messageparams);
|
||||
$messages = [];
|
||||
foreach ($messageset as $message) {
|
||||
$id = $message->convo_signature;
|
||||
if (!isset($messages[$id]) || empty($message->timeread)) {
|
||||
$messages[$id] = $message;
|
||||
}
|
||||
}
|
||||
$messageset->close();
|
||||
|
||||
// We need to pull out the list of other users that are part of each of these conversations. This
|
||||
// needs to be done in a separate query to avoid doing a join on the messages tables and the user
|
||||
// tables because on large sites these tables are massive which results in extremely slow
|
||||
// performance (typically due to join buffer exhaustion).
|
||||
$otheruserids = array_map(function($message) use ($userid) {
|
||||
return ($message->useridfrom == $userid) ? $message->useridto : $message->useridfrom;
|
||||
}, array_values($messages));
|
||||
|
||||
list($useridsql, $usersparams) = $DB->get_in_or_equal($otheruserids);
|
||||
$userfields = \user_picture::fields('', array('lastaccess'));
|
||||
$userssql = "SELECT $userfields
|
||||
FROM {user}
|
||||
WHERE id $useridsql";
|
||||
$otherusers = $DB->get_records_sql($userssql, $usersparams);
|
||||
|
||||
// Similar to the above use case, we need to pull the contact information and again this has
|
||||
// specifically been separated into another query to avoid having to do joins on the message
|
||||
// tables.
|
||||
$contactssql = "SELECT contactid, blocked
|
||||
FROM {message_contacts}
|
||||
WHERE userid = ? AND contactid $useridsql";
|
||||
$contacts = $DB->get_records_sql($contactssql, array_merge([$userid], $otheruserids));
|
||||
|
||||
// Finally, let's get the unread messages count for this user so that we can add them
|
||||
// to the conversation.
|
||||
$unreadcountssql = 'SELECT useridfrom, count(*) as count
|
||||
FROM {message}
|
||||
WHERE useridto = ?
|
||||
AND timeusertodeleted = 0
|
||||
AND notification = 0
|
||||
GROUP BY useridfrom';
|
||||
$unreadcounts = $DB->get_records_sql($unreadcountssql, [$userid]);
|
||||
|
||||
// We can close off the transaction now.
|
||||
$DB->commit_delegated_transaction($transaction);
|
||||
|
||||
// Now we need to order the messages back into the same order of the conversations.
|
||||
$orderedconvosigs = array_keys($conversationrecords);
|
||||
usort($messages, function($a, $b) use ($orderedconvosigs) {
|
||||
$aindex = array_search($a->convo_signature, $orderedconvosigs);
|
||||
$bindex = array_search($b->convo_signature, $orderedconvosigs);
|
||||
|
||||
return ($aindex < $bindex) ? -1 : 1;
|
||||
});
|
||||
|
||||
// Preload the contexts before we construct the conversation to prevent the
|
||||
// create_contact helper from needing to query the DB so often.
|
||||
$ctxselect = \context_helper::get_preload_record_columns_sql('ctx');
|
||||
$sql = "SELECT {$ctxselect}
|
||||
FROM {context} ctx
|
||||
WHERE ctx.contextlevel = ? AND
|
||||
ctx.instanceid {$useridsql}";
|
||||
$contexts = [];
|
||||
$contexts = $DB->get_records_sql($sql, array_merge([CONTEXT_USER], $usersparams));
|
||||
foreach ($contexts as $context) {
|
||||
\context_helper::preload_from_record($context);
|
||||
}
|
||||
|
||||
$userproperties = explode(',', $userfields);
|
||||
$arrconversations = array();
|
||||
// The last step now is to bring all of the data we've gathered together to create
|
||||
// a conversation (or contact, as the API is named...).
|
||||
foreach ($messages as $message) {
|
||||
$conversation = new \stdClass();
|
||||
$otheruserid = ($message->useridfrom == $userid) ? $message->useridto : $message->useridfrom;
|
||||
$otheruser = isset($otherusers[$otheruserid]) ? $otherusers[$otheruserid] : null;
|
||||
$contact = isset($contacts[$otheruserid]) ? $contacts[$otheruserid] : null;
|
||||
|
||||
// Add the other user's information to the conversation, if we have one.
|
||||
foreach ($userproperties as $prop) {
|
||||
$conversation->$prop = ($otheruser) ? $otheruser->$prop : null;
|
||||
}
|
||||
|
||||
// Add the contact's information, if we have one.
|
||||
$conversation->blocked = ($contact) ? $contact->blocked : null;
|
||||
|
||||
// Add the message information.
|
||||
$conversation->messageid = $message->id;
|
||||
$conversation->smallmessage = $message->smallmessage;
|
||||
$conversation->useridfrom = $message->useridfrom;
|
||||
|
||||
// Only consider it unread if $user has unread messages.
|
||||
if (isset($unreadcounts[$otheruserid])) {
|
||||
$conversation->isread = false;
|
||||
$conversation->unreadcount = $unreadcounts[$otheruserid]->count;
|
||||
} else {
|
||||
$conversation->isread = true;
|
||||
}
|
||||
|
||||
$arrconversations[$message->convo_signature] = helper::create_contact($conversation);
|
||||
}
|
||||
|
||||
return $arrconversations;
|
||||
}
|
||||
|
||||
-138
@@ -228,144 +228,6 @@ function message_count_unread_messages($user1=null, $user2=null) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the users recent conversations meaning all the people they've recently
|
||||
* sent or received a message from plus the most recent message sent to or received from each other user
|
||||
*
|
||||
* @param object|int $userorid the current user or user id
|
||||
* @param int $limitfrom can be used for paging
|
||||
* @param int $limitto can be used for paging
|
||||
* @return array
|
||||
*/
|
||||
function message_get_recent_conversations($userorid, $limitfrom = 0, $limitto = 100) {
|
||||
global $DB;
|
||||
|
||||
if (is_object($userorid)) {
|
||||
$user = $userorid;
|
||||
} else {
|
||||
$userid = $userorid;
|
||||
$user = new stdClass();
|
||||
$user->id = $userid;
|
||||
}
|
||||
|
||||
$userfields = user_picture::fields('otheruser', array('lastaccess'));
|
||||
|
||||
// This query retrieves the most recent message received from or sent to
|
||||
// seach other user.
|
||||
//
|
||||
// If two messages have the same timecreated, we take the one with the
|
||||
// larger id.
|
||||
//
|
||||
// There is a separate query for read and unread messages as they are stored
|
||||
// in different tables. They were originally retrieved in one query but it
|
||||
// was so large that it was difficult to be confident in its correctness.
|
||||
$uniquefield = $DB->sql_concat('message.useridfrom', "'-'", 'message.useridto');
|
||||
$sql = "SELECT $uniquefield, $userfields,
|
||||
message.id as mid, message.notification, message.useridfrom, message.useridto,
|
||||
message.smallmessage, message.fullmessage, message.fullmessagehtml,
|
||||
message.fullmessageformat, message.timecreated,
|
||||
contact.id as contactlistid, contact.blocked
|
||||
FROM {message_read} message
|
||||
JOIN (
|
||||
SELECT MAX(id) AS messageid,
|
||||
matchedmessage.useridto,
|
||||
matchedmessage.useridfrom
|
||||
FROM {message_read} matchedmessage
|
||||
INNER JOIN (
|
||||
SELECT MAX(recentmessages.timecreated) timecreated,
|
||||
recentmessages.useridfrom,
|
||||
recentmessages.useridto
|
||||
FROM {message_read} recentmessages
|
||||
WHERE (
|
||||
(recentmessages.useridfrom = :userid1 AND recentmessages.timeuserfromdeleted = 0) OR
|
||||
(recentmessages.useridto = :userid2 AND recentmessages.timeusertodeleted = 0)
|
||||
)
|
||||
GROUP BY recentmessages.useridfrom, recentmessages.useridto
|
||||
) recent ON matchedmessage.useridto = recent.useridto
|
||||
AND matchedmessage.useridfrom = recent.useridfrom
|
||||
AND matchedmessage.timecreated = recent.timecreated
|
||||
WHERE (
|
||||
(matchedmessage.useridfrom = :userid6 AND matchedmessage.timeuserfromdeleted = 0) OR
|
||||
(matchedmessage.useridto = :userid7 AND matchedmessage.timeusertodeleted = 0)
|
||||
)
|
||||
GROUP BY matchedmessage.useridto, matchedmessage.useridfrom
|
||||
) messagesubset ON messagesubset.messageid = message.id
|
||||
JOIN {user} otheruser ON (message.useridfrom = :userid4 AND message.useridto = otheruser.id)
|
||||
OR (message.useridto = :userid5 AND message.useridfrom = otheruser.id)
|
||||
LEFT JOIN {message_contacts} contact ON contact.userid = :userid3 AND contact.contactid = otheruser.id
|
||||
WHERE otheruser.deleted = 0 AND message.notification = 0
|
||||
ORDER BY message.timecreated DESC";
|
||||
$params = array(
|
||||
'userid1' => $user->id,
|
||||
'userid2' => $user->id,
|
||||
'userid3' => $user->id,
|
||||
'userid4' => $user->id,
|
||||
'userid5' => $user->id,
|
||||
'userid6' => $user->id,
|
||||
'userid7' => $user->id
|
||||
);
|
||||
$read = $DB->get_records_sql($sql, $params, $limitfrom, $limitto);
|
||||
|
||||
// We want to get the messages that have not been read. These are stored in the 'message' table. It is the
|
||||
// exact same query as the one above, except for the table we are querying. So, simply replace references to
|
||||
// the 'message_read' table with the 'message' table.
|
||||
$sql = str_replace('{message_read}', '{message}', $sql);
|
||||
$unread = $DB->get_records_sql($sql, $params, $limitfrom, $limitto);
|
||||
|
||||
$unreadcountssql = 'SELECT useridfrom, count(*) as count
|
||||
FROM {message}
|
||||
WHERE useridto = :userid
|
||||
AND timeusertodeleted = 0
|
||||
AND notification = 0
|
||||
GROUP BY useridfrom';
|
||||
$unreadcounts = $DB->get_records_sql($unreadcountssql, array('userid' => $user->id));
|
||||
|
||||
// Union the 2 result sets together looking for the message with the most
|
||||
// recent timecreated for each other user.
|
||||
// $conversation->id (the array key) is the other user's ID.
|
||||
$conversations = array();
|
||||
$conversation_arrays = array($unread, $read);
|
||||
foreach ($conversation_arrays as $conversation_array) {
|
||||
foreach ($conversation_array as $conversation) {
|
||||
// Only consider it unread if $user has unread messages.
|
||||
if (isset($unreadcounts[$conversation->useridfrom])) {
|
||||
$conversation->isread = 0;
|
||||
$conversation->unreadcount = $unreadcounts[$conversation->useridfrom]->count;
|
||||
} else {
|
||||
$conversation->isread = 1;
|
||||
}
|
||||
|
||||
if (!isset($conversations[$conversation->id])) {
|
||||
$conversations[$conversation->id] = $conversation;
|
||||
} else {
|
||||
$current = $conversations[$conversation->id];
|
||||
// We need to maintain the isread and unreadcount values from existing
|
||||
// parts of the conversation if we're replacing it.
|
||||
$conversation->isread = ($conversation->isread && $current->isread);
|
||||
if (isset($current->unreadcount) && !isset($conversation->unreadcount)) {
|
||||
$conversation->unreadcount = $current->unreadcount;
|
||||
}
|
||||
|
||||
if ($current->timecreated < $conversation->timecreated) {
|
||||
$conversations[$conversation->id] = $conversation;
|
||||
} else if ($current->timecreated == $conversation->timecreated) {
|
||||
if ($current->mid < $conversation->mid) {
|
||||
$conversations[$conversation->id] = $conversation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the conversations by $conversation->timecreated, newest to oldest
|
||||
// There may be multiple conversations with the same timecreated
|
||||
// The conversations array contains both read and unread messages (different tables) so sorting by ID won't work
|
||||
$result = core_collator::asort_objects_by_property($conversations, 'timecreated', core_collator::SORT_NUMERIC);
|
||||
$conversations = array_reverse($conversations);
|
||||
|
||||
return $conversations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess how to convert the message to html.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="message/output/popup/db" VERSION="20160729" COMMENT="XMLDB file for Moodle message/output/popup"
|
||||
<XMLDB PATH="message/output/popup/db" VERSION="20161221" COMMENT="XMLDB file for Moodle message/output/popup"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
@@ -15,6 +15,7 @@
|
||||
</KEYS>
|
||||
<INDEXES>
|
||||
<INDEX NAME="messageid-isread" UNIQUE="true" FIELDS="messageid, isread"/>
|
||||
<INDEX NAME="isread" UNIQUE="false" FIELDS="isread"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
</TABLES>
|
||||
|
||||
@@ -74,5 +74,20 @@ function xmldb_message_popup_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2016122100) {
|
||||
|
||||
// Define index isread (not unique) to be added to message_popup.
|
||||
$table = new xmldb_table('message_popup');
|
||||
$index = new xmldb_index('isread', XMLDB_INDEX_NOTUNIQUE, array('isread'));
|
||||
|
||||
// Conditionally launch add index isread.
|
||||
if (!$dbman->index_exists($table, $index)) {
|
||||
$dbman->add_index($table, $index);
|
||||
}
|
||||
|
||||
// Popup savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2016122100, 'message', 'popup');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2016120500; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->version = 2016122100; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2016112900; // Requires this Moodle version
|
||||
$plugin->component = 'message_popup'; // Full name of the plugin (used for diagnostics)
|
||||
|
||||
+391
-8
@@ -338,16 +338,16 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
|
||||
$this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
|
||||
$this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
|
||||
$this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
|
||||
$this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
|
||||
$messageid1 = $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
|
||||
|
||||
$this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
|
||||
$this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
|
||||
$this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
|
||||
$this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
|
||||
$messageid2 = $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
|
||||
|
||||
$this->send_fake_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?', 0, $time + 9);
|
||||
$this->send_fake_message($user4, $user1, 'Yah brah, it\'s pretty rad.', 0, $time + 10);
|
||||
$this->send_fake_message($user1, $user4, 'Dope.', 0, $time + 11);
|
||||
$messageid3 = $this->send_fake_message($user1, $user4, 'Dope.', 0, $time + 11);
|
||||
|
||||
// Retrieve the conversations.
|
||||
$conversations = \core_message\api::get_conversations($user1->id);
|
||||
@@ -363,17 +363,17 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
|
||||
$this->assertEquals($user1->id, $message1->useridfrom);
|
||||
$this->assertTrue($message1->ismessaging);
|
||||
$this->assertEquals('Dope.', $message1->lastmessage);
|
||||
$this->assertNull($message1->messageid);
|
||||
$this->assertEquals($messageid3, $message1->messageid);
|
||||
$this->assertNull($message1->isonline);
|
||||
$this->assertTrue($message1->isread);
|
||||
$this->assertFalse($message1->isread);
|
||||
$this->assertFalse($message1->isblocked);
|
||||
$this->assertEquals(0, $message1->unreadcount);
|
||||
$this->assertEquals(1, $message1->unreadcount);
|
||||
|
||||
$this->assertEquals($user3->id, $message2->userid);
|
||||
$this->assertEquals($user3->id, $message2->useridfrom);
|
||||
$this->assertTrue($message2->ismessaging);
|
||||
$this->assertEquals('Cool.', $message2->lastmessage);
|
||||
$this->assertNull($message2->messageid);
|
||||
$this->assertEquals($messageid2, $message2->messageid);
|
||||
$this->assertNull($message2->isonline);
|
||||
$this->assertFalse($message2->isread);
|
||||
$this->assertFalse($message2->isblocked);
|
||||
@@ -383,13 +383,396 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
|
||||
$this->assertEquals($user2->id, $message3->useridfrom);
|
||||
$this->assertTrue($message3->ismessaging);
|
||||
$this->assertEquals('Word.', $message3->lastmessage);
|
||||
$this->assertNull($message3->messageid);
|
||||
$this->assertEquals($messageid1, $message3->messageid);
|
||||
$this->assertNull($message3->isonline);
|
||||
$this->assertFalse($message3->isread);
|
||||
$this->assertFalse($message3->isblocked);
|
||||
$this->assertEquals(2, $message3->unreadcount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests retrieving conversations with a limit and offset to ensure pagination works correctly.
|
||||
*/
|
||||
public function test_get_conversations_limit_offset() {
|
||||
// Create some users.
|
||||
$user1 = self::getDataGenerator()->create_user();
|
||||
$user2 = self::getDataGenerator()->create_user();
|
||||
$user3 = self::getDataGenerator()->create_user();
|
||||
$user4 = self::getDataGenerator()->create_user();
|
||||
|
||||
// The person doing the search.
|
||||
$this->setUser($user1);
|
||||
|
||||
// Send some messages back and forth, have some different conversations with different users.
|
||||
$time = 1;
|
||||
$this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
|
||||
$this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
|
||||
$this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
|
||||
$messageid1 = $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
|
||||
|
||||
$this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
|
||||
$this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
|
||||
$this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
|
||||
$messageid2 = $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
|
||||
|
||||
$this->send_fake_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?', 0, $time + 9);
|
||||
$this->send_fake_message($user4, $user1, 'Yah brah, it\'s pretty rad.', 0, $time + 10);
|
||||
$messageid3 = $this->send_fake_message($user1, $user4, 'Dope.', 0, $time + 11);
|
||||
|
||||
// Retrieve the conversations.
|
||||
$conversations = \core_message\api::get_conversations($user1->id, 1, 1);
|
||||
|
||||
// We should only have one conversation because of the limit.
|
||||
$this->assertCount(1, $conversations);
|
||||
|
||||
$conversation = array_shift($conversations);
|
||||
|
||||
$this->assertEquals($user3->id, $conversation->userid);
|
||||
$this->assertEquals($user3->id, $conversation->useridfrom);
|
||||
$this->assertTrue($conversation->ismessaging);
|
||||
$this->assertEquals('Cool.', $conversation->lastmessage);
|
||||
$this->assertEquals($messageid2, $conversation->messageid);
|
||||
$this->assertNull($conversation->isonline);
|
||||
$this->assertFalse($conversation->isread);
|
||||
$this->assertFalse($conversation->isblocked);
|
||||
$this->assertEquals(2, $conversation->unreadcount);
|
||||
|
||||
// Retrieve the next conversation.
|
||||
$conversations = \core_message\api::get_conversations($user1->id, 2, 1);
|
||||
|
||||
// We should only have one conversation because of the limit.
|
||||
$this->assertCount(1, $conversations);
|
||||
|
||||
$conversation = array_shift($conversations);
|
||||
|
||||
$this->assertEquals($user2->id, $conversation->userid);
|
||||
$this->assertEquals($user2->id, $conversation->useridfrom);
|
||||
$this->assertTrue($conversation->ismessaging);
|
||||
$this->assertEquals('Word.', $conversation->lastmessage);
|
||||
$this->assertEquals($messageid1, $conversation->messageid);
|
||||
$this->assertNull($conversation->isonline);
|
||||
$this->assertFalse($conversation->isread);
|
||||
$this->assertFalse($conversation->isblocked);
|
||||
$this->assertEquals(2, $conversation->unreadcount);
|
||||
|
||||
// Ask for an offset that doesn't exist.
|
||||
$conversations = \core_message\api::get_conversations($user1->id, 4, 1);
|
||||
|
||||
// We should not get any conversations back.
|
||||
$this->assertCount(0, $conversations);
|
||||
}
|
||||
|
||||
/**
|
||||
* The data provider for get_conversations_mixed.
|
||||
*
|
||||
* This provides sets of data to for testing.
|
||||
* @return array
|
||||
*/
|
||||
public function get_conversations_mixed_provider() {
|
||||
return array(
|
||||
'Test that conversations with messages contacts is correctly ordered.' => array(
|
||||
'users' => array(
|
||||
'user1',
|
||||
'user2',
|
||||
'user3',
|
||||
),
|
||||
'contacts' => array(
|
||||
),
|
||||
'messages' => array(
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user2',
|
||||
'state' => 'unread',
|
||||
'subject' => 'S1',
|
||||
),
|
||||
array(
|
||||
'from' => 'user2',
|
||||
'to' => 'user1',
|
||||
'state' => 'unread',
|
||||
'subject' => 'S2',
|
||||
),
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user2',
|
||||
'state' => 'unread',
|
||||
'timecreated' => 0,
|
||||
'subject' => 'S3',
|
||||
),
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user3',
|
||||
'state' => 'read',
|
||||
'timemodifier' => 1,
|
||||
'subject' => 'S4',
|
||||
),
|
||||
array(
|
||||
'from' => 'user3',
|
||||
'to' => 'user1',
|
||||
'state' => 'read',
|
||||
'timemodifier' => 1,
|
||||
'subject' => 'S5',
|
||||
),
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user3',
|
||||
'state' => 'read',
|
||||
'timecreated' => 0,
|
||||
'subject' => 'S6',
|
||||
),
|
||||
),
|
||||
'expectations' => array(
|
||||
'user1' => array(
|
||||
// User1 has conversed most recently with user3. The most recent message is M5.
|
||||
array(
|
||||
'messageposition' => 0,
|
||||
'with' => 'user3',
|
||||
'subject' => 'S5',
|
||||
),
|
||||
// User1 has also conversed with user2. The most recent message is S2.
|
||||
array(
|
||||
'messageposition' => 1,
|
||||
'with' => 'user2',
|
||||
'subject' => 'S2',
|
||||
),
|
||||
),
|
||||
'user2' => array(
|
||||
// User2 has only conversed with user1. Their most recent shared message was S2.
|
||||
array(
|
||||
'messageposition' => 0,
|
||||
'with' => 'user1',
|
||||
'subject' => 'S2',
|
||||
),
|
||||
),
|
||||
'user3' => array(
|
||||
// User3 has only conversed with user1. Their most recent shared message was S5.
|
||||
array(
|
||||
'messageposition' => 0,
|
||||
'with' => 'user1',
|
||||
'subject' => 'S5',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'Test that users with contacts and messages to self work as expected' => array(
|
||||
'users' => array(
|
||||
'user1',
|
||||
'user2',
|
||||
'user3',
|
||||
),
|
||||
'contacts' => array(
|
||||
'user1' => array(
|
||||
'user2' => 0,
|
||||
'user3' => 0,
|
||||
),
|
||||
'user2' => array(
|
||||
'user3' => 0,
|
||||
),
|
||||
),
|
||||
'messages' => array(
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user1',
|
||||
'state' => 'unread',
|
||||
'subject' => 'S1',
|
||||
),
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user1',
|
||||
'state' => 'unread',
|
||||
'subject' => 'S2',
|
||||
),
|
||||
),
|
||||
'expectations' => array(
|
||||
'user1' => array(
|
||||
// User1 has conversed most recently with user1. The most recent message is S2.
|
||||
array(
|
||||
'messageposition' => 0,
|
||||
'with' => 'user1',
|
||||
'subject' => 'S2',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'Test conversations with a single user, where some messages are read and some are not.' => array(
|
||||
'users' => array(
|
||||
'user1',
|
||||
'user2',
|
||||
),
|
||||
'contacts' => array(
|
||||
),
|
||||
'messages' => array(
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user2',
|
||||
'state' => 'read',
|
||||
'subject' => 'S1',
|
||||
),
|
||||
array(
|
||||
'from' => 'user2',
|
||||
'to' => 'user1',
|
||||
'state' => 'read',
|
||||
'subject' => 'S2',
|
||||
),
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user2',
|
||||
'state' => 'unread',
|
||||
'timemodifier' => 1,
|
||||
'subject' => 'S3',
|
||||
),
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user2',
|
||||
'state' => 'unread',
|
||||
'timemodifier' => 1,
|
||||
'subject' => 'S4',
|
||||
),
|
||||
),
|
||||
'expectations' => array(
|
||||
// The most recent message between user1 and user2 was S4.
|
||||
'user1' => array(
|
||||
array(
|
||||
'messageposition' => 0,
|
||||
'with' => 'user2',
|
||||
'subject' => 'S4',
|
||||
),
|
||||
),
|
||||
'user2' => array(
|
||||
// The most recent message between user1 and user2 was S4.
|
||||
array(
|
||||
'messageposition' => 0,
|
||||
'with' => 'user1',
|
||||
'subject' => 'S4',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'Test conversations with a single user, where some messages are read and some are not, and messages ' .
|
||||
'are out of order' => array(
|
||||
// This can happen through a combination of factors including multi-master DB replication with messages
|
||||
// read somehow (e.g. API).
|
||||
'users' => array(
|
||||
'user1',
|
||||
'user2',
|
||||
),
|
||||
'contacts' => array(
|
||||
),
|
||||
'messages' => array(
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user2',
|
||||
'state' => 'read',
|
||||
'subject' => 'S1',
|
||||
'timemodifier' => 1,
|
||||
),
|
||||
array(
|
||||
'from' => 'user2',
|
||||
'to' => 'user1',
|
||||
'state' => 'read',
|
||||
'subject' => 'S2',
|
||||
'timemodifier' => 2,
|
||||
),
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user2',
|
||||
'state' => 'unread',
|
||||
'subject' => 'S3',
|
||||
),
|
||||
array(
|
||||
'from' => 'user1',
|
||||
'to' => 'user2',
|
||||
'state' => 'unread',
|
||||
'subject' => 'S4',
|
||||
),
|
||||
),
|
||||
'expectations' => array(
|
||||
// The most recent message between user1 and user2 was S2, even though later IDs have not been read.
|
||||
'user1' => array(
|
||||
array(
|
||||
'messageposition' => 0,
|
||||
'with' => 'user2',
|
||||
'subject' => 'S2',
|
||||
),
|
||||
),
|
||||
'user2' => array(
|
||||
array(
|
||||
'messageposition' => 0,
|
||||
'with' => 'user1',
|
||||
'subject' => 'S2',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_conversations with a mixture of messages.
|
||||
*
|
||||
* @dataProvider get_conversations_mixed_provider
|
||||
* @param array $usersdata The list of users to create for this test.
|
||||
* @param array $messagesdata The list of messages to create.
|
||||
* @param array $expectations The list of expected outcomes.
|
||||
*/
|
||||
public function test_get_conversations_mixed($usersdata, $contacts, $messagesdata, $expectations) {
|
||||
global $DB;
|
||||
|
||||
// Create all of the users.
|
||||
$users = array();
|
||||
foreach ($usersdata as $username) {
|
||||
$users[$username] = $this->getDataGenerator()->create_user(array('username' => $username));
|
||||
}
|
||||
|
||||
foreach ($contacts as $username => $contact) {
|
||||
foreach ($contact as $contactname => $blocked) {
|
||||
$record = new stdClass();
|
||||
$record->userid = $users[$username]->id;
|
||||
$record->contactid = $users[$contactname]->id;
|
||||
$record->blocked = $blocked;
|
||||
$record->id = $DB->insert_record('message_contacts', $record);
|
||||
}
|
||||
}
|
||||
|
||||
$defaulttimecreated = time();
|
||||
foreach ($messagesdata as $messagedata) {
|
||||
$from = $users[$messagedata['from']];
|
||||
$to = $users[$messagedata['to']];
|
||||
$subject = $messagedata['subject'];
|
||||
|
||||
if (isset($messagedata['state']) && $messagedata['state'] == 'unread') {
|
||||
$table = 'message';
|
||||
$messageid = $this->send_fake_message($from, $to, $subject);
|
||||
} else {
|
||||
// If there is no state, or the state is not 'unread', assume the message is read.
|
||||
$table = 'message_read';
|
||||
$messageid = message_post_message($from, $to, $subject, FORMAT_PLAIN);
|
||||
}
|
||||
|
||||
$updatemessage = new stdClass();
|
||||
$updatemessage->id = $messageid;
|
||||
if (isset($messagedata['timecreated'])) {
|
||||
$updatemessage->timecreated = $messagedata['timecreated'];
|
||||
} else if (isset($messagedata['timemodifier'])) {
|
||||
$updatemessage->timecreated = $defaulttimecreated + $messagedata['timemodifier'];
|
||||
} else {
|
||||
$updatemessage->timecreated = $defaulttimecreated;
|
||||
}
|
||||
$DB->update_record($table, $updatemessage);
|
||||
}
|
||||
|
||||
foreach ($expectations as $username => $data) {
|
||||
// Get the recent conversations for the specified user.
|
||||
$user = $users[$username];
|
||||
$conversations = array_values(\core_message\api::get_conversations($user->id));
|
||||
foreach ($data as $expectation) {
|
||||
$otheruser = $users[$expectation['with']];
|
||||
$conversation = $conversations[$expectation['messageposition']];
|
||||
$this->assertEquals($otheruser->id, $conversation->userid);
|
||||
$this->assertEquals($expectation['subject'], $conversation->lastmessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests retrieving contacts.
|
||||
*/
|
||||
|
||||
@@ -69,7 +69,8 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
|
||||
$record->fullmessage = $message;
|
||||
$record->timecreated = $time;
|
||||
$record->notification = $notification;
|
||||
$DB->insert_record('message', $record);
|
||||
|
||||
return $DB->insert_record('message', $record);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1536,16 +1537,16 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->send_message($user1, $user2, 'Yo!', 0, $time);
|
||||
$this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
|
||||
$this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
|
||||
$this->send_message($user2, $user1, 'Word.', 0, $time + 3);
|
||||
$messageid1 = $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
|
||||
|
||||
$this->send_message($user1, $user3, 'Booyah', 0, $time + 4);
|
||||
$this->send_message($user3, $user1, 'Whaaat?', 0, $time + 5);
|
||||
$this->send_message($user1, $user3, 'Nothing.', 0, $time + 6);
|
||||
$this->send_message($user3, $user1, 'Cool.', 0, $time + 7);
|
||||
$messageid2 = $this->send_message($user3, $user1, 'Cool.', 0, $time + 7);
|
||||
|
||||
$this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?', 0, $time + 8);
|
||||
$this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.', 0, $time + 9);
|
||||
$this->send_message($user1, $user4, 'Dope.', 0, $time + 10);
|
||||
$messageid3 = $this->send_message($user1, $user4, 'Dope.', 0, $time + 10);
|
||||
|
||||
// Retrieve the conversations.
|
||||
$result = core_message_external::data_for_messagearea_conversations($user1->id);
|
||||
@@ -1566,17 +1567,17 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertTrue($message1['ismessaging']);
|
||||
$this->assertTrue($message1['sentfromcurrentuser']);
|
||||
$this->assertEquals('Dope.', $message1['lastmessage']);
|
||||
$this->assertNull($message1['messageid']);
|
||||
$this->assertEquals($messageid3, $message1['messageid']);
|
||||
$this->assertNull($message1['isonline']);
|
||||
$this->assertTrue($message1['isread']);
|
||||
$this->assertFalse($message1['isread']);
|
||||
$this->assertFalse($message1['isblocked']);
|
||||
$this->assertEquals(0, $message1['unreadcount']);
|
||||
$this->assertEquals(1, $message1['unreadcount']);
|
||||
|
||||
$this->assertEquals($user3->id, $message2['userid']);
|
||||
$this->assertTrue($message2['ismessaging']);
|
||||
$this->assertFalse($message2['sentfromcurrentuser']);
|
||||
$this->assertEquals('Cool.', $message2['lastmessage']);
|
||||
$this->assertNull($message2['messageid']);
|
||||
$this->assertEquals($messageid2, $message2['messageid']);
|
||||
$this->assertNull($message2['isonline']);
|
||||
$this->assertFalse($message2['isread']);
|
||||
$this->assertFalse($message2['isblocked']);
|
||||
@@ -1586,7 +1587,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertTrue($message3['ismessaging']);
|
||||
$this->assertFalse($message3['sentfromcurrentuser']);
|
||||
$this->assertEquals('Word.', $message3['lastmessage']);
|
||||
$this->assertNull($message3['messageid']);
|
||||
$this->assertEquals($messageid1, $message3['messageid']);
|
||||
$this->assertNull($message3['isonline']);
|
||||
$this->assertFalse($message3['isread']);
|
||||
$this->assertFalse($message3['isblocked']);
|
||||
@@ -1613,16 +1614,16 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->send_message($user1, $user2, 'Yo!', 0, $time);
|
||||
$this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
|
||||
$this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
|
||||
$this->send_message($user2, $user1, 'Word.', 0, $time + 3);
|
||||
$messageid1 = $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
|
||||
|
||||
$this->send_message($user1, $user3, 'Booyah', 0, $time + 4);
|
||||
$this->send_message($user3, $user1, 'Whaaat?', 0, $time + 5);
|
||||
$this->send_message($user1, $user3, 'Nothing.', 0, $time + 6);
|
||||
$this->send_message($user3, $user1, 'Cool.', 0, $time + 7);
|
||||
$messageid2 = $this->send_message($user3, $user1, 'Cool.', 0, $time + 7);
|
||||
|
||||
$this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?', 0, $time + 8);
|
||||
$this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.', 0, $time + 9);
|
||||
$this->send_message($user1, $user4, 'Dope.', 0, $time + 10);
|
||||
$messageid3 = $this->send_message($user1, $user4, 'Dope.', 0, $time + 10);
|
||||
|
||||
// Retrieve the conversations.
|
||||
$result = core_message_external::data_for_messagearea_conversations($user1->id);
|
||||
@@ -1643,17 +1644,17 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertTrue($message1['ismessaging']);
|
||||
$this->assertTrue($message1['sentfromcurrentuser']);
|
||||
$this->assertEquals('Dope.', $message1['lastmessage']);
|
||||
$this->assertNull($message1['messageid']);
|
||||
$this->assertEquals($messageid3, $message1['messageid']);
|
||||
$this->assertFalse($message1['isonline']);
|
||||
$this->assertTrue($message1['isread']);
|
||||
$this->assertFalse($message1['isread']);
|
||||
$this->assertFalse($message1['isblocked']);
|
||||
$this->assertEquals(0, $message1['unreadcount']);
|
||||
$this->assertEquals(1, $message1['unreadcount']);
|
||||
|
||||
$this->assertEquals($user3->id, $message2['userid']);
|
||||
$this->assertTrue($message2['ismessaging']);
|
||||
$this->assertFalse($message2['sentfromcurrentuser']);
|
||||
$this->assertEquals('Cool.', $message2['lastmessage']);
|
||||
$this->assertNull($message2['messageid']);
|
||||
$this->assertEquals($messageid2, $message2['messageid']);
|
||||
$this->assertFalse($message2['isonline']);
|
||||
$this->assertFalse($message2['isread']);
|
||||
$this->assertFalse($message2['isblocked']);
|
||||
@@ -1663,7 +1664,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertTrue($message3['ismessaging']);
|
||||
$this->assertFalse($message3['sentfromcurrentuser']);
|
||||
$this->assertEquals('Word.', $message3['lastmessage']);
|
||||
$this->assertNull($message3['messageid']);
|
||||
$this->assertEquals($messageid1, $message3['messageid']);
|
||||
$this->assertFalse($message3['isonline']);
|
||||
$this->assertFalse($message3['isread']);
|
||||
$this->assertFalse($message3['isblocked']);
|
||||
|
||||
@@ -605,6 +605,7 @@ class core_message_messagelib_testcase extends advanced_testcase {
|
||||
// Get the recent conversations for the specified user.
|
||||
$user = $users[$username];
|
||||
$conversations = message_get_recent_conversations($user);
|
||||
$this->assertDebuggingCalled();
|
||||
foreach ($data as $expectation) {
|
||||
$otheruser = $users[$expectation['with']];
|
||||
$conversation = $conversations[$expectation['messageposition']];
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2017020200.01; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2017020700.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user