MDL-63213 core_message: update get_conversations to support favourites
authorJake Dallimore <jake@moodle.com>
Mon, 22 Oct 2018 01:25:59 +0000 (09:25 +0800)
committerJake Dallimore <jake@moodle.com>
Mon, 22 Oct 2018 01:43:33 +0000 (09:43 +0800)
Added the type param here, which will be used in MDL-63549.

message/classes/api.php
message/tests/api_test.php

index d9b507e..992afe2 100644 (file)
@@ -24,6 +24,8 @@
 
 namespace core_message;
 
+use core_favourites\local\entity\favourite;
+
 defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->dirroot . '/lib/messagelib.php');
@@ -274,11 +276,31 @@ class api {
      * @param int $userid The user id
      * @param int $limitfrom
      * @param int $limitnum
+     * @param int $type the conversation type.
+     * @param bool $favouritesonly whether to retrieve only the favourite conversations for the user, or not.
      * @return array
      */
-    public static function get_conversations($userid, $limitfrom = 0, $limitnum = 20) {
+    public static function get_conversations($userid, $limitfrom = 0, $limitnum = 20, int $type = null,
+            bool $favouritesonly = false) {
         global $DB;
 
+        $favouritesql = "";
+        $favouriteparams = [];
+        if ($favouritesonly) {
+            // Ask the favourites subsystem for the user's favourite conversations.
+            $service = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($userid));
+            $favourites = $service->find_favourites_by_type('core_message', 'message_conversations');
+            if (empty($favourites)) {
+                return []; // No favourited conversations, so return none.
+            }
+            $favids = array_values(array_map(function ($fav) {
+                return $fav->itemid;
+            }, $favourites));
+            list ($insql, $inparams) = $DB->get_in_or_equal($favids, SQL_PARAMS_NAMED, 'favouriteids');
+            $favouritesql = " AND m.conversationid {$insql} ";
+            $favouriteparams = $inparams;
+        }
+
         // Get the last message from each conversation that the user belongs to.
         $sql = "SELECT m.id, m.conversationid, m.useridfrom, mcm2.userid as useridto, m.smallmessage, m.timecreated
                   FROM {messages} m
@@ -305,10 +327,12 @@ class api {
             INNER JOIN {message_conversation_members} mcm2
                     ON mcm2.conversationid = m.conversationid
                  WHERE mcm.userid = m.useridfrom
-                   AND mcm.id != mcm2.id
+                   AND mcm.id != mcm2.id $favouritesql
               ORDER BY m.timecreated DESC";
-        $messageset = $DB->get_recordset_sql($sql, ['userid' => $userid, 'action' => self::MESSAGE_ACTION_DELETED,
-            'userid2' => $userid], $limitfrom, $limitnum);
+
+        $params = array_merge($favouriteparams, ['userid' => $userid, 'action' => self::MESSAGE_ACTION_DELETED,
+            'userid2' => $userid]);
+        $messageset = $DB->get_recordset_sql($sql, $params, $limitfrom, $limitnum);
 
         $messages = [];
         foreach ($messageset as $message) {
@@ -409,6 +433,34 @@ class api {
         return $arrconversations;
     }
 
+    /**
+     * Mark a conversation as a favourite for the given user.
+     *
+     * @param int $conversationid the id of the conversation to mark as a favourite.
+     * @param int $userid the id of the user to whom the favourite belongs.
+     * @return favourite the favourite object.
+     * @throws \moodle_exception if the user or conversation don't exist.
+     */
+    public static function set_favourite_conversation(int $conversationid, int $userid) : favourite {
+        if (!self::is_user_in_conversation($userid, $conversationid)) {
+            throw new \moodle_exception("Conversation doesn't exist or user is not a member");
+        }
+        $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($userid));
+        return $ufservice->create_favourite('core_message', 'message_conversations', $conversationid, \context_system::instance());
+    }
+
+    /**
+     * Unset a conversation as a favourite for the given user.
+     *
+     * @param int $conversationid the id of the conversation to unset as a favourite.
+     * @param int $userid the id to whom the favourite belongs.
+     * @throws \moodle_exception if the favourite does not exist for the user.
+     */
+    public static function unset_favourite_conversation(int $conversationid, int $userid) {
+        $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($userid));
+        $ufservice->delete_favourite('core_message', 'message_conversations', $conversationid, \context_system::instance());
+    }
+
     /**
      * Returns the contacts to display in the contacts area.
      *
index 8be0f3f..23ad644 100644 (file)
@@ -341,6 +341,304 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->assertNull($message3->unreadcount);
     }
 
+    /**
+     * Test verifying that favourited conversations can be retrieved.
+     */
+    public function test_get_favourite_conversations() {
+        // Create some users.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+        $user4 = self::getDataGenerator()->create_user();
+
+        // The person doing the search.
+        $this->setUser($user1);
+
+        // No conversations yet.
+        $this->assertEquals([], \core_message\api::get_conversations($user1->id));
+
+        // Create some conversations for user1.
+        $time = 1;
+        $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
+        $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
+        $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
+        $messageid1 = $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
+
+        $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
+        $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
+        $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
+        $messageid2 = $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
+
+        $this->send_fake_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?', 0, $time + 9);
+        $this->send_fake_message($user4, $user1, 'Yah brah, it\'s pretty rad.', 0, $time + 10);
+        $messageid3 = $this->send_fake_message($user1, $user4, 'Dope.', 0, $time + 11);
+
+        // Favourite the first 2 conversations for user1.
+        $convoids = [];
+        $convoids[] = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
+        $convoids[] = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
+        $user1context = context_user::instance($user1->id);
+        $service = \core_favourites\service_factory::get_service_for_user_context($user1context);
+        foreach ($convoids as $convoid) {
+            $service->create_favourite('core_message', 'message_conversations', $convoid, $user1context);
+        }
+
+        // We should have 3 conversations.
+        $this->assertCount(3, \core_message\api::get_conversations($user1->id));
+
+        // And 2 favourited conversations.
+        $conversations = \core_message\api::get_conversations($user1->id, 0, 20, null, true);
+        $this->assertCount(2, $conversations);
+    }
+
+    /**
+     * Tests retrieving favourite conversations with a limit and offset to ensure pagination works correctly.
+     */
+    public function test_get_favourite_conversations_limit_offset() {
+        // Create some users.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+        $user4 = self::getDataGenerator()->create_user();
+
+        // The person doing the search.
+        $this->setUser($user1);
+
+        // No conversations yet.
+        $this->assertEquals([], \core_message\api::get_conversations($user1->id));
+
+        // Create some conversations for user1.
+        $time = 1;
+        $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
+        $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
+        $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
+        $messageid1 = $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
+
+        $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
+        $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
+        $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
+        $messageid2 = $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
+
+        $this->send_fake_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?', 0, $time + 9);
+        $this->send_fake_message($user4, $user1, 'Yah brah, it\'s pretty rad.', 0, $time + 10);
+        $messageid3 = $this->send_fake_message($user1, $user4, 'Dope.', 0, $time + 11);
+
+        // Favourite the all conversations for user1.
+        $convoids = [];
+        $convoids[] = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
+        $convoids[] = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
+        $convoids[] = \core_message\api::get_conversation_between_users([$user1->id, $user4->id]);
+        $user1context = context_user::instance($user1->id);
+        $service = \core_favourites\service_factory::get_service_for_user_context($user1context);
+        foreach ($convoids as $convoid) {
+            $service->create_favourite('core_message', 'message_conversations', $convoid, $user1context);
+        }
+
+        // Get all records, using offset 0 and large limit.
+        $this->assertCount(2, \core_message\api::get_conversations($user1->id, 1, 10, null, true));
+
+        // Now, get 10 conversations starting at the second record. We should see 2 conversations.
+        $this->assertCount(2, \core_message\api::get_conversations($user1->id, 1, 10, null, true));
+
+        // Now, try to get favourited conversations using an invalid offset.
+        $this->assertCount(0, \core_message\api::get_conversations($user1->id, 4, 10, null, true));
+    }
+
+    /**
+     * Tests retrieving favourite conversations when a conversation contains a deleted user.
+     */
+    public function test_get_favourite_conversations_with_deleted_user() {
+        // Create some users.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        // Send some messages back and forth, have some different conversations with different users.
+        $time = 1;
+        $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
+        $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
+        $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
+        $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
+
+        $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
+        $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
+        $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
+        $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
+
+        // Favourite the all conversations for user1.
+        $convoids = [];
+        $convoids[] = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
+        $convoids[] = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
+        $user1context = context_user::instance($user1->id);
+        $service = \core_favourites\service_factory::get_service_for_user_context($user1context);
+        foreach ($convoids as $convoid) {
+            $service->create_favourite('core_message', 'message_conversations', $convoid, $user1context);
+        }
+
+        // Delete the second user.
+        delete_user($user2);
+
+        // Retrieve the conversations.
+        $conversations = \core_message\api::get_conversations($user1->id, 0, 20, null, true);
+
+        // We should only have one conversation because the other user was deleted.
+        $this->assertCount(1, $conversations);
+
+        // Confirm the conversation is from the non-deleted user.
+        $conversation = reset($conversations);
+        $this->assertEquals($user3->id, $conversation->userid);
+    }
+
+    /**
+     * Test confirming that conversations can be marked as favourites.
+     */
+    public function test_set_favourite_conversation() {
+        // Create some users.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        // Send some messages back and forth, have some different conversations with different users.
+        $time = 1;
+        $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
+        $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
+        $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
+        $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
+
+        $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
+        $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
+        $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
+        $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
+
+        // Favourite the first conversation as user 1.
+        $conversationid1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
+        \core_message\api::set_favourite_conversation($conversationid1, $user1->id);
+
+        // Verify we have a single favourite conversation a user 1.
+        $this->assertCount(1, \core_message\api::get_conversations($user1->id, 0, 20, null, true));
+
+        // Verify we have no favourites as user2, despite being a member in that conversation.
+        $this->assertCount(0, \core_message\api::get_conversations($user2->id, 0, 20, null, true));
+
+        // Try to favourite the same conversation again.
+        $this->expectException(\moodle_exception::class);
+        \core_message\api::set_favourite_conversation($conversationid1, $user1->id);
+    }
+
+    /**
+     * Test verifying that trying to mark a non-existent conversation as a favourite, results in an exception.
+     */
+    public function test_set_favourite_conversation_nonexistent_conversation() {
+        // Create some users.
+        $user1 = self::getDataGenerator()->create_user();
+        // Try to favourite a non-existent conversation.
+        $this->expectException(\moodle_exception::class);
+        \core_message\api::set_favourite_conversation(0, $user1->id);
+    }
+
+    /**
+     * Test verifying that a conversation cannot be marked as favourite unless the user is a member of that conversation.
+     */
+    public function test_set_favourite_conversation_non_member() {
+        // Create some users.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        // Send some messages back and forth, have some different conversations with different users.
+        $time = 1;
+        $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
+        $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
+        $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
+        $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
+
+        $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
+        $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
+        $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
+        $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
+
+        // Try to favourite the first conversation as user 3, who is not a member.
+        $conversationid1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
+        $this->expectException(\moodle_exception::class);
+        \core_message\api::set_favourite_conversation($conversationid1, $user3->id);
+    }
+
+    /**
+     * Test confirming that those conversations marked as favourites can be unfavourited.
+     */
+    public function test_unset_favourite_conversation() {
+        // Create some users.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        // Send some messages back and forth, have some different conversations with different users.
+        $time = 1;
+        $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
+        $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
+        $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
+        $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
+
+        $this->send_fake_message($user1, $user3, 'Booyah', 0, $time + 5);
+        $this->send_fake_message($user3, $user1, 'Whaaat?', 0, $time + 6);
+        $this->send_fake_message($user1, $user3, 'Nothing.', 0, $time + 7);
+        $this->send_fake_message($user3, $user1, 'Cool.', 0, $time + 8);
+
+        // Favourite the first conversation as user 1 and the second as user 3.
+        $conversationid1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
+        $conversationid2 = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
+        \core_message\api::set_favourite_conversation($conversationid1, $user1->id);
+        \core_message\api::set_favourite_conversation($conversationid2, $user3->id);
+
+        // Verify we have a single favourite conversation for both user 1 and user 3.
+        $this->assertCount(1, \core_message\api::get_conversations($user1->id, 0, 20, null, true));
+        $this->assertCount(1, \core_message\api::get_conversations($user3->id, 0, 20, null, true));
+
+        // Now unfavourite the conversation as user 1.
+        \core_message\api::unset_favourite_conversation($conversationid1, $user1->id);
+
+        // Verify we have a single favourite conversation user 3 only, and none for user1.
+        $this->assertCount(1, \core_message\api::get_conversations($user3->id, 0, 20, null, true));
+        $this->assertCount(0, \core_message\api::get_conversations($user1->id, 0, 20, null, true));
+
+        // Try to favourite the same conversation again as user 1.
+        $this->expectException(\moodle_exception::class);
+        \core_message\api::unset_favourite_conversation($conversationid1, $user1->id);
+    }
+
+    /**
+     * Test verifying that a valid conversation cannot be unset as a favourite if it's not marked as a favourite.
+     */
+    public function test_unset_favourite_conversation_not_favourite() {
+        // Create some users.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        // Send some messages back and forth, have some different conversations with different users.
+        $time = 1;
+        $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
+        $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
+        $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
+        $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
+
+        // Now try to unfavourite the conversation as user 1.
+        $conversationid1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
+        $this->expectException(\moodle_exception::class);
+        \core_message\api::unset_favourite_conversation($conversationid1, $user1->id);
+    }
+
+    /**
+     * Test verifying that a non-existent conversation cannot be unset as a favourite.
+     */
+    public function test_unset_favourite_conversation_non_existent_conversation() {
+        // Create some users.
+        $user1 = self::getDataGenerator()->create_user();
+
+        // Now try to unfavourite the conversation as user 1.
+        $this->expectException(\moodle_exception::class);
+        \core_message\api::unset_favourite_conversation(0, $user1->id);
+    }
+
     /**
      * Tests retrieving conversations.
      */