diff --git a/lib/db/services.php b/lib/db/services.php index 43f6fc23c40..346ea3c811f 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1000,7 +1000,8 @@ $functions = array( 'classname' => 'core_message_external', 'methodname' => 'data_for_messagearea_search_users', 'classpath' => 'message/externallib.php', - 'description' => 'Retrieve the template data for searching for people', + 'description' => '** DEPRECATED ** Please do not call this function any more. + Retrieve the template data for searching for people', 'type' => 'read', 'ajax' => true, ), @@ -1008,10 +1009,20 @@ $functions = array( 'classname' => 'core_message_external', 'methodname' => 'data_for_messagearea_search_users_in_course', 'classpath' => 'message/externallib.php', - 'description' => 'Retrieve the template data for searching for people in a course', + 'description' => '** DEPRECATED ** Please do not call this function any more. + Retrieve the template data for searching for people in a course', 'type' => 'read', 'ajax' => true, ), + 'core_message_message_search_users' => array( + 'classname' => 'core_message_external', + 'methodname' => 'message_search_users', + 'classpath' => 'message/externallib.php', + 'description' => 'Retrieve the data for searching for people', + 'type' => 'read', + 'ajax' => true, + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + ), 'core_message_data_for_messagearea_conversations' => array( 'classname' => 'core_message_external', 'methodname' => 'data_for_messagearea_conversations', diff --git a/message/classes/api.php b/message/classes/api.php index 7556365eff2..62995e31415 100644 --- a/message/classes/api.php +++ b/message/classes/api.php @@ -151,6 +151,11 @@ class api { /** * Handles searching for user in a particular course in the message area. * + * TODO: This function should be removed once new group messaging UI is in place and old messaging UI is removed. + * For now we are not removing/deprecating this function for backwards compatibility with messaging UI. + * But we are deprecating data_for_messagearea_search_users_in_course external function. + * Followup: MDL-63915 + * * @param int $userid The user id doing the searching * @param int $courseid The id of the course we are searching in * @param string $search The string the user is searching @@ -192,6 +197,11 @@ class api { /** * Handles searching for user in the message area. * + * TODO: This function should be removed once new group messaging UI is in place and old messaging UI is removed. + * For now we are not removing/deprecating this function for backwards compatibility with messaging UI. + * But we are deprecating data_for_messagearea_search_users external function. + * Followup: MDL-63915 + * * @param int $userid The user id doing the searching * @param string $search The string the user is searching * @param int $limitnum @@ -208,22 +218,23 @@ class api { $excludeusers = array($userid, $CFG->siteguest); list($exclude, $excludeparams) = $DB->get_in_or_equal($excludeusers, SQL_PARAMS_NAMED, 'param', false); + $params = array('search' => '%' . $search . '%', 'userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid); + // Ok, let's search for contacts first. $contacts = array(); $sql = "SELECT $ufields, mub.id as isuserblocked - FROM {user} u - JOIN {message_contacts} mc - ON u.id = mc.contactid - LEFT JOIN {message_users_blocked} mub - ON (mub.userid = :userid2 AND mub.blockeduserid = u.id) - WHERE mc.userid = :userid - AND u.deleted = 0 - AND u.confirmed = 1 - AND " . $DB->sql_like($fullname, ':search', false) . " - AND u.id $exclude - ORDER BY " . $DB->sql_fullname(); - if ($users = $DB->get_records_sql($sql, array('userid' => $userid, 'userid2' => $userid, - 'search' => '%' . $search . '%') + $excludeparams, 0, $limitnum)) { + FROM {user} u + JOIN {message_contacts} mc + ON (u.id = mc.contactid AND mc.userid = :userid1) OR (u.id = mc.userid AND mc.contactid = :userid2) + LEFT JOIN {message_users_blocked} mub + ON (mub.userid = :userid3 AND mub.blockeduserid = u.id) + WHERE u.deleted = 0 + AND u.confirmed = 1 + AND " . $DB->sql_like($fullname, ':search', false) . " + AND u.id $exclude + ORDER BY " . $DB->sql_fullname(); + + if ($users = $DB->get_records_sql($sql, $params + $excludeparams, 0, $limitnum)) { foreach ($users as $user) { $user->blocked = $user->isuserblocked ? 1 : 0; $contacts[] = helper::create_contact($user); @@ -251,22 +262,46 @@ class api { } } - // Let's get those non-contacts. Toast them gears boi. - // Note - you can only block contacts, so these users will not be blocked, so no need to get that - // extra detail from the database. + // Let's get those non-contacts. $noncontacts = array(); - $sql = "SELECT $ufields - FROM {user} u - WHERE u.deleted = 0 - AND u.confirmed = 1 - AND " . $DB->sql_like($fullname, ':search', false) . " - AND u.id $exclude - AND u.id NOT IN (SELECT contactid - FROM {message_contacts} - WHERE userid = :userid) - ORDER BY " . $DB->sql_fullname(); - if ($users = $DB->get_records_sql($sql, array('userid' => $userid, 'search' => '%' . $search . '%') + $excludeparams, - 0, $limitnum)) { + if ($CFG->messagingallusers) { + // In case $CFG->messagingallusers is enabled, search for all users site-wide but are not user's contact. + $sql = "SELECT $ufields + FROM {user} u + LEFT JOIN {message_users_blocked} mub + ON (mub.userid = :userid1 AND mub.blockeduserid = u.id) + WHERE u.deleted = 0 + AND u.confirmed = 1 + AND " . $DB->sql_like($fullname, ':search', false) . " + AND u.id $exclude + AND NOT EXISTS (SELECT mc.id + FROM {message_contacts} mc + WHERE (mc.userid = u.id AND mc.contactid = :userid2) + OR (mc.userid = :userid3 AND mc.contactid = u.id)) + ORDER BY " . $DB->sql_fullname(); + } else { + // In case $CFG->messagingallusers is disabled, search for users you have a conversation with. + // Messaging setting could change, so could exist an old conversation with users you cannot message anymore. + $sql = "SELECT $ufields, mub.id as isuserblocked + FROM {user} u + LEFT JOIN {message_users_blocked} mub + ON (mub.userid = :userid1 AND mub.blockeduserid = u.id) + INNER JOIN {message_conversation_members} cm + ON u.id = cm.userid + INNER JOIN {message_conversation_members} cm2 + ON cm.conversationid = cm2.conversationid AND cm2.userid = :userid + WHERE u.deleted = 0 + AND u.confirmed = 1 + AND " . $DB->sql_like($fullname, ':search', false) . " + AND u.id $exclude + AND NOT EXISTS (SELECT mc.id + FROM {message_contacts} mc + WHERE (mc.userid = u.id AND mc.contactid = :userid2) + OR (mc.userid = :userid3 AND mc.contactid = u.id)) + ORDER BY " . $DB->sql_fullname(); + $params['userid'] = $userid; + } + if ($users = $DB->get_records_sql($sql, $params + $excludeparams, 0, $limitnum)) { foreach ($users as $user) { $noncontacts[] = helper::create_contact($user); } @@ -275,6 +310,102 @@ class api { return array($contacts, $courses, $noncontacts); } + /** + * Handles searching for user. + * + * @param int $userid The user id doing the searching + * @param string $search The string the user is searching + * @param int $limitfrom + * @param int $limitnum + * @return array + */ + public static function message_search_users(int $userid, string $search, int $limitfrom = 0, int $limitnum = 1000) : array { + global $CFG, $DB; + + // Used to search for contacts. + $fullname = $DB->sql_fullname(); + + // Users not to include. + $excludeusers = array($userid, $CFG->siteguest); + list($exclude, $excludeparams) = $DB->get_in_or_equal($excludeusers, SQL_PARAMS_NAMED, 'param', false); + + $params = array('search' => '%' . $search . '%', 'userid1' => $userid, 'userid2' => $userid); + + // Ok, let's search for contacts first. + $sql = "SELECT u.id + FROM {user} u + JOIN {message_contacts} mc + ON (u.id = mc.contactid AND mc.userid = :userid1) OR (u.id = mc.userid AND mc.contactid = :userid2) + WHERE u.deleted = 0 + AND u.confirmed = 1 + AND " . $DB->sql_like($fullname, ':search', false) . " + AND u.id $exclude + ORDER BY " . $DB->sql_fullname(); + $foundusers = $DB->get_records_sql_menu($sql, $params + $excludeparams, $limitfrom, $limitnum); + + $orderedcontacs = array(); + if (!empty($foundusers)) { + $contacts = helper::get_member_info($userid, array_keys($foundusers)); + // The get_member_info returns an associative array, so is not ordered in the same way. + // We need to reorder it again based on query's result. + foreach ($foundusers as $key => $value) { + $contact = $contacts[$key]; + $contact->conversations = self::get_conversations_between_users($userid, $key, 0, 1000); + $orderedcontacs[] = $contact; + } + } + + // Let's get those non-contacts. + if ($CFG->messagingallusers) { + // In case $CFG->messagingallusers is enabled, search for all users site-wide but are not user's contact. + $sql = "SELECT u.id + FROM {user} u + WHERE u.deleted = 0 + AND u.confirmed = 1 + AND " . $DB->sql_like($fullname, ':search', false) . " + AND u.id $exclude + AND NOT EXISTS (SELECT mc.id + FROM {message_contacts} mc + WHERE (mc.userid = u.id AND mc.contactid = :userid1) + OR (mc.userid = :userid2 AND mc.contactid = u.id)) + ORDER BY " . $DB->sql_fullname(); + } else { + // In case $CFG->messagingallusers is disabled, search for users you have a conversation with. + // Messaging setting could change, so could exist an old conversation with users you cannot message anymore. + $sql = "SELECT u.id + FROM {user} u + INNER JOIN {message_conversation_members} cm + ON u.id = cm.userid + INNER JOIN {message_conversation_members} cm2 + ON cm.conversationid = cm2.conversationid AND cm2.userid = :userid + WHERE u.deleted = 0 + AND u.confirmed = 1 + AND " . $DB->sql_like($fullname, ':search', false) . " + AND u.id $exclude + AND NOT EXISTS (SELECT mc.id + FROM {message_contacts} mc + WHERE (mc.userid = u.id AND mc.contactid = :userid1) + OR (mc.userid = :userid2 AND mc.contactid = u.id)) + ORDER BY " . $DB->sql_fullname(); + $params['userid'] = $userid; + } + $foundusers = $DB->get_records_sql_menu($sql, $params + $excludeparams, $limitfrom, $limitnum); + + $orderednoncontacs = array(); + if (!empty($foundusers)) { + $noncontacts = helper::get_member_info($userid, array_keys($foundusers)); + // The get_member_info returns an associative array, so is not ordered in the same way. + // We need to reorder it again based on query's result. + foreach ($foundusers as $key => $value) { + $contact = $noncontacts[$key]; + $contact->conversations = self::get_conversations_between_users($userid, $key, 0, 1000); + $orderednoncontacs[] = $contact; + } + } + + return array($orderedcontacs, $orderednoncontacs); + } + /** * Gets extra fields, like image url and subname for any conversations linked to components. * @@ -591,6 +722,41 @@ class api { return $arrconversations; } + /** + * Returns all conversations between two users + * + * @param int $userid1 One of the user's id + * @param int $userid2 The other user's id + * @param int $limitfrom + * @param int $limitnum + * @return array + * @throws \dml_exception + */ + public static function get_conversations_between_users(int $userid1, int $userid2, + int $limitfrom = 0, int $limitnum = 20) : array { + + global $DB; + + if ($userid1 == $userid2) { + return array(); + } + + // Get all conversation where both user1 and user2 are members. + // TODO: Add subname value. Waiting for definite table structure. + $sql = "SELECT mc.id, mc.type, mc.name, mc.timecreated + FROM {message_conversations} mc + INNER JOIN {message_conversation_members} mcm1 + ON mc.id = mcm1.conversationid + INNER JOIN {message_conversation_members} mcm2 + ON mc.id = mcm2.conversationid + WHERE mcm1.userid = :userid1 + AND mcm2.userid = :userid2 + AND mc.enabled != 0 + ORDER BY mc.timecreated DESC"; + + return $DB->get_records_sql($sql, array('userid1' => $userid1, 'userid2' => $userid2), $limitfrom, $limitnum); + } + /** * Mark a conversation as a favourite for the given user. * diff --git a/message/externallib.php b/message/externallib.php index fcafb714e7d..af3740a426d 100644 --- a/message/externallib.php +++ b/message/externallib.php @@ -949,6 +949,7 @@ 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( @@ -976,10 +977,12 @@ class core_message_external extends external_api { * Return the structure of a conversation member. * * @param bool $includecontactrequests Are we including contact requests? + * @param bool $includeconversations Are we including conversations? * @return external_single_structure * @since Moodle 3.6 */ - private static function get_conversation_member_structure(bool $includecontactrequests = false) { + private static function get_conversation_member_structure(bool $includecontactrequests = false, + bool $includeconversations = false) { $result = [ 'id' => new external_value(PARAM_INT, 'The user id'), 'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'), @@ -1004,6 +1007,18 @@ class core_message_external extends external_api { ); } + if ($includeconversations) { + $result['conversations'] = new external_multiple_structure(new external_single_structure( + array( + 'id' => new external_value(PARAM_INT, 'Conversations id'), + 'type' => new external_value(PARAM_INT, 'Conversation type: private or public'), + 'name' => new external_value(PARAM_TEXT, 'Multilang compatible conversation name'. VALUE_OPTIONAL), + 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the conversation'), + ), 'information about conversation', VALUE_OPTIONAL), + 'Conversations between users', VALUE_OPTIONAL + ); + } + return new external_single_structure( $result ); @@ -1052,6 +1067,8 @@ class core_message_external extends external_api { /** * Get messagearea search users in course parameters. * + * @deprecated since 3.6 + * * @return external_function_parameters * @since 3.2 */ @@ -1070,6 +1087,12 @@ class core_message_external extends external_api { /** * Get messagearea search users in course results. * + * @deprecated since 3.6 + * + * NOTE: We are deprecating this function but not search_users_in_course API function for backwards compatibility + * with messaging UI. But should be removed once new group messaging UI is in place and old messaging UI is removed. + * Followup: MDL-63915 + * * @param int $userid The id of the user who is performing the search * @param int $courseid The id of the course * @param string $search The string being searched @@ -1114,6 +1137,8 @@ class core_message_external extends external_api { /** * Get messagearea search users in course returns. * + * @deprecated since 3.6 + * * @return external_single_structure * @since 3.2 */ @@ -1127,9 +1152,20 @@ class core_message_external extends external_api { ); } + /** + * Marking the method as deprecated. + * + * @return bool + */ + public static function data_for_messagearea_search_users_in_course_is_deprecated() { + return true; + } + /** * Get messagearea search users parameters. * + * @deprecated since 3.6 + * * @return external_function_parameters * @since 3.2 */ @@ -1146,6 +1182,12 @@ class core_message_external extends external_api { /** * Get messagearea search users results. * + * @deprecated since 3.6 + * + * NOTE: We are deprecating this function but not search_users API function for backwards compatibility + * with messaging UI. But should be removed once new group messaging UI is in place and old messaging UI is removed. + * Followup: MDL-63915 + * * @param int $userid The id of the user who is performing the search * @param string $search The string being searched * @param int $limitnum @@ -1185,6 +1227,8 @@ class core_message_external extends external_api { /** * Get messagearea search users returns. * + * @deprecated since 3.6 + * * @return external_single_structure * @since 3.2 */ @@ -1210,6 +1254,94 @@ class core_message_external extends external_api { ); } + /** + * Marking the method as deprecated. + * + * @return bool + */ + public static function data_for_messagearea_search_users_is_deprecated() { + return true; + } + + /** + * Get messagearea message search users parameters. + * + * @return external_function_parameters + * @since 3.6 + */ + public static function message_search_users_parameters() { + return new external_function_parameters( + array( + 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'), + 'search' => new external_value(PARAM_RAW, 'The string being searched'), + 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0), + 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0), + ) + ); + } + + /** + * Get search users results. + * + * @param int $userid The id of the user who is performing the search + * @param string $search The string being searched + * @param int $limitfrom + * @param int $limitnum + * @return array + * @throws moodle_exception + * @since 3.6 + */ + public static function message_search_users($userid, $search, $limitfrom = 0, $limitnum = 0) { + global $CFG, $USER; + + // Check if messaging is enabled. + if (empty($CFG->messaging)) { + throw new moodle_exception('disabled', 'message'); + } + + $systemcontext = context_system::instance(); + + $params = array( + 'userid' => $userid, + 'search' => $search, + 'limitfrom' => $limitfrom, + 'limitnum' => $limitnum + ); + $params = self::validate_parameters(self::message_search_users_parameters(), $params); + self::validate_context($systemcontext); + + if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) { + throw new moodle_exception('You do not have permission to perform this action.'); + } + + list($contacts, $noncontacts) = \core_message\api::message_search_users( + $params['userid'], + $params['search'], + $params['limitfrom'], + $params['limitnum']); + + return array('contacts' => $contacts, 'noncontacts' => $noncontacts); + } + + /** + * Get messagearea message search users returns. + * + * @return external_single_structure + * @since 3.2 + */ + public static function message_search_users_returns() { + return new external_single_structure( + array( + 'contacts' => new external_multiple_structure( + self::get_conversation_member_structure(false, true) + ), + 'noncontacts' => new external_multiple_structure( + self::get_conversation_member_structure(false, true) + ) + ) + ); + } + /** * Get messagearea search messages parameters. * diff --git a/message/tests/api_test.php b/message/tests/api_test.php index 4d44b61cfbb..76f27779ac8 100644 --- a/message/tests/api_test.php +++ b/message/tests/api_test.php @@ -258,7 +258,8 @@ class core_message_api_testcase extends core_message_messagelib_testcase { $course5context = context_course::instance($course5->id); assign_capability('moodle/course:viewparticipants', CAP_PROHIBIT, $role->id, $course5context->id); - // Perform a search. + // Perform a search $CFG->messagingallusers setting enabled. + set_config('messagingallusers', 1); list($contacts, $courses, $noncontacts) = \core_message\api::search_users($user1->id, 'search'); // Check that we retrieved the correct contacts. @@ -276,6 +277,277 @@ class core_message_api_testcase extends core_message_messagelib_testcase { $this->assertEquals($user5->id, $noncontacts[0]->userid); } + /** + * Tests searching users with empty result. + */ + public function test_search_users_with_empty_result() { + + // Create some users. + $user1 = new stdClass(); + $user1->firstname = 'User'; + $user1->lastname = 'One'; + $user1 = self::getDataGenerator()->create_user($user1); + + // Set as the user performing the search. + $this->setUser($user1); + + $user2 = new stdClass(); + $user2->firstname = 'User'; + $user2->lastname = 'Two'; + $user2 = self::getDataGenerator()->create_user($user2); + + // Perform a search $CFG->messagingallusers setting enabled. + set_config('messagingallusers', 1); + list($contacts, $courses, $noncontacts) = \core_message\api::search_users($user1->id, 'search'); + + // Check results are empty. + $this->assertEquals(0, count($contacts)); + $this->assertEquals(0, count($courses)); + $this->assertEquals(0, count($noncontacts)); + } + + /** + * Tests searching users. + */ + public function test_message_search_users() { + // Create some users. + $user1 = new stdClass(); + $user1->firstname = 'User search'; + $user1->lastname = 'One'; + $user1 = self::getDataGenerator()->create_user($user1); + + // Set as the user performing the search. + $this->setUser($user1); + + $user2 = new stdClass(); + $user2->firstname = 'User search'; + $user2->lastname = 'Two'; + $user2 = self::getDataGenerator()->create_user($user2); + + $user3 = new stdClass(); + $user3->firstname = 'User search'; + $user3->lastname = 'Three'; + $user3 = self::getDataGenerator()->create_user($user3); + + $user4 = new stdClass(); + $user4->firstname = 'User'; + $user4->lastname = 'Four'; + $user4 = self::getDataGenerator()->create_user($user4); + + $user5 = new stdClass(); + $user5->firstname = 'User search'; + $user5->lastname = 'Five'; + $user5 = self::getDataGenerator()->create_user($user5); + + $user6 = new stdClass(); + $user6->firstname = 'User search'; + $user6->lastname = 'Six'; + $user6 = self::getDataGenerator()->create_user($user6); + + $user7 = new stdClass(); + $user7->firstname = 'User search'; + $user7->lastname = 'Seven'; + $user7 = self::getDataGenerator()->create_user($user7); + + // Add some users as contacts. + \core_message\api::add_contact($user1->id, $user2->id); + \core_message\api::add_contact($user3->id, $user1->id); + \core_message\api::add_contact($user1->id, $user4->id); + + // Create private conversations with some users. + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user1->id, $user6->id)); + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user7->id, $user1->id)); + + // Perform a search $CFG->messagingallusers setting enabled. + set_config('messagingallusers', 1); + list($contacts, $noncontacts) = \core_message\api::message_search_users($user1->id, 'search'); + + // Check that we retrieved the correct contacts. + $this->assertCount(2, $contacts); + $this->assertEquals($user3->id, $contacts[0]->id); + $this->assertEquals($user2->id, $contacts[1]->id); + + // Check that we retrieved the correct non-contacts. + $this->assertCount(3, $noncontacts); + $this->assertEquals($user5->id, $noncontacts[0]->id); + $this->assertEquals($user7->id, $noncontacts[1]->id); + $this->assertEquals($user6->id, $noncontacts[2]->id); + + // Perform a search $CFG->messagingallusers setting disabled. + set_config('messagingallusers', 0); + list($contacts, $noncontacts) = \core_message\api::message_search_users($user1->id, 'search'); + + // Check that we retrieved the correct contacts. + $this->assertCount(2, $contacts); + $this->assertEquals($user3->id, $contacts[0]->id); + $this->assertEquals($user2->id, $contacts[1]->id); + + // Check that we retrieved the correct non-contacts. + $this->assertCount(2, $noncontacts); + $this->assertEquals($user7->id, $noncontacts[0]->id); + $this->assertEquals($user6->id, $noncontacts[1]->id); + } + + /** + * Tests getting conversations between 2 users. + */ + public function test_get_conversations_between_users() { + // Create some users. + $user1 = new stdClass(); + $user1->firstname = 'User'; + $user1->lastname = 'One'; + $user1 = self::getDataGenerator()->create_user($user1); + + $user2 = new stdClass(); + $user2->firstname = 'User'; + $user2->lastname = 'Two'; + $user2 = self::getDataGenerator()->create_user($user2); + + $user3 = new stdClass(); + $user3->firstname = 'User search'; + $user3->lastname = 'Three'; + $user3 = self::getDataGenerator()->create_user($user3); + + $user4 = new stdClass(); + $user4->firstname = 'User'; + $user4->lastname = 'Four'; + $user4 = self::getDataGenerator()->create_user($user4); + + $user5 = new stdClass(); + $user5->firstname = 'User'; + $user5->lastname = 'Five'; + $user5 = self::getDataGenerator()->create_user($user5); + + $user6 = new stdClass(); + $user6->firstname = 'User search'; + $user6->lastname = 'Six'; + $user6 = self::getDataGenerator()->create_user($user6); + + // Add some users as contacts. + \core_message\api::add_contact($user1->id, $user2->id); + \core_message\api::add_contact($user6->id, $user1->id); + + // Create private conversations with some users. + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user1->id, $user2->id)); + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user3->id, $user1->id)); + + // Create a group conversation with users. + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, + array($user1->id, $user2->id, $user3->id, $user4->id), + 'Project chat'); + + // Check that we retrieved the correct conversations. + $this->assertCount(2, \core_message\api::get_conversations_between_users($user1->id, $user2->id)); + $this->assertCount(2, \core_message\api::get_conversations_between_users($user2->id, $user1->id)); + $this->assertCount(2, \core_message\api::get_conversations_between_users($user1->id, $user3->id)); + $this->assertCount(2, \core_message\api::get_conversations_between_users($user3->id, $user1->id)); + $this->assertCount(1, \core_message\api::get_conversations_between_users($user1->id, $user4->id)); + $this->assertCount(1, \core_message\api::get_conversations_between_users($user4->id, $user1->id)); + $this->assertCount(0, \core_message\api::get_conversations_between_users($user1->id, $user5->id)); + $this->assertCount(0, \core_message\api::get_conversations_between_users($user5->id, $user1->id)); + $this->assertCount(0, \core_message\api::get_conversations_between_users($user1->id, $user6->id)); + $this->assertCount(0, \core_message\api::get_conversations_between_users($user6->id, $user1->id)); + } + + /** + * Tests searching users with and without conversations. + */ + public function test_message_search_users_with_and_without_conversations() { + // Create some users. + $user1 = new stdClass(); + $user1->firstname = 'User search'; + $user1->lastname = 'One'; + $user1 = self::getDataGenerator()->create_user($user1); + + // Set as the user performing the search. + $this->setUser($user1); + + $user2 = new stdClass(); + $user2->firstname = 'User search'; + $user2->lastname = 'Two'; + $user2 = self::getDataGenerator()->create_user($user2); + + $user3 = new stdClass(); + $user3->firstname = 'User search'; + $user3->lastname = 'Three'; + $user3 = self::getDataGenerator()->create_user($user3); + + $user4 = new stdClass(); + $user4->firstname = 'User'; + $user4->lastname = 'Four'; + $user4 = self::getDataGenerator()->create_user($user4); + + $user5 = new stdClass(); + $user5->firstname = 'User search'; + $user5->lastname = 'Five'; + $user5 = self::getDataGenerator()->create_user($user5); + + // Add a user as contact. + \core_message\api::add_contact($user1->id, $user2->id); + + // Create private conversations with some users. + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user1->id, $user2->id)); + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user3->id, $user1->id)); + + // Create a group conversation with users. + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, + array($user1->id, $user2->id, $user4->id), + 'Project chat'); + + // Perform a search $CFG->messagingallusers setting enabled. + set_config('messagingallusers', 1); + list($contacts, $noncontacts) = \core_message\api::message_search_users($user1->id, 'search'); + + // Check that we retrieved the correct contacts. + $this->assertCount(1, $contacts); + + // Check that we retrieved the correct conversations for contacts. + $this->assertCount(2, $contacts[0]->conversations); + + // Check that we retrieved the correct non-contacts. + $this->assertCount(2, $noncontacts); + $this->assertEquals($user5->id, $noncontacts[0]->id); + $this->assertEquals($user3->id, $noncontacts[1]->id); + + // Check that we retrieved the correct conversations for non-contacts. + $this->assertCount(0, $noncontacts[0]->conversations); + $this->assertCount(1, $noncontacts[1]->conversations); + } + + /** + * Tests searching users with empty result. + */ + public function test_message_search_users_with_empty_result() { + + // Create some users. + $user1 = new stdClass(); + $user1->firstname = 'User'; + $user1->lastname = 'One'; + $user1 = self::getDataGenerator()->create_user($user1); + + // Set as the user performing the search. + $this->setUser($user1); + + $user2 = new stdClass(); + $user2->firstname = 'User'; + $user2->lastname = 'Two'; + $user2 = self::getDataGenerator()->create_user($user2); + + // Perform a search $CFG->messagingallusers setting enabled. + set_config('messagingallusers', 1); + list($contacts, $noncontacts) = \core_message\api::message_search_users($user1->id, 'search'); + + // Check results are empty. + $this->assertEquals(0, count($contacts)); + $this->assertEquals(0, count($noncontacts)); + } + /** * Tests searching messages. */ diff --git a/message/tests/externallib_test.php b/message/tests/externallib_test.php index d6fd8cabb1b..0781adc416f 100644 --- a/message/tests/externallib_test.php +++ b/message/tests/externallib_test.php @@ -1821,7 +1821,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { /** * Tests searching users in a course. */ - public function test_messagearea_search_users_in_course() { + public function test_data_for_messagearea_search_users_in_course() { $this->resetAfterTest(true); // Create some users. @@ -1885,7 +1885,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { /** * Tests searching users in course as another user. */ - public function test_messagearea_search_users_in_course_as_other_user() { + public function test_data_for_messagearea_search_users_in_course_as_other_user() { $this->resetAfterTest(true); // The person doing the search for another user. @@ -1944,7 +1944,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { /** * Tests searching users in course as another user without the proper capabilities. */ - public function test_messagearea_search_users_in_course_as_other_user_without_cap() { + public function test_data_for_messagearea_search_users_in_course_as_other_user_without_cap() { $this->resetAfterTest(true); // Create some users. @@ -1960,12 +1960,13 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { // Ensure an exception is thrown. $this->expectException('moodle_exception'); core_message_external::data_for_messagearea_search_users_in_course($user2->id, $course->id, 'User'); + $this->assertDebuggingCalled(); } /** * Tests searching users in course with messaging disabled. */ - public function test_messagearea_search_users_in_course_messaging_disabled() { + public function test_data_for_messagearea_search_users_in_course_messaging_disabled() { global $CFG; $this->resetAfterTest(true); @@ -1983,12 +1984,13 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { // Ensure an exception is thrown. $this->expectException('moodle_exception'); core_message_external::data_for_messagearea_search_users_in_course($user->id, $course->id, 'User'); + $this->assertDebuggingCalled(); } /** * Tests searching users. */ - public function test_messagearea_search_users() { + public function test_data_for_messagearea_search_users() { $this->resetAfterTest(true); // Create some users. @@ -2055,7 +2057,8 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { \core_message\api::add_contact($user1->id, $user3->id); \core_message\api::add_contact($user1->id, $user4->id); - // Perform a search. + // Perform a search $CFG->messagingallusers setting enabled. + set_config('messagingallusers', 1); $result = core_message_external::data_for_messagearea_search_users($user1->id, 'search'); // We need to execute the return values cleaning process to simulate the web service server. @@ -2085,7 +2088,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { /** * Tests searching users as another user. */ - public function test_messagearea_search_users_as_other_user() { + public function test_data_for_messagearea_search_users_as_other_user() { $this->resetAfterTest(true); // The person doing the search. @@ -2143,7 +2146,8 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { \core_message\api::add_contact($user1->id, $user3->id); \core_message\api::add_contact($user1->id, $user4->id); - // Perform a search. + // Perform a search $CFG->messagingallusers setting enabled. + set_config('messagingallusers', 1); $result = core_message_external::data_for_messagearea_search_users($user1->id, 'search'); // We need to execute the return values cleaning process to simulate the web service server. @@ -2171,7 +2175,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { /** * Tests searching users as another user without the proper capabilities. */ - public function test_messagearea_search_users_as_other_user_without_cap() { + public function test_data_for_messagearea_search_users_as_other_user_without_cap() { $this->resetAfterTest(true); // Create some users. @@ -2184,12 +2188,13 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { // Ensure an exception is thrown. $this->expectException('moodle_exception'); core_message_external::data_for_messagearea_search_users($user2->id, 'User'); + $this->assertDebuggingCalled(); } /** * Tests searching users with messaging disabled. */ - public function test_messagearea_search_users_messaging_disabled() { + public function test_data_for_messagearea_search_users_messaging_disabled() { global $CFG; $this->resetAfterTest(true); @@ -2206,6 +2211,327 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { // Ensure an exception is thrown. $this->expectException('moodle_exception'); core_message_external::data_for_messagearea_search_users($user->id, 'User'); + $this->assertDebuggingCalled(); + } + + /** + * Tests searching users. + */ + public function test_message_search_users() { + $this->resetAfterTest(true); + + // Create some users. + $user1 = new stdClass(); + $user1->firstname = 'User search'; + $user1->lastname = 'One'; + $user1 = self::getDataGenerator()->create_user($user1); + // Set as the user performing the search. + $this->setUser($user1); + + $user2 = new stdClass(); + $user2->firstname = 'User search'; + $user2->lastname = 'Two'; + $user2 = self::getDataGenerator()->create_user($user2); + + $user3 = new stdClass(); + $user3->firstname = 'User search'; + $user3->lastname = 'Three'; + $user3 = self::getDataGenerator()->create_user($user3); + + $user4 = new stdClass(); + $user4->firstname = 'User'; + $user4->lastname = 'Four'; + $user4 = self::getDataGenerator()->create_user($user4); + + $user5 = new stdClass(); + $user5->firstname = 'User search'; + $user5->lastname = 'Five'; + $user5 = self::getDataGenerator()->create_user($user5); + + $user6 = new stdClass(); + $user6->firstname = 'User search'; + $user6->lastname = 'Six'; + $user6 = self::getDataGenerator()->create_user($user6); + + $user7 = new stdClass(); + $user7->firstname = 'User search'; + $user7->lastname = 'Seven'; + $user7 = self::getDataGenerator()->create_user($user7); + + // Add some users as contacts. + \core_message\api::add_contact($user1->id, $user2->id); + \core_message\api::add_contact($user3->id, $user1->id); + \core_message\api::add_contact($user1->id, $user4->id); + + // Create private conversations with some users. + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user1->id, $user6->id)); + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user7->id, $user1->id)); + + // Perform a search $CFG->messagingallusers setting enabled. + set_config('messagingallusers', 1); + $result = core_message_external::message_search_users($user1->id, 'search'); + + // We need to execute the return values cleaning process to simulate the web service server. + $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), + $result); + + // Confirm that we returns contacts and non-contacts. + $contacts = $result['contacts']; + $noncontacts = $result['noncontacts']; + + // Check that we retrieved the correct contacts. + $this->assertCount(2, $contacts); + $this->assertEquals($user3->id, $contacts[0]['id']); + $this->assertEquals($user2->id, $contacts[1]['id']); + + // Check that we retrieved the correct non-contacts. + $this->assertCount(3, $noncontacts); + $this->assertEquals($user5->id, $noncontacts[0]['id']); + $this->assertEquals($user7->id, $noncontacts[1]['id']); + $this->assertEquals($user6->id, $noncontacts[2]['id']); + + // Perform a search $CFG->messagingallusers setting disabled. + set_config('messagingallusers', 0); + $result = core_message_external::message_search_users($user1->id, 'search'); + + // We need to execute the return values cleaning process to simulate the web service server. + $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), + $result); + + // Confirm that we returns contacts and non-contacts. + $contacts = $result['contacts']; + $noncontacts = $result['noncontacts']; + + // Check that we retrieved the correct contacts. + $this->assertCount(2, $contacts); + $this->assertEquals($user3->id, $contacts[0]['id']); + $this->assertEquals($user2->id, $contacts[1]['id']); + + // Check that we retrieved the correct non-contacts. + $this->assertCount(2, $noncontacts); + $this->assertEquals($user7->id, $noncontacts[0]['id']); + $this->assertEquals($user6->id, $noncontacts[1]['id']); + } + + /** + * Tests searching users as another user. + */ + public function test_message_search_users_as_other_user() { + $this->resetAfterTest(true); + + // The person doing the search. + $this->setAdminUser(); + + // Create some users. + $user1 = new stdClass(); + $user1->firstname = 'User search'; + $user1->lastname = 'One'; + $user1 = self::getDataGenerator()->create_user($user1); + + $user2 = new stdClass(); + $user2->firstname = 'User search'; + $user2->lastname = 'Two'; + $user2 = self::getDataGenerator()->create_user($user2); + + $user3 = new stdClass(); + $user3->firstname = 'User search'; + $user3->lastname = 'Three'; + $user3 = self::getDataGenerator()->create_user($user3); + + $user4 = new stdClass(); + $user4->firstname = 'User'; + $user4->lastname = 'Four'; + $user4 = self::getDataGenerator()->create_user($user4); + + $user5 = new stdClass(); + $user5->firstname = 'User search'; + $user5->lastname = 'Five'; + $user5 = self::getDataGenerator()->create_user($user5); + + $user6 = new stdClass(); + $user6->firstname = 'User search'; + $user6->lastname = 'Six'; + $user6 = self::getDataGenerator()->create_user($user6); + + $user7 = new stdClass(); + $user7->firstname = 'User search'; + $user7->lastname = 'Seven'; + $user7 = self::getDataGenerator()->create_user($user7); + + // Add some users as contacts. + \core_message\api::add_contact($user1->id, $user2->id); + \core_message\api::add_contact($user3->id, $user1->id); + \core_message\api::add_contact($user1->id, $user4->id); + + // Create private conversations with some users. + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user1->id, $user6->id)); + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user7->id, $user1->id)); + + // Perform a search $CFG->messagingallusers setting enabled. + set_config('messagingallusers', 1); + $result = core_message_external::message_search_users($user1->id, 'search'); + + // We need to execute the return values cleaning process to simulate the web service server. + $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), + $result); + + // Confirm that we returns contacts and non-contacts. + $contacts = $result['contacts']; + $noncontacts = $result['noncontacts']; + + // Check that we retrieved the correct contacts. + $this->assertCount(2, $contacts); + $this->assertEquals($user3->id, $contacts[0]['id']); + $this->assertEquals($user2->id, $contacts[1]['id']); + + // Check that we retrieved the correct non-contacts. + $this->assertCount(3, $noncontacts); + $this->assertEquals($user5->id, $noncontacts[0]['id']); + $this->assertEquals($user7->id, $noncontacts[1]['id']); + $this->assertEquals($user6->id, $noncontacts[2]['id']); + + // Perform a search $CFG->messagingallusers setting disabled. + set_config('messagingallusers', 0); + $result = core_message_external::message_search_users($user1->id, 'search'); + + // We need to execute the return values cleaning process to simulate the web service server. + $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), + $result); + + // Confirm that we returns contacts and non-contacts. + $contacts = $result['contacts']; + $noncontacts = $result['noncontacts']; + + // Check that we retrieved the correct contacts. + $this->assertCount(2, $contacts); + $this->assertEquals($user3->id, $contacts[0]['id']); + $this->assertEquals($user2->id, $contacts[1]['id']); + + // Check that we retrieved the correct non-contacts. + $this->assertCount(2, $noncontacts); + $this->assertEquals($user7->id, $noncontacts[0]['id']); + $this->assertEquals($user6->id, $noncontacts[1]['id']); + } + + /** + * Tests searching users as another user without the proper capabilities. + */ + public function test_message_search_users_as_other_user_without_cap() { + $this->resetAfterTest(true); + + // Create some users. + $user1 = self::getDataGenerator()->create_user(); + $user2 = self::getDataGenerator()->create_user(); + + // The person doing the search for another user. + $this->setUser($user1); + + // Ensure an exception is thrown. + $this->expectException('moodle_exception'); + core_message_external::message_search_users($user2->id, 'User'); + $this->assertDebuggingCalled(); + } + + /** + * Tests searching users with and without conversations. + */ + public function test_message_search_users_with_and_without_conversations() { + $this->resetAfterTest(true); + + // Create some users. + $user1 = new stdClass(); + $user1->firstname = 'User search'; + $user1->lastname = 'One'; + $user1 = self::getDataGenerator()->create_user($user1); + + // Set as the user performing the search. + $this->setUser($user1); + + $user2 = new stdClass(); + $user2->firstname = 'User search'; + $user2->lastname = 'Two'; + $user2 = self::getDataGenerator()->create_user($user2); + + $user3 = new stdClass(); + $user3->firstname = 'User search'; + $user3->lastname = 'Three'; + $user3 = self::getDataGenerator()->create_user($user3); + + $user4 = new stdClass(); + $user4->firstname = 'User'; + $user4->lastname = 'Four'; + $user4 = self::getDataGenerator()->create_user($user4); + + $user5 = new stdClass(); + $user5->firstname = 'User search'; + $user5->lastname = 'Five'; + $user5 = self::getDataGenerator()->create_user($user5); + + // Add a user as contact. + \core_message\api::add_contact($user1->id, $user2->id); + + // Create private conversations with some users. + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user1->id, $user2->id)); + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + array($user3->id, $user1->id)); + + // Create a group conversation with users. + \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, + array($user1->id, $user2->id, $user4->id), + 'Project chat'); + + // Perform a search $CFG->messagingallusers setting enabled. + set_config('messagingallusers', 1); + $result = core_message_external::message_search_users($user1->id, 'search'); + + // We need to execute the return values cleaning process to simulate the web service server. + $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), + $result); + + // Confirm that we returns contacts and non-contacts. + $contacts = $result['contacts']; + $noncontacts = $result['noncontacts']; + + // Check that we retrieved the correct contacts. + $this->assertCount(1, $contacts); + + // Check that we retrieved the correct conversations for contacts. + $this->assertCount(2, $contacts[0]['conversations']); + + // Check that we retrieved the correct non-contacts. + $this->assertCount(2, $noncontacts); + $this->assertEquals($user5->id, $noncontacts[0]['id']); + $this->assertEquals($user3->id, $noncontacts[1]['id']); + + // Check that we retrieved the correct conversations for non-contacts. + $this->assertCount(0, $noncontacts[0]['conversations']); + $this->assertCount(1, $noncontacts[1]['conversations']); + } + + /** + * Tests searching users with messaging disabled. + */ + public function test_message_search_users_messaging_disabled() { + $this->resetAfterTest(true); + + // Create some skeleton data just so we can call the WS. + $user = self::getDataGenerator()->create_user(); + + // The person doing the search. + $this->setUser($user); + + // Disable messaging. + set_config('messaging', 0); + + // Ensure an exception is thrown. + $this->expectException('moodle_exception'); + core_message_external::message_search_users($user->id, 'User'); + $this->assertDebuggingCalled(); } /** diff --git a/message/upgrade.txt b/message/upgrade.txt index 5eaf7926325..eb7a159ddbd 100644 --- a/message/upgrade.txt +++ b/message/upgrade.txt @@ -35,6 +35,7 @@ information provided here is intended especially for developers. - \core_message\api::get_most_recent_message() - \core_message\helper::get_messages() - \core_message\helper::create_messages() + - \core_message\api::search_users() * The method \core_message\api::can_delete_conversation() now expects a 'conversationid' to be passed as the second parameter. * The following web services have been deprecated. Please do not call these any more. @@ -46,6 +47,9 @@ information provided here is intended especially for developers. core_message_external::core_message_mark_all_conversation_messages_as_read() instead. - core_message_external::data_for_messagearea_conversations(), please use core_message_external::get_conversations() instead + - core_message_external::data_for_messagearea_search_users_in_course(). + - core_message_external::data_for_messagearea_search_users(), + please use core_message_external::message_search_users() instead. * The following function has been added for getting the privacy messaging preference: - get_user_privacy_messaging_preference()