MDL-63622 core_grading: Add support for removal of context users.

Core grading now implements the new core_userlist_provider
interface to allow for deletion of specific users in a context.
This commit is contained in:
Adrian Greeve 2018-10-12 13:18:55 +08:00 committed by Andrew Nicols
parent 448bd578d8
commit 980425022b
4 changed files with 199 additions and 86 deletions

View 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.
}
}

View 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.

View 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;
}
}

View 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.
*