MDL-61951 grading: Implement privacy API
authorSara Arjona <sara@moodle.com>
Wed, 2 May 2018 12:29:59 +0000 (14:29 +0200)
committerSara Arjona <sara@moodle.com>
Wed, 9 May 2018 20:41:04 +0000 (22:41 +0200)
grade/grading/classes/privacy/gradingform_legacy_polyfill.php [new file with mode: 0644]
grade/grading/classes/privacy/gradingform_provider.php [new file with mode: 0644]
grade/grading/classes/privacy/provider.php [new file with mode: 0644]
grade/grading/form/guide/classes/privacy/provider.php [new file with mode: 0644]
grade/grading/form/guide/lang/en/gradingform_guide.php
grade/grading/form/guide/tests/privacy_test.php [new file with mode: 0644]
grade/grading/form/rubric/classes/privacy/provider.php [new file with mode: 0644]
grade/grading/form/rubric/lang/en/gradingform_rubric.php
grade/grading/tests/privacy_test.php [new file with mode: 0644]
lang/en/grading.php

diff --git a/grade/grading/classes/privacy/gradingform_legacy_polyfill.php b/grade/grading/classes/privacy/gradingform_legacy_polyfill.php
new file mode 100644 (file)
index 0000000..1fee16c
--- /dev/null
@@ -0,0 +1,69 @@
+<?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/>.
+
+/**
+ * This file contains the polyfill to allow a plugin to operate with Moodle 3.3 up.
+ *
+ * @package    core_grading
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_grading\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+use core_privacy\local\request\approved_contextlist;
+
+/**
+ * The trait used to provide backwards compatability for third-party plugins.
+ *
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+trait gradingform_legacy_polyfill {
+
+    /**
+     * This method is used to export any user data this sub-plugin has using the object to get the context and userid.
+     *
+     * @param \context $context Context owner of the data.
+     * @param \stdClass $definition Grading definition entry to export.
+     * @param  int $userid The user whose information is to be exported.
+     *
+     * @return \stdClass The data to export.
+     */
+    public static function get_gradingform_export_data(\context $context, $definition, $userid) {
+        return static::_get_gradingform_export_data($context, $definition, $userid);
+    }
+
+    /**
+     * Any call to this method should delete all user data for the context defined.
+     *
+     * @param \context $context Context owner of the data.
+     */
+    public static function delete_gradingform_for_context(\context $context) {
+        static::_delete_gradingform_for_context($context);
+    }
+
+    /**
+     * A call to this method should delete user data (where practicle) from the userid and context.
+     *
+     * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
+     */
+    public static function delete_gradingform_for_userid(approved_contextlist $contextlist) {
+        static::delete_gradingform_for_userid($contextlist);
+    }
+}
diff --git a/grade/grading/classes/privacy/gradingform_provider.php b/grade/grading/classes/privacy/gradingform_provider.php
new file mode 100644 (file)
index 0000000..f730594
--- /dev/null
@@ -0,0 +1,60 @@
+<?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/>.
+
+/**
+ * This file contains the grading method interface.
+ *
+ * Grading method plugins should implement this if they store personal information.
+ *
+ * @package    core_grading
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_grading\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+use core_privacy\local\request\approved_contextlist;
+
+interface gradingform_provider extends
+    \core_privacy\local\request\plugin\subplugin_provider {
+
+    /**
+     * This method is used to export any user data this sub-plugin has using the object to get the context and userid.
+     *
+     * @param \context $context Context owner of the data.
+     * @param \stdClass $definition Grading definition entry to export.
+     * @param  int $userid The user whose information is to be exported.
+     *
+     * @return \stdClass The data to export.
+     */
+    public static function get_gradingform_export_data(\context $context, $definition, int $userid);
+
+    /**
+     * Any call to this method should delete all user data for the context defined.
+     *
+     * @param \context $context Context owner of the data.
+     */
+    public static function delete_gradingform_for_context(\context $context);
+
+    /**
+     * A call to this method should delete user data (where practicle) from the userid and context.
+     *
+     * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
+     */
+    public static function delete_gradingform_for_userid(approved_contextlist $contextlist);
+}
diff --git a/grade/grading/classes/privacy/provider.php b/grade/grading/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..7584068
--- /dev/null
@@ -0,0 +1,287 @@
+<?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_grading
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_grading\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+use \core_privacy\local\metadata\collection;
+use \core_privacy\local\request\approved_contextlist;
+use \core_privacy\local\request\contextlist;
+use \core_privacy\local\request\transform;
+use \core_privacy\local\request\writer;
+use \core_privacy\manager;
+
+/**
+ * Privacy class for requesting user data.
+ *
+ * @copyright  2018 Sara Arjona <sara@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\subsystem\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_database_table('grading_definitions', [
+                'method' => 'privacy:metadata:grading_definitions:method',
+                'areaid' => 'privacy:metadata:grading_definitions:areaid',
+                'name' => 'privacy:metadata:grading_definitions:name',
+                'description' => 'privacy:metadata:grading_definitions:description',
+                'status' => 'privacy:metadata:grading_definitions:status',
+                'copiedfromid' => 'privacy:metadata:grading_definitions:copiedfromid',
+                'timecopied' => 'privacy:metadata:grading_definitions:timecopied',
+                'timecreated' => 'privacy:metadata:grading_definitions:timecreated',
+                'usercreated' => 'privacy:metadata:grading_definitions:usercreated',
+                'timemodified' => 'privacy:metadata:grading_definitions:timemodified',
+                'usermodified' => 'privacy:metadata:grading_definitions:usermodified',
+                'options' => 'privacy:metadata:grading_definitions:options',
+            ], 'privacy:metadata:grading_definitions');
+
+        $collection->add_database_table('grading_instances', [
+                'raterid' => 'privacy:metadata:grading_instances:raterid',
+                'rawgrade' => 'privacy:metadata:grading_instances:rawgrade',
+                'status' => 'privacy:metadata:grading_instances:status',
+                'feedback' => 'privacy:metadata:grading_instances:feedback',
+                'feedbackformat' => 'privacy:metadata:grading_instances:feedbackformat',
+                'timemodified' => 'privacy:metadata:grading_instances:timemodified',
+            ], 'privacy:metadata:grading_instances');
+
+        // Link to subplugin.
+        $collection->add_plugintype_link('gradingform', [], 'privacy:metadata:gradingformpluginsummary');
+
+        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 {
+        $contextlist = new contextlist();
+
+        $sql = "SELECT c.id
+                  FROM {context} c
+                  JOIN {grading_areas} a ON a.contextid = c.id
+                  JOIN {grading_definitions} d ON d.areaid = a.id
+             LEFT JOIN {grading_instances} i ON i.definitionid = d.id
+                 WHERE c.contextlevel = :contextlevel
+                   AND (d.usercreated = :usercreated OR d.usermodified = :usermodified OR i.raterid = :raterid)";
+        $params = [
+            'usercreated' => $userid,
+            'usermodified' => $userid,
+            'raterid' => $userid,
+            'contextlevel' => CONTEXT_MODULE
+        ];
+        $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) {
+        // Remove contexts different from MODULE.
+        $contexts = array_reduce($contextlist->get_contexts(), function($carry, $context) {
+            if ($context->contextlevel == CONTEXT_MODULE) {
+                $carry[] = $context;
+            }
+            return $carry;
+        }, []);
+
+        if (empty($contexts)) {
+            return;
+        }
+
+        $userid = $contextlist->get_user()->id;
+        $subcontext = [get_string('gradingmethod', 'grading')];
+        foreach ($contexts as $context) {
+            // Export grading definitions created or modified on this context.
+            self::export_definitions($context, $subcontext, $userid);
+        }
+    }
+
+    /**
+     * Exports the data related to grading definitions within the specified context/subcontext.
+     *
+     * @param  \context         $context Context owner of the data.
+     * @param  array            $subcontext Subcontext owner of the data.
+     * @param  int              $userid The user whose information is to be exported.
+     */
+    protected static function export_definitions(\context $context, array $subcontext, int $userid = 0) {
+        global $DB;
+
+        $join = "JOIN {grading_areas} a ON a.id = d.areaid
+                 JOIN {context} c ON a.contextid = c.id AND c.contextlevel = :contextlevel";
+        $select = 'a.contextid = :contextid';
+        $params = [
+            'contextlevel' => CONTEXT_MODULE,
+            'contextid'    => $context->id
+        ];
+
+        if (!empty($userid)) {
+            $join .= ' LEFT JOIN {grading_instances} i ON i.definitionid = d.id';
+            $select .= ' AND (usercreated = :usercreated
+                OR usermodified = :usermodified OR i.raterid = :raterid)';
+            $params['usercreated'] = $userid;
+            $params['usermodified'] = $userid;
+            $params['raterid'] = $userid;
+        }
+
+        $sql = "SELECT DISTINCT d.id,
+                                d.method,
+                                d.name,
+                                d.description,
+                                d.timecopied,
+                                d.timecreated,
+                                d.usercreated,
+                                d.timemodified,
+                                d.usermodified
+                           FROM {grading_definitions} d
+                           $join
+                           WHERE $select";
+        $definitions = $DB->get_recordset_sql($sql, $params);
+        $defdata = [];
+        foreach ($definitions as $definition) {
+            $tmpdata = [
+                'method' => $definition->method,
+                'name' => $definition->name,
+                'description' => $definition->description,
+                'timecreated' => transform::datetime($definition->timecreated),
+                'usercreated' => transform::user($definition->usercreated),
+                'timemodified' => transform::datetime($definition->timemodified),
+                'usermodified' => transform::user($definition->usermodified),
+            ];
+            if (!empty($definition->timecopied)) {
+                $tmpdata['timecopied'] = transform::datetime($definition->timecopied);
+            }
+            // Export gradingform information (if needed).
+            $instancedata = manager::component_class_callback(
+                "gradingform_{$definition->method}",
+                gradingform_provider::class,
+                'get_gradingform_export_data',
+                [$context, $definition, $userid]
+            );
+            if (null !== $instancedata) {
+                $tmpdata = array_merge($tmpdata, $instancedata);
+            }
+
+            $defdata[] = (object) $tmpdata;
+
+            // Export grading_instances information.
+            self::export_grading_instances($context, $subcontext, $definition->id, $userid);
+        }
+        $definitions->close();
+
+        if (!empty($defdata)) {
+            $data = (object) [
+                'definitions' => $defdata,
+            ];
+
+            writer::with_context($context)->export_data($subcontext, $data);
+        }
+    }
+
+    /**
+     * Exports the data related to grading instances within the specified definition.
+     *
+     * @param  \context         $context Context owner of the data.
+     * @param  array            $subcontext Subcontext owner of the data.
+     * @param  int              $definitionid The definition ID whose grading instance information is to be exported.
+     * @param  int              $userid The user whose information is to be exported.
+     */
+    protected static function export_grading_instances(\context $context, array $subcontext, int $definitionid, int $userid = 0) {
+        global $DB;
+
+        $params = ['definitionid' => $definitionid];
+        if (!empty($userid)) {
+            $params['raterid'] = $userid;
+        }
+        $instances = $DB->get_recordset('grading_instances', $params);
+        $instancedata = [];
+        foreach ($instances as $instance) {
+            // TODO: Get the status name (instead of the ID).
+            $tmpdata = [
+                'rawgrade' => $instance->rawgrade,
+                'status' => $instance->status,
+                'feedback' => $instance->feedback,
+                'feedbackformat' => $instance->feedbackformat,
+                'timemodified' => transform::datetime($instance->timemodified),
+            ];
+            $instancedata[] = (object) $tmpdata;
+        }
+        $instances->close();
+
+        if (!empty($instancedata)) {
+            $data = (object) [
+                'instances' => $instancedata,
+            ];
+
+            writer::with_context($context)->export_related_data($subcontext, 'gradinginstances', $data);
+        }
+    }
+
+    /**
+     * Delete all use data which matches the specified $context.
+     *
+     * We never delete grading content.
+     *
+     * @param context $context A user context.
+     */
+    public static function delete_data_for_all_users_in_context(\context $context) {
+        manager::plugintype_class_callback(
+            'gradingform',
+            gradingform_provider::class,
+            'delete_gradingform_for_context',
+            [$context]
+        );
+    }
+
+    /**
+     * Delete all user data for the specified user, in the specified contexts.
+     *
+     * We never delete grading content.
+     *
+     * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
+     */
+    public static function delete_data_for_user(approved_contextlist $contextlist) {
+        manager::plugintype_class_callback(
+            'gradingform',
+            gradingform_provider::class,
+            'delete_gradingform_for_userid',
+            [$contextlist]
+        );
+    }
+}
diff --git a/grade/grading/form/guide/classes/privacy/provider.php b/grade/grading/form/guide/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..a0f4494
--- /dev/null
@@ -0,0 +1,89 @@
+<?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    gradingform_guide
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace gradingform_guide\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+use \core_privacy\local\metadata\collection;
+use \core_privacy\local\request\transform;
+use \core_privacy\local\request\writer;
+
+/**
+ * Privacy class for requesting user data.
+ *
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements
+        \core_privacy\local\request\user_preference_provider {
+
+    /**
+     * Return the fields which contain personal data.
+     *
+     * @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_user_preference(
+            'gradingform_guide-showmarkerdesc',
+            'privacy:metadata:preference:showmarkerdesc'
+        );
+        $collection->add_user_preference(
+            'gradingform_guide-showstudentdesc',
+            'privacy:metadata:preference:showstudentdesc'
+        );
+
+        return $collection;
+    }
+
+    /**
+     * 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) {
+        $prefvalue = get_user_preferences('gradingform_guide-showmarkerdesc', null, $userid);
+        if ($prefvalue !== null) {
+            $transformedvalue = transform::yesno($prefvalue);
+            writer::export_user_preference(
+                'gradingform_guide',
+                'gradingform_guide-showmarkerdesc',
+                $transformedvalue,
+                get_string('privacy:metadata:preference:showmarkerdesc', 'gradingform_guide')
+            );
+        }
+
+        $prefvalue = get_user_preferences('gradingform_guide-showstudentdesc', null, $userid);
+        if ($prefvalue !== null) {
+            $transformedvalue = transform::yesno($prefvalue);
+            writer::export_user_preference(
+                'gradingform_guide',
+                'gradingform_guide-showstudentdesc',
+                $transformedvalue,
+                get_string('privacy:metadata:preference:showstudentdesc', 'gradingform_guide')
+            );
+        }
+    }
+}
index ed2ae2d..3073ae5 100644 (file)
@@ -75,6 +75,8 @@ $string['name'] = 'Name';
 $string['needregrademessage'] = 'The marking guide definition was changed after this student had been graded. The student can not see this marking guide until you check the marking guide and update the grade.';
 $string['pluginname'] = 'Marking guide';
 $string['previewmarkingguide'] = 'Preview marking guide';
 $string['needregrademessage'] = 'The marking guide definition was changed after this student had been graded. The student can not see this marking guide until you check the marking guide and update the grade.';
 $string['pluginname'] = 'Marking guide';
 $string['previewmarkingguide'] = 'Preview marking guide';
+$string['privacy:metadata:preference:showmarkerdesc'] = 'Whether to show marker criterion descriptions';
+$string['privacy:metadata:preference:showstudentdesc'] = 'Whether to show student criterion descriptions';
 $string['regrademessage1'] = 'You are about to save changes to a marking guide that has already been used for grading. Please indicate if existing grades need to be reviewed. If you set this then the marking guide will be hidden from students until their item is regraded.';
 $string['regrademessage5'] = 'You are about to save significant changes to a marking guide that has already been used for grading. The gradebook value will be unchanged, but the marking guide will be hidden from students until their item is regraded.';
 $string['regradeoption0'] = 'Do not mark for regrade';
 $string['regrademessage1'] = 'You are about to save changes to a marking guide that has already been used for grading. Please indicate if existing grades need to be reviewed. If you set this then the marking guide will be hidden from students until their item is regraded.';
 $string['regrademessage5'] = 'You are about to save significant changes to a marking guide that has already been used for grading. The gradebook value will be unchanged, but the marking guide will be hidden from students until their item is regraded.';
 $string['regradeoption0'] = 'Do not mark for regrade';
diff --git a/grade/grading/form/guide/tests/privacy_test.php b/grade/grading/form/guide/tests/privacy_test.php
new file mode 100644 (file)
index 0000000..4fc4278
--- /dev/null
@@ -0,0 +1,77 @@
+<?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 gradingform_guide.
+ *
+ * @package    gradingform_guide
+ * @category   test
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+use \core_privacy\tests\provider_testcase;
+use \core_privacy\local\request\writer;
+use \gradingform_guide\privacy\provider;
+
+/**
+ * Privacy tests for gradingform_guide.
+ *
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class gradingform_guide_privacy_testcase extends provider_testcase {
+
+    /**
+     * Ensure that export_user_preferences returns no data if the user has no data.
+     */
+    public function test_export_user_preferences_not_defined() {
+        $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 single preferences.
+     */
+    public function test_export_user_preferences() {
+        $this->resetAfterTest();
+
+        // Define a user preference.
+        $user = $this->getDataGenerator()->create_user();
+        $this->setUser($user);
+        set_user_preference('gradingform_guide-showmarkerdesc', 0, $user);
+        set_user_preference('gradingform_guide-showstudentdesc', 1, $user);
+
+        // Validate exported data.
+        provider::export_user_preferences($user->id);
+        $context = context_user::instance($user->id);
+        $writer = writer::with_context($context);
+        $this->assertTrue($writer->has_any_data());
+        $prefs = $writer->get_user_preferences('gradingform_guide');
+        $this->assertCount(2, (array) $prefs);
+        $this->assertEquals(
+            get_string('privacy:metadata:preference:showstudentdesc', 'gradingform_guide'),
+            $prefs->{'gradingform_guide-showstudentdesc'}->description
+        );
+        $this->assertEquals(get_string('no'), $prefs->{'gradingform_guide-showmarkerdesc'}->value);
+        $this->assertEquals(get_string('yes'), $prefs->{'gradingform_guide-showstudentdesc'}->value);
+    }
+}
diff --git a/grade/grading/form/rubric/classes/privacy/provider.php b/grade/grading/form/rubric/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..53bd41b
--- /dev/null
@@ -0,0 +1,47 @@
+<?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    gradingform_rubric
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace gradingform_rubric\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy class for requesting user data.
+ *
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements
+        \core_privacy\local\metadata\null_provider {
+
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
index e269175..be7e180 100644 (file)
@@ -59,6 +59,7 @@ $string['name'] = 'Name';
 $string['needregrademessage'] = 'The rubric definition was changed after this student had been graded. The student can not see this rubric until you check the rubric and update the grade.';
 $string['pluginname'] = 'Rubric';
 $string['previewrubric'] = 'Preview rubric';
 $string['needregrademessage'] = 'The rubric definition was changed after this student had been graded. The student can not see this rubric until you check the rubric and update the grade.';
 $string['pluginname'] = 'Rubric';
 $string['previewrubric'] = 'Preview rubric';
+$string['privacy:metadata'] = 'The rubric grading form plugin does not store any personal data.';
 $string['regrademessage1'] = 'You are about to save changes to a rubric that has already been used for grading. Please indicate if existing grades need to be reviewed. If you set this then the rubric will be hidden from students until their item is regraded.';
 $string['regrademessage5'] = 'You are about to save significant changes to a rubric that has already been used for grading. The gradebook value will be unchanged, but the rubric will be hidden from students until their item is regraded.';
 $string['regradeoption0'] = 'Do not mark for regrade';
 $string['regrademessage1'] = 'You are about to save changes to a rubric that has already been used for grading. Please indicate if existing grades need to be reviewed. If you set this then the rubric will be hidden from students until their item is regraded.';
 $string['regrademessage5'] = 'You are about to save significant changes to a rubric that has already been used for grading. The gradebook value will be unchanged, but the rubric will be hidden from students until their item is regraded.';
 $string['regradeoption0'] = 'Do not mark for regrade';
diff --git a/grade/grading/tests/privacy_test.php b/grade/grading/tests/privacy_test.php
new file mode 100644 (file)
index 0000000..8fcc46c
--- /dev/null
@@ -0,0 +1,330 @@
+<?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_grading.
+ *
+ * @package    core_grading
+ * @category   test
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+use \core_privacy\tests\provider_testcase;
+use \core_privacy\local\request\approved_contextlist;
+use \core_privacy\local\request\transform;
+use \core_privacy\local\request\writer;
+use \core_grading\privacy\provider;
+
+/**
+ * Privacy tests for core_grading.
+ *
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_grading_privacy_testcase extends provider_testcase {
+
+    /** @var stdClass User without data. */
+    protected $user0;
+
+    /** @var stdClass User with data. */
+    protected $user1;
+
+    /** @var stdClass User with data. */
+    protected $user2;
+
+    /** @var context context_module of an activity without grading definitions. */
+    protected $instancecontext0;
+
+    /** @var context context_module of the activity where the grading definitions are. */
+    protected $instancecontext1;
+
+    /** @var context context_module of the activity where the grading definitions are. */
+    protected $instancecontext2;
+
+    /**
+     * Test getting the context for the user ID related to this plugin.
+     */
+    public function test_get_contexts_for_userid() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->grading_setup_test_scenario_data();
+        $this->assertCount(2, $DB->get_records('grading_definitions'));
+
+        // User1 has created grading definitions for instance1 and instance2.
+        $contextlist = provider::get_contexts_for_userid($this->user1->id);
+        $this->assertCount(2, $contextlist);
+        $this->assertContains($this->instancecontext1->id, $contextlist->get_contextids());
+        $this->assertContains($this->instancecontext2->id, $contextlist->get_contextids());
+        $this->assertNotContains($this->instancecontext0->id, $contextlist->get_contextids());
+
+        // User2 has only modified grading definitions for instance2.
+        $contextlist = provider::get_contexts_for_userid($this->user2->id);
+        $this->assertCount(1, $contextlist);
+        $this->assertContains($this->instancecontext2->id, $contextlist->get_contextids());
+
+        // User0 hasn't created or modified any grading definition.
+        $contextlist = provider::get_contexts_for_userid($this->user0->id);
+        $this->assertCount(0, $contextlist);
+    }
+
+    /**
+     * Export for a user with no grading definitions created or modified will not have any data exported.
+     */
+    public function test_export_user_data_no_content() {
+        $this->resetAfterTest();
+
+        $user = $this->getDataGenerator()->create_user();
+        $this->setUser($user);
+        $context = \context_system::instance();
+
+        $writer = writer::with_context($context);
+        $this->assertFalse($writer->has_any_data());
+        $this->export_context_data_for_user($user->id, $context, 'core_grading');
+        $this->assertFalse(writer::with_context($context)->has_any_data());
+    }
+
+    /**
+     * Test that data is exported correctly for this plugin.
+     */
+    public function test_export_user_data() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $now = time();
+        $defnameprefix = 'fakename';
+        $this->grading_setup_test_scenario_data($defnameprefix, $now);
+        $this->assertCount(2, $DB->get_records('grading_definitions'));
+
+        // Validate exported data: instance1 - user0 has NO data.
+        $this->setUser($this->user0);
+        writer::reset();
+        $writer = writer::with_context($this->instancecontext1);
+        $this->assertFalse($writer->has_any_data());
+        $this->export_context_data_for_user($this->user0->id, $this->instancecontext1, 'core_grading');
+        $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
+        $this->assertEmpty($data);
+
+        // Validate exported data: instance0 - user1 has NO data.
+        $this->setUser($this->user1);
+        writer::reset();
+        $writer = writer::with_context($this->instancecontext0);
+        $this->assertFalse($writer->has_any_data());
+        $this->export_context_data_for_user($this->user1->id, $this->instancecontext0, 'core_grading');
+        $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
+        $this->assertEmpty($data);
+
+        // Validate exported data: instance1 - user1 has data (user has created and modified it).
+        writer::reset();
+        $writer = writer::with_context($this->instancecontext1);
+        $this->assertFalse($writer->has_any_data());
+        $this->export_context_data_for_user($this->user1->id, $this->instancecontext1, 'core_grading');
+        $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
+        $this->assertCount(1, $data->definitions);
+
+        $firstkey = reset($data->definitions);
+        $this->assertNotEmpty($firstkey->name);
+        $this->assertEquals('test_method', $firstkey->method);
+        $this->assertEquals(transform::datetime($now), $firstkey->timecreated);
+        $this->assertEquals($this->user1->id, $firstkey->usercreated);
+        $this->assertEquals($defnameprefix.'1', $firstkey->name);
+
+        // Validate exported data: instance2 - user1 has data (user has created it).
+        writer::reset();
+        $writer = writer::with_context($this->instancecontext2);
+        $this->assertFalse($writer->has_any_data());
+        $this->export_context_data_for_user($this->user1->id, $this->instancecontext2, 'core_grading');
+        $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
+        $this->assertCount(1, $data->definitions);
+
+        $firstkey = reset($data->definitions);
+        $this->assertNotEmpty($firstkey->name);
+        $this->assertEquals('test_method', $firstkey->method);
+        $this->assertEquals(transform::datetime($now), $firstkey->timecreated);
+        $this->assertEquals($this->user1->id, $firstkey->usercreated);
+        $this->assertEquals($defnameprefix.'2', $firstkey->name);
+
+        // Validate exported data: instance1 - user2 has NO data.
+        $this->setUser($this->user2);
+        writer::reset();
+        $writer = writer::with_context($this->instancecontext1);
+        $this->assertFalse($writer->has_any_data());
+        $this->export_context_data_for_user($this->user2->id, $this->instancecontext1, 'core_grading');
+        $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
+        $this->assertEmpty($data);
+
+        // Validate exported data: instance2 - user2 has data (user has modified it).
+        $this->setUser($this->user2);
+        writer::reset();
+        $writer = writer::with_context($this->instancecontext2);
+        $this->assertFalse($writer->has_any_data());
+        $this->export_context_data_for_user($this->user2->id, $this->instancecontext2, 'core_grading');
+        $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
+        $this->assertCount(1, $data->definitions);
+    }
+
+    /**
+     * Test for provider::delete_data_for_all_users_in_context().
+     */
+    public function test_delete_data_for_all_users_in_context() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->grading_setup_test_scenario_data();
+
+        // Before deletion, we should have 2 grading_definitions.
+        $this->assertCount(2, $DB->get_records('grading_definitions'));
+
+        // Delete data.
+        provider::delete_data_for_all_users_in_context($this->instancecontext0);
+        provider::delete_data_for_all_users_in_context($this->instancecontext1);
+        provider::delete_data_for_all_users_in_context($this->instancecontext2);
+
+        // Before deletion, we should have same grading_definitions (nothing was deleted).
+        $this->assertCount(2, $DB->get_records('grading_definitions'));
+    }
+
+    /**
+     * Test for provider::delete_data_for_user().
+     */
+    public function test_delete_data_for_user() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->grading_setup_test_scenario_data();
+
+        // Before deletion, we should have 2 grading_definitions.
+        $this->assertCount(2, $DB->get_records('grading_definitions'));
+
+        // Delete data for $user0.
+        $contextlist = provider::get_contexts_for_userid($this->user0->id);
+        $approvedcontextlist = new approved_contextlist(
+            $this->user0,
+            'core_grading',
+            $contextlist->get_contextids()
+        );
+        provider::delete_data_for_user($approvedcontextlist);
+
+        // Delete data for $user1.
+        $contextlist = provider::get_contexts_for_userid($this->user1->id);
+        $approvedcontextlist = new approved_contextlist(
+            $this->user1,
+            'core_grading',
+            $contextlist->get_contextids()
+        );
+        provider::delete_data_for_user($approvedcontextlist);
+
+        // Delete data for $user2.
+        $contextlist = provider::get_contexts_for_userid($this->user2->id);
+        $approvedcontextlist = new approved_contextlist(
+            $this->user2,
+            'core_grading',
+            $contextlist->get_contextids()
+        );
+        provider::delete_data_for_user($approvedcontextlist);
+
+        // Before deletion, we should have same grading_definitions (nothing was deleted).
+        $this->assertCount(2, $DB->get_records('grading_definitions'));
+    }
+
+    /**
+     * Helper function to setup the environment.
+     *
+     * course
+     *  |
+     *  +--instance0 (assignment)
+     *  |   |
+     *  +--instance1 (assignment)
+     *  |   |
+     *  |   +--grading_definition1 (created and modified by user1)
+     *  |   |
+     *  +--instance2 (assignment)
+     *  |   |
+     *  |   +--grading_definition2 (created by user1 and modified by user2)
+     *
+     *
+     * user0 hasn't any data.
+     *
+     * @param string $defnameprefix
+     * @param timestamp $now
+     */
+    protected function grading_setup_test_scenario_data($defnameprefix = null, $now = null) {
+        global $DB;
+
+        $this->user0 = $this->getDataGenerator()->create_user();
+        $this->user1 = $this->getDataGenerator()->create_user();
+        $this->user2 = $this->getDataGenerator()->create_user();
+
+        // Create a course.
+        $course = $this->getDataGenerator()->create_course();
+        $coursecontext = context_course::instance($course->id);
+
+        // Create some assignment instances.
+        $params = (object)array(
+            'course' => $course->id,
+            'name'   => 'Testing instance'
+        );
+        $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
+        $instance0 = $generator->create_instance($params);
+        $cm0 = get_coursemodule_from_instance('assign', $instance0->id);
+        $this->instancecontext0 = context_module::instance($cm0->id);
+        $instance1 = $generator->create_instance($params);
+        $cm1 = get_coursemodule_from_instance('assign', $instance1->id);
+        $this->instancecontext1 = context_module::instance($cm1->id);
+        $instance2 = $generator->create_instance($params);
+        $cm2 = get_coursemodule_from_instance('assign', $instance2->id);
+        $this->instancecontext2 = context_module::instance($cm2->id);
+
+        // Create fake grading areas.
+        $fakearea1 = (object)array(
+            'contextid'    => $this->instancecontext1->id,
+            'component'    => 'mod_assign',
+            'areaname'     => 'submissions',
+            'activemethod' => 'test_method'
+        );
+        $fakeareaid1 = $DB->insert_record('grading_areas', $fakearea1);
+        $fakearea2 = clone($fakearea1);
+        $fakearea2->contextid = $this->instancecontext2->id;
+        $fakeareaid2 = $DB->insert_record('grading_areas', $fakearea2);
+
+        // Create fake grading definitions.
+        if (empty($now)) {
+            $now = time();
+        }
+        if (empty($defnameprefix)) {
+            $defnameprefix = 'fakename';
+        }
+        $fakedefinition1 = (object)array(
+            'areaid'       => $fakeareaid1,
+            'method'       => 'test_method',
+            'name'         => $defnameprefix.'1',
+            'status'       => 0,
+            'timecreated'  => $now,
+            'usercreated'  => $this->user1->id,
+            'timemodified' => $now + 1,
+            'usermodified' => $this->user1->id,
+        );
+        $fakedefid1 = $DB->insert_record('grading_definitions', $fakedefinition1);
+        $fakedefinition2 = clone($fakedefinition1);
+        $fakedefinition2->areaid = $fakeareaid2;
+        $fakedefinition2->name = $defnameprefix.'2';
+        $fakedefinition2->usermodified = $this->user2->id;
+        $fakedefid2 = $DB->insert_record('grading_definitions', $fakedefinition2);
+    }
+}
index c0849e1..d19e3ff 100644 (file)
@@ -57,6 +57,27 @@ $string['manageactionshareconfirm'] = 'You are going to save a copy of the gradi
 $string['manageactionsharedone'] = 'The form was successfully saved as a template';
 $string['noitemid'] = 'Grading not possible. The graded item does not exist.';
 $string['nosharedformfound'] = 'No template found';
 $string['manageactionsharedone'] = 'The form was successfully saved as a template';
 $string['noitemid'] = 'Grading not possible. The graded item does not exist.';
 $string['nosharedformfound'] = 'No template found';
