MDL-63622 core_grading: Add support for removal of context users.
authorAdrian Greeve <abgreeve@gmail.com>
Fri, 12 Oct 2018 05:18:55 +0000 (13:18 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Sun, 28 Oct 2018 23:37:44 +0000 (07:37 +0800)
Core grading now implements the new core_userlist_provider
interface to allow for deletion of specific users in a context.

grade/grading/classes/privacy/provider.php
grade/grading/form/guide/tests/privacy_test.php
grade/grading/tests/fixtures/marking_guide.php
grade/grading/tests/privacy_test.php

index 24ce67a..95b6267 100644 (file)
@@ -41,6 +41,8 @@ use \core_privacy\manager;
  */
 class provider implements
     \core_privacy\local\metadata\provider,
+    \core_privacy\local\request\plugin\provider,
+    \core_privacy\local\request\core_userlist_provider,
     \core_privacy\local\request\subsystem\provider {
 
     /**
@@ -107,6 +109,34 @@ class provider implements
         return $contextlist;
     }
 
+    /**
+     * Get the list of contexts that contain user information for the specified user.
+     *
+     * @param   userlist    $userlist   The userlist containing the list of users who have data in this context/plugin combination.
+     */
+    public static function get_users_in_context(\core_privacy\local\request\userlist $userlist) {
+        $context = $userlist->get_context();
+        if ($context->contextlevel != CONTEXT_MODULE) {
+            return;
+        }
+
+        $params = ['contextid' => $context->id];
+
+        $sql = "SELECT d.usercreated, d.usermodified
+                  FROM {grading_definitions} d
+                  JOIN {grading_areas} a ON a.id = d.areaid
+                  WHERE a.contextid = :contextid";
+        $userlist->add_from_sql('usercreated', $sql, $params);
+        $userlist->add_from_sql('usermodified', $sql, $params);
+
+        $sql = "SELECT i.raterid
+                  FROM {grading_definitions} d
+                  JOIN {grading_areas} a ON a.id = d.areaid
+                  JOIN {grading_instances} i ON i.definitionid = d.id
+                  WHERE a.contextid = :contextid";
+        $userlist->add_from_sql('raterid', $sql, $params);
+    }
+
     /**
      * Export all user data for the specified user, in the specified contexts.
      *
@@ -171,12 +201,27 @@ class provider implements
      * @param  int|null $itemid  An optional item ID to refine the deletion.
      */
     public static function delete_instance_data(\context $context, int $itemid = null) {
+        if (is_null($itemid)) {
+            self::delete_data_for_instances($context);
+        } else {
+            self::delete_data_for_instances($context, [$itemid]);
+        }
+    }
+
+    /**
+     * Deletes all user data related to a context and possibly itemids.
+     *
+     * @param  \context $context The context to delete on.
+     * @param  array $itemids  An optional list of item IDs to refine the deletion.
+     */
+    public static function delete_data_for_instances(\context $context, array $itemids = []) {
         global $DB;
         $itemsql = '';
         $params = ['contextid' => $context->id];
-        if (isset($itemid)) {
-            $params['itemid'] = $itemid;
-            $itemsql = 'AND gi.itemid = :itemid';
+        if (!empty($itemids)) {
+            list($itemsql, $itemparams) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED);
+            $params = array_merge($params, $itemparams);
+            $itemsql = "AND itemid $itemsql";
         }
         $sql = "SELECT gi.id AS instanceid, gd.id, gd.method
                   FROM {grading_definitions} gd
@@ -359,4 +404,13 @@ class provider implements
         );
         // End of section to be removed for final deprecation.
     }
+
+    /**
+     * Delete multiple users within a single context.
+     *
+     * @param approved_userlist $userlist The approved context and user information to delete information for.
+     */
+    public static function delete_data_for_users(\core_privacy\local\request\approved_userlist $userlist) {
+        // The only information left to be deleted here is the grading definitions. Currently we are not deleting these.
+    }
 }
