diff --git a/group/classes/privacy/provider.php b/group/classes/privacy/provider.php new file mode 100644 index 00000000000..8c93dc62cd5 --- /dev/null +++ b/group/classes/privacy/provider.php @@ -0,0 +1,242 @@ +. + +/** + * Privacy Subsystem implementation for core_group. + * + * @package core_group + * @category privacy + * @copyright 2018 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_group\privacy; + +defined('MOODLE_INTERNAL') || die(); + +use core_privacy\local\metadata\collection; +use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\contextlist; +use core_privacy\local\request\transform; + +/** + * Privacy Subsystem implementation for core_group. + * + * @copyright 2018 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements + // Groups store user data. + \core_privacy\local\metadata\provider, + + // The group subsystem contains user's group memberships. + \core_privacy\local\request\subsystem\provider, + + // The group subsystem can provide information to other plugins. + \core_privacy\local\request\subsystem\plugin_provider { + + /** + * Returns meta data about this system. + * + * @param collection $collection The initialised collection to add items to. + * @return collection A listing of user data stored through this system. + */ + public static function get_metadata(collection $collection) : collection { + $collection->add_database_table('groups_members', [ + 'groupid' => 'privacy:metadata:groups:groupid', + 'userid' => 'privacy:metadata:groups:userid', + 'timeadded' => 'privacy:metadata:groups:timeadded', + ], 'privacy:metadata:groups'); + + return $collection; + } + + /** + * Writes user data to the writer for the user to download. + * + * @param \context $context The context to export data for. + * @param string $component The component that is calling this function. Empty string means no component. + * @param array $subcontext The sub-context in which to export this data. + * @param int $itemid Optional itemid associated with component. + */ + public static function export_groups(\context $context, string $component, array $subcontext = [], int $itemid = 0) { + global $DB, $USER; + + if (!$context instanceof \context_course) { + return; + } + + $subcontext[] = get_string('groups', 'core_group'); + + $sql = "SELECT gm.id, gm.timeadded, gm.userid, g.name + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = :courseid + AND gm.component = :component + AND gm.userid = :userid"; + $params = [ + 'courseid' => $context->instanceid, + 'component' => $component, + 'userid' => $USER->id + ]; + + if ($itemid) { + $sql .= ' AND gm.itemid = :itemid'; + $params['itemid'] = $itemid; + } + + $groups = $DB->get_records_sql($sql, $params); + + $groups = array_map(function($group) { + return (object) [ + 'name' => format_string($group->name), + 'timeadded' => transform::datetime($group->timeadded), + ]; + }, $groups); + + if (!empty($groups)) { + \core_privacy\local\request\writer::with_context($context) + ->export_data($subcontext, (object) [ + 'groups' => $groups, + ]); + } + } + + /** + * Deletes all group memberships for a specified context and component. + * + * @param \context $context Details about which context to delete group memberships for. + * @param string $component Component to delete. Empty string means no component (manual group memberships). + * @param int $itemid Optional itemid associated with component. + */ + public static function delete_groups_for_all_users(\context $context, string $component, int $itemid = 0) { + global $DB; + + if (!$context instanceof \context_course) { + return; + } + + if (!$DB->record_exists('groups', ['courseid' => $context->instanceid])) { + return; + } + + $select = "component = :component AND groupid IN (SELECT g.id FROM {groups} g WHERE courseid = :courseid)"; + $params = ['component' => $component, 'courseid' => $context->instanceid]; + + if ($itemid) { + $select .= ' AND itemid = :itemid'; + $params['itemid'] = $itemid; + } + + $DB->delete_records_select('groups_members', $select, $params); + } + + /** + * Deletes all records for a user from a list of approved contexts. + * + * @param approved_contextlist $contextlist Contains the user ID and a list of contexts to be deleted from. + * @param string $component Component to delete from. Empty string means no component (manual memberships). + * @param int $itemid Optional itemid associated with component. + */ + public static function delete_groups_for_user(approved_contextlist $contextlist, string $component, int $itemid = 0) { + global $DB; + + $userid = $contextlist->get_user()->id; + + $contextids = $contextlist->get_contextids(); + + if (!$contextids) { + return; + } + + list($contextsql, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED); + $contextparams += ['contextcourse' => CONTEXT_COURSE]; + $groupselect = "SELECT g.id + FROM {groups} g + JOIN {context} ctx ON g.courseid = ctx.instanceid AND ctx.contextlevel = :contextcourse + WHERE ctx.id $contextsql"; + + if (!$DB->record_exists_sql($groupselect, $contextparams)) { + return; + } + + $select = "userid = :userid AND component = :component AND groupid IN ({$groupselect})"; + $params = ['userid' => $userid, 'component' => $component] + $contextparams; + + if ($itemid) { + $select .= ' AND itemid = :itemid'; + $params['itemid'] = $itemid; + } + + $DB->delete_records_select('groups_members', $select, $params); + } + + /** + * Get the list of contexts that contain user information for the specified user. + * + * @param int $userid The user to search. + * @return contextlist The contextlist containing the list of contexts used in this plugin. + */ + public static function get_contexts_for_userid(int $userid) : contextlist { + $contextlist = new contextlist(); + + $sql = "SELECT ctx.id + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + JOIN {context} ctx ON g.courseid = ctx.instanceid AND ctx.contextlevel = :contextcourse + WHERE gm.userid = :userid"; + + $params = [ + 'contextcourse' => CONTEXT_COURSE, + 'userid' => $userid + ]; + + $contextlist->add_from_sql($sql, $params); + + return $contextlist; + } + + /** + * Export all user data for the specified user, in the specified contexts. + * + * @param approved_contextlist $contextlist The approved contexts to export information for. + */ + public static function export_user_data(approved_contextlist $contextlist) { + $contexts = $contextlist->get_contexts(); + + foreach ($contexts as $context) { + static::export_groups($context, ''); + } + } + + /** + * Delete all data for all users in the specified context. + * + * @param context $context The specific context to delete data for. + */ + public static function delete_data_for_all_users_in_context(\context $context) { + static::delete_groups_for_all_users($context, ''); + } + + /** + * Delete all user data for the specified user, in the specified contexts. + * + * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. + */ + public static function delete_data_for_user(approved_contextlist $contextlist) { + static::delete_groups_for_user($contextlist, ''); + } +} diff --git a/group/tests/privacy_provider_test.php b/group/tests/privacy_provider_test.php new file mode 100644 index 00000000000..207b562df4b --- /dev/null +++ b/group/tests/privacy_provider_test.php @@ -0,0 +1,733 @@ +. + +/** + * Privacy provider tests. + * + * @package core_group + * @category test + * @copyright 2018 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +use core_privacy\tests\provider_testcase; +use core_privacy\local\metadata\collection; +use core_group\privacy\provider; +use core_privacy\local\request\writer; + +/** + * Class core_group_privacy_provider_testcase. + * + * @copyright 2018 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_group_privacy_provider_testcase extends provider_testcase { + + /** + * Test for provider::get_metadata(). + */ + public function test_get_metadata() { + $collection = new collection('core_group'); + $newcollection = provider::get_metadata($collection); + $itemcollection = $newcollection->get_collection(); + $this->assertCount(1, $itemcollection); + + $table = reset($itemcollection); + + $this->assertEquals('groups_members', $table->get_name()); + $this->assertEquals('privacy:metadata:groups', $table->get_summary()); + + $privacyfields = $table->get_privacy_fields(); + $this->assertArrayHasKey('groupid', $privacyfields); + $this->assertArrayHasKey('userid', $privacyfields); + $this->assertArrayHasKey('timeadded', $privacyfields); + } + + /** + * Test for provider::export_groups() to export manual group memberships. + */ + public function test_export_groups() { + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group3 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group4 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $this->getDataGenerator()->enrol_user($user1->id, $course->id); + $this->getDataGenerator()->enrol_user($user2->id, $course->id); + + // Add user1 to group1 and group2. + $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user1->id)); + + // Add user2 to group2 and group3. + $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user2->id)); + + $context = context_course::instance($course->id); + + // Retrieve groups for user1. + $this->setUser($user1); + $writer = writer::with_context($context); + provider::export_groups($context, ''); + + $data = $writer->get_data([get_string('groups', 'core_group')]); + $exportedgroups = $data->groups; + + // User1 belongs to group1 and group2. + $this->assertEquals( + [$group1->name, $group2->name], + array_column($exportedgroups, 'name'), + '', 0.0, 10, true); + } + + /** + * Test for provider::export_groups() to export group memberships of a component. + */ + public function test_export_groups_for_component() { + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group3 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group4 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group5 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $this->getDataGenerator()->enrol_user($user1->id, $course->id, null, 'self'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, null, 'self'); + + // Add user1 to group1 (via enrol_self) and group2 and group3. + $this->getDataGenerator()->create_group_member( + array('groupid' => $group1->id, 'userid' => $user1->id, 'component' => 'enrol_self')); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user1->id)); + + // Add user2 to group3 (via enrol_self) and group4. + $this->getDataGenerator()->create_group_member( + array('groupid' => $group3->id, 'userid' => $user2->id, 'component' => 'enrol_self')); + $this->getDataGenerator()->create_group_member(array('groupid' => $group4->id, 'userid' => $user2->id)); + + $context = context_course::instance($course->id); + + // Retrieve groups for user1. + $this->setUser($user1); + $writer = writer::with_context($context); + provider::export_groups($context, 'enrol_self'); + + $data = $writer->get_data([get_string('groups', 'core_group')]); + $exportedgroups = $data->groups; + + // User1 only belongs to group1 via enrol_self. + $this->assertCount(1, $exportedgroups); + $exportedgroup = reset($exportedgroups); + $this->assertEquals($group1->name, $exportedgroup->name); + } + + /** + * Test for provider::delete_groups_for_all_users() to delete manual group memberships. + */ + public function test_delete_groups_for_all_users() { + global $DB; + + $this->resetAfterTest(); + + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + + $group1a = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group1b = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group2a = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group2b = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course2->id); + + $this->getDataGenerator()->create_group_member(array('groupid' => $group1a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group1b->id, 'userid' => $user2->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2b->id, 'userid' => $user2->id)); + + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + + $coursecontext1 = context_course::instance($course1->id); + provider::delete_groups_for_all_users($coursecontext1, ''); + + $this->assertEquals( + 0, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + } + + /** + * Test for provider::delete_groups_for_all_users() to delete group memberships of a component. + */ + public function test_delete_groups_for_all_users_for_component() { + global $DB; + + $this->resetAfterTest(); + + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + + $group1a = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group1b = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group2a = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group2b = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course1->id, null, 'self'); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id, null, 'self'); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id, null, 'self'); + $this->getDataGenerator()->enrol_user($user2->id, $course2->id, null, 'self'); + + $this->getDataGenerator()->create_group_member( + array('groupid' => $group1a->id, 'userid' => $user1->id, 'component' => 'enrol_self')); + $this->getDataGenerator()->create_group_member(array('groupid' => $group1b->id, 'userid' => $user2->id)); + $this->getDataGenerator()->create_group_member( + array('groupid' => $group2a->id, 'userid' => $user1->id, 'component' => 'enrol_self')); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2b->id, 'userid' => $user2->id)); + + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + + $coursecontext1 = context_course::instance($course1->id); + provider::delete_groups_for_all_users($coursecontext1, 'enrol_self'); + + $this->assertEquals( + 1, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + } + + /** + * Test for provider::delete_groups_for_user() to delete manual group memberships. + */ + public function test_delete_groups_for_user() { + global $DB; + + $this->resetAfterTest(); + + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $course3 = $this->getDataGenerator()->create_course(); + + $group1a = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group1b = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group2a = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group2b = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group3a = $this->getDataGenerator()->create_group(array('courseid' => $course3->id)); + $group3b = $this->getDataGenerator()->create_group(array('courseid' => $course3->id)); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id); + $this->getDataGenerator()->enrol_user($user1->id, $course3->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course2->id); + $this->getDataGenerator()->enrol_user($user2->id, $course3->id); + + $this->getDataGenerator()->create_group_member(array('groupid' => $group1a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group1b->id, 'userid' => $user2->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2b->id, 'userid' => $user2->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group3a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group3b->id, 'userid' => $user2->id)); + + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + $this->assertEquals( + 3, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE gm.userid = ?", [$user1->id]) + ); + + $this->setUser($user1); + $coursecontext1 = context_course::instance($course1->id); + $coursecontext2 = context_course::instance($course2->id); + $approvedcontextlist = new \core_privacy\tests\request\approved_contextlist($user1, 'core_group', + [$coursecontext1->id, $coursecontext2->id]); + provider::delete_groups_for_user($approvedcontextlist, ''); + + $this->assertEquals( + 1, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 1, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course3->id]) + ); + $this->assertEquals( + 1, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE gm.userid = ?", [$user1->id]) + ); + } + + /** + * Test for provider::delete_groups_for_user() to delete group memberships of a component. + */ + public function test_delete_groups_for_user_for_component() { + global $DB; + + $this->resetAfterTest(); + + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $course3 = $this->getDataGenerator()->create_course(); + + $group1a = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group1b = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group2a = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group2b = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group3a = $this->getDataGenerator()->create_group(array('courseid' => $course3->id)); + $group3b = $this->getDataGenerator()->create_group(array('courseid' => $course3->id)); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course1->id, null, 'self'); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id, null, 'self'); + $this->getDataGenerator()->enrol_user($user1->id, $course3->id, null, 'self'); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id, null, 'self'); + $this->getDataGenerator()->enrol_user($user2->id, $course2->id, null, 'self'); + $this->getDataGenerator()->enrol_user($user2->id, $course3->id, null, 'self'); + + $this->getDataGenerator()->create_group_member( + array('groupid' => $group1a->id, 'userid' => $user1->id, 'component' => 'enrol_self')); + $this->getDataGenerator()->create_group_member( + array('groupid' => $group1b->id, 'userid' => $user2->id, 'component' => 'enrol_self')); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2b->id, 'userid' => $user2->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group3a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group3b->id, 'userid' => $user2->id)); + + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + $this->assertEquals( + 3, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE gm.userid = ?", [$user1->id]) + ); + + $this->setUser($user1); + $coursecontext1 = context_course::instance($course1->id); + $coursecontext2 = context_course::instance($course2->id); + $approvedcontextlist = new \core_privacy\tests\request\approved_contextlist($user1, 'core_group', + [$coursecontext1->id, $coursecontext2->id]); + provider::delete_groups_for_user($approvedcontextlist, 'enrol_self'); + + $this->assertEquals( + 1, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course3->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE gm.userid = ?", [$user1->id]) + ); + } + + /** + * Test for provider::get_contexts_for_userid(). + */ + public function test_get_contexts_for_userid() { + $this->resetAfterTest(); + + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $course3 = $this->getDataGenerator()->create_course(); + + $group1a = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group1b = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group2a = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group2b = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group3a = $this->getDataGenerator()->create_group(array('courseid' => $course3->id)); + $group3b = $this->getDataGenerator()->create_group(array('courseid' => $course3->id)); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id); + $this->getDataGenerator()->enrol_user($user1->id, $course3->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course2->id); + $this->getDataGenerator()->enrol_user($user2->id, $course3->id); + + $this->getDataGenerator()->create_group_member(array('userid' => $user1->id, 'groupid' => $group1a->id)); + $this->getDataGenerator()->create_group_member(array('userid' => $user1->id, 'groupid' => $group2a->id)); + $this->getDataGenerator()->create_group_member(array('userid' => $user2->id, 'groupid' => $group1b->id)); + $this->getDataGenerator()->create_group_member(array('userid' => $user2->id, 'groupid' => $group2b->id)); + $this->getDataGenerator()->create_group_member(array('userid' => $user2->id, 'groupid' => $group3b->id)); + + $coursecontext1 = context_course::instance($course1->id); + $coursecontext2 = context_course::instance($course2->id); + + // User1 is member of some groups in course1 and course2. + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertCount(2, $contextlist); + $this->assertEquals( + [$coursecontext1->id, $coursecontext2->id], + $contextlist->get_contextids(), + '', 0.0, 10, true); + } + + /** + * Test for provider::export_user_data(). + */ + public function test_export_user_data() { + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group3 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group4 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $this->getDataGenerator()->enrol_user($user1->id, $course->id); + $this->getDataGenerator()->enrol_user($user2->id, $course->id); + + // Add user1 to group1 and group2. + $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user1->id)); + + // Add user2 to group2 and group3. + $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user2->id)); + + $context = context_course::instance($course->id); + + $this->setUser($user1); + + // Export all of the data for the context. + $this->export_context_data_for_user($user1->id, $context, 'core_group'); + + $writer = writer::with_context($context); + $this->assertTrue($writer->has_any_data()); + + $data = $writer->get_data([get_string('groups', 'core_group')]); + $exportedgroups = $data->groups; + + // User1 belongs to group1 and group2. + $this->assertEquals( + [$group1->name, $group2->name], + array_column($exportedgroups, 'name'), + '', 0.0, 10, true); + } + + /** + * Test for provider::delete_data_for_all_users_in_context(). + */ + public function test_delete_data_for_all_users_in_context() { + global $DB; + + $this->resetAfterTest(); + + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + + $group1a = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group1b = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group2a = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group2b = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course2->id); + + $this->getDataGenerator()->create_group_member(array('groupid' => $group1a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group1b->id, 'userid' => $user2->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2b->id, 'userid' => $user2->id)); + + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + + $coursecontext1 = context_course::instance($course1->id); + provider::delete_data_for_all_users_in_context($coursecontext1); + + $this->assertEquals( + 0, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + } + + /** + * Test for provider::delete_data_for_user(). + */ + public function test_delete_data_for_user() { + global $DB; + + $this->resetAfterTest(); + + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $course3 = $this->getDataGenerator()->create_course(); + + $group1a = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group1b = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $group2a = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group2b = $this->getDataGenerator()->create_group(array('courseid' => $course2->id)); + $group3a = $this->getDataGenerator()->create_group(array('courseid' => $course3->id)); + $group3b = $this->getDataGenerator()->create_group(array('courseid' => $course3->id)); + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id); + $this->getDataGenerator()->enrol_user($user1->id, $course3->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course2->id); + $this->getDataGenerator()->enrol_user($user2->id, $course3->id); + + $this->getDataGenerator()->create_group_member(array('groupid' => $group1a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group1b->id, 'userid' => $user2->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group2b->id, 'userid' => $user2->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group3a->id, 'userid' => $user1->id)); + $this->getDataGenerator()->create_group_member(array('groupid' => $group3b->id, 'userid' => $user2->id)); + + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + $this->assertEquals( + 3, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE gm.userid = ?", [$user1->id]) + ); + + $this->setUser($user1); + $coursecontext1 = context_course::instance($course1->id); + $coursecontext2 = context_course::instance($course2->id); + $approvedcontextlist = new \core_privacy\tests\request\approved_contextlist($user1, 'core_group', + [$coursecontext1->id, $coursecontext2->id]); + provider::delete_data_for_user($approvedcontextlist); + + $this->assertEquals( + 1, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course1->id]) + ); + $this->assertEquals( + 1, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course2->id]) + ); + $this->assertEquals( + 2, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE g.courseid = ?", [$course3->id]) + ); + $this->assertEquals( + 1, + $DB->count_records_sql("SELECT COUNT(gm.id) + FROM {groups_members} gm + JOIN {groups} g ON gm.groupid = g.id + WHERE gm.userid = ?", [$user1->id]) + ); + } +} diff --git a/lang/en/group.php b/lang/en/group.php index ead53e5d0af..b349065c706 100644 --- a/lang/en/group.php +++ b/lang/en/group.php @@ -173,6 +173,10 @@ $string['overview'] = 'Overview'; $string['potentialmembers'] = 'Potential members: {$a}'; $string['potentialmembs'] = 'Potential members'; $string['printerfriendly'] = 'Printer-friendly display'; +$string['privacy:metadata:groups'] = 'A record of group membership.'; +$string['privacy:metadata:groups:groupid'] = 'The ID of the group.'; +$string['privacy:metadata:groups:timeadded'] = 'The timestamp indicating when the user was added to the group.'; +$string['privacy:metadata:groups:userid'] = 'The ID of the user which is associated to the group.'; $string['random'] = 'Randomly'; $string['removegroupfromselectedgrouping'] = 'Remove group from grouping'; $string['removefromgroup'] = 'Remove user from group {$a}';