MDL-63303 message: add get_conversation to message api

This commit is contained in:
Ryan Wyllie 2018-11-05 16:11:26 +08:00
parent 6399c7ef14
commit 4e3130269c
5 changed files with 406 additions and 2 deletions

View File

@ -1087,6 +1087,15 @@ $functions = array(
'type' => 'read',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_message_get_conversation' => array(
'classname' => 'core_message_external',
'methodname' => 'get_conversation',
'classpath' => 'message/externallib.php',
'description' => 'Retrieve a conversation for a user',
'type' => 'read',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
'ajax' => true
),
'core_message_get_messages' => array(
'classname' => 'core_message_external',
'methodname' => 'get_messages',

View File

@ -801,6 +801,121 @@ class api {
return $DB->get_records_sql($sql, array('userid1' => $userid1, 'userid2' => $userid2), $limitfrom, $limitnum);
}
/**
* Return a conversation.
*
* @param int $userid The user id to get the conversation for
* @param int $conversationid The id of the conversation to fetch
* @param bool $includecontactrequests Should contact requests be included between members
* @param bool $includeprivacyinfo Should privacy info be included between members
* @param int $memberlimit Limit number of members to load
* @param int $memberoffset Offset members by this amount
* @param int $messagelimit Limit number of messages to load
* @param int $messageoffset Offset the messages
* @param bool $newestmessagesfirst Order messages by newest first
* @return \stdClass
*/
public static function get_conversation(
int $userid,
int $conversationid,
bool $includecontactrequests = false,
bool $includeprivacyinfo = false,
int $memberlimit = 0,
int $memberoffset = 0,
int $messagelimit = 0,
int $messageoffset = 0,
bool $newestmessagesfirst = true
) {
global $USER, $DB;
$systemcontext = \context_system::instance();
$canreadallmessages = has_capability('moodle/site:readallmessages', $systemcontext);
if (($USER->id != $userid) && !$canreadallmessages) {
throw new \moodle_exception('You do not have permission to perform this action.');
}
$conversation = $DB->get_record('message_conversations', ['id' => $conversationid]);
if (!$conversation) {
return null;
}
$isconversationmember = $DB->record_exists(
'message_conversation_members',
[
'conversationid' => $conversationid,
'userid' => $userid
]
);
if (!$isconversationmember && !$canreadallmessages) {
throw new \moodle_exception('You do not have permission to view this conversation.');
}
$members = self::get_conversation_members(
$userid,
$conversationid,
$includecontactrequests,
$memberoffset,
$memberlimit
);
// Strip out the requesting user to match what get_conversations does.
$members = array_filter($members, function($member) use ($userid) {
return $member->id != $userid;
});
$messages = self::get_conversation_messages(
$userid,
$conversationid,
$messageoffset,
$messagelimit,
$newestmessagesfirst ? 'timecreated DESC' : 'timecreated ASC'
);
$service = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($userid));
$isfavourite = $service->favourite_exists('core_message', 'message_conversations', $conversationid, $systemcontext);
$convextrafields = self::get_linked_conversation_extra_fields([$conversation]);
$subname = isset($convextrafields[$conversationid]) ? $convextrafields[$conversationid]['subname'] : null;
$imageurl = isset($convextrafields[$conversationid]) ? $convextrafields[$conversationid]['imageurl'] : null;
$unreadcountssql = 'SELECT count(m.id)
FROM {messages} m
INNER JOIN {message_conversations} mc
ON mc.id = m.conversationid
LEFT JOIN {message_user_actions} mua
ON (mua.messageid = m.id AND mua.userid = ? AND
(mua.action = ? OR mua.action = ?))
WHERE m.conversationid = ?
AND m.useridfrom != ?
AND mua.id is NULL';
$unreadcount = $DB->count_records_sql(
$unreadcountssql,
[
$userid,
self::MESSAGE_ACTION_READ,
self::MESSAGE_ACTION_DELETED,
$conversationid,
$userid
]
);
$membercount = $DB->count_records('message_conversation_members', ['conversationid' => $conversationid]);
return (object) [
'id' => $conversation->id,
'name' => $conversation->name,
'subname' => $subname,
'imageurl' => $imageurl,
'type' => $conversation->type,
'membercount' => $membercount,
'isfavourite' => $isfavourite,
'isread' => empty($unreadcount),
'unreadcount' => $unreadcount,
'members' => $members,
'messages' => $messages['messages']
];
}
/**
* Mark a conversation as a favourite for the given user.
*

View File

@ -1050,7 +1050,6 @@ class core_message_external extends external_api {
* @return external_single_structure
* @since Moodle 3.6
*/
private static function get_conversation_structure() {
return new external_single_structure(
array(
@ -1616,6 +1615,106 @@ class core_message_external extends external_api {
);
}
/**
* Get conversation parameters.
*
* @return external_function_parameters
*/
public static function get_conversation_parameters() {
return new external_function_parameters(
array(
'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
'conversationid' => new external_value(PARAM_INT, 'The id of the conversation to fetch'),
'includecontactrequests' => new external_value(PARAM_BOOL, 'Include contact requests in the members'),
'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Include privacy info in the members'),
'memberlimit' => new external_value(PARAM_INT, 'Limit for number of members', VALUE_DEFAULT, 0),
'memberoffset' => new external_value(PARAM_INT, 'Offset for member list', VALUE_DEFAULT, 0),
'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
)
);
}
/**
* Get a single conversation.
*
* @param int $userid The user id to get the conversation for
* @param int $conversationid The id of the conversation to fetch
* @param bool $includecontactrequests Should contact requests be included between members
* @param bool $includeprivacyinfo Should privacy info be included between members
* @param int $memberlimit Limit number of members to load
* @param int $memberoffset Offset members by this amount
* @param int $messagelimit Limit number of messages to load
* @param int $messageoffset Offset the messages
* @param bool $newestmessagesfirst Order messages by newest first
* @return stdClass
* @throws \moodle_exception if the messaging feature is disabled on the site.
*/
public static function get_conversation(
int $userid,
int $conversationid,
bool $includecontactrequests = false,
bool $includeprivacyinfo = false,
int $memberlimit = 0,
int $memberoffset = 0,
int $messagelimit = 0,
int $messageoffset = 0,
bool $newestmessagesfirst = true
) {
global $CFG, $DB, $USER;
// All the standard BL checks.
if (empty($CFG->messaging)) {
throw new moodle_exception('disabled', 'message');
}
$params = [
'userid' => $userid,
'conversationid' => $conversationid,
'includecontactrequests' => $includecontactrequests,
'includeprivacyinfo' => $includeprivacyinfo,
'memberlimit' => $memberlimit,
'memberoffset' => $memberoffset,
'messagelimit' => $messagelimit,
'messageoffset' => $messageoffset,
'newestmessagesfirst' => $newestmessagesfirst
];
self::validate_parameters(self::get_conversation_parameters(), $params);
$systemcontext = context_system::instance();
self::validate_context($systemcontext);
$conversation = \core_message\api::get_conversation(
$params['userid'],
$params['conversationid'],
$params['includecontactrequests'],
$params['includeprivacyinfo'],
$params['memberlimit'],
$params['memberoffset'],
$params['messagelimit'],
$params['messageoffset'],
$params['newestmessagesfirst']
);
if ($conversation) {
return $conversation;
} else {
// We have to throw an exception here because the external functions annoyingly
// don't accept null to be returned for a single structure.
throw new \moodle_exception('Conversation does not exist');
}
}
/**
* Get conversation returns.
*
* @return external_single_structure
*/
public static function get_conversation_returns() {
return self::get_conversation_structure();
}
/**
* The messagearea conversations parameters.
*

View File

@ -5632,4 +5632,185 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
$this->expectException(\moodle_exception::class);
$writtenmessages = core_message_external::send_messages_to_conversation($gc1->id, $messages);
}
/**
* Test getting a conversation that doesn't exist.
*/
public function test_get_conversation_no_conversation() {
$this->resetAfterTest();
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$name = 'lol conversation';
$conversation = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[
$user1->id,
$user2->id,
],
$name
);
$conversationid = $conversation->id;
$this->setUser($user1);
$this->expectException('moodle_exception');
$conv = core_message_external::get_conversation($user1->id, $conversationid + 1);
external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
}
/**
* Test getting a conversation with no messages.
*/
public function test_get_conversation_no_messages() {
$this->resetAfterTest();
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$name = 'lol conversation';
$conversation = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[
$user1->id,
$user2->id,
],
$name
);
$conversationid = $conversation->id;
$this->setUser($user1);
$conv = core_message_external::get_conversation($user1->id, $conversationid);
external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
$conv = (array) $conv;
$this->assertEquals($conversationid, $conv['id']);
$this->assertEquals($name, $conv['name']);
$this->assertArrayHasKey('subname', $conv);
$this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $conv['type']);
$this->assertEquals(2, $conv['membercount']);
$this->assertEquals(false, $conv['isfavourite']);
$this->assertEquals(true, $conv['isread']);
$this->assertEquals(0, $conv['unreadcount']);
$this->assertCount(1, $conv['members']);
foreach ($conv['members'] as $member) {
$member = (array) $member;
$this->assertArrayHasKey('id', $member);
$this->assertArrayHasKey('fullname', $member);
$this->assertArrayHasKey('profileimageurl', $member);
$this->assertArrayHasKey('profileimageurlsmall', $member);
$this->assertArrayHasKey('isonline', $member);
$this->assertArrayHasKey('showonlinestatus', $member);
$this->assertArrayHasKey('isblocked', $member);
$this->assertArrayHasKey('iscontact', $member);
}
$this->assertEmpty($conv['messages']);
}
/**
* Test getting a conversation with messages.
*/
public function test_get_conversation_with_messages() {
$this->resetAfterTest();
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
// Some random conversation.
$otherconversation = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[
$user1->id,
$user2->id,
]
);
$conversation = \core_message\api::create_conversation(
\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[
$user1->id,
$user2->id,
]
);
$conversationid = $conversation->id;
$time = time();
$message1id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'A', $time - 10);
$message2id = testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'B', $time - 5);
$message3id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'C', $time);
// Add some messages to the other convo to make sure they aren't included.
testhelper::send_fake_message_to_conversation($user1, $otherconversation->id, 'foo');
$this->setUser($user1);
// Test newest first.
$conv = core_message_external::get_conversation(
$user1->id,
$conversationid,
false,
false,
0,
0,
0,
0,
true
);
external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
$conv = (array) $conv;
$this->assertEquals(false, $conv['isread']);
$this->assertEquals(1, $conv['unreadcount']);
$this->assertCount(3, $conv['messages']);
$this->assertEquals($message3id, $conv['messages'][0]->id);
$this->assertEquals($user1->id, $conv['messages'][0]->useridfrom);
$this->assertEquals($message2id, $conv['messages'][1]->id);
$this->assertEquals($user2->id, $conv['messages'][1]->useridfrom);
$this->assertEquals($message1id, $conv['messages'][2]->id);
$this->assertEquals($user1->id, $conv['messages'][2]->useridfrom);
// Test newest last.
$conv = core_message_external::get_conversation(
$user1->id,
$conversationid,
false,
false,
0,
0,
0,
0,
false
);
external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
$conv = (array) $conv;
$this->assertCount(3, $conv['messages']);
$this->assertEquals($message3id, $conv['messages'][2]->id);
$this->assertEquals($user1->id, $conv['messages'][2]->useridfrom);
$this->assertEquals($message2id, $conv['messages'][1]->id);
$this->assertEquals($user2->id, $conv['messages'][1]->useridfrom);
$this->assertEquals($message1id, $conv['messages'][0]->id);
$this->assertEquals($user1->id, $conv['messages'][0]->useridfrom);
// Test message offest and limit.
$conv = core_message_external::get_conversation(
$user1->id,
$conversationid,
false,
false,
0,
0,
1,
1,
true
);
external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
$conv = (array) $conv;
$this->assertCount(1, $conv['messages']);
$this->assertEquals($message2id, $conv['messages'][0]->id);
$this->assertEquals($user2->id, $conv['messages'][0]->useridfrom);
}
}

View File

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