index d2e118c..a2122eb 100644 (file)
@@ -106,28 +106,13 @@ class gradingform_guide_privacy_testcase extends provider_testcase {
         );
         $guide->create_guide();
 
-        $controller = $guide->manager->get_controller('guide');
         // In the situation of mod_assign this would be the id from assign_grades.
         $itemid = 1;
-        $instance = $controller->create_instance($user->id, $itemid);
-        // I need the ids for the criteria and there doesn't seem to be a nice method to get it.
-        $criteria = $DB->get_records('gradingform_guide_criteria');
-        $data = ['criteria' => []];
-        foreach ($criteria as $key => $value) {
-            if ($value->shortname == 'Spelling mistakes') {
-                $data['criteria'][$key]['remark'] = 'This user made several mistakes.';
-                $data['criteria'][$key]['remarkformat'] = 0;
-                $data['criteria'][$key]['score'] = 5;
-            } else {
-                $data['criteria'][$key]['remark'] = 'This user has two pictures.';
-                $data['criteria'][$key]['remarkformat'] = 0;
-                $data['criteria'][$key]['score'] = 10;
-            }
-        }
-        $data['itemid'] = $itemid;
-
-        // Update this instance with data.
-        $instance->update($data);
+        $gradedata = [
+            ['remark' => 'This user made several mistakes.', 'score' => 5],
+            ['remark' => 'This user has two pictures.', 'score' => 10]
+        ];
+        $instance = $guide->grade_item($user->id, $itemid, $gradedata);
         $instanceid = $instance->get_data('id');
 
         // Let's try the method we are testing.
index 027aeb0..d3b31ab 100644 (file)
@@ -45,6 +45,8 @@ class test_guide {
     protected $criterionid = 0;
     /** @var integer $sortorder The current id for the sort order. */
     protected $sortorder = 0;
+    /** @var gradingform_controller The grading form controller. */
+    protected $controller;
 
     /** @var grading_manager $manager The grading manager to handle creating the real marking guide. */
     public $manager;
@@ -92,8 +94,8 @@ class test_guide {
             'status' => 20
         ];
 
-        $controller = $this->manager->get_controller('guide');
-        $controller->update_definition($data);
+        $this->controller = $this->manager->get_controller('guide');
+        $this->controller->update_definition($data);
     }
 
     /**
@@ -115,4 +117,42 @@ class test_guide {
             'maxscore' => $maxscore
         ];
     }
+
+    /**
+     * Update the grade for the item provided.
+     * Keep the gradeinfo array in the same order as the definition of the criteria.
+     * The array should be [['remark' => remark, 'score' => intvalue],['remark' => remark, 'score' => intvalue]]
+     * for a guide that has two criteria.
+     *
+     * @param  int $userid The user we are updating.
+     * @param  int $itemid The itemid that the grade will be for
+     * @param  array $gradeinfo Comments and grades for the grade.
+     * @return gradingform_guide_instance The created instance associated with the grade created.
+     */
+    public function grade_item(int $userid, int $itemid, array $gradeinfo) : gradingform_guide_instance {
+        global $DB;
+
+        if (!isset($this->controller)) {
+            throw new Exception("Please call create_guide before calling this method", 1);
+        }
+
+        $instance = $this->controller->create_instance($userid, $itemid);
+
+        // I need the ids for the criteria and there doesn't seem to be a nice method to get it.
+        $criteria = $DB->get_records('gradingform_guide_criteria');
+        $data = ['criteria' => []];
+        $i = 0;
+        // The sort order should keep everything here in order.
+        foreach ($criteria as $key => $value) {
+            $data['criteria'][$key]['remark'] = $gradeinfo[$i]['remark'];
+            $data['criteria'][$key]['remarkformat'] = 0;
+            $data['criteria'][$key]['score'] = $gradeinfo[$i]['score'];
+            $i++;
+        }
+        $data['itemid'] = $itemid;
+
+        // Update this instance with data.
+        $instance->update($data);
+        return $instance;
+    }
 }
