MDL-65132 core_message: Added API to delete message for all users
authorcescobedo <carlos.escobedo@gmail.com>
Thu, 2 May 2019 13:57:12 +0000 (15:57 +0200)
committercescobedo <carlos.escobedo@gmail.com>
Mon, 6 May 2019 07:38:54 +0000 (09:38 +0200)
message/classes/api.php
message/tests/api_test.php

index 2b3bcee..4ba966e 100644 (file)
@@ -802,8 +802,11 @@ class api {
             // If not set, the context is always context_user.
             if (is_null($conversation->contextid)) {
                 $convcontext = \context_user::instance($userid);
+                // We'll need to check the capability to delete messages for all users in context system when contextid is null.
+                $contexttodeletemessageforall = \context_system::instance();
             } else {
                 $convcontext = \context::instance_by_id($conversation->contextid);
+                $contexttodeletemessageforall = $convcontext;
             }
             $conv->name = format_string($conversation->conversationname, true, ['context' => $convcontext]);
 
@@ -819,6 +822,8 @@ class api {
 
             // Add the most recent message information.
             $conv->messages = [];
+            // Add if the user has to allow delete messages for all users in the conversation.
+            $conv->candeletemessagesforallusers = has_capability('moodle/site:deleteanymessage',  $contexttodeletemessageforall);
             if ($conversation->smallmessage) {
                 $msg = new \stdClass();
                 $msg->id = $conversation->messageid;
@@ -983,6 +988,9 @@ class api {
             $ismuted = true;
         }
 
+        // Get the context of the conversation. This will be used to check if the user can delete all messages in the conversation.
+        $deleteallcontext = empty($conversation->contextid) ? $systemcontext : \context::instance_by_id($conversation->contextid);
+
         return (object) [
             'id' => $conversation->id,
             'name' => $conversation->name,
@@ -995,7 +1003,8 @@ class api {
             'unreadcount' => $unreadcount,
             'ismuted' => $ismuted,
             'members' => $members,
-            'messages' => $messages['messages']
+            'messages' => $messages['messages'],
+            'candeletemessagesforallusers' => has_capability('moodle/site:deleteanymessage', $deleteallcontext)
         ];
     }
 
@@ -3324,4 +3333,60 @@ class api {
             $DB->delete_records('messages', ['conversationid' => $conversationid]);
         }
     }
+
+    /**
+     * Checks if a user can delete a message for all users.
+     *
+     * @param int $userid the user id of who we want to delete the message for all users
+     * @param int $messageid The message id
+     * @return bool Returns true if a user can delete the message for all users, false otherwise.
+     */
+    public static function can_delete_message_for_all_users(int $userid, int $messageid) : bool {
+        global $DB;
+
+        $sql = "SELECT mc.id, mc.contextid
+                  FROM {message_conversations} mc
+            INNER JOIN {messages} m
+                    ON mc.id = m.conversationid
+                 WHERE m.id = :messageid";
+        $conversation = $DB->get_record_sql($sql, ['messageid' => $messageid]);
+
+        if (!empty($conversation->contextid)) {
+            return has_capability('moodle/site:deleteanymessage',
+                \context::instance_by_id($conversation->contextid), $userid);
+        }
+
+        return has_capability('moodle/site:deleteanymessage', \context_system::instance(), $userid);
+    }
+    /**
+     * Delete a message for all users.
+     *
+     * This function does not verify any permissions.
+     *
+     * @param int $messageid The message id
+     * @return void
+     */
+    public static function delete_message_for_all_users(int $messageid) {
+        global $DB, $USER;
+
+        if (!$DB->record_exists('messages', ['id' => $messageid])) {
+            return false;
+        }
+
+        // Get all members in the conversation where the message belongs.
+        $membersql = "SELECT mcm.id, mcm.userid
+                        FROM {message_conversation_members} mcm
+                  INNER JOIN {messages} m
+                          ON mcm.conversationid = m.conversationid
+                       WHERE m.id = :messageid";
+        $params = [
+            'messageid' => $messageid
+        ];
+        $members = $DB->get_records_sql($membersql, $params);
+        if ($members) {
+            foreach ($members as $member) {
+                self::delete_message($member->userid, $messageid);
+            }
+        }
+    }
 }
index a94c6ca..8f9a645 100644 (file)
@@ -6897,6 +6897,177 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->assertEquals(1, $DB->count_records('messages', ['id' => $g2m4]));
     }
 
