From: Adrian Greeve Date: Wed, 18 Apr 2018 07:05:05 +0000 (+0800) Subject: MDL-62029 core_course: Create privacy files for core_course. X-Git-Tag: v3.5.0-rc1~87^2~2 X-Git-Url: http://git.moodle.org/gw?p=moodle.git;a=commitdiff_plain;h=fd9c70c43ff49cf39ab3bd0deaa0de3b7ad9349c MDL-62029 core_course: Create privacy files for core_course. --- diff --git a/course/classes/privacy/provider.php b/course/classes/privacy/provider.php new file mode 100644 index 00000000000..a4c209d4eff --- /dev/null +++ b/course/classes/privacy/provider.php @@ -0,0 +1,200 @@ +. + +/** + * Privacy class for requesting user data. + * + * @package core_course + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_course\privacy; + +defined('MOODLE_INTERNAL') || die(); + +use \core_privacy\local\metadata\collection; +use \core_privacy\local\request\contextlist; +use \core_privacy\local\request\approved_contextlist; +use \core_privacy\local\request\writer; +use \core_privacy\local\request\transform; + +/** + * Privacy class for requesting user data. + * + * @package core_course + * @copyright 2018 Adrian Greeve + * @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\context_aware_provider, + \core_privacy\local\request\plugin\provider, + \core_privacy\local\request\user_preference_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_subsystem_link('core_completion', [], 'privacy:metadata:completionsummary'); + $collection->add_user_preference('coursecat_management_perpage', 'privacy:perpage'); + 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 { + list($join, $where, $params) = \core_completion\privacy\provider::get_course_completion_join_sql($userid, 'cc', 'c.id'); + $sql = "SELECT ctx.id + FROM {context} ctx + JOIN {course} c ON ctx.instanceid = c.id AND ctx.contextlevel = :contextcourse + {$join} + WHERE {$where}"; + $params['contextcourse'] = CONTEXT_COURSE; + $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) { + global $DB; + + // Get the course. + list($select, $params) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); + $params['contextcourse'] = CONTEXT_COURSE; + + $sql = "SELECT c.* + FROM {course} c + JOIN {context} ctx ON c.id = ctx.instanceid AND ctx.contextlevel = :contextcourse + WHERE ctx.id $select"; + + $courses = $DB->get_recordset_sql($sql, $params); + foreach ($courses as $course) { + $coursecompletion = \core_completion\privacy\provider::get_course_completion_info($contextlist->get_user(), $course); + writer::with_context(\context_course::instance($course->id))->export_data( + [get_string('privacy:completionpath', 'course')], (object) $coursecompletion); + } + $courses->close(); + } + + /** + * Exports course information based on the whole approved context list collection. + * + * @param \core_privacy\local\request\contextlist_collection $contextcollection The collection of approved context lists. + */ + public static function export_complete_context_data(\core_privacy\local\request\contextlist_collection $completelist) { + global $DB; + + $coursecontextids = $DB->get_records('context', ['contextlevel' => CONTEXT_COURSE], '', 'id, instanceid'); + + $courseids = []; + foreach ($completelist as $component) { + foreach ($component->get_contexts() as $context) { + if ($context->contextlevel == CONTEXT_USER + || $context->contextlevel == CONTEXT_SYSTEM + || $context->contextlevel == CONTEXT_COURSECAT) { + // Move onto the next context as these will not contain course contexts. + continue; + } + foreach ($coursecontextids as $contextid => $record) { + if (stripos($context->path, '/' . $contextid . '/') !== false) { + $courseids[$contextid] = $record->instanceid; + } + } + } + } + if (empty($courseids)) { + return; + } + + // Export general data for these contexts. + list($sql, $params) = $DB->get_in_or_equal($courseids); + $sql = 'id ' . $sql; + $coursedata = $DB->get_records_select('course', $sql, $params); + + foreach ($coursedata as $course) { + $context = \context_course::instance($course->id); + $data = (object) [ + 'fullname' => $course->fullname, + 'shortname' => $course->shortname, + 'idnumber' => $course->idnumber, + 'summary' => writer::with_context($context)->rewrite_pluginfile_urls([], 'course', 'summary', 0, $course->summary), + 'format' => get_string('pluginname', 'format_' . $course->format), + 'startdate' => transform::datetime($course->startdate), + 'enddate' => transform::datetime($course->enddate) + ]; + writer::with_context($context) + ->export_area_files([], 'course', 'summary', 0) + ->export_area_files([], 'course', 'overviewfiles', 0) + ->export_data([], $data); + } + } + + /** + * Export 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) { + $perpage = get_user_preferences('coursecat_management_perpage', null, $userid); + if (isset($perpage)) { + writer::export_user_preference('core_course', + 'coursecat_management_perpage', + $perpage, + get_string('privacy:perpage', 'course') + ); + } + } + + /** + * 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) { + // Check what context we've been delivered. + if ($context->contextlevel == CONTEXT_COURSE) { + // Delete course completion data. + \core_completion\privacy\provider::delete_completion(null, $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) { + foreach ($contextlist as $context) { + if ($context->contextlevel == CONTEXT_COURSE) { + // Delete course completion data. + \core_completion\privacy\provider::delete_completion($contextlist->get_user(), $context->instanceid); + } + } + } +} diff --git a/course/tests/privacy_test.php b/course/tests/privacy_test.php new file mode 100644 index 00000000000..c8b55b95ab3 --- /dev/null +++ b/course/tests/privacy_test.php @@ -0,0 +1,144 @@ +. +/** + * Privacy tests for core_course. + * + * @package core_course + * @category test + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/completion/tests/fixtures/completion_creation.php'); + +/** + * Unit tests for course/classes/privacy/policy + * + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_course_privacy_testcase extends \core_privacy\tests\provider_testcase { + + use completion_creation; + + /** + * Test getting the appropriate context for the userid. This should only ever + * return the user context for the user id supplied. + */ + public function test_get_contexts_for_userid() { + $this->resetAfterTest(); + $user = $this->getDataGenerator()->create_user(); + $this->create_course_completion(); + $this->complete_course($user); + $contextlist = \core_course\privacy\provider::get_contexts_for_userid($user->id); + $this->assertEquals($this->coursecontext->id, $contextlist->current()->id); + } + + /** + * Test that user data is exported. + */ + public function test_export_user_data() { + $this->resetAfterTest(); + $user = $this->getDataGenerator()->create_user(); + $this->create_course_completion(); + $this->complete_course($user); + $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'core_course', + [$this->coursecontext->id]); + $writer = \core_privacy\local\request\writer::with_context($this->coursecontext); + \core_course\privacy\provider::export_user_data($approvedlist); + $completiondata = $writer->get_data([get_string('privacy:completionpath', 'course')]); + $this->assertEquals('In progress', $completiondata->status); + $this->assertCount(2, $completiondata->criteria); + } + + public function test_export_complete_context_data() { + $this->resetAfterTest(); + $user = $this->getDataGenerator()->create_user(); + $course1 = $this->getDataGenerator()->create_course(['fullname' => 'Course 1', 'shortname' => 'C1']); + $context1 = context_course::instance($course1->id); + $course2 = $this->getDataGenerator()->create_course(['fullname' => 'Course 2', 'shortname' => 'C2']); + $context2 = context_course::instance($course2->id); + $course3 = $this->getDataGenerator()->create_course(['fullname' => 'Course 3', 'shortname' => 'C3']); + + $this->setUser($user); + $modforum = $this->getDataGenerator()->create_module('forum', ['course' => $course1->id]); + $modresource = $this->getDataGenerator()->create_module('resource', ['course' => $course2->id]); + $modpage = $this->getDataGenerator()->create_module('page', ['course' => $course3->id]); + $forumcontext = context_module::instance($modforum->cmid); + $resourcecontext = context_module::instance($modresource->cmid); + + $collection = new \core_privacy\local\request\contextlist_collection($user->id); + $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'mod_forum', [$forumcontext->id]); + $collection->add_contextlist($approvedlist); + $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'mod_resource', [$resourcecontext->id]); + $collection->add_contextlist($approvedlist); + + $writer = \core_privacy\local\request\writer::with_context(context_system::instance()); + \core_course\privacy\provider::export_complete_context_data($collection); + $courses = $writer->get_data(); + print_object($courses); + // print_object($writer); + } + + /** + * Test deleting all user data for one context. + */ + public function test_delete_data_for_all_users_in_context() { + global $DB; + $this->resetAfterTest(); + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $this->create_course_completion(); + $this->complete_course($user1); + $this->complete_course($user2); + $records = $DB->get_records('course_modules_completion'); + $this->assertCount(2, $records); + $records = $DB->get_records('course_completion_crit_compl'); + $this->assertCount(2, $records); + \core_course\privacy\provider::delete_data_for_all_users_in_context($this->coursecontext); + $records = $DB->get_records('course_modules_completion'); + $this->assertCount(0, $records); + $records = $DB->get_records('course_completion_crit_compl'); + $this->assertCount(0, $records); + } + + /** + * Test deleting data for only one user. + */ + public function test_delete_data_for_user() { + global $DB; + $this->resetAfterTest(); + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $this->create_course_completion(); + $this->complete_course($user1); + $this->complete_course($user2); + $records = $DB->get_records('course_modules_completion'); + $this->assertCount(2, $records); + $records = $DB->get_records('course_completion_crit_compl'); + $this->assertCount(2, $records); + $approvedlist = new \core_privacy\local\request\approved_contextlist($user1, 'core_course', + [$this->coursecontext->id]); + \core_course\privacy\provider::delete_data_for_user($approvedlist); + $records = $DB->get_records('course_modules_completion'); + $this->assertCount(1, $records); + $records = $DB->get_records('course_completion_crit_compl'); + $this->assertCount(1, $records); + } +} diff --git a/lang/en/course.php b/lang/en/course.php new file mode 100644 index 00000000000..bd1a9923ce7 --- /dev/null +++ b/lang/en/course.php @@ -0,0 +1,27 @@ +. + +/** + * Strings for component 'course', language 'en', branch 'MOODLE_20_STABLE' + * + * @package core_course + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['privacy:perpage'] = 'The number of courses to show per page.'; +$string['privacy:completionpath'] = 'Course completion'; +$string['privacy:metadata:completionsummary'] = 'The course contains completion information about the user.';