diff --git a/message/classes/privacy/provider.php b/message/classes/privacy/provider.php index b9049e595bd..5c2454451f4 100644 --- a/message/classes/privacy/provider.php +++ b/message/classes/privacy/provider.php @@ -18,6 +18,7 @@ * Privacy Subsystem implementation for core_message. * * @package core_message + * @category privacy * @copyright 2018 Mark Nelson * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -25,8 +26,10 @@ namespace core_message\privacy; use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\approved_userlist; use core_privacy\local\request\contextlist; use core_privacy\local\request\transform; +use core_privacy\local\request\userlist; use core_privacy\local\request\writer; defined('MOODLE_INTERNAL') || die(); @@ -40,7 +43,8 @@ defined('MOODLE_INTERNAL') || die(); class provider implements \core_privacy\local\metadata\provider, \core_privacy\local\request\subsystem\provider, - \core_privacy\local\request\user_preference_provider { + \core_privacy\local\request\user_preference_provider, + \core_privacy\local\request\core_userlist_provider { /** * Return the fields which contain personal data. @@ -175,13 +179,72 @@ class provider implements * @return contextlist the list of contexts containing user info for the user. */ public static function get_contexts_for_userid(int $userid) : contextlist { - // Messages are in the system context. + global $DB; + $contextlist = new contextlist(); - $contextlist->add_system_context(); + + // Messages are in the user context. + // For the sake of performance, there is no need to call add_from_sql for each of the below cases. + // It is enough to add the user's context as soon as we come to the conclusion that the user has some data. + // Also, the order of checking is sorted by the probability of occurrence (just by guess). + // There is no need to check the message_user_actions table, as there needs to be a message in order to be a message action. + // So, checking messages table would suffice. + + $hasdata = false; + $hasdata = $hasdata || $DB->record_exists_select('notifications', 'useridfrom = ? OR useridto = ?', [$userid, $userid]); + $hasdata = $hasdata || $DB->record_exists('message_conversation_members', ['userid' => $userid]); + $hasdata = $hasdata || $DB->record_exists('messages', ['useridfrom' => $userid]); + $hasdata = $hasdata || $DB->record_exists_select('message_contacts', 'userid = ? OR contactid = ?', [$userid, $userid]); + $hasdata = $hasdata || $DB->record_exists_select('message_users_blocked', 'userid = ? OR blockeduserid = ?', + [$userid, $userid]); + $hasdata = $hasdata || $DB->record_exists_select('message_contact_requests', 'userid = ? OR requesteduserid = ?', + [$userid, $userid]); + + if ($hasdata) { + $contextlist->add_user_context($userid); + } return $contextlist; } + /** + * Get the list of users who have data within a context. + * + * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. + */ + public static function get_users_in_context(userlist $userlist) { + global $DB; + + $context = $userlist->get_context(); + + if (!$context instanceof \context_user) { + return; + } + + $userid = $context->instanceid; + + // Messages are in the user context. + // For the sake of performance, there is no need to call add_from_sql for each of the below cases. + // It is enough to add the user's context as soon as we come to the conclusion that the user has some data. + // Also, the order of checking is sorted by the probability of occurrence (just by guess). + // There is no need to check the message_user_actions table, as there needs to be a message in order to be a message action. + // So, checking messages table would suffice. + + $hasdata = false; + $hasdata = $hasdata || $DB->record_exists_select('notifications', 'useridfrom = ? OR useridto = ?', [$userid, $userid]); + $hasdata = $hasdata || $DB->record_exists('message_conversation_members', ['userid' => $userid]); + $hasdata = $hasdata || $DB->record_exists('messages', ['useridfrom' => $userid]); + $hasdata = $hasdata || $DB->record_exists_select('message_contacts', 'userid = ? OR contactid = ?', [$userid, $userid]); + $hasdata = $hasdata || $DB->record_exists_select('message_users_blocked', 'userid = ? OR blockeduserid = ?', + [$userid, $userid]); + $hasdata = $hasdata || $DB->record_exists_select('message_contact_requests', 'userid = ? OR requesteduserid = ?', + [$userid, $userid]); + + if ($hasdata) { + $userlist->add_user($userid); + } + } + /** * Export personal data for the given approved_contextlist. User and context information is contained within the contextlist. * @@ -192,17 +255,17 @@ class provider implements return; } - // Remove non-system contexts. If it ends up empty then early return. - $contexts = array_filter($contextlist->get_contexts(), function($context) { - return $context->contextlevel == CONTEXT_SYSTEM; + $userid = $contextlist->get_user()->id; + + // Remove non-user and invalid contexts. If it ends up empty then early return. + $contexts = array_filter($contextlist->get_contexts(), function($context) use($userid) { + return $context->contextlevel == CONTEXT_USER && $context->instanceid == $userid; }); if (empty($contexts)) { return; } - $userid = $contextlist->get_user()->id; - // Export the contacts. self::export_user_data_contacts($userid); @@ -225,19 +288,9 @@ class provider implements * @param \context $context the context to delete in. */ public static function delete_data_for_all_users_in_context(\context $context) { - global $DB; - - if (!$context instanceof \context_system) { - return; + if ($context instanceof \context_user) { + static::delete_user_data($context->instanceid); } - - $DB->delete_records('messages'); - $DB->delete_records('message_user_actions'); - $DB->delete_records('message_conversation_members'); - $DB->delete_records('message_contacts'); - $DB->delete_records('message_contact_requests'); - $DB->delete_records('message_users_blocked'); - $DB->delete_records('notifications'); } /** @@ -246,22 +299,55 @@ class provider implements * @param approved_contextlist $contextlist a list of contexts approved for deletion. */ public static function delete_data_for_user(approved_contextlist $contextlist) { - global $DB; - if (empty($contextlist->count())) { return; } - // Remove non-system contexts. If it ends up empty then early return. - $contexts = array_filter($contextlist->get_contexts(), function($context) { - return $context->contextlevel == CONTEXT_SYSTEM; + $userid = $contextlist->get_user()->id; + + // Remove non-user and invalid contexts. If it ends up empty then early return. + $contexts = array_filter($contextlist->get_contexts(), function($context) use($userid) { + return $context->contextlevel == CONTEXT_USER && $context->instanceid == $userid; }); if (empty($contexts)) { return; } - $userid = $contextlist->get_user()->id; + static::delete_user_data($userid); + } + + /** + * Delete multiple users within a single context. + * + * @param approved_userlist $userlist The approved context and user information to delete information for. + */ + public static function delete_data_for_users(approved_userlist $userlist) { + $context = $userlist->get_context(); + + if (!$context instanceof \context_user) { + return; + } + + // Remove invalid users. If it ends up empty then early return. + $userids = array_filter($userlist->get_userids(), function($userid) use($context) { + return $context->instanceid == $userid; + }); + + if (empty($userids)) { + return; + } + + static::delete_user_data($context->instanceid); + } + + /** + * Delete all user data for the specified user. + * + * @param int $userid The user id + */ + protected static function delete_user_data(int $userid) { + global $DB; $DB->delete_records('messages', ['useridfrom' => $userid]); $DB->delete_records('message_user_actions', ['userid' => $userid]); @@ -280,7 +366,7 @@ class provider implements protected static function export_user_data_contacts(int $userid) { global $DB; - $context = \context_system::instance(); + $context = \context_user::instance($userid); // Get the user's contacts. if ($contacts = $DB->get_records_select('message_contacts', 'userid = ? OR contactid = ?', [$userid, $userid], 'id ASC')) { @@ -302,7 +388,7 @@ class provider implements protected static function export_user_data_contact_requests(int $userid) { global $DB; - $context = \context_system::instance(); + $context = \context_user::instance($userid); if ($contactrequests = $DB->get_records_select('message_contact_requests', 'userid = ? OR requesteduserid = ?', [$userid, $userid], 'id ASC')) { @@ -334,7 +420,7 @@ class provider implements protected static function export_user_data_blocked_users(int $userid) { global $DB; - $context = \context_system::instance(); + $context = \context_user::instance($userid); if ($blockedusers = $DB->get_records('message_users_blocked', ['userid' => $userid], 'id ASC')) { $blockedusersdata = []; @@ -355,7 +441,7 @@ class provider implements protected static function export_user_data_messages(int $userid) { global $DB; - $context = \context_system::instance(); + $context = \context_user::instance($userid); $sql = "SELECT DISTINCT mcm.conversationid as id FROM {message_conversation_members} mcm @@ -427,7 +513,7 @@ class provider implements protected static function export_user_data_notifications(int $userid) { global $DB; - $context = \context_system::instance(); + $context = \context_user::instance($userid); $notificationdata = []; $select = "useridfrom = ? OR useridto = ?"; diff --git a/message/tests/privacy_provider_test.php b/message/tests/privacy_provider_test.php index 2e15a7bb57a..101adbba016 100644 --- a/message/tests/privacy_provider_test.php +++ b/message/tests/privacy_provider_test.php @@ -192,16 +192,184 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide } /** - * Test for provider::get_contexts_for_userid(). + * Test for provider::get_contexts_for_userid() when there is no message or notification. */ - public function test_get_contexts_for_userid() { + public function test_get_contexts_for_userid_no_data() { $this->resetAfterTest(); $user = $this->getDataGenerator()->create_user(); $contextlist = provider::get_contexts_for_userid($user->id); + $this->assertEmpty($contextlist); + } + + /** + * Test for provider::get_contexts_for_userid() when there is a message between users. + */ + public function test_get_contexts_for_userid_with_message() { + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + // Test nothing is found before message is sent. + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertCount(0, $contextlist); + $contextlist = provider::get_contexts_for_userid($user2->id); + $this->assertCount(0, $contextlist); + + $this->create_message($user1->id, $user2->id, time() - (9 * DAYSECS)); + + // Test for the sender. + $contextlist = provider::get_contexts_for_userid($user1->id); $this->assertCount(1, $contextlist); $contextforuser = $contextlist->current(); - $this->assertEquals(SYSCONTEXTID, $contextforuser->id); + $this->assertEquals( + context_user::instance($user1->id)->id, + $contextforuser->id); + + // Test for the receiver. + $contextlist = provider::get_contexts_for_userid($user2->id); + $this->assertCount(1, $contextlist); + $contextforuser = $contextlist->current(); + $this->assertEquals( + context_user::instance($user2->id)->id, + $contextforuser->id); + } + + /** + * Test for provider::get_contexts_for_userid() when there is a notification between users. + */ + public function test_get_contexts_for_userid_with_notification() { + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + // Test nothing is found before notification is created. + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertCount(0, $contextlist); + $contextlist = provider::get_contexts_for_userid($user2->id); + $this->assertCount(0, $contextlist); + + $this->create_notification($user1->id, $user2->id, time() - (9 * DAYSECS)); + + // Test for the sender. + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertCount(1, $contextlist); + $contextforuser = $contextlist->current(); + $this->assertEquals( + context_user::instance($user1->id)->id, + $contextforuser->id); + + // Test for the receiver. + $contextlist = provider::get_contexts_for_userid($user2->id); + $this->assertCount(1, $contextlist); + $contextforuser = $contextlist->current(); + $this->assertEquals( + context_user::instance($user2->id)->id, + $contextforuser->id); + } + + /** + * Test for provider::get_contexts_for_userid() when a users has a contact. + */ + public function test_get_contexts_for_userid_with_contact() { + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + // Test nothing is found before contact is created. + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertCount(0, $contextlist); + $contextlist = provider::get_contexts_for_userid($user2->id); + $this->assertCount(0, $contextlist); + + \core_message\api::add_contact($user1->id, $user2->id); + + // Test for the user adding the contact. + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertCount(1, $contextlist); + $contextforuser = $contextlist->current(); + $this->assertEquals( + context_user::instance($user1->id)->id, + $contextforuser->id); + + // Test for the user who is the contact. + $contextlist = provider::get_contexts_for_userid($user2->id); + $this->assertCount(1, $contextlist); + $contextforuser = $contextlist->current(); + $this->assertEquals( + context_user::instance($user2->id)->id, + $contextforuser->id); + } + + /** + * Test for provider::get_contexts_for_userid() when a user makes a contact request. + */ + public function test_get_contexts_for_userid_with_contact_request() { + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + // Test nothing is found before request is created. + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertCount(0, $contextlist); + $contextlist = provider::get_contexts_for_userid($user2->id); + $this->assertCount(0, $contextlist); + + \core_message\api::create_contact_request($user1->id, $user2->id); + + // Test for the user requesting the contact. + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertCount(1, $contextlist); + $contextforuser = $contextlist->current(); + $this->assertEquals( + context_user::instance($user1->id)->id, + $contextforuser->id); + + // Test for the user receiving the contact request. + $contextlist = provider::get_contexts_for_userid($user2->id); + $this->assertCount(1, $contextlist); + $contextforuser = $contextlist->current(); + $this->assertEquals( + context_user::instance($user2->id)->id, + $contextforuser->id); + } + + /** + * Test for provider::get_contexts_for_userid() when a user is blocked. + */ + public function test_get_contexts_for_userid_with_blocked_contact() { + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + // Test nothing is found before user is blocked. + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertCount(0, $contextlist); + $contextlist = provider::get_contexts_for_userid($user2->id); + $this->assertCount(0, $contextlist); + + \core_message\api::block_user($user1->id, $user2->id); + + // Test for the blocking user. + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertCount(1, $contextlist); + $contextforuser = $contextlist->current(); + $this->assertEquals( + context_user::instance($user1->id)->id, + $contextforuser->id); + + // Test for the user who is blocked. + $contextlist = provider::get_contexts_for_userid($user2->id); + $this->assertCount(1, $contextlist); + $contextforuser = $contextlist->current(); + $this->assertEquals( + context_user::instance($user2->id)->id, + $contextforuser->id); } /** @@ -220,9 +388,11 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide \core_message\api::add_contact($user1->id, $user3->id); \core_message\api::add_contact($user1->id, $user4->id); - $this->export_context_data_for_user($user1->id, \context_system::instance(), 'core_message'); + $user1context = context_user::instance($user1->id); - $writer = writer::with_context(\context_system::instance()); + $this->export_context_data_for_user($user1->id, $user1context, 'core_message'); + + $writer = writer::with_context($user1context); $contacts = (array) $writer->get_data([get_string('contacts', 'core_message')]); usort($contacts, ['static', 'sort_contacts']); @@ -255,9 +425,11 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide \core_message\api::create_contact_request($user3->id, $user1->id); \core_message\api::create_contact_request($user1->id, $user4->id); - $this->export_context_data_for_user($user1->id, \context_system::instance(), 'core_message'); + $user1context = context_user::instance($user1->id); - $writer = writer::with_context(\context_system::instance()); + $this->export_context_data_for_user($user1->id, $user1context, 'core_message'); + + $writer = writer::with_context($user1context); $contactrequests = (array) $writer->get_data([get_string('contactrequests', 'core_message')]); @@ -292,9 +464,11 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide \core_message\api::block_user($user1->id, $user3->id); \core_message\api::block_user($user1->id, $user4->id); - $this->export_context_data_for_user($user1->id, \context_system::instance(), 'core_message'); + $user1context = context_user::instance($user1->id); - $writer = writer::with_context(\context_system::instance()); + $this->export_context_data_for_user($user1->id, $user1context, 'core_message'); + + $writer = writer::with_context($user1context); $blockedusers = (array) $writer->get_data([get_string('blockedusers', 'core_message')]); @@ -344,9 +518,11 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide \core_message\api::delete_message($user1->id, $m2); \core_message\api::delete_message($user1->id, $m5); - $this->export_context_data_for_user($user1->id, \context_system::instance(), 'core_message'); + $user1context = context_user::instance($user1->id); - $writer = writer::with_context(\context_system::instance()); + $this->export_context_data_for_user($user1->id, $user1context, 'core_message'); + + $writer = writer::with_context($user1context); $this->assertTrue($writer->has_any_data()); @@ -440,9 +616,11 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide $this->create_notification($user2->id, $user3->id, $now + (2 * DAYSECS)); $this->create_notification($user3->id, $user2->id, $now + (1 * DAYSECS)); - $this->export_context_data_for_user($user1->id, \context_system::instance(), 'core_message'); + $user1context = context_user::instance($user1->id); - $writer = writer::with_context(\context_system::instance()); + $this->export_context_data_for_user($user1->id, $user1context, 'core_message'); + + $writer = writer::with_context($user1context); $this->assertTrue($writer->has_any_data()); @@ -465,63 +643,100 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide $user2 = $this->getDataGenerator()->create_user(); $user3 = $this->getDataGenerator()->create_user(); $user4 = $this->getDataGenerator()->create_user(); + $user5 = $this->getDataGenerator()->create_user(); $now = time(); $timeread = $now - DAYSECS; - $systemcontext = \context_system::instance(); + $user1context = context_user::instance($user1->id); // Create contacts. \core_message\api::add_contact($user1->id, $user2->id); + \core_message\api::add_contact($user2->id, $user3->id); // Create contact requests. \core_message\api::create_contact_request($user1->id, $user3->id); + \core_message\api::create_contact_request($user2->id, $user4->id); // Block a user. \core_message\api::block_user($user1->id, $user3->id); + \core_message\api::block_user($user3->id, $user4->id); // Create messages. $m1 = $this->create_message($user1->id, $user2->id, $now + (9 * DAYSECS), true); $m2 = $this->create_message($user2->id, $user1->id, $now + (8 * DAYSECS)); + $m3 = $this->create_message($user2->id, $user3->id, $now + (7 * DAYSECS)); // Create notifications. $n1 = $this->create_notification($user1->id, $user2->id, $now + (9 * DAYSECS), $timeread); $n2 = $this->create_notification($user2->id, $user1->id, $now + (8 * DAYSECS)); + $n3 = $this->create_notification($user2->id, $user3->id, $now + (7 * DAYSECS)); // Delete one of the messages. \core_message\api::delete_message($user1->id, $m2); - // There should be 1 contact. - $this->assertEquals(1, $DB->count_records('message_contacts')); + // There should be 2 contacts. + $this->assertEquals(2, $DB->count_records('message_contacts')); - // There should be 1 contact request. - $this->assertEquals(1, $DB->count_records('message_contact_requests')); + // There should be 2 contact requests. + $this->assertEquals(2, $DB->count_records('message_contact_requests')); - // There should be 1 blocked user. - $this->assertEquals(1, $DB->count_records('message_users_blocked')); + // There should be 2 blocked users. + $this->assertEquals(2, $DB->count_records('message_users_blocked')); - // There should be two messages. - $this->assertEquals(2, $DB->count_records('messages')); + // There should be 3 messages. + $this->assertEquals(3, $DB->count_records('messages')); - // There should be two user actions - one for reading the message, one for deleting. + // There should be 2 user actions - one for reading the message, one for deleting. $this->assertEquals(2, $DB->count_records('message_user_actions')); - // There should be two conversation members. - $this->assertEquals(2, $DB->count_records('message_conversation_members')); + // There should be 4 conversation members. + $this->assertEquals(4, $DB->count_records('message_conversation_members')); - // There should be two notifications + one for the contact request. - $this->assertEquals(3, $DB->count_records('notifications')); + // There should be 3 notifications + 2 for the contact request. + $this->assertEquals(5, $DB->count_records('notifications')); - provider::delete_data_for_all_users_in_context($systemcontext); + provider::delete_data_for_all_users_in_context($user1context); - // Confirm all has been deleted. - $this->assertEquals(0, $DB->count_records('message_contacts')); - $this->assertEquals(0, $DB->count_records('message_contact_requests')); - $this->assertEquals(0, $DB->count_records('message_users_blocked')); - $this->assertEquals(0, $DB->count_records('messages')); - $this->assertEquals(0, $DB->count_records('message_user_actions')); - $this->assertEquals(0, $DB->count_records('message_conversation_members')); - $this->assertEquals(0, $DB->count_records('notifications')); + // Confirm there is only 1 contact left. + $this->assertEquals(1, $DB->count_records('message_contacts')); + // And it is not related to user1. + $this->assertEquals(0, + $DB->count_records_select('message_contacts', 'userid = ? OR contactid = ?', [$user1->id, $user1->id])); + + // Confirm there is only 1 contact request left. + $this->assertEquals(1, $DB->count_records('message_contact_requests')); + // And it is not related to user1. + $this->assertEquals(0, + $DB->count_records_select('message_contact_requests', 'userid = ? OR requesteduserid = ?', + [$user1->id, $user1->id])); + + // Confirm there is only 1 blocked user left. + $this->assertEquals(1, $DB->count_records('message_users_blocked')); + // And it is not related to user1. + $this->assertEquals(0, + $DB->count_records_select('message_users_blocked', 'userid = ? OR blockeduserid = ?', [$user1->id, $user1->id])); + + // Confirm there are only 2 messages left. + $this->assertEquals(2, $DB->count_records('messages')); + // And none of them are from user1. + $this->assertEquals(0, $DB->count_records('messages', ['useridfrom' => $user1->id])); + + // Confirm there is only 1 user action left - the one that is for user2 reading the message. + $this->assertEquals(1, $DB->count_records('message_user_actions')); + // And it is not for user1. + $this->assertEquals(0, $DB->count_records('message_user_actions', ['userid' => $user1->id])); + + // Confirm there are only 3 conversation members left. + $this->assertEquals(3, $DB->count_records('message_conversation_members')); + // And user1 is not in any conversation. + $this->assertEquals(0, $DB->count_records('message_conversation_members', ['userid' => $user1->id])); + + // Confirm there is only 1 notification + 1 for the contact request. + $this->assertEquals(2, $DB->count_records('notifications')); + // And it is not related to user1. + $this->assertEquals(0, + $DB->count_records_select('notifications', 'useridfrom = ? OR useridto = ? ', [$user1->id, $user1->id])); } /** @@ -588,9 +803,9 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide // There should be three notifications + two for the contact requests. $this->assertEquals(5, $DB->count_records('notifications')); - $systemcontext = \context_system::instance(); + $user1context = context_user::instance($user1->id); $contextlist = new \core_privacy\local\request\approved_contextlist($user1, 'core_message', - [$systemcontext->id]); + [$user1context->id]); provider::delete_data_for_user($contextlist); // Confirm the user 2 data still exists. @@ -643,6 +858,327 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide $this->assertEquals($user3->id, $notification->useridto); } + /** + * Test for provider::get_users_in_context() when there is no message or notification. + */ + public function test_get_users_in_context_no_data() { + $this->resetAfterTest(); + + $user = $this->getDataGenerator()->create_user(); + $usercontext = context_user::instance($user->id); + + $userlist = new \core_privacy\local\request\userlist($usercontext, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + + $this->assertEmpty($userlist->get_userids()); + } + + /** + * Test for provider::get_users_in_context() when there is a message between users. + */ + public function test_get_users_in_context_with_message() { + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $user1context = context_user::instance($user1->id); + $user2context = context_user::instance($user2->id); + + // Test nothing is found before message is sent. + $userlist = new \core_privacy\local\request\userlist($user1context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(0, $userlist); + $userlist = new \core_privacy\local\request\userlist($user2context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(0, $userlist); + + $this->create_message($user1->id, $user2->id, time() - (9 * DAYSECS)); + + // Test for the sender. + $userlist = new \core_privacy\local\request\userlist($user1context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(1, $userlist); + $userincontext = $userlist->current(); + $this->assertEquals($user1->id, $userincontext->id); + + // Test for the receiver. + $userlist = new \core_privacy\local\request\userlist($user2context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(1, $userlist); + $userincontext = $userlist->current(); + $this->assertEquals($user2->id, $userincontext->id); + } + + /** + * Test for provider::get_users_in_context() when there is a notification between users. + */ + public function test_get_users_in_context_with_notification() { + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $user1context = context_user::instance($user1->id); + $user2context = context_user::instance($user2->id); + + // Test nothing is found before notification is created. + $userlist = new \core_privacy\local\request\userlist($user1context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(0, $userlist); + $userlist = new \core_privacy\local\request\userlist($user2context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(0, $userlist); + + $this->create_notification($user1->id, $user2->id, time() - (9 * DAYSECS)); + + // Test for the sender. + $userlist = new \core_privacy\local\request\userlist($user1context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(1, $userlist); + $userincontext = $userlist->current(); + $this->assertEquals($user1->id, $userincontext->id); + + // Test for the receiver. + $userlist = new \core_privacy\local\request\userlist($user2context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(1, $userlist); + $userincontext = $userlist->current(); + $this->assertEquals($user2->id, $userincontext->id); + } + + /** + * Test for provider::get_users_in_context() when a users has a contact. + */ + public function test_get_users_in_context_with_contact() { + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $user1context = context_user::instance($user1->id); + $user2context = context_user::instance($user2->id); + + // Test nothing is found before contact is created. + $userlist = new \core_privacy\local\request\userlist($user1context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(0, $userlist); + $userlist = new \core_privacy\local\request\userlist($user2context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(0, $userlist); + + \core_message\api::add_contact($user1->id, $user2->id); + + // Test for the user adding the contact. + $userlist = new \core_privacy\local\request\userlist($user1context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(1, $userlist); + $userincontext = $userlist->current(); + $this->assertEquals($user1->id, $userincontext->id); + + // Test for the user who is the contact. + $userlist = new \core_privacy\local\request\userlist($user2context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(1, $userlist); + $userincontext = $userlist->current(); + $this->assertEquals($user2->id, $userincontext->id); + } + + /** + * Test for provider::get_users_in_context() when a user makes a contact request. + */ + public function test_get_users_in_context_with_contact_request() { + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $user1context = context_user::instance($user1->id); + $user2context = context_user::instance($user2->id); + + // Test nothing is found before request is created. + $userlist = new \core_privacy\local\request\userlist($user1context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(0, $userlist); + $userlist = new \core_privacy\local\request\userlist($user2context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(0, $userlist); + + \core_message\api::create_contact_request($user1->id, $user2->id); + + // Test for the user requesting the contact. + $userlist = new \core_privacy\local\request\userlist($user1context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(1, $userlist); + $userincontext = $userlist->current(); + $this->assertEquals($user1->id, $userincontext->id); + + // Test for the user receiving the contact request. + $userlist = new \core_privacy\local\request\userlist($user2context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(1, $userlist); + $userincontext = $userlist->current(); + $this->assertEquals($user2->id, $userincontext->id); + } + + /** + * Test for provider::get_users_in_context() when a user is blocked. + */ + public function test_get_users_in_context_with_blocked_contact() { + $this->resetAfterTest(); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $user1context = context_user::instance($user1->id); + $user2context = context_user::instance($user2->id); + + // Test nothing is found before user is blocked. + $userlist = new \core_privacy\local\request\userlist($user1context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(0, $userlist); + $userlist = new \core_privacy\local\request\userlist($user2context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(0, $userlist); + + \core_message\api::block_user($user1->id, $user2->id); + + // Test for the blocking user. + $userlist = new \core_privacy\local\request\userlist($user1context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(1, $userlist); + $userincontext = $userlist->current(); + $this->assertEquals($user1->id, $userincontext->id); + + // Test for the user who is blocked. + $userlist = new \core_privacy\local\request\userlist($user2context, 'core_message'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertCount(1, $userlist); + $userincontext = $userlist->current(); + $this->assertEquals($user2->id, $userincontext->id); + } + + /** + * Test for provider::delete_data_for_users(). + */ + public function test_delete_data_for_users() { + global $DB; + + $this->resetAfterTest(); + + // Create users to test with. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $user3 = $this->getDataGenerator()->create_user(); + $user4 = $this->getDataGenerator()->create_user(); + $user5 = $this->getDataGenerator()->create_user(); + $user6 = $this->getDataGenerator()->create_user(); + + $now = time(); + $timeread = $now - DAYSECS; + + // Create contacts. + \core_message\api::add_contact($user1->id, $user2->id); + \core_message\api::add_contact($user2->id, $user3->id); + + // Create contact requests. + \core_message\api::create_contact_request($user1->id, $user3->id); + \core_message\api::create_contact_request($user2->id, $user4->id); + + // Block users. + \core_message\api::block_user($user1->id, $user5->id); + \core_message\api::block_user($user2->id, $user6->id); + + // Create messages. + $m1 = $this->create_message($user1->id, $user2->id, $now + (9 * DAYSECS), $timeread); + $m2 = $this->create_message($user2->id, $user1->id, $now + (8 * DAYSECS)); + + // Create notifications. + $n1 = $this->create_notification($user1->id, $user2->id, $now + (9 * DAYSECS), $timeread); + $n2 = $this->create_notification($user2->id, $user1->id, $now + (8 * DAYSECS)); + $n2 = $this->create_notification($user2->id, $user3->id, $now + (8 * DAYSECS)); + + // Delete one of the messages. + \core_message\api::delete_message($user1->id, $m2); + + // There should be 2 contacts. + $this->assertEquals(2, $DB->count_records('message_contacts')); + + // There should be 1 contact request. + $this->assertEquals(2, $DB->count_records('message_contact_requests')); + + // There should be 1 blocked user. + $this->assertEquals(2, $DB->count_records('message_users_blocked')); + + // There should be two messages. + $this->assertEquals(2, $DB->count_records('messages')); + + // There should be two user actions - one for reading the message, one for deleting. + $this->assertEquals(2, $DB->count_records('message_user_actions')); + + // There should be two conversation members. + $this->assertEquals(2, $DB->count_records('message_conversation_members')); + + // There should be three notifications + two for the contact requests. + $this->assertEquals(5, $DB->count_records('notifications')); + + $user1context = context_user::instance($user1->id); + $approveduserlist = new \core_privacy\local\request\approved_userlist($user1context, 'core_message', + [$user1->id, $user2->id]); + provider::delete_data_for_users($approveduserlist); + + // Only user1's data should be deleted. User2 should be skipped as user2 is an invalid user for user1context. + + // Confirm the user 2 data still exists. + $contacts = $DB->get_records('message_contacts'); + $contactrequests = $DB->get_records('message_contact_requests'); + $blockedusers = $DB->get_records('message_users_blocked'); + $messages = $DB->get_records('messages'); + $muas = $DB->get_records('message_user_actions'); + $mcms = $DB->get_records('message_conversation_members'); + $notifications = $DB->get_records('notifications'); + + $this->assertCount(1, $contacts); + $contact = reset($contacts); + $this->assertEquals($user2->id, $contact->userid); + $this->assertEquals($user3->id, $contact->contactid); + + $this->assertCount(1, $contactrequests); + $contactrequest = reset($contactrequests); + $this->assertEquals($user2->id, $contactrequest->userid); + $this->assertEquals($user4->id, $contactrequest->requesteduserid); + + $this->assertCount(1, $blockedusers); + $blockeduser = reset($blockedusers); + $this->assertEquals($user2->id, $blockeduser->userid); + $this->assertEquals($user6->id, $blockeduser->blockeduserid); + + $this->assertCount(1, $messages); + $message = reset($messages); + $this->assertEquals($m2, $message->id); + + $this->assertCount(1, $muas); + $mua = reset($muas); + $this->assertEquals($user2->id, $mua->userid); + $this->assertEquals($m1, $mua->messageid); + $this->assertEquals(\core_message\api::MESSAGE_ACTION_READ, $mua->action); + + $this->assertCount(1, $mcms); + $mcm = reset($mcms); + $this->assertEquals($user2->id, $mcm->userid); + + $this->assertCount(2, $notifications); + ksort($notifications); + + $notification = array_shift($notifications); + $this->assertEquals($user2->id, $notification->useridfrom); + $this->assertEquals($user4->id, $notification->useridto); + + $notification = array_shift($notifications); + $this->assertEquals($user2->id, $notification->useridfrom); + $this->assertEquals($user3->id, $notification->useridto); + } + /** * Creates a message to be used for testing. *