index 864da6b..d2a0feb 100644 (file)
@@ -87,6 +87,19 @@ class core_grading_privacy_testcase extends provider_testcase {
         $this->assertCount(0, $contextlist);
     }
 
+    /**
+     * Test retrieval of user ids in a given context.
+     */
+    public function test_get_users_in_context() {
+        $this->resetAfterTest();
+        $this->grading_setup_test_scenario_data();
+        // Instance two has one user who created the definitions and another who modified it.
+        $userlist = new \core_privacy\local\request\userlist($this->instancecontext2, 'core_grading');
+        provider::get_users_in_context($userlist);
+        // Check that we get both.
+        $this->assertCount(2, $userlist->get_userids());
+    }
+
     /**
      * Export for a user with no grading definitions created or modified will not have any data exported.
      */
@@ -274,32 +287,16 @@ class core_grading_privacy_testcase extends provider_testcase {
         );
         $guide->create_guide();
 
-        $controller = $guide->manager->get_controller('guide');
         // In the situation of mod_assign this would be the id from assign_grades.
         $itemid = 1;
-        $instance = $controller->create_instance($user->id, $itemid);
-        // I need the ids for the criteria and there doesn't seem to be a nice method to get it.
-        $criteria = $DB->get_records('gradingform_guide_criteria');
-        $data = ['criteria' => []];
-        foreach ($criteria as $key => $value) {
-            if ($value->shortname == 'Spelling mistakes') {
-                $data['criteria'][$key]['remark'] = 'This user made several mistakes.';
-                $data['criteria'][$key]['remarkformat'] = 0;
-                $data['criteria'][$key]['score'] = 5;
-            } else {
-                $data['criteria'][$key]['remark'] = 'This user has two pictures.';
-                $data['criteria'][$key]['remarkformat'] = 0;
-                $data['criteria'][$key]['score'] = 10;
-            }
-        }
-        $data['itemid'] = $itemid;
-
-        // Update this instance with data.
-        $instance->update($data);
-        $instanceid = $instance->get_data('id');
+        $gradedata = [
+            ['remark' => 'This user made several mistakes.', 'score' => 5],
+            ['remark' => 'This user has two pictures.', 'score' => 10]
+        ];
+        $instance = $guide->grade_item($user->id, $itemid, $gradedata);
 
         provider::export_item_data($modulecontext, $itemid, ['Test']);
-        $data = (array) writer::with_context($modulecontext)->get_data(['Test', 'Marking guide', $instanceid]);
+        $data = (array) writer::with_context($modulecontext)->get_data(['Test', 'Marking guide', $instance->get_data('id')]);
         $this->assertCount(2, $data);
         $this->assertEquals('This user made several mistakes.', $data['Spelling mistakes']->remark);
         $this->assertEquals(5, $data['Spelling mistakes']->score);
@@ -335,49 +332,20 @@ class core_grading_privacy_testcase extends provider_testcase {
         );
         $guide->create_guide();
 
-        $controller = $guide->manager->get_controller('guide');
         // In the situation of mod_assign this would be the id from assign_grades.
         $itemid = 1;
-        $instance = $controller->create_instance($user->id, $itemid);
-        // I need the ids for the criteria and there doesn't seem to be a nice method to get it.
-        $criteria = $DB->get_records('gradingform_guide_criteria');
-        $data = ['criteria' => []];
-        foreach ($criteria as $key => $value) {
-            if ($value->shortname == 'Spelling mistakes') {
-                $data['criteria'][$key]['remark'] = 'This user made several mistakes.';
-                $data['criteria'][$key]['remarkformat'] = 0;
-                $data['criteria'][$key]['score'] = 5;
-            } else {
-                $data['criteria'][$key]['remark'] = 'This user has two pictures.';
-                $data['criteria'][$key]['remarkformat'] = 0;
-                $data['criteria'][$key]['score'] = 10;
-            }
-        }
-        $data['itemid'] = $itemid;
-
-        // Update this instance with data.
-        $instance->update($data);
+        $gradedata = [
+            ['remark' => 'This user made several mistakes.', 'score' => 5],
+            ['remark' => 'This user has two pictures.', 'score' => 10]
+        ];
+        $instance = $guide->grade_item($user->id, $itemid, $gradedata);
 
         $itemid = 2;