+    /**
+     * Tests the user can delete message for all users as a teacher.
+     */
+    public function test_can_delete_message_for_all_users_teacher() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        // Create fake data to test it.
+        list($teacher, $student1, $student2, $convgroup, $convindividual) = $this->create_delete_message_test_data();
+
+        // Allow Teacher can delete messages for all.
+        $editingteacher = $DB->get_record('role', ['shortname' => 'editingteacher']);
+        assign_capability('moodle/site:deleteanymessage', CAP_ALLOW, $editingteacher->id, context_system::instance());
+
+        // Set as the first user.
+        $this->setUser($teacher);
+
+        // Send a message to private conversation and in a group conversation.
+        $messageidind = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convindividual->id);
+        $messageidgrp = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convgroup->id);
+
+        // Teacher cannot delete message for everyone in a private conversation.
+        $this->assertFalse(\core_message\api::can_delete_message_for_all_users($teacher->id, $messageidind));
+
+        // Teacher can delete message for everyone in a group conversation.
+        $this->assertTrue(\core_message\api::can_delete_message_for_all_users($teacher->id, $messageidgrp));
+    }
+
+    /**
+     * Tests the user can delete message for all users as a student.
+     */
+    public function test_can_delete_message_for_all_users_student() {
+        $this->resetAfterTest(true);
+
+        // Create fake data to test it.
+        list($teacher, $student1, $student2, $convgroup, $convindividual) = $this->create_delete_message_test_data();
+
+        // Set as the first user.
+        $this->setUser($student1);
+
+        // Send a message to private conversation and in a group conversation.
+        $messageidind = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convindividual->id);
+        $messageidgrp = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convgroup->id);
+
+        // Student1 cannot delete message for everyone in a private conversation.
+        $this->assertFalse(\core_message\api::can_delete_message_for_all_users($student1->id, $messageidind));
+
+        // Student1 cannot delete message for everyone in a group conversation.
+        $this->assertFalse(\core_message\api::can_delete_message_for_all_users($student1->id, $messageidgrp));
+    }
+
+    /**
+     * Tests tdelete message for all users in group conversation.
+     */
+    public function test_delete_message_for_all_users_group_conversation() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        // Create fake data to test it.
+        list($teacher, $student1, $student2, $convgroup, $convindividual) = $this->create_delete_message_test_data();
+
+        // Send 3 messages to a group conversation.
+        $mgid1 = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convgroup->id);
+        $mgid2 = \core_message\tests\helper::send_fake_message_to_conversation($student1, $convgroup->id);
+        $mgid3 = \core_message\tests\helper::send_fake_message_to_conversation($student2, $convgroup->id);
+
+        // Delete message 1 for all users.
+        \core_message\api::delete_message_for_all_users($mgid1);
+
+        // Get the messages to check if the message 1 was deleted for teacher.
+        $convmessages1 = \core_message\api::get_conversation_messages($teacher->id, $convgroup->id);
+        // Only has to remains 2 messages.
+        $this->assertCount(2, $convmessages1['messages']);
+        // Check if no one of the two messages is message 1.
+        foreach ($convmessages1['messages'] as $message) {
+            $this->assertNotEquals($mgid1, $message->id);
+        }
+
+        // Get the messages to check if the message 1 was deleted for student1.
+        $convmessages2 = \core_message\api::get_conversation_messages($student1->id, $convgroup->id);
+        // Only has to remains 2 messages.
+        $this->assertCount(2, $convmessages2['messages']);
+        // Check if no one of the two messages is message 1.
+        foreach ($convmessages2['messages'] as $message) {
+            $this->assertNotEquals($mgid1, $message->id);
+        }
+
+        // Get the messages to check if the message 1 was deleted for student2.
+        $convmessages3 = \core_message\api::get_conversation_messages($student2->id, $convgroup->id);
+        // Only has to remains 2 messages.
+        $this->assertCount(2, $convmessages3['messages']);
+        // Check if no one of the two messages is message 1.
+        foreach ($convmessages3['messages'] as $message) {
+            $this->assertNotEquals($mgid1, $message->id);
+        }
+    }
+
+    /**
+     * Tests delete message for all users in private conversation.
+     */
+    public function test_delete_message_for_all_users_individual_conversation() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        // Create fake data to test it.
+        list($teacher, $student1, $student2, $convgroup, $convindividual) = $this->create_delete_message_test_data();
+
+        // Send 2 messages in a individual conversation.
+        $mid1 = \core_message\tests\helper::send_fake_message_to_conversation($teacher, $convindividual->id);
+        $mid2 = \core_message\tests\helper::send_fake_message_to_conversation($student1, $convindividual->id);
+
+        // Delete the first message for all users.
+        \core_message\api::delete_message_for_all_users($mid1);
+
+        // Get the messages to check if the message 1 was deleted for teacher.
+        $convmessages1 = \core_message\api::get_conversation_messages($teacher->id, $convindividual->id);
+        // Only has to remains 1 messages for teacher.
+        $this->assertCount(1, $convmessages1['messages']);
+        // Check the one messages remains not is the first message.
+        $this->assertNotEquals($mid1, $convmessages1['messages'][0]->id);
+
+        // Get the messages to check if the message 1 was deleted for student1.
+        $convmessages2 = \core_message\api::get_conversation_messages($student1->id, $convindividual->id);
+        // Only has to remains 1 messages for student1.
+        $this->assertCount(1, $convmessages2['messages']);
+        // Check the one messages remains not is the first message.
+        $this->assertNotEquals($mid1, $convmessages2['messages'][0]->id);
+    }
+
+    /**
+     * Helper to seed the database with initial state with data.
+     */
+    protected function create_delete_message_test_data() {
+        // Create some users.
+        $teacher = self::getDataGenerator()->create_user();
+        $student1 = self::getDataGenerator()->create_user();
+        $student2 = self::getDataGenerator()->create_user();
+
+        // Create a course and enrol the users.
+        $course = $this->getDataGenerator()->create_course();
+        $coursecontext = context_course::instance($course->id);
+        $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
+        $this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student');
+        $this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student');
+
+        // Create a group and added the users into.
+        $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
+        groups_add_member($group1->id, $teacher->id);
+        groups_add_member($group1->id, $student1->id);
+        groups_add_member($group1->id, $student2->id);
+
+        // Create a group conversation linked with the course.
+        $convgroup = \core_message\api::create_conversation(
+            \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
+            [$teacher->id, $student1->id, $student2->id],
+            'Group test delete for everyone', \core_message\api::MESSAGE_CONVERSATION_ENABLED,
+            'core_group',
+            'groups',
+            $group1->id,
+            context_course::instance($course->id)->id
+        );
+
+        // Create and individual conversation.
+        $convindividual = \core_message\api::create_conversation(
+            \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
+            [$teacher->id, $student1->id]
+        );
+
+        return [$teacher, $student1, $student2, $convgroup, $convindividual];
+    }
+
     /**
      * Comparison function for sorting contacts.
      *