+$string['privacy:metadata:gradingformpluginsummary'] = 'Data for the grading method.';
+$string['privacy:metadata:grading_definitions'] = 'Basic information about an advanced grading form defined in a gradable area.';
+$string['privacy:metadata:grading_definitions:areaid'] = 'The area ID where the advanced grading form is defined.';
+$string['privacy:metadata:grading_definitions:copiedfromid'] = 'The grading definition ID from where this was copied.';
+$string['privacy:metadata:grading_definitions:description'] = 'The description of the advanced grading method.';
+$string['privacy:metadata:grading_definitions:method'] = 'The grading method which is responsible for the definition.';
+$string['privacy:metadata:grading_definitions:name'] = 'The name of the advanced grading definition.';
+$string['privacy:metadata:grading_definitions:options'] = 'Some settings of this grading definition.';
+$string['privacy:metadata:grading_definitions:status'] = 'The status of this advanced grading definition.';
+$string['privacy:metadata:grading_definitions:timecopied'] = 'The time when the grading definition was copied.';
+$string['privacy:metadata:grading_definitions:timecreated'] = 'The time when the grading definition was created.';
+$string['privacy:metadata:grading_definitions:timemodified'] = 'The time when the grading definition was last modified.';
+$string['privacy:metadata:grading_definitions:usercreated'] = 'The ID of the user who created the grading definition.';
+$string['privacy:metadata:grading_definitions:usermodified'] = 'The ID of the user who last modified the grading definition.';
+$string['privacy:metadata:grading_instances'] = 'Assessment record for one gradable item assessed by one rater.';
+$string['privacy:metadata:grading_instances:feedback'] = 'The feedback given by the user.';
+$string['privacy:metadata:grading_instances:feedbackformat'] = 'The text format of the feedback given by the user.';
+$string['privacy:metadata:grading_instances:raterid'] = 'The ID of the user who rated the grading instance.';
+$string['privacy:metadata:grading_instances:rawgrade'] = 'The grade for the grading instance.';
+$string['privacy:metadata:grading_instances:status'] = 'The status of this grading instance.';
+$string['privacy:metadata:grading_instances:timemodified'] = 'The time when the grading instance was last modified.';
 $string['searchtemplate'] = 'Grading forms search';
 $string['searchtemplate_help'] = 'You can search for a grading form and use it as a template for the new grading form here. Simply type words that should appear somewhere in the form name, its description or the form body itself. To search for a phrase, wrap the whole query in double quotes.
 
 $string['searchtemplate'] = 'Grading forms search';
 $string['searchtemplate_help'] = 'You can search for a grading form and use it as a template for the new grading form here. Simply type words that should appear somewhere in the form name, its description or the form body itself. To search for a phrase, wrap the whole query in double quotes.