MDL-36941 core: convert existing api to use new table structure

Also deprecated the following functions -

1. message_move_userfrom_unread2read - It is not necessary
   for us to mark a message as read on user deletion.
2. message_get_blocked_users - Horrible logic used to
   determine if a user is blocked via reference on some
   randomly chosen 'isblocked' variable.
3. message_get_contacts - The same as above. This can be
   done in a much nicer way.
4. message_mark_message_read - We want two functions to do
   this to avoid confusing messages and notifications.
5. message_can_delete_message - This assumed the variable
   $message contained the 'useridto' property, which
   was present in the old table structure. We do not want
   future usages where a query is done on the new table
   and is simply passed as this won't contain this property.
6. message_delete_message - Same as above.
This commit is contained in:
Mark Nelson 2018-01-04 15:01:37 +08:00
parent 4cd439887a
commit 883ce42127
29 changed files with 1714 additions and 1096 deletions

View file

@ -64,147 +64,6 @@ define('MESSAGE_DEFAULT_MIN_POLL_IN_SECONDS', 10);
define('MESSAGE_DEFAULT_MAX_POLL_IN_SECONDS', 2 * MINSECS);
define('MESSAGE_DEFAULT_TIMEOUT_POLL_IN_SECONDS', 5 * MINSECS);
/**
* Retrieve users blocked by $user1
*
* @param object $user1 the user whose messages are being viewed
* @param object $user2 the user $user1 is talking to. If they are being blocked
* they will have a variable called 'isblocked' added to their user object
* @return array the users blocked by $user1
*/
function message_get_blocked_users($user1=null, $user2=null) {
global $DB, $USER;
if (empty($user1)) {
$user1 = $USER;
}
if (!empty($user2)) {
$user2->isblocked = false;
}
$blockedusers = array();
$userfields = user_picture::fields('u', array('lastaccess'));
$blockeduserssql = "SELECT $userfields, COUNT(m.id) AS messagecount
FROM {message_contacts} mc
JOIN {user} u ON u.id = mc.contactid
LEFT OUTER JOIN {message} m ON m.useridfrom = mc.contactid AND m.useridto = :user1id1
WHERE u.deleted = 0 AND mc.userid = :user1id2 AND mc.blocked = 1
GROUP BY $userfields
ORDER BY u.firstname ASC";
$rs = $DB->get_recordset_sql($blockeduserssql, array('user1id1' => $user1->id, 'user1id2' => $user1->id));
foreach($rs as $rd) {
$blockedusers[] = $rd;
if (!empty($user2) && $user2->id == $rd->id) {
$user2->isblocked = true;
}
}
$rs->close();
return $blockedusers;
}
/**
* Retrieve $user1's contacts (online, offline and strangers)
*
* @param object $user1 the user whose messages are being viewed
* @param object $user2 the user $user1 is talking to. If they are a contact
* they will have a variable called 'iscontact' added to their user object
* @return array containing 3 arrays. array($onlinecontacts, $offlinecontacts, $strangers)
*/
function message_get_contacts($user1=null, $user2=null) {
global $DB, $CFG, $USER;
if (empty($user1)) {
$user1 = $USER;
}
if (!empty($user2)) {
$user2->iscontact = false;
}
$timetoshowusers = 300; //Seconds default
if (isset($CFG->block_online_users_timetosee)) {
$timetoshowusers = $CFG->block_online_users_timetosee * 60;
}
// time which a user is counting as being active since
$timefrom = time()-$timetoshowusers;
// people in our contactlist who are online
$onlinecontacts = array();
// people in our contactlist who are offline
$offlinecontacts = array();
// people who are not in our contactlist but have sent us a message
$strangers = array();
$userfields = user_picture::fields('u', array('lastaccess'));
// get all in our contactlist who are not blocked in our contact list
// and count messages we have waiting from each of them
$contactsql = "SELECT $userfields, COUNT(m.id) AS messagecount
FROM {message_contacts} mc
JOIN {user} u ON u.id = mc.contactid
LEFT OUTER JOIN {message} m ON m.useridfrom = mc.contactid AND m.useridto = ?
WHERE u.deleted = 0 AND mc.userid = ? AND mc.blocked = 0
GROUP BY $userfields
ORDER BY u.firstname ASC";
$rs = $DB->get_recordset_sql($contactsql, array($user1->id, $user1->id));
foreach ($rs as $rd) {
if ($rd->lastaccess >= $timefrom) {
// they have been active recently, so are counted online
$onlinecontacts[] = $rd;
} else {
$offlinecontacts[] = $rd;
}
if (!empty($user2) && $user2->id == $rd->id) {
$user2->iscontact = true;
}
}
$rs->close();
// get messages from anyone who isn't in our contact list and count the number
// of messages we have from each of them
$strangersql = "SELECT $userfields, count(m.id) as messagecount
FROM {message} m
JOIN {user} u ON u.id = m.useridfrom
LEFT OUTER JOIN {message_contacts} mc ON mc.contactid = m.useridfrom AND mc.userid = m.useridto
WHERE u.deleted = 0 AND mc.id IS NULL AND m.useridto = ?
GROUP BY $userfields
ORDER BY u.firstname ASC";
$rs = $DB->get_recordset_sql($strangersql, array($USER->id));
// Add user id as array index, so supportuser and noreply user don't get duplicated (if they are real users).
foreach ($rs as $rd) {
$strangers[$rd->id] = $rd;
}
$rs->close();
// Add noreply user and support user to the list, if they don't exist.
$supportuser = core_user::get_support_user();
if (!isset($strangers[$supportuser->id])) {
$supportuser->messagecount = message_count_unread_messages($USER, $supportuser);
if ($supportuser->messagecount > 0) {
$strangers[$supportuser->id] = $supportuser;
}
}
$noreplyuser = core_user::get_noreply_user();
if (!isset($strangers[$noreplyuser->id])) {
$noreplyuser->messagecount = message_count_unread_messages($USER, $noreplyuser);
if ($noreplyuser->messagecount > 0) {
$strangers[$noreplyuser->id] = $noreplyuser;
}
}
return array($onlinecontacts, $offlinecontacts, $strangers);
}
/**
* Returns the count of unread messages for user. Either from a specific user or from all users.
*
@ -219,15 +78,24 @@ function message_count_unread_messages($user1=null, $user2=null) {
$user1 = $USER;
}
$sql = "SELECT COUNT(m.id)
FROM {messages} m
INNER JOIN {message_conversations} mc
ON mc.id = m.conversationid
INNER JOIN {message_conversation_members} mcm
ON mcm.conversationid = mc.id
LEFT JOIN {message_user_actions} mua
ON (mua.messageid = m.id AND mua.userid = ? AND (mua.action = ? OR mua.action = ?))
WHERE mua.id is NULL
AND mcm.userid = ?";
$params = [$user1->id, \core_message\api::MESSAGE_ACTION_DELETED, \core_message\api::MESSAGE_ACTION_READ, $user1->id];
if (!empty($user2)) {
return $DB->count_records_select('message', "useridto = ? AND useridfrom = ? AND notification = 0
AND timeusertodeleted = 0",
array($user1->id, $user2->id), "COUNT('id')");
} else {
return $DB->count_records_select('message', "useridto = ? AND notification = 0
AND timeusertodeleted = 0",
array($user1->id), "COUNT('id')");
$sql .= " AND m.useridfrom = ?";
$params[] = $user2->id;
}
return $DB->count_records_sql($sql, $params);
}
/**
@ -249,7 +117,7 @@ function message_format_message_text($message, $forcetexttohtml = false) {
$format = $message->fullmessageformat;
if (strval($message->smallmessage) !== '') {
if ($message->notification == 1) {
if (!empty($message->notification)) {
if (strval($message->fullmessagehtml) !== '' or strval($message->fullmessage) !== '') {
$format = FORMAT_PLAIN;
}
@ -421,86 +289,6 @@ function message_block_contact($contactid, $userid = 0) {
return message_add_contact($contactid, 1, $userid);
}
/**
* Checks if a user can delete a message.
*
* @param stdClass $message the message to delete
* @param string $userid the user id of who we want to delete the message for (this may be done by the admin
* but will still seem as if it was by the user)
* @return bool Returns true if a user can delete the message, false otherwise.
*/
function message_can_delete_message($message, $userid) {
global $USER;
if ($message->useridfrom == $userid) {
$userdeleting = 'useridfrom';
} else if ($message->useridto == $userid) {
$userdeleting = 'useridto';
} else {
return false;
}
$systemcontext = context_system::instance();
// Let's check if the user is allowed to delete this message.
if (has_capability('moodle/site:deleteanymessage', $systemcontext) ||
((has_capability('moodle/site:deleteownmessage', $systemcontext) &&
$USER->id == $message->$userdeleting))) {
return true;
}
return false;
}
/**
* Deletes a message.
*
* This function does not verify any permissions.
*
* @param stdClass $message the message to delete
* @param string $userid the user id of who we want to delete the message for (this may be done by the admin
* but will still seem as if it was by the user)
* @return bool
*/
function message_delete_message($message, $userid) {
global $DB;
// The column we want to alter.
if ($message->useridfrom == $userid) {
$coltimedeleted = 'timeuserfromdeleted';
} else if ($message->useridto == $userid) {
$coltimedeleted = 'timeusertodeleted';
} else {
return false;
}
// Don't update it if it's already been deleted.
if ($message->$coltimedeleted > 0) {
return false;
}
// Get the table we want to update.
if (isset($message->timeread)) {
$messagetable = 'message_read';
} else {
$messagetable = 'message';
}
// Mark the message as deleted.
$updatemessage = new stdClass();
$updatemessage->id = $message->id;
$updatemessage->$coltimedeleted = time();
$success = $DB->update_record($messagetable, $updatemessage);
if ($success) {
// Trigger event for deleting a message.
\core\event\message_deleted::create_from_ids($message->useridfrom, $message->useridto,
$userid, $messagetable, $message->id)->trigger();
}
return $success;
}
/**
* Load a user's contact record
*
@ -722,71 +510,6 @@ function message_post_message($userfrom, $userto, $message, $format) {
return message_send($eventdata);
}
/**
* Moves messages from a particular user from the message table (unread messages) to message_read
* This is typically only used when a user is deleted
*
* @param object $userid User id
* @return boolean success
*/
function message_move_userfrom_unread2read($userid) {
global $DB;
// move all unread messages from message table to message_read
if ($messages = $DB->get_records_select('message', 'useridfrom = ?', array($userid), 'timecreated')) {
foreach ($messages as $message) {
message_mark_message_read($message, 0); //set timeread to 0 as the message was never read
}
}
return true;
}
/**
* Mark a single message as read
*
* @param stdClass $message An object with an object property ie $message->id which is an id in the message table
* @param int $timeread the timestamp for when the message should be marked read. Usually time().
* @param bool $messageworkingempty Is the message_working table already confirmed empty for this message?
* @return int the ID of the message in the message_read table
*/
function message_mark_message_read($message, $timeread, $messageworkingempty=false) {
global $DB;
$message->timeread = $timeread;
$messageid = $message->id;
unset($message->id);//unset because it will get a new id on insert into message_read
//If any processors have pending actions abort them
if (!$messageworkingempty) {
$DB->delete_records('message_working', array('unreadmessageid' => $messageid));
}
$messagereadid = $DB->insert_record('message_read', $message);
$DB->delete_records('message', array('id' => $messageid));
// Get the context for the user who received the message.
$context = context_user::instance($message->useridto, IGNORE_MISSING);
// If the user no longer exists the context value will be false, in this case use the system context.
if ($context === false) {
$context = context_system::instance();
}
// Trigger event for reading a message.
$event = \core\event\message_viewed::create(array(
'objectid' => $messagereadid,
'userid' => $message->useridto, // Using the user who read the message as they are the ones performing the action.
'context' => $context,
'relateduserid' => $message->useridfrom,
'other' => array(
'messageid' => $messageid
)
));
$event->trigger();
return $messagereadid;
}
/**
* Get all message processors, validate corresponding plugin existance and
* system configuration
@ -962,42 +685,108 @@ function message_get_messages($useridto, $useridfrom = 0, $notifications = -1, $
$sort = 'mr.timecreated DESC', $limitfrom = 0, $limitnum = 0) {
global $DB;
$messagetable = $read ? '{message_read}' : '{message}';
$params = array('deleted' => 0);
// Empty useridto means that we are going to retrieve messages send by the useridfrom to any user.
// If the 'useridto' value is empty then we are going to retrieve messages sent by the useridfrom to any user.
if (empty($useridto)) {
$userfields = get_all_user_name_fields(true, 'u', '', 'userto');
$joinsql = "JOIN {user} u ON u.id = mr.useridto";
$usersql = "mr.useridfrom = :useridfrom AND u.deleted = :deleted";
$params['useridfrom'] = $useridfrom;
} else {
$userfields = get_all_user_name_fields(true, 'u', '', 'userfrom');
}
// Create the SQL we will be using.
$messagesql = "SELECT mr.*, $userfields, 0 as notification, '' as contexturl, '' as contexturlname,
mua.timecreated as timeusertodeleted, mua2.timecreated as timeread,
mua3.timecreated as timeuserfromdeleted
FROM {messages} mr
INNER JOIN {message_conversations} mc
ON mc.id = mr.conversationid
INNER JOIN {message_conversation_members} mcm
ON mcm.conversationid = mc.id ";
$notificationsql = "SELECT mr.*, $userfields, 1 as notification
FROM {notifications} mr ";
$messagejoinsql = "LEFT JOIN {message_user_actions} mua
ON (mua.messageid = mr.id AND mua.userid = mcm.userid AND mua.action = ?)
LEFT JOIN {message_user_actions} mua2
ON (mua2.messageid = mr.id AND mua2.userid = mcm.userid AND mua2.action = ?)
LEFT JOIN {message_user_actions} mua3
ON (mua3.messageid = mr.id AND mua3.userid = mr.useridfrom AND mua3.action = ?)";
$messagejoinparams = [\core_message\api::MESSAGE_ACTION_DELETED, \core_message\api::MESSAGE_ACTION_READ,
\core_message\api::MESSAGE_ACTION_DELETED];
$notificationsparams = [];
// If the 'useridto' value is empty then we are going to retrieve messages sent by the useridfrom to any user.
if (empty($useridto)) {
// Create the messaging query and params.
$messagesql .= "INNER JOIN {user} u
ON u.id = mcm.userid
$messagejoinsql
WHERE mr.useridfrom = ?
AND mr.useridfrom != mcm.userid
AND u.deleted = 0 ";
$messageparams = array_merge($messagejoinparams, [$useridfrom]);
// Create the notifications query and params.
$notificationsql .= "INNER JOIN {user} u
ON u.id = mr.useridto
WHERE mr.useridfrom = ?
AND u.deleted = 0 ";
$notificationsparams[] = $useridfrom;
} else {
// Create the messaging query and params.
// Left join because useridfrom may be -10 or -20 (no-reply and support users).
$joinsql = "LEFT JOIN {user} u ON u.id = mr.useridfrom";
$usersql = "mr.useridto = :useridto AND (u.deleted IS NULL OR u.deleted = :deleted)";
$params['useridto'] = $useridto;
$messagesql .= "LEFT JOIN {user} u
ON u.id = mr.useridfrom
$messagejoinsql
WHERE mcm.userid = ?
AND mr.useridfrom != mcm.userid
AND u.deleted = 0 ";
$messageparams = array_merge($messagejoinparams, [$useridto]);
if (!empty($useridfrom)) {
$usersql .= " AND mr.useridfrom = :useridfrom";
$params['useridfrom'] = $useridfrom;
$messagesql .= " AND mr.useridfrom = ? ";
$messageparams[] = $useridfrom;
}
// Create the notifications query and params.
// Left join because useridfrom may be -10 or -20 (no-reply and support users).
$notificationsql .= "LEFT JOIN {user} u
ON (u.id = mr.useridfrom AND u.deleted = 0)
WHERE mr.useridto = ? ";
$notificationsparams[] = $useridto;
if (!empty($useridfrom)) {
$notificationsql .= " AND mr.useridfrom = ? ";
$notificationsparams[] = $useridfrom;
}
}
if ($read) {
$notificationsql .= "AND mr.timeread IS NOT NULL ";
} else {
$notificationsql .= "AND mr.timeread IS NULL ";
}
$messagesql .= "ORDER BY $sort";
$notificationsql .= "ORDER BY $sort";
// Now, if retrieve notifications, conversations or both.
$typesql = "";
if ($notifications !== -1) {
$typesql = "AND mr.notification = :notification";
$params['notification'] = ($notifications) ? 1 : 0;
// Handle messages if needed.
if ($notifications === -1 || $notifications === 0) {
$messages = $DB->get_records_sql($messagesql, $messageparams, $limitfrom, $limitnum);
// Get rid of the messages that have either been read or not read depending on the value of $read.
$messages = array_filter($messages, function ($message) use ($read) {
if ($read) {
return !is_null($message->timeread);
}
return is_null($message->timeread);
});
}
$sql = "SELECT mr.*, $userfields
FROM $messagetable mr
$joinsql
WHERE $usersql
$typesql
ORDER BY $sort";
// All.
if ($notifications === -1) {
return array_merge($messages, $DB->get_records_sql($notificationsql, $notificationsparams, $limitfrom, $limitnum));
} else if ($notifications === 1) { // Just notifications.
return $DB->get_records_sql($notificationsql, $notificationsparams, $limitfrom, $limitnum);
}
$messages = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
// Just messages.
return $messages;
}