From fd9c70c43ff49cf39ab3bd0deaa0de3b7ad9349c Mon Sep 17 00:00:00 2001 From: Adrian Greeve Date: Wed, 18 Apr 2018 15:05:05 +0800 Subject: [PATCH 1/3] MDL-62029 core_course: Create privacy files for core_course. --- course/classes/privacy/provider.php | 200 ++++++++++++++++++++++++++++ course/tests/privacy_test.php | 144 ++++++++++++++++++++ lang/en/course.php | 27 ++++ 3 files changed, 371 insertions(+) create mode 100644 course/classes/privacy/provider.php create mode 100644 course/tests/privacy_test.php create mode 100644 lang/en/course.php 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.'; From 9faf51a7caccba711784a07609e545975c2357de Mon Sep 17 00:00:00 2001 From: Adrian Greeve Date: Tue, 1 May 2018 11:18:29 +0800 Subject: [PATCH 2/3] MDL-62029 core_privacy: context aware provider creation. --- .../local/request/context_aware_provider.php | 45 +++++++++++++++++++ privacy/classes/manager.php | 10 ++++- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 privacy/classes/local/request/context_aware_provider.php diff --git a/privacy/classes/local/request/context_aware_provider.php b/privacy/classes/local/request/context_aware_provider.php new file mode 100644 index 00000000000..bc5a4b33708 --- /dev/null +++ b/privacy/classes/local/request/context_aware_provider.php @@ -0,0 +1,45 @@ +. + +/** + * This file contains the \core_privacy\local\request\plugin\provider interface to describe + * a class which provides data in some form for a plugin. + * + * Plugins should implement this if they need access to all approved contexts. + * + * @package core_privacy + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace core_privacy\local\request; + +defined('MOODLE_INTERNAL') || die(); + +/** + * The provider interface for plugins which need access to all approved contexts to fill in user data. + * + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +interface context_aware_provider extends \core_privacy\local\request\core_data_provider { + + /** + * Export context 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 $contextcollection); +} diff --git a/privacy/classes/manager.php b/privacy/classes/manager.php index 922930adabd..14f20c7fda3 100644 --- a/privacy/classes/manager.php +++ b/privacy/classes/manager.php @@ -216,7 +216,7 @@ class manager { // told to export. $this->get_provider_classname($component)::export_user_data($approvedcontextlist); } - } else { + } else if (!$this->component_implements($component, \core_privacy\local\request\context_aware_provider::class)) { // This plugin does not know that it has data - export the shared data it doesn't know about. local\request\helper::export_data_for_null_provider($approvedcontextlist); } @@ -230,6 +230,14 @@ class manager { } } + // Check each component for context aware providers. + foreach ($this->get_component_list() as $component) { + // Core user preference providers. + if ($this->component_implements($component, \core_privacy\local\request\context_aware_provider::class)) { + $this->get_provider_classname($component)::export_complete_context_data($contextlistcollection); + } + } + return local\request\writer::with_context(\context_system::instance())->finalise_content(); } From e4a37fbef19aeba5287adfa800b5364ef8a84d69 Mon Sep 17 00:00:00 2001 From: Jake Dallimore Date: Mon, 7 May 2018 09:56:34 +0800 Subject: [PATCH 3/3] MDL-62029 core_course: Fixes to context-aware provider implementation. --- course/classes/privacy/provider.php | 48 ++++++++---- course/tests/privacy_test.php | 74 ++++++++++++++----- .../local/request/context_aware_provider.php | 14 ++-- privacy/classes/manager.php | 8 +- 4 files changed, 100 insertions(+), 44 deletions(-) diff --git a/course/classes/privacy/provider.php b/course/classes/privacy/provider.php index a4c209d4eff..41a8136fa4a 100644 --- a/course/classes/privacy/provider.php +++ b/course/classes/privacy/provider.php @@ -35,7 +35,6 @@ 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 */ @@ -103,27 +102,47 @@ class provider implements } /** - * Exports course information based on the whole approved context list collection. + * Give the component a chance to include any contextual information deemed relevant to any child contexts which are + * exporting personal data. * - * @param \core_privacy\local\request\contextlist_collection $contextcollection The collection of approved context lists. + * By giving the component access to the full list of contexts being exported across all components, it can determine whether a + * descendant context is being exported, and decide whether to add relevant contextual information about itself. Having access + * to the full list of contexts being exported is what makes this component a context aware provider. + * + * E.g. + * If, during the core export process, a course module is included in the contextlist_collection but the course containing the + * module is not (perhaps there's no longer a user enrolment), then the course should include general contextual information in + * the export so we know basic details about which course the module belongs to. This method allows the course to make that + * decision, based on the existence of any decendant module contexts in the collection. + * + * @param \core_privacy\local\request\contextlist_collection $contextlistcollection */ - public static function export_complete_context_data(\core_privacy\local\request\contextlist_collection $completelist) { + public static function export_context_data(\core_privacy\local\request\contextlist_collection $contextlistcollection) { global $DB; - $coursecontextids = $DB->get_records('context', ['contextlevel' => CONTEXT_COURSE], '', 'id, instanceid'); - + $coursecontextids = $DB->get_records_menu('context', ['contextlevel' => CONTEXT_COURSE], '', 'id, instanceid'); $courseids = []; - foreach ($completelist as $component) { + foreach ($contextlistcollection 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. + // All course contexts have been accounted for, so skip all checks. + if (empty($coursecontextids)) { + break; + } + // Only course, module, and block contexts are checked. + if (in_array($context->contextlevel, [CONTEXT_USER, CONTEXT_SYSTEM, CONTEXT_COURSECAT])) { continue; } - foreach ($coursecontextids as $contextid => $record) { + // If the context is a course, then we just add it without the need to check context path. + if ($context->contextlevel == CONTEXT_COURSE) { + $courseids[$context->id] = $context->instanceid; + unset($coursecontextids[$context->id]); + continue; + } + // Otherwise, we need to check all the course context paths, to see if this context is a descendant. + foreach ($coursecontextids as $contextid => $instanceid) { if (stripos($context->path, '/' . $contextid . '/') !== false) { - $courseids[$contextid] = $record->instanceid; + $courseids[$contextid] = $instanceid; + unset($coursecontextids[$contextid]); } } } @@ -143,7 +162,8 @@ class provider implements 'fullname' => $course->fullname, 'shortname' => $course->shortname, 'idnumber' => $course->idnumber, - 'summary' => writer::with_context($context)->rewrite_pluginfile_urls([], 'course', 'summary', 0, $course->summary), + 'summary' => writer::with_context($context)->rewrite_pluginfile_urls([], 'course', 'summary', 0, + format_string($course->summary)), 'format' => get_string('pluginname', 'format_' . $course->format), 'startdate' => transform::datetime($course->startdate), 'enddate' => transform::datetime($course->enddate) diff --git a/course/tests/privacy_test.php b/course/tests/privacy_test.php index c8b55b95ab3..b2d903363f0 100644 --- a/course/tests/privacy_test.php +++ b/course/tests/privacy_test.php @@ -67,33 +67,67 @@ class core_course_privacy_testcase extends \core_privacy\tests\provider_testcase $this->assertCount(2, $completiondata->criteria); } - public function test_export_complete_context_data() { + /** + * Verify that if a module context is included in the contextlist_collection and its parent course is not, the + * export_context_data() call picks this up, and that the contextual course information is included. + */ + public function test_export_context_data_module_context_only() { $this->resetAfterTest(); - $user = $this->getDataGenerator()->create_user(); + + // Create a course and a single module. $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); + $modassign = $this->getDataGenerator()->create_module('assign', ['course' => $course1->id, 'name' => 'assign test 1']); + $assigncontext = context_module::instance($modassign->cmid); + // Now, let's assume during user info export, only the coursemodule context is returned in the contextlist_collection. + $user = $this->getDataGenerator()->create_user(); $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]); + $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'mod_assign', [$assigncontext->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); + // Now, verify that core_course will detect this, and add relevant contextual information. + \core_course\privacy\provider::export_context_data($collection); + $writer = \core_privacy\local\request\writer::with_context($context1); + $this->assertTrue($writer->has_any_data()); + $writerdata = $writer->get_data(); + $this->assertObjectHasAttribute('fullname', $writerdata); + $this->assertObjectHasAttribute('shortname', $writerdata); + $this->assertObjectHasAttribute('idnumber', $writerdata); + $this->assertObjectHasAttribute('summary', $writerdata); + } + + /** + * Verify that if a module context and its parent course context are both included in the contextlist_collection, that course + * contextual information is present in the export. + */ + public function test_export_context_data_course_and_module_contexts() { + $this->resetAfterTest(); + + // Create a course and a single module. + $course1 = $this->getDataGenerator()->create_course(['fullname' => 'Course 1', 'shortname' => 'C1']); + $context1 = context_course::instance($course1->id); + $modassign = $this->getDataGenerator()->create_module('assign', ['course' => $course1->id, 'name' => 'assign test 1']); + $assigncontext = context_module::instance($modassign->cmid); + + // Now, assume during user info export, that both module and course contexts are returned in the contextlist_collection. + $user = $this->getDataGenerator()->create_user(); + $collection = new \core_privacy\local\request\contextlist_collection($user->id); + $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'mod_assign', [$assigncontext->id]); + $approvedlist2 = new \core_privacy\local\request\approved_contextlist($user, 'core_course', [$context1->id]); + $collection->add_contextlist($approvedlist); + $collection->add_contextlist($approvedlist2); + + // Now, verify that core_course still adds relevant contextual information, even for courses which are explicitly listed in + // the contextlist_collection. + \core_course\privacy\provider::export_context_data($collection); + $writer = \core_privacy\local\request\writer::with_context($context1); + $this->assertTrue($writer->has_any_data()); + $writerdata = $writer->get_data(); + $this->assertObjectHasAttribute('fullname', $writerdata); + $this->assertObjectHasAttribute('shortname', $writerdata); + $this->assertObjectHasAttribute('idnumber', $writerdata); + $this->assertObjectHasAttribute('summary', $writerdata); } /** diff --git a/privacy/classes/local/request/context_aware_provider.php b/privacy/classes/local/request/context_aware_provider.php index bc5a4b33708..01baad9beca 100644 --- a/privacy/classes/local/request/context_aware_provider.php +++ b/privacy/classes/local/request/context_aware_provider.php @@ -15,8 +15,7 @@ // along with Moodle. If not, see . /** - * This file contains the \core_privacy\local\request\plugin\provider interface to describe - * a class which provides data in some form for a plugin. + * File containing the provider interface for plugins needing access to all approved contexts to fill in relevant contextual data. * * Plugins should implement this if they need access to all approved contexts. * @@ -29,7 +28,7 @@ namespace core_privacy\local\request; defined('MOODLE_INTERNAL') || die(); /** - * The provider interface for plugins which need access to all approved contexts to fill in user data. + * The provider interface for plugins which need access to all approved contexts to fill in relevant contextual data. * * @copyright 2018 Adrian Greeve * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -37,9 +36,14 @@ defined('MOODLE_INTERNAL') || die(); interface context_aware_provider extends \core_privacy\local\request\core_data_provider { /** - * Export context information based on the whole approved context list collection. + * Give the component a chance to include any contextual information deemed relevant to any child contexts which are + * exporting personal data. + * + * By giving the component access to the full list of contexts being exported across all components, it can determine whether a + * descendant context is being exported, and decide whether to add relevant contextual information about itself. Having access + * to the full list of contexts being exported is what makes this component a context aware provider. * * @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 $contextcollection); + public static function export_context_data(\core_privacy\local\request\contextlist_collection $contextcollection); } diff --git a/privacy/classes/manager.php b/privacy/classes/manager.php index 14f20c7fda3..0cd66f29beb 100644 --- a/privacy/classes/manager.php +++ b/privacy/classes/manager.php @@ -228,13 +228,11 @@ class manager { if ($this->component_implements($component, \core_privacy\local\request\user_preference_provider::class)) { $this->get_provider_classname($component)::export_user_preferences($contextlistcollection->get_userid()); } - } - // Check each component for context aware providers. - foreach ($this->get_component_list() as $component) { - // Core user preference providers. + // Contextual information providers. Give each component a chance to include context information based on the + // existence of a child context in the contextlist_collection. if ($this->component_implements($component, \core_privacy\local\request\context_aware_provider::class)) { - $this->get_provider_classname($component)::export_complete_context_data($contextlistcollection); + $this->get_provider_classname($component)::export_context_data($contextlistcollection); } }