-        $instance = $controller->create_instance($user->id, $itemid);
-        // I need the ids for the criteria and there doesn't seem to be a nice method to get it.
-        $criteria = $DB->get_records('gradingform_guide_criteria');
-        $data = ['criteria' => []];
-        foreach ($criteria as $key => $value) {
-            if ($value->shortname == 'Spelling mistakes') {
-                $data['criteria'][$key]['remark'] = 'This user made no mistakes.';
-                $data['criteria'][$key]['remarkformat'] = 0;
-                $data['criteria'][$key]['score'] = 25;
-            } else {
-                $data['criteria'][$key]['remark'] = 'This user has one pictures.';
-                $data['criteria'][$key]['remarkformat'] = 0;
-                $data['criteria'][$key]['score'] = 5;
-            }
-        }
-        $data['itemid'] = $itemid;
-
-        // Update this instance with data.
-        $instance->update($data);
+        $gradedata = [
+            ['remark' => 'This user made no mistakes.', 'score' => 25],
+            ['remark' => 'This user has one picture.', 'score' => 5]
+        ];
+        $instance = $guide->grade_item($user->id, $itemid, $gradedata);
 
         // Check how many records we have in the fillings table.
         $records = $DB->get_records('gradingform_guide_fillings');
@@ -395,6 +363,72 @@ class core_grading_privacy_testcase extends provider_testcase {
         $this->assertEmpty($records);
     }
 
+    /**
+     * Test the deletion of multiple instances at once.
+     */
+    public function test_delete_data_for_instances() {
+        global $DB;
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $module = $this->getDataGenerator()->create_module('assign', ['course' => $course]);
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $user3 = $this->getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        $modulecontext = context_module::instance($module->cmid);
+        $guide = new test_guide($modulecontext, 'testrubrib', 'Description text');
+        $guide->add_criteria(
+            'Spelling mistakes',
+            'Full marks will be given for no spelling mistakes.',
+            'Deduct 5 points per spelling mistake made.',
+            25
+        );
+        $guide->add_criteria(
+            'Pictures',
+            'Full marks will be given for including 3 pictures.',
+            'Give 5 points for each picture present',
+            15
+        );
+        $guide->create_guide();
+
+        // In the situation of mod_assign this would be the id from assign_grades.
+        $itemid1 = 1;
+        $gradedata = [
+            ['remark' => 'This user made several mistakes.', 'score' => 5],
+            ['remark' => 'This user has two pictures.', 'score' => 10]
+        ];
+        $instance1 = $guide->grade_item($user1->id, $itemid1, $gradedata);
+
+        $itemid2 = 2;
+        $gradedata = [
+            ['remark' => 'This user made a couple of mistakes.', 'score' => 15],
+            ['remark' => 'This user has one picture.', 'score' => 10]
+        ];
+        $instance2 = $guide->grade_item($user2->id, $itemid2, $gradedata);
+
+        $itemid3 = 3;
+        $gradedata = [
+            ['remark' => 'This user made one mistakes.', 'score' => 20],
+            ['remark' => 'This user has one picture.', 'score' => 10]
+        ];
+        $instance3 = $guide->grade_item($user3->id, $itemid3, $gradedata);
+
+        $records = $DB->get_records('gradingform_guide_fillings');
+        $this->assertCount(6, $records);
+
+        // Delete all user data for items 1 and 3.
+        provider::delete_data_for_instances($modulecontext, [$itemid1, $itemid3]);
+        $records = $DB->get_records('gradingform_guide_fillings');
+        $this->assertCount(2, $records);
+        $instanceid = $instance2->get_data('id');
+        // The instance id should match for all remaining records.
+        foreach ($records as $record) {
+            $this->assertEquals($instanceid, $record->instanceid);
+        }
+    }
+
     /**
      * Helper function to setup the environment.
      *