From a15712ef6eb1a78f7d6c79b0b18e5ee47e6633d9 Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Thu, 3 May 2018 15:51:52 +0800 Subject: [PATCH] MDL-62030 profilefield_menu: Add privacy files and unit tests --- .../field/menu/classes/privacy/provider.php | 173 +++++++++++++++ .../field/menu/lang/en/profilefield_menu.php | 5 + .../profile/field/menu/tests/privacy_test.php | 209 ++++++++++++++++++ 3 files changed, 387 insertions(+) create mode 100644 user/profile/field/menu/classes/privacy/provider.php create mode 100644 user/profile/field/menu/tests/privacy_test.php diff --git a/user/profile/field/menu/classes/privacy/provider.php b/user/profile/field/menu/classes/privacy/provider.php new file mode 100644 index 00000000000..588f5d45a90 --- /dev/null +++ b/user/profile/field/menu/classes/privacy/provider.php @@ -0,0 +1,173 @@ +. + +/** + * Privacy class for requesting user data. + * + * @package profilefield_menu + * @copyright 2018 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace profilefield_menu\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 class for requesting user data. + * + * @copyright 2018 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements + \core_privacy\local\metadata\provider, + \core_privacy\local\request\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 { + return $collection->add_database_table('user_info_data', [ + 'userid' => 'privacy:metadata:profilefield_menu:userid', + 'fieldid' => 'privacy:metadata:profilefield_menu:fieldid', + 'data' => 'privacy:metadata:profilefield_menu:data', + 'dataformat' => 'privacy:metadata:profilefield_menu:dataformat' + ], 'privacy:metadata:profilefield_menu:tableexplanation'); + } + + /** + * 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 { + $sql = "SELECT ctx.id + FROM {user_info_data} uda + JOIN {user_info_field} uif ON uda.fieldid = uif.id + JOIN {context} ctx ON ctx.instanceid = uda.userid + AND ctx.contextlevel = :contextlevel + WHERE uda.userid = :userid + AND uif.datatype = :datatype"; + $params = [ + 'userid' => $userid, + 'contextlevel' => CONTEXT_USER, + 'datatype' => 'menu' + ]; + $contextlist = new contextlist(); + $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) { + $user = $contextlist->get_user(); + foreach ($contextlist->get_contexts() as $context) { + // Check if the context is a user context. + if ($context->contextlevel == CONTEXT_USER && $context->instanceid == $user->id) { + $results = static::get_records($user->id); + foreach ($results as $result) { + $data = (object) [ + 'name' => $result->name, + 'description' => $result->description, + 'data' => $result->data + ]; + \core_privacy\local\request\writer::with_context($context)->export_data([ + get_string('pluginname', 'profilefield_menu')], $data); + } + } + } + } + + /** + * Delete all user data which matches the specified context. + * + * @param context $context A user context. + */ + public static function delete_data_for_all_users_in_context(\context $context) { + // Delete data only for user context. + if ($context->contextlevel == CONTEXT_USER) { + static::delete_data($context->instanceid); + } + } + + /** + * 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) { + $user = $contextlist->get_user(); + foreach ($contextlist->get_contexts() as $context) { + // Check if the context is a user context. + if ($context->contextlevel == CONTEXT_USER && $context->instanceid == $user->id) { + static::delete_data($context->instanceid); + } + } + } + + /** + * Delete data related to a userid. + * + * @param int $userid The user ID + */ + protected static function delete_data($userid) { + global $DB; + + $params = [ + 'userid' => $userid, + 'datatype' => 'menu' + ]; + + $DB->delete_records_select('user_info_data', "fieldid IN ( + SELECT id FROM {user_info_field} WHERE datatype = :datatype) + AND userid = :userid", $params); + } + + /** + * Get records related to this plugin and user. + * + * @param int $userid The user ID + * @return array An array of records. + */ + protected static function get_records($userid) { + global $DB; + + $sql = "SELECT * + FROM {user_info_data} uda + JOIN {user_info_field} uif ON uda.fieldid = uif.id + WHERE uda.userid = :userid + AND uif.datatype = :datatype"; + $params = [ + 'userid' => $userid, + 'datatype' => 'menu' + ]; + + return $DB->get_records_sql($sql, $params); + } +} diff --git a/user/profile/field/menu/lang/en/profilefield_menu.php b/user/profile/field/menu/lang/en/profilefield_menu.php index 9a2e7dc76d6..20ebb741dcb 100644 --- a/user/profile/field/menu/lang/en/profilefield_menu.php +++ b/user/profile/field/menu/lang/en/profilefield_menu.php @@ -23,3 +23,8 @@ */ $string['pluginname'] = 'Dropdown menu'; +$string['privacy:metadata:profilefield_menu:userid'] = 'The ID of the user which data is stored by the Dropdown menu plugin.'; +$string['privacy:metadata:profilefield_menu:fieldid'] = 'The ID of the profile field.'; +$string['privacy:metadata:profilefield_menu:data'] = 'The stored user data.'; +$string['privacy:metadata:profilefield_menu:dataformat'] = 'The format of the stored user data.'; +$string['privacy:metadata:profilefield_menu:tableexplanation'] = 'Additional user information is stored here.'; diff --git a/user/profile/field/menu/tests/privacy_test.php b/user/profile/field/menu/tests/privacy_test.php new file mode 100644 index 00000000000..331f2640848 --- /dev/null +++ b/user/profile/field/menu/tests/privacy_test.php @@ -0,0 +1,209 @@ +. + +/** + * Base class for unit tests for profilefield_menu. + * + * @package profilefield_menu + * @copyright 2018 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +use \core_privacy\tests\provider_testcase; + +/** + * Unit tests for user\profile\field\menu\classes\privacy\provider.php + * + * @copyright 2018 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class profilefield_menu_testcase extends provider_testcase { + + /** + * Basic setup for these tests. + */ + public function setUp() { + $this->resetAfterTest(true); + } + + /** + * Test getting the context for the user ID related to this plugin. + */ + public function test_get_contexts_for_userid() { + global $DB; + // Create profile category. + $categoryid = $this->add_profile_category(); + // Create profile field. + $profilefieldid = $this->add_profile_field($categoryid, 'menu'); + // Create a user. + $user = $this->getDataGenerator()->create_user(); + $this->add_user_info_data($user->id, $profilefieldid, 'test data'); + // Get the field that was created. + $userfielddata = $DB->get_records('user_info_data', array('userid' => $user->id)); + // Confirm we got the right number of user field data. + $this->assertCount(1, $userfielddata); + $context = context_user::instance($user->id); + $contextlist = \profilefield_menu\privacy\provider::get_contexts_for_userid($user->id); + $this->assertEquals($context, $contextlist->current()); + } + + /** + * Test that data is exported correctly for this plugin. + */ + public function test_export_user_data() { + // Create profile category. + $categoryid = $this->add_profile_category(); + // Create menu profile field. + $menuprofilefieldid = $this->add_profile_field($categoryid, 'menu'); + // Create checkbox profile field. + $checkboxprofilefieldid = $this->add_profile_field($categoryid, 'checkbox'); + // Create a user. + $user = $this->getDataGenerator()->create_user(); + $context = context_user::instance($user->id); + // Add menu user info data. + $this->add_user_info_data($user->id, $menuprofilefieldid, 'test menu'); + // Add checkbox user info data. + $this->add_user_info_data($user->id, $checkboxprofilefieldid, 'test data'); + $writer = \core_privacy\local\request\writer::with_context($context); + $this->assertFalse($writer->has_any_data()); + $this->export_context_data_for_user($user->id, $context, 'profilefield_menu'); + $data = $writer->get_data([get_string('pluginname', 'profilefield_menu')]); + $this->assertCount(3, (array) $data); + $this->assertEquals('Test field', $data->name); + $this->assertEquals('This is a test.', $data->description); + $this->assertEquals('test menu', $data->data); + } + + /** + * Test that user data is deleted using the context. + */ + public function test_delete_data_for_all_users_in_context() { + global $DB; + // Create profile category. + $categoryid = $this->add_profile_category(); + // Create menu profile field. + $menuprofilefieldid = $this->add_profile_field($categoryid, 'menu'); + // Create checkbox profile field. + $checkboxprofilefieldid = $this->add_profile_field($categoryid, 'checkbox'); + // Create a user. + $user = $this->getDataGenerator()->create_user(); + $context = context_user::instance($user->id); + // Add menu user info data. + $this->add_user_info_data($user->id, $menuprofilefieldid, 'test menu'); + // Add checkbox user info data. + $this->add_user_info_data($user->id, $checkboxprofilefieldid, 'test data'); + // Check that we have two entries. + $userinfodata = $DB->get_records('user_info_data', ['userid' => $user->id]); + $this->assertCount(2, $userinfodata); + \profilefield_menu\privacy\provider::delete_data_for_all_users_in_context($context); + // Check that the correct profile field has been deleted. + $userinfodata = $DB->get_records('user_info_data', ['userid' => $user->id]); + $this->assertCount(1, $userinfodata); + $this->assertNotEquals('test menu', reset($userinfodata)->data); + } + + /** + * Test that user data is deleted for this user. + */ + public function test_delete_data_for_user() { + global $DB; + // Create profile category. + $categoryid = $this->add_profile_category(); + // Create menu profile field. + $menuprofilefieldid = $this->add_profile_field($categoryid, 'menu'); + // Create checkbox profile field. + $checkboxprofilefieldid = $this->add_profile_field($categoryid, 'checkbox'); + // Create a user. + $user = $this->getDataGenerator()->create_user(); + $context = context_user::instance($user->id); + // Add menu user info data. + $this->add_user_info_data($user->id, $menuprofilefieldid, 'test menu'); + // Add checkbox user info data. + $this->add_user_info_data($user->id, $checkboxprofilefieldid, 'test data'); + // Check that we have two entries. + $userinfodata = $DB->get_records('user_info_data', ['userid' => $user->id]); + $this->assertCount(2, $userinfodata); + $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'profilefield_menu', + [$context->id]); + \profilefield_menu\privacy\provider::delete_data_for_user($approvedlist); + // Check that the correct profile field has been deleted. + $userinfodata = $DB->get_records('user_info_data', ['userid' => $user->id]); + $this->assertCount(1, $userinfodata); + $this->assertNotEquals('test menu', reset($userinfodata)->data); + } + + /** + * Add dummy user info data. + * + * @param int $userid The ID of the user + * @param int $fieldid The ID of the field + * @param string $data The data + */ + private function add_user_info_data($userid, $fieldid, $data) { + global $DB; + + $userinfodata = array( + 'userid' => $userid, + 'fieldid' => $fieldid, + 'data' => $data, + 'dataformat' => 0 + ); + + $DB->insert_record('user_info_data', $userinfodata); + } + + /** + * Add dummy profile category. + * + * @return int The ID of the profile category + */ + private function add_profile_category() { + global $DB; + // Create a new profile category. + $cat = new stdClass(); + $cat->name = 'Test category'; + $cat->sortorder = 1; + + return $DB->insert_record('user_info_category', $cat); + } + + /** + * Add dummy profile field. + * + * @param int $categoryid The ID of the profile category + * @param string $datatype The datatype of the profile field + * @return int The ID of the profile field + */ + private function add_profile_field($categoryid, $datatype) { + global $DB; + // Create a new profile field. + $data = new stdClass(); + $data->datatype = $datatype; + $data->shortname = 'tstField'; + $data->name = 'Test field'; + $data->description = 'This is a test.'; + $data->required = false; + $data->locked = false; + $data->forceunique = false; + $data->signup = false; + $data->visible = '0'; + $data->categoryid = $categoryid; + + return $DB->insert_record('user_info_field', $data); + } +} -- 2.43.0