diff --git a/admin/tool/usertours/classes/privacy/provider.php b/admin/tool/usertours/classes/privacy/provider.php new file mode 100644 index 00000000000..4441ba84b99 --- /dev/null +++ b/admin/tool/usertours/classes/privacy/provider.php @@ -0,0 +1,95 @@ +. + +/** + * Privacy Subsystem implementation for tool_usertours. + * + * @package tool_usertours + * @copyright 2018 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_usertours\privacy; + +use \core_privacy\local\request\writer; +use \core_privacy\local\metadata\collection; +use \core_privacy\local\request\transform; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Implementation of the privacy subsystem plugin provider for the user tours feature. + * + * @copyright 2018 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements + // This plugin has data. + \core_privacy\local\metadata\provider, + + // This plugin has some sitewide user preferences to export. + \core_privacy\local\request\user_preference_provider +{ + + /** + * Returns meta data about this system. + * + * @param collection $itemcollection The initialised item collection to add items to. + * @return collection A listing of user data stored through this system. + */ + public static function get_metadata(collection $items) : collection { + // There are several user preferences. + $items->add_user_preference(\tool_usertours\tour::TOUR_REQUESTED_BY_USER, 'privacy:metadata:preference:requested'); + $items->add_user_preference(\tool_usertours\tour::TOUR_LAST_COMPLETED_BY_USER, 'privacy:metadata:preference:completed'); + + return $items; + } + + /** + * Store all user preferences for the plugin. + * + * @param int $userid The userid of the user whose data is to be exported. + */ + public static function export_user_preferences(int $userid) { + $preferences = get_user_preferences(); + foreach ($preferences as $name => $value) { + $descriptionidentifier = null; + $tourid = null; + if (strpos($name, \tool_usertours\tour::TOUR_REQUESTED_BY_USER) === 0) { + $descriptionidentifier = 'privacy:request:preference:requested'; + $tourid = substr($name, strlen(\tool_usertours\tour::TOUR_REQUESTED_BY_USER)); + } else if (strpos($name, \tool_usertours\tour::TOUR_LAST_COMPLETED_BY_USER) === 0) { + $descriptionidentifier = 'privacy:request:preference:completed'; + $tourid = substr($name, strlen(\tool_usertours\tour::TOUR_LAST_COMPLETED_BY_USER)); + } + + if ($descriptionidentifier !== null) { + $time = transform::datetime($value); + $tour = \tool_usertours\tour::instance($tourid); + + writer::export_user_preference( + 'tool_usertours', + $name, + $time, + get_string($descriptionidentifier, 'tool_usertours', (object) [ + 'name' => $tour->get_name(), + 'time' => $time, + ]) + ); + } + } + } +} diff --git a/admin/tool/usertours/lang/en/tool_usertours.php b/admin/tool/usertours/lang/en/tool_usertours.php index 5d8dd8ad179..4c4333caf06 100644 --- a/admin/tool/usertours/lang/en/tool_usertours.php +++ b/admin/tool/usertours/lang/en/tool_usertours.php @@ -175,3 +175,7 @@ $string['tour2_title_addingblocks'] = 'Adding blocks'; $string['tour2_content_addingblocks'] = 'You can add blocks to this page using this button. However, think carefully about including any blocks on your pages. Blocks are not shown on the Moodle Mobile app, so for the best user experience it is better to make sure your course works well without any blocks.'; $string['tour2_title_end'] = 'End of tour'; $string['tour2_content_end'] = 'This is the end of your user tour. It won\'t show again unless you reset it using the link in the footer. The site admin can also create further tours for this site if required.'; +$string['privacy:metadata:preference:requested'] = 'The time that a user last manually requested a user tour.'; +$string['privacy:metadata:preference:completed'] = 'The time that a user last completed a user tour.'; +$string['privacy:request:preference:requested'] = 'You last requested the "{$a->name}" user tour on {$a->time}'; +$string['privacy:request:preference:completed'] = 'You last marked the "{$a->name}" user tour as completed on {$a->time}'; diff --git a/admin/tool/usertours/tests/privcacy_provider_test.php b/admin/tool/usertours/tests/privcacy_provider_test.php new file mode 100644 index 00000000000..8cfc81a515c --- /dev/null +++ b/admin/tool/usertours/tests/privcacy_provider_test.php @@ -0,0 +1,115 @@ +. + +/** + * Unit tests for the block_html implementation of the privacy API. + * + * @package block_html + * @category test + * @copyright 2018 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +use \core_privacy\local\metadata\collection; +use \core_privacy\local\request\writer; +use \core_privacy\local\request\approved_contextlist; +use \core_privacy\local\request\deletion_criteria; +use \tool_usertours\tour; +use \tool_usertours\privacy\provider; + +/** + * Unit tests for the block_html implementation of the privacy API. + * + * @copyright 2018 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tool_usertours_privacy_testcase extends \core_privacy\tests\provider_testcase { + + /** + * Ensure that get_metadata exports valid content. + */ + public function test_get_metadata() { + $items = new collection('tool_usertours'); + $result = provider::get_metadata($items); + $this->assertSame($items, $result); + $this->assertInstanceOf(collection::class, $result); + } + + /** + * Ensure that export_user_preferences returns no data if the user has completed no tours. + */ + public function test_export_user_preferences_no_pref() { + $user = \core_user::get_user_by_username('admin'); + provider::export_user_preferences($user->id); + + $writer = writer::with_context(\context_system::instance()); + + $this->assertFalse($writer->has_any_data()); + } + + /** + * Ensure that export_user_preferences returns request completion data. + */ + public function test_export_user_preferences_completed() { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $alltours = $DB->get_records('tool_usertours_tours'); + $tourdata = reset($alltours); + + $user = \core_user::get_user_by_username('admin'); + $tour = tour::instance($tourdata->id); + $tour->mark_user_completed(); + provider::export_user_preferences($user->id); + + $writer = writer::with_context(\context_system::instance()); + + $this->assertTrue($writer->has_any_data()); + $prefs = $writer->get_user_preferences('tool_usertours'); + + $this->assertCount(1, (array) $prefs); + } + + /** + * Ensure that export_user_preferences returns request completion data. + */ + public function test_export_user_preferences_requested() { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $alltours = $DB->get_records('tool_usertours_tours'); + $tourdata = reset($alltours); + + $user = \core_user::get_user_by_username('admin'); + $tour = tour::instance($tourdata->id); + $tour->mark_user_completed(); + $tour->request_user_reset(); + provider::export_user_preferences($user->id); + + $writer = writer::with_context(\context_system::instance()); + + $this->assertTrue($writer->has_any_data()); + $prefs = $writer->get_user_preferences('tool_usertours'); + + $this->assertCount(2, (array) $prefs); + } +}