From 267effaaa20083b226143507bcebf14039a098ec Mon Sep 17 00:00:00 2001 From: cescobedo Date: Mon, 14 May 2018 10:55:52 +0200 Subject: [PATCH] MDL-62356 enrol_meta: Add privacy implementation for enrol_meta --- enrol/meta/classes/privacy/provider.php | 92 ++++++++++- enrol/meta/lang/en/enrol_meta.php | 2 +- enrol/meta/tests/privacy_test.php | 198 ++++++++++++++++++++++++ 3 files changed, 284 insertions(+), 8 deletions(-) create mode 100644 enrol/meta/tests/privacy_test.php diff --git a/enrol/meta/classes/privacy/provider.php b/enrol/meta/classes/privacy/provider.php index 04db729d42d..31fd319a4ca 100644 --- a/enrol/meta/classes/privacy/provider.php +++ b/enrol/meta/classes/privacy/provider.php @@ -22,20 +22,98 @@ */ namespace enrol_meta\privacy; defined('MOODLE_INTERNAL') || die(); +use \core_privacy\local\metadata\collection; +use \core_privacy\local\request\contextlist; +use \core_privacy\local\request\approved_contextlist; + /** - * Privacy Subsystem for enrol_meta implementing null_provider. + * Privacy provider for enrol_meta. * * @copyright 2018 Carlos Escobedo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class provider implements \core_privacy\local\metadata\null_provider { +class provider implements + \core_privacy\local\metadata\provider, + \core_privacy\local\request\plugin\provider { /** - * Get the language string identifier with the component's language - * file to explain why this plugin stores no data. + * Returns meta data about this system. * - * @return string + * @param collection $collection The initialised item collection to add items to. + * @return collection A listing of user data stored through this system. */ - public static function get_reason() : string { - return 'privacy:metadata'; + public static function get_metadata(collection $collection) : collection { + + $collection->add_subsystem_link('core_group', [], 'privacy:metadata:core_group'); + return $collection; + } + /** + * Get the list of contexts that contain user information for the specified user. + * + * @param int $userid The user to search. + * @return contextlist $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 = :contextlevel + WHERE gm.userid = :userid + AND gm.component = 'enrol_meta'"; + + $params = [ + 'contextlevel' => 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) { + if (empty($contextlist)) { + return; + } + foreach ($contextlist as $context) { + if ($context->contextlevel == CONTEXT_COURSE) { + \core_group\privacy\provider::export_groups( + $context, + 'enrol_meta', + [get_string('pluginname', 'enrol_meta')] + ); + } + } + } + + /** + * Delete all use data which matches the specified deletion_criteria. + * + * @param context $context A user context. + */ + public static function delete_data_for_all_users_in_context(\context $context) { + if (empty($context)) { + return; + } + if ($context->contextlevel == CONTEXT_COURSE) { + // Delete all the associated groups. + \core_group\privacy\provider::delete_groups_for_all_users($context, 'enrol_meta'); + } + } + /** + * 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) { + if (empty($contextlist->count())) { + return; + } + \core_group\privacy\provider::delete_groups_for_user($contextlist, 'enrol_meta'); } } \ No newline at end of file diff --git a/enrol/meta/lang/en/enrol_meta.php b/enrol/meta/lang/en/enrol_meta.php index 92989d17bf5..205a35fbfae 100644 --- a/enrol/meta/lang/en/enrol_meta.php +++ b/enrol/meta/lang/en/enrol_meta.php @@ -38,4 +38,4 @@ $string['pluginname'] = 'Course meta link'; $string['pluginname_desc'] = 'Course meta link enrolment plugin synchronises enrolments and roles in two different courses.'; $string['syncall'] = 'Synchronise all enrolled users'; $string['syncall_desc'] = 'If enabled all enrolled users are synchronised even if they have no role in parent course, if disabled only users that have at least one synchronised role are enrolled in child course.'; -$string['privacy:metadata'] = 'The Course meta link enrolment plugin does not store any personal data.'; +$string['privacy:metadata:core_group'] = 'Enrol meta plugin can create a new group or use an existing group to add all the participants of the course linked.'; diff --git a/enrol/meta/tests/privacy_test.php b/enrol/meta/tests/privacy_test.php new file mode 100644 index 00000000000..4e1966df5f4 --- /dev/null +++ b/enrol/meta/tests/privacy_test.php @@ -0,0 +1,198 @@ +. +/** + * Base class for unit tests for enrol_meta. + * + * @package enrol_meta + * @category test + * @copyright 2018 Carlos Escobedo + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +defined('MOODLE_INTERNAL') || die(); +use \core_privacy\local\request\writer; +use \core_privacy\local\request\approved_contextlist; +use \enrol_meta\privacy\provider; +/** + * Unit tests for the enrol_meta implementation of the privacy API. + * + * @copyright 2018 Carlos Escobedo + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class enrol_meta_privacy_testcase extends \core_privacy\tests\provider_testcase { + /** + * Enable enrol_meta plugin. + */ + protected function enable_plugin() { + $enabled = enrol_get_plugins(true); + $enabled['meta'] = true; + $enabled = array_keys($enabled); + set_config('enrol_plugins_enabled', implode(',', $enabled)); + } + /** + * Test getting the context for the user ID related to this plugin. + */ + public function test_get_contexts_for_userid() { + global $DB; + + $this->resetAfterTest(); + $metaplugin = enrol_get_plugin('meta'); + $user1 = $this->getDataGenerator()->create_user(); + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + + $this->enable_plugin(); + $metaplugin->add_instance($course1, array('customint1' => $course2->id, 'customint2' => $group1->id)); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id, $studentrole->id); + // Check if user1 is enrolled into course1 in group 1. + $this->assertTrue(groups_is_member($group1->id, $user1->id)); + $this->assertTrue($DB->record_exists('groups_members', + array( + 'groupid' => $group1->id, + 'userid' => $user1->id, + 'component' => 'enrol_meta' + ) + )); + // Check context course fro provider to user1. + $context = \context_course::instance($course1->id); + $contextlist = provider::get_contexts_for_userid($user1->id); + $this->assertEquals($context->id, $contextlist->current()->id); + } + + /** + * Test that user data is exported correctly. + */ + public function test_export_user_data() { + global $DB; + + $this->resetAfterTest(); + $metaplugin = enrol_get_plugin('meta'); + $user1 = $this->getDataGenerator()->create_user(); + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + + $this->enable_plugin(); + $metaplugin->add_instance($course1, array('customint1' => $course2->id, 'customint2' => $group1->id)); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id, $studentrole->id); + + $this->setUser($user1); + $contextlist = provider::get_contexts_for_userid($user1->id); + $approvedcontextlist = new approved_contextlist($user1, 'enrol_meta', $contextlist->get_contextids()); + provider::export_user_data($approvedcontextlist); + foreach ($contextlist as $context) { + $writer = writer::with_context($context); + $data = $writer->get_data([ + get_string('pluginname', 'enrol_meta'), + get_string('groups', 'core_group') + ]); + $this->assertTrue($writer->has_any_data()); + if ($context->contextlevel == CONTEXT_COURSE) { + $exportedgroups = $data->groups; + // User1 only belongs to group1 via enrol_meta. + $this->assertCount(1, $exportedgroups); + $exportedgroup = reset($exportedgroups); + $this->assertEquals($group1->name, $exportedgroup->name); + } + } + } + /** + * Test for provider::delete_data_for_all_users_in_context(). + */ + public function test_delete_data_for_all_users_in_context() { + global $DB; + + $this->resetAfterTest(); + $metaplugin = enrol_get_plugin('meta'); + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $user3 = $this->getDataGenerator()->create_user(); + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + + $this->enable_plugin(); + $metaplugin->add_instance($course1, array('customint1' => $course2->id, 'customint2' => $group1->id)); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id, $studentrole->id); + $this->getDataGenerator()->enrol_user($user2->id, $course2->id, $studentrole->id); + $this->getDataGenerator()->enrol_user($user3->id, $course2->id, $studentrole->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 g.courseid = ?", [$course1->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]) + ); + } + /** + * Test for provider::delete_data_for_user(). + */ + public function test_delete_data_for_user() { + global $DB; + + $this->resetAfterTest(); + $metaplugin = enrol_get_plugin('meta'); + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $user3 = $this->getDataGenerator()->create_user(); + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id)); + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + + $this->enable_plugin(); + $metaplugin->add_instance($course1, array('customint1' => $course2->id, 'customint2' => $group1->id)); + $this->getDataGenerator()->enrol_user($user1->id, $course2->id, $studentrole->id); + $this->getDataGenerator()->enrol_user($user2->id, $course2->id, $studentrole->id); + $this->getDataGenerator()->enrol_user($user3->id, $course2->id, $studentrole->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 g.courseid = ?", [$course1->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, 'enrol_meta', + [$coursecontext1->id]); + provider::delete_data_for_user($approvedcontextlist); + // Check we have 2 users in groups because we are deleted user1. + $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]) + ); + } +} \ No newline at end of file -- 2.43.0