diff --git a/group/lib.php b/group/lib.php index c2edce9890d..42e7fbec1cc 100644 --- a/group/lib.php +++ b/group/lib.php @@ -548,12 +548,20 @@ function groups_delete_group($grouporid) { } } + $context = context_course::instance($group->courseid); + // delete group calendar events $DB->delete_records('event', array('groupid'=>$groupid)); //first delete usage in groupings_groups $DB->delete_records('groupings_groups', array('groupid'=>$groupid)); //delete members $DB->delete_records('groups_members', array('groupid'=>$groupid)); + + // Delete any members in a conversation related to this group. + if ($conversation = \core_message\api::get_conversation_by_area('core_group', 'groups', $groupid, $context->id)) { + \core_message\api::delete_all_conversation_data($conversation->id); + } + //group itself last $DB->delete_records('groups', array('id'=>$groupid)); diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index a6b4ae80523..e0965258a58 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2806,5 +2806,42 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2019030800.00); } + if ($oldversion < 2019030800.02) { + // Remove any conversations and their members associated with non-existent groups. + $sql = "SELECT mc.id + FROM {message_conversations} mc + LEFT JOIN {groups} g + ON mc.itemid = g.id + WHERE mc.component = :component + AND mc.itemtype = :itemtype + AND g.id is NULL"; + $conversations = $DB->get_records_sql($sql, ['component' => 'core_group', 'itemtype' => 'groups']); + + if ($conversations) { + $conversationids = array_keys($conversations); + + $DB->delete_records_list('message_conversations', 'id', $conversationids); + $DB->delete_records_list('message_conversation_members', 'conversationid', $conversationids); + $DB->delete_records_list('message_conversation_actions', 'conversationid', $conversationids); + + // Now, go through each conversation and delete any messages and related message actions. + foreach ($conversationids as $conversationid) { + if ($messages = $DB->get_records('messages', ['conversationid' => $conversationid])) { + $messageids = array_keys($messages); + + // Delete the actions. + list($insql, $inparams) = $DB->get_in_or_equal($messageids); + $DB->delete_records_select('message_user_actions', "messageid $insql", $inparams); + + // Delete the messages. + $DB->delete_records('messages', ['conversationid' => $conversationid]); + } + } + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2019030800.02); + } + return true; } diff --git a/message/classes/api.php b/message/classes/api.php index 9376f5b93d3..ebbb9f08143 100644 --- a/message/classes/api.php +++ b/message/classes/api.php @@ -3204,4 +3204,28 @@ class api { ] ); } + + /** + * Completely removes all related data in the DB for a given conversation. + * + * @param int $conversationid The id of the conversation + */ + public static function delete_all_conversation_data(int $conversationid) { + global $DB; + + $DB->delete_records('message_conversations', ['id' => $conversationid]); + $DB->delete_records('message_conversation_members', ['conversationid' => $conversationid]); + $DB->delete_records('message_conversation_actions', ['conversationid' => $conversationid]); + + // Now, go through and delete any messages and related message actions for the conversation. + if ($messages = $DB->get_records('messages', ['conversationid' => $conversationid])) { + $messageids = array_keys($messages); + + list($insql, $inparams) = $DB->get_in_or_equal($messageids); + $DB->delete_records_select('message_user_actions', "messageid $insql", $inparams); + + // Delete the messages now. + $DB->delete_records('messages', ['conversationid' => $conversationid]); + } + } } diff --git a/message/tests/api_test.php b/message/tests/api_test.php index ba5ed1888fb..7c7cb7e312d 100644 --- a/message/tests/api_test.php +++ b/message/tests/api_test.php @@ -6439,6 +6439,103 @@ class core_message_api_testcase extends core_message_messagelib_testcase { $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP]); } + public function test_delete_all_conversation_data() { + global $DB; + + $this->resetAfterTest(); + + $this->setAdminUser(); + + $course1 = $this->getDataGenerator()->create_course(); + $coursecontext1 = context_course::instance($course1->id); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + + $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id, 'enablemessaging' => 1)); + $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id, 'enablemessaging' => 1)); + + // Add users to both groups. + $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id)); + + $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2->id)); + + $groupconversation1 = \core_message\api::get_conversation_by_area( + 'core_group', + 'groups', + $group1->id, + $coursecontext1->id + ); + + $groupconversation2 = \core_message\api::get_conversation_by_area( + 'core_group', + 'groups', + $group2->id, + $coursecontext1->id + ); + + // Send a few messages. + $g1m1 = \core_message\tests\helper::send_fake_message_to_conversation($user1, $groupconversation1->id); + $g1m2 = \core_message\tests\helper::send_fake_message_to_conversation($user2, $groupconversation1->id); + $g1m3 = \core_message\tests\helper::send_fake_message_to_conversation($user1, $groupconversation1->id); + $g1m4 = \core_message\tests\helper::send_fake_message_to_conversation($user2, $groupconversation1->id); + + $g2m1 = \core_message\tests\helper::send_fake_message_to_conversation($user1, $groupconversation2->id); + $g2m2 = \core_message\tests\helper::send_fake_message_to_conversation($user2, $groupconversation2->id); + $g2m3 = \core_message\tests\helper::send_fake_message_to_conversation($user1, $groupconversation2->id); + $g2m4 = \core_message\tests\helper::send_fake_message_to_conversation($user2, $groupconversation2->id); + + // Delete a few messages. + \core_message\api::delete_message($user1->id, $g1m1); + \core_message\api::delete_message($user1->id, $g1m2); + \core_message\api::delete_message($user1->id, $g2m1); + \core_message\api::delete_message($user1->id, $g2m2); + + // Mute the conversations. + \core_message\api::mute_conversation($user1->id, $groupconversation1->id); + \core_message\api::mute_conversation($user1->id, $groupconversation2->id); + + // Now, delete all the data for the group 1 conversation. + \core_message\api::delete_all_conversation_data($groupconversation1->id); + + // Confirm group conversation was deleted just for the group 1 conversation. + $this->assertEquals(0, $DB->count_records('message_conversations', ['id' => $groupconversation1->id])); + $this->assertEquals(1, $DB->count_records('message_conversations', ['id' => $groupconversation2->id])); + + // Confirm conversation members were deleted just for the group 1 conversation. + $this->assertEquals(0, $DB->count_records('message_conversation_members', ['conversationid' => $groupconversation1->id])); + $this->assertEquals(2, $DB->count_records('message_conversation_members', ['conversationid' => $groupconversation2->id])); + + // Confirm message conversation actions were deleted just for the group 1 conversation. + $this->assertEquals(0, $DB->count_records('message_conversation_actions', ['conversationid' => $groupconversation1->id])); + $this->assertEquals(1, $DB->count_records('message_conversation_actions', ['conversationid' => $groupconversation2->id])); + + // Confirm message user actions were deleted just for the group 1 conversation. + $this->assertEquals(0, $DB->count_records('message_user_actions', ['messageid' => $g1m1])); + $this->assertEquals(0, $DB->count_records('message_user_actions', ['messageid' => $g1m2])); + $this->assertEquals(0, $DB->count_records('message_user_actions', ['messageid' => $g1m3])); + $this->assertEquals(0, $DB->count_records('message_user_actions', ['messageid' => $g1m4])); + $this->assertEquals(1, $DB->count_records('message_user_actions', ['messageid' => $g2m1])); + $this->assertEquals(1, $DB->count_records('message_user_actions', ['messageid' => $g2m2])); + $this->assertEquals(0, $DB->count_records('message_user_actions', ['messageid' => $g2m3])); + $this->assertEquals(0, $DB->count_records('message_user_actions', ['messageid' => $g2m4])); + + // Confirm messages were deleted just for the group 1 conversation. + $this->assertEquals(0, $DB->count_records('messages', ['id' => $g1m1])); + $this->assertEquals(0, $DB->count_records('messages', ['id' => $g1m2])); + $this->assertEquals(0, $DB->count_records('messages', ['id' => $g1m3])); + $this->assertEquals(0, $DB->count_records('messages', ['id' => $g1m4])); + $this->assertEquals(1, $DB->count_records('messages', ['id' => $g2m1])); + $this->assertEquals(1, $DB->count_records('messages', ['id' => $g2m2])); + $this->assertEquals(1, $DB->count_records('messages', ['id' => $g2m3])); + $this->assertEquals(1, $DB->count_records('messages', ['id' => $g2m4])); + } + /** * Comparison function for sorting contacts. * diff --git a/version.php b/version.php index 5656a45cc8f..439bb67e16b 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2019030800.01; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2019030800.02; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.