diff --git a/user/profile/field/checkbox/classes/privacy/provider.php b/user/profile/field/checkbox/classes/privacy/provider.php new file mode 100644 index 0000000000000..e20bdaa302ebf --- /dev/null +++ b/user/profile/field/checkbox/classes/privacy/provider.php @@ -0,0 +1,166 @@ +. + +/** + * Privacy class for requesting user data. + * + * @package profilefield_checkbox + * @copyright 2018 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace profilefield_checkbox\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. + * + * @package profilefield_checkbox + * @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->add_database_table('user_info_data', [ + 'userid' => 'privacy:metadata:profilefield_checkbox:userid', + 'fieldid' => 'privacy:metadata:profilefield_checkbox:fieldid', + 'data' => 'privacy:metadata:profilefield_checkbox:data', + 'dataformat' => 'privacy:metadata:profilefield_checkbox:dataformat' + ], 'privacy:metadata:profilefield_checkbox:tableexplanation'); + 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($userid) { + $sql = "SELECT ctx.id + FROM {user_info_data} uid + JOIN {user_info_field} uif + ON uid.fieldid = uif.id + JOIN {context} ctx + ON ctx.instanceid = uid.userid + AND ctx.contextlevel = :contextlevel + WHERE uid.userid = :userid + AND uif.datatype = :datatype"; + $params = [ + 'userid' => $userid, + 'contextlevel' => CONTEXT_USER, + 'datatype' => 'checkbox' + ]; + $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) { + $results = static::get_records($contextlist->get_user()->id); + foreach ($results as $result) { + $data = (object) [ + 'name' => $result->name, + 'description' => $result->description, + 'data' => $result->data + ]; + \core_privacy\local\request\writer::with_context($contextlist->current())->export_data([ + get_string('pluginname', 'profilefield_checkbox')], $data); + } + } + + /** + * 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) { + // 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) { + static::delete_data($contextlist->get_user()->id); + } + + /** + * Delete data related to a userid. + * + * @param int $userid The user ID + */ + protected static function delete_data($userid) { + global $DB; + + $params = [ + 'userid' => $userid, + 'datatype' => 'checkbox' + ]; + + $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} uid + JOIN {user_info_field} uif + ON uid.fieldid = uif.id + WHERE uid.userid = :userid + AND uif.datatype = :datatype"; + $params = [ + 'userid' => $userid, + 'datatype' => 'checkbox' + ]; + + return $DB->get_records_sql($sql, $params); + } +} diff --git a/user/profile/field/checkbox/lang/en/profilefield_checkbox.php b/user/profile/field/checkbox/lang/en/profilefield_checkbox.php index 2d6c7cb4eee8c..b588e3b4b48c5 100644 --- a/user/profile/field/checkbox/lang/en/profilefield_checkbox.php +++ b/user/profile/field/checkbox/lang/en/profilefield_checkbox.php @@ -23,3 +23,8 @@ */ $string['pluginname'] = 'Checkbox'; +$string['privacy:metadata:profilefield_checkbox:userid'] = 'The ID of the user which data is stored by the Checkbox plugin.'; +$string['privacy:metadata:profilefield_checkbox:fieldid'] = 'The ID of the profile field.'; +$string['privacy:metadata:profilefield_checkbox:data'] = 'The stored user data.'; +$string['privacy:metadata:profilefield_checkbox:dataformat'] = 'The format of the stored user data.'; +$string['privacy:metadata:profilefield_checkbox:tableexplanation'] = 'Additional user information is stored here.'; diff --git a/user/profile/field/checkbox/tests/privacy_test.php b/user/profile/field/checkbox/tests/privacy_test.php new file mode 100644 index 0000000000000..453ebe2210342 --- /dev/null +++ b/user/profile/field/checkbox/tests/privacy_test.php @@ -0,0 +1,208 @@ +. + +/** + * Base class for unit tests for profilefield_checkbox. + * + * @package profilefield_checkbox + * @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\checkbox\classes\privacy\provider.php + * + * @copyright 2018 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class profilefield_checkbox_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, 'checkbox'); + // 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_checkbox\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 checkbox profile field. + $checkboxprofilefieldid = $this->add_profile_field($categoryid, 'checkbox'); + // Create datetime profile field. + $datetimeprofilefieldid = $this->add_profile_field($categoryid, 'datetime'); + // Create a user. + $user = $this->getDataGenerator()->create_user(); + $context = context_user::instance($user->id); + // Add checkbox user info data. + $this->add_user_info_data($user->id, $checkboxprofilefieldid, 'test data'); + // Add datetime user info data. + $this->add_user_info_data($user->id, $datetimeprofilefieldid, '1524067200'); + $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_checkbox'); + $data = $writer->get_data([get_string('pluginname', 'profilefield_checkbox')]); + $this->assertCount(3, (array) $data); + $this->assertEquals('Test field', $data->name); + $this->assertEquals('This is a test.', $data->description); + $this->assertEquals('test data', $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 checkbox profile field. + $checkboxprofilefieldid = $this->add_profile_field($categoryid, 'checkbox'); + // Create datetime profile field. + $datetimeprofilefieldid = $this->add_profile_field($categoryid, 'datetime'); + // Create a user. + $user = $this->getDataGenerator()->create_user(); + $context = context_user::instance($user->id); + // Add checkbox user info data. + $this->add_user_info_data($user->id, $checkboxprofilefieldid, 'test data'); + // Add datetime user info data. + $this->add_user_info_data($user->id, $datetimeprofilefieldid, '1524067200'); + // Check that we have two entries. + $userinfodata = $DB->get_records('user_info_data', ['userid' => $user->id]); + $this->assertCount(2, $userinfodata); + \profilefield_checkbox\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 data', 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 checkbox profile field. + $checkboxprofilefieldid = $this->add_profile_field($categoryid, 'checkbox'); + // Create datetime profile field. + $datetimeprofilefieldid = $this->add_profile_field($categoryid, 'datetime'); + // Create a user. + $user = $this->getDataGenerator()->create_user(); + $context = context_user::instance($user->id); + // Add checkbox user info data. + $this->add_user_info_data($user->id, $checkboxprofilefieldid, 'test data'); + // Add datetime user info data. + $this->add_user_info_data($user->id, $datetimeprofilefieldid, '1524067200'); + // 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_checkbox', + [$context->id]); + \profilefield_checkbox\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 data', 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); + } +}