MDL-62029 core_course: Create privacy files for core_course.
authorAdrian Greeve <adrian@moodle.com>
Wed, 18 Apr 2018 07:05:05 +0000 (15:05 +0800)
committerJake Dallimore <jake@moodle.com>
Wed, 9 May 2018 02:11:41 +0000 (10:11 +0800)
course/classes/privacy/provider.php [new file with mode: 0644]
course/tests/privacy_test.php [new file with mode: 0644]
lang/en/course.php [new file with mode: 0644]

diff --git a/course/classes/privacy/provider.php b/course/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..a4c209d
--- /dev/null
@@ -0,0 +1,200 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy class for requesting user data.
+ *
+ * @package    core_course
+ * @copyright  2018 Adrian Greeve <adrian@moodle.com>
+ * @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 <adrian@moodle.com>
+ * @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 (file)
index 0000000..c8b55b9
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Privacy tests for core_course.
+ *
+ * @package    core_course
+ * @category   test
+ * @copyright  2018 Adrian Greeve <adrian@moodle.com>
+ * @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 <adrian@moodle.com>
+ * @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 (file)
index 0000000..bd1a992
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Strings for component 'course', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package   core_course
+ * @copyright 2018 Adrian Greeve <adriangreeve.com>
+ * @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.';