diff --git a/grade/grading/classes/privacy/provider.php b/grade/grading/classes/privacy/provider.php index 24ce67af9d6..95b62672848 100644 --- a/grade/grading/classes/privacy/provider.php +++ b/grade/grading/classes/privacy/provider.php @@ -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. + } } diff --git a/grade/grading/form/guide/tests/privacy_test.php b/grade/grading/form/guide/tests/privacy_test.php index d2e118cbeca..a2122ebeb4c 100644 --- a/grade/grading/form/guide/tests/privacy_test.php +++ b/grade/grading/form/guide/tests/privacy_test.php @@ -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. diff --git a/grade/grading/tests/fixtures/marking_guide.php b/grade/grading/tests/fixtures/marking_guide.php index 027aeb0ef97..d3b31abe787 100644 --- a/grade/grading/tests/fixtures/marking_guide.php +++ b/grade/grading/tests/fixtures/marking_guide.php @@ -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; + } } diff --git a/grade/grading/tests/privacy_test.php b/grade/grading/tests/privacy_test.php index 864da6b9fc9..d2a0feb7654 100644 --- a/grade/grading/tests/privacy_test.php +++ b/grade/grading/tests/privacy_test.php @@ -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. *