From 6ecd6e5ba85b53aaa7f2dbb062a90656494ad2e2 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Tue, 23 Oct 2018 09:50:16 +0800 Subject: [PATCH 1/2] MDL-63725 core_message: added api::get_conversation_members --- message/classes/api.php | 45 +++++++++- message/tests/api_test.php | 171 +++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+), 2 deletions(-) diff --git a/message/classes/api.php b/message/classes/api.php index ee60a6bf69c..17b427591b8 100644 --- a/message/classes/api.php +++ b/message/classes/api.php @@ -1727,9 +1727,7 @@ class api { public static function get_contact_requests(int $userid) : array { global $DB; - // Used to search for contacts. $ufields = \user_picture::fields('u'); - $sql = "SELECT $ufields, mcr.id as contactrequestid FROM {user} u JOIN {message_contact_requests} mcr @@ -2167,4 +2165,47 @@ class api { } } } + + /** + * Returns a list of conversation members. + * + * @param int $userid The user we are returning the conversation members for, used by helper::get_member_info. + * @param int $conversationid The id of the conversation + * @param bool $includecontactrequests Do we want to include contact requests with this data? + * @param int $limitfrom + * @param int $limitnum + * @return array + */ + public static function get_conversation_members(int $userid, int $conversationid, bool $includecontactrequests = false, + int $limitfrom = 0, int $limitnum = 0) : array { + global $DB; + + if ($members = $DB->get_records('message_conversation_members', ['conversationid' => $conversationid], + 'timecreated ASC, id ASC', 'userid', $limitfrom, $limitnum)) { + $userids = array_keys($members); + $members = helper::get_member_info($userid, $userids); + + // Check if we want to include contact requests as well. + if ($includecontactrequests) { + list($useridsql, $usersparams) = $DB->get_in_or_equal($userids); + + $wheresql = "(userid $useridsql OR requesteduserid $useridsql)"; + if ($contactrequests = $DB->get_records_select('message_contact_requests', $wheresql, + array_merge($usersparams, $usersparams), 'timecreated ASC, id ASC')) { + foreach ($contactrequests as $contactrequest) { + if (isset($members[$contactrequest->userid])) { + $members[$contactrequest->userid]->contactrequests[] = $contactrequest; + } + if (isset($members[$contactrequest->requesteduserid])) { + $members[$contactrequest->requesteduserid]->contactrequests[] = $contactrequest; + } + } + } + } + + return $members; + } + + return []; + } } diff --git a/message/tests/api_test.php b/message/tests/api_test.php index 2b9cbc4b78e..97759577e98 100644 --- a/message/tests/api_test.php +++ b/message/tests/api_test.php @@ -3888,6 +3888,177 @@ class core_message_api_testcase extends core_message_messagelib_testcase { ); } + /** + * Test returning members in a conversation with no contact requests. + */ + public function test_get_conversation_members() { + $lastaccess = new stdClass(); + $lastaccess->lastaccess = time(); + + $user1 = self::getDataGenerator()->create_user($lastaccess); + $user2 = self::getDataGenerator()->create_user(); + $user3 = self::getDataGenerator()->create_user(); + + // This user will not be in the conversation, but a contact request will exist for them. + $user4 = self::getDataGenerator()->create_user(); + + // Add some contact requests. + \core_message\api::create_contact_request($user1->id, $user3->id); + \core_message\api::create_contact_request($user1->id, $user4->id); + \core_message\api::create_contact_request($user2->id, $user3->id); + + // User 1 and 2 are already contacts. + \core_message\api::add_contact($user1->id, $user2->id); + + // User 1 has blocked user 3. + \core_message\api::block_user($user1->id, $user3->id); + $conversation = \core_message\api::create_conversation( + \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, + [ + $user1->id, + $user2->id, + $user3->id + ] + ); + $conversationid = $conversation->id; + $members = \core_message\api::get_conversation_members($user1->id, $conversationid, false); + + // Sort them by id. + ksort($members); + $this->assertCount(3, $members); + $member1 = array_shift($members); + $member2 = array_shift($members); + $member3 = array_shift($members); + + // Confirm the standard fields are OK. + $this->assertEquals($user1->id, $member1->id); + $this->assertEquals(fullname($user1), $member1->fullname); + $this->assertEquals(true, $member1->isonline); + $this->assertEquals(true, $member1->showonlinestatus); + $this->assertEquals(false, $member1->iscontact); + $this->assertEquals(false, $member1->isblocked); + $this->assertObjectNotHasAttribute('contactrequests', $member1); + + $this->assertEquals($user2->id, $member2->id); + $this->assertEquals(fullname($user2), $member2->fullname); + $this->assertEquals(false, $member2->isonline); + $this->assertEquals(true, $member2->showonlinestatus); + $this->assertEquals(true, $member2->iscontact); + $this->assertEquals(false, $member2->isblocked); + $this->assertObjectNotHasAttribute('contactrequests', $member2); + + $this->assertEquals($user3->id, $member3->id); + $this->assertEquals(fullname($user3), $member3->fullname); + $this->assertEquals(false, $member3->isonline); + $this->assertEquals(true, $member3->showonlinestatus); + $this->assertEquals(false, $member3->iscontact); + $this->assertEquals(true, $member3->isblocked); + $this->assertObjectNotHasAttribute('contactrequests', $member3); + } + + /** + * Test returning members in a conversation with contact requests. + */ + public function test_get_conversation_members_with_contact_requests() { + $lastaccess = new stdClass(); + $lastaccess->lastaccess = time(); + + $user1 = self::getDataGenerator()->create_user($lastaccess); + $user2 = self::getDataGenerator()->create_user(); + $user3 = self::getDataGenerator()->create_user(); + + // This user will not be in the conversation, but a contact request will exist for them. + $user4 = self::getDataGenerator()->create_user(); + // Add some contact requests. + \core_message\api::create_contact_request($user1->id, $user2->id); + \core_message\api::create_contact_request($user1->id, $user3->id); + \core_message\api::create_contact_request($user1->id, $user4->id); + \core_message\api::create_contact_request($user2->id, $user3->id); + + // User 1 and 2 are already contacts. + \core_message\api::add_contact($user1->id, $user2->id); + // User 1 has blocked user 3. + \core_message\api::block_user($user1->id, $user3->id); + + $conversation = \core_message\api::create_conversation( + \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, + [ + $user1->id, + $user2->id, + $user3->id + ] + ); + $conversationid = $conversation->id; + + $members = \core_message\api::get_conversation_members($user1->id, $conversationid, true); + + // Sort them by id. + ksort($members); + + $this->assertCount(3, $members); + + $member1 = array_shift($members); + $member2 = array_shift($members); + $member3 = array_shift($members); + + // Confirm the standard fields are OK. + $this->assertEquals($user1->id, $member1->id); + $this->assertEquals(fullname($user1), $member1->fullname); + $this->assertEquals(true, $member1->isonline); + $this->assertEquals(true, $member1->showonlinestatus); + $this->assertEquals(false, $member1->iscontact); + $this->assertEquals(false, $member1->isblocked); + $this->assertCount(3, $member1->contactrequests); + + $this->assertEquals($user2->id, $member2->id); + $this->assertEquals(fullname($user2), $member2->fullname); + $this->assertEquals(false, $member2->isonline); + $this->assertEquals(true, $member2->showonlinestatus); + $this->assertEquals(true, $member2->iscontact); + $this->assertEquals(false, $member2->isblocked); + $this->assertCount(2, $member2->contactrequests); + + $this->assertEquals($user3->id, $member3->id); + $this->assertEquals(fullname($user3), $member3->fullname); + $this->assertEquals(false, $member3->isonline); + $this->assertEquals(true, $member3->showonlinestatus); + $this->assertEquals(false, $member3->iscontact); + $this->assertEquals(true, $member3->isblocked); + $this->assertCount(2, $member3->contactrequests); + + // Confirm the contact requests are OK. + $request1 = array_shift($member1->contactrequests); + $request2 = array_shift($member1->contactrequests); + $request3 = array_shift($member1->contactrequests); + + $this->assertEquals($user1->id, $request1->userid); + $this->assertEquals($user2->id, $request1->requesteduserid); + + $this->assertEquals($user1->id, $request2->userid); + $this->assertEquals($user3->id, $request2->requesteduserid); + + $this->assertEquals($user1->id, $request3->userid); + $this->assertEquals($user4->id, $request3->requesteduserid); + + $request1 = array_shift($member2->contactrequests); + $request2 = array_shift($member2->contactrequests); + + $this->assertEquals($user1->id, $request1->userid); + $this->assertEquals($user2->id, $request1->requesteduserid); + + $this->assertEquals($user2->id, $request2->userid); + $this->assertEquals($user3->id, $request2->requesteduserid); + + $request1 = array_shift($member3->contactrequests); + $request2 = array_shift($member3->contactrequests); + + $this->assertEquals($user1->id, $request1->userid); + $this->assertEquals($user3->id, $request1->requesteduserid); + + $this->assertEquals($user2->id, $request2->userid); + $this->assertEquals($user3->id, $request2->requesteduserid); + } + /** * Comparison function for sorting contacts. * From 5584c48a33c532fee47aee59c6a0aa044eb94732 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Fri, 26 Oct 2018 07:52:22 +0800 Subject: [PATCH 2/2] MDL-63725 core_message: added get_conversation_members web service --- lib/db/services.php | 9 ++ message/externallib.php | 108 +++++++++++++-- message/tests/externallib_test.php | 211 +++++++++++++++++++++++++++++ version.php | 2 +- 4 files changed, 318 insertions(+), 12 deletions(-) diff --git a/lib/db/services.php b/lib/db/services.php index 1c4a37fc5d3..0007fe11e2b 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1045,6 +1045,15 @@ $functions = array( 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, ), + 'core_message_get_conversation_members' => array( + 'classname' => 'core_message_external', + 'methodname' => 'get_conversation_members', + 'classpath' => 'message/externallib.php', + 'description' => 'Retrieve a list of members in a conversation', + 'type' => 'read', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + 'ajax' => true, + ), 'core_message_get_unread_conversations_count' => array( 'classname' => 'core_message_external', 'methodname' => 'get_unread_conversations_count', diff --git a/message/externallib.php b/message/externallib.php index 75c19ecf660..dc95f88b067 100644 --- a/message/externallib.php +++ b/message/externallib.php @@ -667,6 +667,76 @@ class core_message_external extends external_api { ); } + /** + * Returns get conversation members parameters description. + * + * @return external_function_parameters + */ + public static function get_conversation_members_parameters() { + return new external_function_parameters( + [ + 'userid' => new external_value(PARAM_INT, 'The id of the user we are performing this action on behalf of'), + 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation'), + 'includecontactrequests' => new external_value(PARAM_BOOL, 'Do we want to include contact requests?', + VALUE_DEFAULT, false), + 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0), + 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0) + ] + ); + } + + /** + * Returns a list of conversation members. + * + * @param int $userid The user we are returning the conversation members for, used by helper::get_member_info. + * @param int $conversationid The id of the conversation + * @param bool $includecontactrequests Do we want to include contact requests with this data? + * @param int $limitfrom + * @param int $limitnum + * @return array + */ + public static function get_conversation_members(int $userid, int $conversationid, bool $includecontactrequests = false, + int $limitfrom = 0, int $limitnum = 0) { + global $CFG, $USER; + + // Check if messaging is enabled. + if (empty($CFG->messaging)) { + throw new moodle_exception('disabled', 'message'); + } + + // Validate context. + $context = context_system::instance(); + self::validate_context($context); + + $capability = 'moodle/site:manageallmessaging'; + if (($USER->id != $userid) && !has_capability($capability, $context)) { + throw new required_capability_exception($context, $capability, 'nopermissions', ''); + } + + $params = [ + 'userid' => $userid, + 'conversationid' => $conversationid, + 'includecontactrequests' => $includecontactrequests, + 'limitfrom' => $limitfrom, + 'limitnum' => $limitnum + ]; + self::validate_parameters(self::get_conversation_members_parameters(), $params); + + return \core_message\api::get_conversation_members($userid, $conversationid, $includecontactrequests, + $limitfrom, $limitnum); + } + + /** + * Returns the get conversation members return description. + * + * @return external_description + */ + public static function get_conversation_members_returns() { + return new external_multiple_structure( + self::get_conversation_member_structure(true) + ); + } + /** * Creates a contact request parameters description. * @@ -871,21 +941,37 @@ class core_message_external extends external_api { /** * Return the structure of a conversation member. * + * @param bool $includecontactrequests Are we including contact requests? * @return external_single_structure * @since Moodle 3.6 */ - private static function get_conversation_member_structure() { + private static function get_conversation_member_structure(bool $includecontactrequests = false) { + $result = [ + 'id' => new external_value(PARAM_INT, 'The user id'), + 'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'), + 'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'), + 'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'), + 'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'), + 'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'), + 'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'), + 'iscontact' => new external_value(PARAM_BOOL, 'Is the user a contact?'), + ]; + + if ($includecontactrequests) { + $result['contactrequests'] = new external_multiple_structure( + new external_single_structure( + [ + 'id' => new external_value(PARAM_INT, 'The id of the message'), + 'userid' => new external_value(PARAM_INT, 'The id of the user who sent the message'), + 'requesteduserid' => new external_value(PARAM_RAW, 'The text of the message'), + 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message'), + ] + ), 'The contact requests', VALUE_OPTIONAL + ); + } + return new external_single_structure( - array( - 'id' => new external_value(PARAM_INT, 'The user id'), - 'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'), - 'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'), - 'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'), - 'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'), - 'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'), - 'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'), - 'iscontact' => new external_value(PARAM_BOOL, 'Is the user a contact?') - ) + $result ); } diff --git a/message/tests/externallib_test.php b/message/tests/externallib_test.php index f406dbdbcb9..1282737ebbe 100644 --- a/message/tests/externallib_test.php +++ b/message/tests/externallib_test.php @@ -4339,4 +4339,215 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { $this->expectException(\moodle_exception::class); $result = core_message_external::unset_favourite_conversations($user1->id, [0]); } + + /** + * Test returning members in a conversation with no contact requests. + */ + public function test_get_conversation_members_messaging_disabled() { + global $CFG; + + $this->resetAfterTest(); + + $CFG->messaging = 0; + + $this->expectException('moodle_exception'); + core_message_external::get_conversation_members(1, 2); + } + + /** + * Test returning members in a conversation with no contact requests. + */ + public function test_get_conversation_members_wrong_user() { + $this->resetAfterTest(); + + $user1 = self::getDataGenerator()->create_user(); + $user2 = self::getDataGenerator()->create_user(); + + $this->setUser($user2); + + $this->expectException('moodle_exception'); + core_message_external::get_conversation_members($user1->id, 2); + } + + /** + * Test returning members in a conversation with no contact requests. + */ + public function test_get_conversation_members() { + $this->resetAfterTest(); + + $lastaccess = new stdClass(); + $lastaccess->lastaccess = time(); + + $user1 = self::getDataGenerator()->create_user($lastaccess); + $user2 = self::getDataGenerator()->create_user(); + $user3 = self::getDataGenerator()->create_user(); + + // This user will not be in the conversation, but a contact request will exist for them. + $user4 = self::getDataGenerator()->create_user(); + + // Add some contact requests. + \core_message\api::create_contact_request($user1->id, $user3->id); + \core_message\api::create_contact_request($user1->id, $user4->id); + \core_message\api::create_contact_request($user2->id, $user3->id); + + // User 1 and 2 are already contacts. + \core_message\api::add_contact($user1->id, $user2->id); + + // User 1 has blocked user 3. + \core_message\api::block_user($user1->id, $user3->id); + + $conversation = \core_message\api::create_conversation( + \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, + [ + $user1->id, + $user2->id, + $user3->id + ] + ); + $conversationid = $conversation->id; + + $this->setAdminUser(); + + $members = core_message_external::get_conversation_members($user1->id, $conversationid, false); + external_api::clean_returnvalue(core_message_external::get_conversation_members_returns(), $members); + + // Sort them by id. + ksort($members); + $this->assertCount(3, $members); + $member1 = array_shift($members); + $member2 = array_shift($members); + $member3 = array_shift($members); + + // Confirm the standard fields are OK. + $this->assertEquals($user1->id, $member1->id); + $this->assertEquals(fullname($user1), $member1->fullname); + $this->assertEquals(true, $member1->isonline); + $this->assertEquals(true, $member1->showonlinestatus); + $this->assertEquals(false, $member1->iscontact); + $this->assertEquals(false, $member1->isblocked); + $this->assertObjectNotHasAttribute('contactrequests', $member1); + + $this->assertEquals($user2->id, $member2->id); + $this->assertEquals(fullname($user2), $member2->fullname); + $this->assertEquals(false, $member2->isonline); + $this->assertEquals(true, $member2->showonlinestatus); + $this->assertEquals(true, $member2->iscontact); + $this->assertEquals(false, $member2->isblocked); + $this->assertObjectNotHasAttribute('contactrequests', $member2); + + $this->assertEquals($user3->id, $member3->id); + $this->assertEquals(fullname($user3), $member3->fullname); + $this->assertEquals(false, $member3->isonline); + $this->assertEquals(true, $member3->showonlinestatus); + $this->assertEquals(false, $member3->iscontact); + $this->assertEquals(true, $member3->isblocked); + $this->assertObjectNotHasAttribute('contactrequests', $member3); + } + + /** + * Test returning members in a conversation with contact requests. + */ + public function test_get_conversation_members_with_contact_requests() { + $this->resetAfterTest(); + + $lastaccess = new stdClass(); + $lastaccess->lastaccess = time(); + + $user1 = self::getDataGenerator()->create_user($lastaccess); + $user2 = self::getDataGenerator()->create_user(); + $user3 = self::getDataGenerator()->create_user(); + + // This user will not be in the conversation, but a contact request will exist for them. + $user4 = self::getDataGenerator()->create_user(); + + // Add some contact requests. + \core_message\api::create_contact_request($user1->id, $user2->id); + \core_message\api::create_contact_request($user1->id, $user3->id); + \core_message\api::create_contact_request($user1->id, $user4->id); + \core_message\api::create_contact_request($user2->id, $user3->id); + + // User 1 and 2 are already contacts. + \core_message\api::add_contact($user1->id, $user2->id); + // User 1 has blocked user 3. + \core_message\api::block_user($user1->id, $user3->id); + + $conversation = \core_message\api::create_conversation( + \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, + [ + $user1->id, + $user2->id, + $user3->id + ] + ); + $conversationid = $conversation->id; + + $this->setAdminUser(); + + $members = core_message_external::get_conversation_members($user1->id, $conversationid, true); + external_api::clean_returnvalue(core_message_external::get_conversation_members_returns(), $members); + + // Sort them by id. + ksort($members); + $this->assertCount(3, $members); + $member1 = array_shift($members); + $member2 = array_shift($members); + $member3 = array_shift($members); + + // Confirm the standard fields are OK. + $this->assertEquals($user1->id, $member1->id); + $this->assertEquals(fullname($user1), $member1->fullname); + $this->assertEquals(true, $member1->isonline); + $this->assertEquals(true, $member1->showonlinestatus); + $this->assertEquals(false, $member1->iscontact); + $this->assertEquals(false, $member1->isblocked); + $this->assertCount(3, $member1->contactrequests); + + $this->assertEquals($user2->id, $member2->id); + $this->assertEquals(fullname($user2), $member2->fullname); + $this->assertEquals(false, $member2->isonline); + $this->assertEquals(true, $member2->showonlinestatus); + $this->assertEquals(true, $member2->iscontact); + $this->assertEquals(false, $member2->isblocked); + $this->assertCount(2, $member2->contactrequests); + + $this->assertEquals($user3->id, $member3->id); + $this->assertEquals(fullname($user3), $member3->fullname); + $this->assertEquals(false, $member3->isonline); + $this->assertEquals(true, $member3->showonlinestatus); + $this->assertEquals(false, $member3->iscontact); + $this->assertEquals(true, $member3->isblocked); + $this->assertCount(2, $member3->contactrequests); + + // Confirm the contact requests are OK. + $request1 = array_shift($member1->contactrequests); + $request2 = array_shift($member1->contactrequests); + $request3 = array_shift($member1->contactrequests); + + $this->assertEquals($user1->id, $request1->userid); + $this->assertEquals($user2->id, $request1->requesteduserid); + + $this->assertEquals($user1->id, $request2->userid); + $this->assertEquals($user3->id, $request2->requesteduserid); + + $this->assertEquals($user1->id, $request3->userid); + $this->assertEquals($user4->id, $request3->requesteduserid); + + $request1 = array_shift($member2->contactrequests); + $request2 = array_shift($member2->contactrequests); + + $this->assertEquals($user1->id, $request1->userid); + $this->assertEquals($user2->id, $request1->requesteduserid); + + $this->assertEquals($user2->id, $request2->userid); + $this->assertEquals($user3->id, $request2->requesteduserid); + + $request1 = array_shift($member3->contactrequests); + $request2 = array_shift($member3->contactrequests); + + $this->assertEquals($user1->id, $request1->userid); + $this->assertEquals($user3->id, $request1->requesteduserid); + + $this->assertEquals($user2->id, $request2->userid); + $this->assertEquals($user3->id, $request2->requesteduserid); + } } diff --git a/version.php b/version.php index 810a9f44316..61e0e2b54bd 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2018103100.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2018110100.00; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.