Skip to content

Commit 7b7665f

Browse files
author
David Monllao
committed
Merge branch 'MDL-63711-34' of git://github.com/abgreeve/moodle into MOODLE_34_STABLE
2 parents 273486c + e606b42 commit 7b7665f

File tree

2 files changed

+362
-1
lines changed

2 files changed

+362
-1
lines changed

grade/classes/privacy/provider.php

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
*/
5151
class provider implements
5252
\core_privacy\local\metadata\provider,
53-
\core_privacy\local\request\subsystem\provider {
53+
\core_privacy\local\request\subsystem\provider,
54+
\core_privacy\local\request\core_userlist_provider {
5455

5556
/**
5657
* Returns metadata.
@@ -259,6 +260,101 @@ public static function get_contexts_for_userid(int $userid) : \core_privacy\loca
259260
return $contextlist;
260261
}
261262

263+
/**
264+
* Get the list of contexts that contain user information for the specified user.
265+
*
266+
* @param \core_privacy\local\request\userlist $userlist The userlist containing the list of users who have data
267+
* in this context/plugin combination.
268+
*/
269+
public static function get_users_in_context(\core_privacy\local\request\userlist $userlist) {
270+
$context = $userlist->get_context();
271+
272+
if ($context->contextlevel == CONTEXT_COURSE) {
273+
$params = ['contextinstanceid' => $context->instanceid];
274+
275+
$sql = "SELECT usermodified
276+
FROM {grade_outcomes}
277+
WHERE courseid = :contextinstanceid";
278+
$userlist->add_from_sql('usermodified', $sql, $params);
279+
280+
$sql = "SELECT loggeduser
281+
FROM {grade_outcomes_history}
282+
WHERE courseid = :contextinstanceid";
283+
$userlist->add_from_sql('loggeduser', $sql, $params);
284+
285+
$sql = "SELECT userid
286+
FROM {scale}
287+
WHERE courseid = :contextinstanceid";
288+
$userlist->add_from_sql('userid', $sql, $params);
289+
290+
$sql = "SELECT loggeduser, userid
291+
FROM {scale_history}
292+
WHERE courseid = :contextinstanceid";
293+
$userlist->add_from_sql('loggeduser', $sql, $params);
294+
$userlist->add_from_sql('userid', $sql, $params);
295+
296+
$sql = "SELECT loggeduser
297+
FROM {grade_items_history}
298+
WHERE courseid = :contextinstanceid";
299+
$userlist->add_from_sql('loggeduser', $sql, $params);
300+
301+
$sql = "SELECT ggh.userid
302+
FROM {grade_grades_history} ggh
303+
JOIN {grade_items} gi ON ggh.itemid = gi.id
304+
WHERE gi.courseid = :contextinstanceid";
305+
$userlist->add_from_sql('userid', $sql, $params);
306+
307+
$sql = "SELECT gg.userid, gg.usermodified
308+
FROM {grade_grades} gg
309+
JOIN {grade_items} gi ON gg.itemid = gi.id
310+
WHERE gi.courseid = :contextinstanceid";
311+
$userlist->add_from_sql('userid', $sql, $params);
312+
$userlist->add_from_sql('usermodified', $sql, $params);
313+
314+
$sql = "SELECT loggeduser
315+
FROM {grade_categories_history}
316+
WHERE courseid = :contextinstanceid";
317+
$userlist->add_from_sql('loggeduser', $sql, $params);
318+
}
319+
320+
// None of these are currently used (user deletion).
321+
if ($context->contextlevel == CONTEXT_SYSTEM) {
322+
$params = ['contextinstanceid' => 0];
323+
324+
$sql = "SELECT usermodified
325+
FROM {grade_outcomes}
326+
WHERE (courseid IS NULL OR courseid < 1)";
327+
$userlist->add_from_sql('usermodified', $sql, []);
328+
329+
$sql = "SELECT loggeduser
330+
FROM {grade_outcomes_history}
331+
WHERE (courseid IS NULL OR courseid < 1)";
332+
$userlist->add_from_sql('loggeduser', $sql, []);
333+
334+
$sql = "SELECT userid
335+
FROM {scale}
336+
WHERE courseid = :contextinstanceid";
337+
$userlist->add_from_sql('userid', $sql, $params);
338+
339+
$sql = "SELECT loggeduser, userid
340+
FROM {scale_history}
341+
WHERE courseid = :contextinstanceid";
342+
$userlist->add_from_sql('loggeduser', $sql, $params);
343+
$userlist->add_from_sql('userid', $sql, $params);
344+
}
345+
346+
if ($context->contextlevel == CONTEXT_USER) {
347+
// If the grade item has been removed and we have an orphan entry then we link to the
348+
// user context.
349+
$sql = "SELECT ggh.userid
350+
FROM {grade_grades_history} ggh
351+
LEFT JOIN {grade_items} gi ON ggh.itemid = gi.id
352+
WHERE gi.id IS NULL
353+
AND ggh.userid = :contextinstanceid";
354+
$userlist->add_from_sql('userid', $sql, ['contextinstanceid' => $context->instanceid]);
355+
}
356+
}
357+
262358
/**
263359
* Export all user data for the specified user, in the specified contexts.
264360
*
@@ -595,6 +691,43 @@ public static function delete_data_for_user(approved_contextlist $contextlist) {
595691
$DB->delete_records_select('grade_grades_history', "itemid $insql AND userid = :userid", $params);
596692
}
597693

694+
695+
/**
696+
* Delete multiple users within a single context.
697+
*
698+
* @param \core_privacy\local\request\approved_userlist $userlist The approved context and user information to
699+
* delete information for.
700+
*/
701+
public static function delete_data_for_users(\core_privacy\local\request\approved_userlist $userlist) {
702+
global $DB;
703+
704+
$context = $userlist->get_context();
705+
$userids = $userlist->get_userids();
706+
if ($context->contextlevel == CONTEXT_USER) {
707+
if (array_search($context->instanceid, $userids) !== false) {
708+
static::delete_orphan_historical_grades($context->instanceid);
709+
}
710+
return;
711+
}
712+
713+
if ($context->contextlevel != CONTEXT_COURSE) {
714+
return;
715+
}
716+
717+
$itemids = static::get_item_ids_from_course_ids([$context->instanceid]);
718+
if (empty($itemids)) {
719+
// Our job here is done!
720+
return;
721+
}
722+
723+
// Delete all the grades.
724+
list($itemsql, $itemparams) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED);
725+
list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
726+
$params = array_merge($itemparams, $userparams);
727+
$DB->delete_records_select('grade_grades', "itemid $itemsql AND userid $usersql", $params);
728+
$DB->delete_records_select('grade_grades_history', "itemid $itemsql AND userid $usersql", $params);
729+
}
730+
598731
/**
599732
* Delete orphan historical grades.
600733
*

grade/tests/privacy_test.php

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,160 @@ public function test_get_contexts_for_userid_grades_and_history() {
248248
$this->assertArrayHasKey(context_user::instance($u2->id)->id, $contexts);
249249
}
250250

251+
/**
252+
* Test that the appropriate user IDs are returned for a given context.
253+
*/
254+
public function test_get_users_in_context_gradebook_edits() {
255+
$dg = $this->getDataGenerator();
256+
257+
$c1 = $dg->create_course();
258+
$c2 = $dg->create_course();
259+
260+
$u1 = $dg->create_user();
261+
$u2 = $dg->create_user();
262+
$u3 = $dg->create_user();
263+
$u4 = $dg->create_user();
264+
$u5 = $dg->create_user();
265+
$u6 = $dg->create_user();
266+
$u7 = $dg->create_user();
267+
$u8 = $dg->create_user();
268+
$u9 = $dg->create_user();
269+
$u10 = $dg->create_user();
270+
$u11 = $dg->create_user();
271+
272+
$sysctx = context_system::instance();
273+
$c1ctx = context_course::instance($c1->id);
274+
$c2ctx = context_course::instance($c2->id);
275+
276+
// Create some stuff.
277+
$gi1a = new grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
278+
$gi1b = new grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
279+
$gi2a = new grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
280+
$gc1a = new grade_category($dg->create_grade_category(['courseid' => $c1->id]), false);
281+
$gc1b = new grade_category($dg->create_grade_category(['courseid' => $c1->id]), false);
282+
$gc2a = new grade_category($dg->create_grade_category(['courseid' => $c2->id]), false);
283+
$go2 = new grade_outcome($dg->create_grade_outcome(['courseid' => $c2->id, 'shortname' => 'go2',
284+
'fullname' => 'go2']), false);
285+
286+
$go0 = new grade_outcome(['shortname' => 'go0', 'fullname' => 'go0', 'usermodified' => $u1->id]);
287+
$go0->insert();
288+
$go1 = new grade_outcome(['shortname' => 'go1', 'fullname' => 'go1', 'courseid' => $c1->id, 'usermodified' => $u11->id]);
289+
$go1->insert();
290+
291+
// Create scales.
292+
$s1 = new grade_scale(['name' => 's1', 'scale' => 'a,b', 'userid' => $u7->id, 'courseid' => 0, 'description' => '']);
293+
$s1->insert();
294+
$s2 = new grade_scale(['name' => 's2', 'scale' => 'a,b', 'userid' => $u8->id, 'courseid' => $c1->id, 'description' => '']);
295+
$s2->insert();
296+
297+
// User 2 creates history.
298+
$this->setUser($u2);
299+
$go0->shortname .= ' edited';
300+
$go0->update();
301+
$gc1a->fullname .= ' edited';
302+
$gc1a->update();
303+
304+
// User 3 creates history.
305+
$this->setUser($u3);
306+
$go1->shortname .= ' edited';
307+
$go1->update();
308+
$gc2a->fullname .= ' a';
309+
$gc2a->update();
310+
311+
// User 4 updates an outcome in course (creates history).
312+
$this->setUser($u4);
313+
$go2->shortname .= ' edited';
314+
$go2->update();
315+
316+
// User 5 updates an item.
317+
$this->setUser($u5);
318+
$gi1a->itemname .= ' edited';
319+
$gi1a->update();
320+
321+
// User 6 creates history.
322+
$this->setUser($u6);
323+
$gi2a->delete();
324+
325+
// User 9 creates history.
326+
$this->setUser($u9);
327+
$s1->name .= ' edited';
328+
$s1->update();
329+
330+
$userlist = new \core_privacy\local\request\userlist($sysctx, 'core_grades');
331+
provider::get_users_in_context($userlist);
332+
$systemcontextuserids = [$u1->id, $u2->id, $u7->id, $u9->id];
333+
$this->assertEquals($systemcontextuserids, $userlist->get_userids());
334+
335+
$userlist = new \core_privacy\local\request\userlist($c1ctx, 'core_grades');
336+
provider::get_users_in_context($userlist);
337+
$course1userids = [$u11->id, $u3->id, $u8->id, $u5->id, $u2->id];
338+
$this->assertEquals($course1userids, $userlist->get_userids());
339+
340+
$userlist = new \core_privacy\local\request\userlist($c2ctx, 'core_grades');
341+
provider::get_users_in_context($userlist);
342+
$course2userids = [$u4->id, $u6->id, $u3->id];
343+
$this->assertEquals($course2userids, $userlist->get_userids());
344+
}
345+
346+
/**
347+
* Test that the appropriate user IDs are returned for a given context.
348+
*/
349+
public function test_get_users_in_context_grades_and_history() {
350+
$dg = $this->getDataGenerator();
351+
352+
$c1 = $dg->create_course();
353+
$c2 = $dg->create_course();
354+
355+
$u1 = $dg->create_user();
356+
$u2 = $dg->create_user();
357+
$u3 = $dg->create_user();
358+
$u4 = $dg->create_user();
359+
$u5 = $dg->create_user();
360+
$u6 = $dg->create_user();
361+
362+
$c1ctx = context_course::instance($c1->id);
363+
$c2ctx = context_course::instance($c2->id);
364+
$u2ctx = context_user::instance($u2->id);
365+
366+
// Create some stuff.
367+
$gi1a = new grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
368+
$gi1b = new grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
369+
$gi2a = new grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
370+
$gi2b = new grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
371+
372+
// User 1 is graded in course 1.
373+
$gi1a->update_final_grade($u1->id, 1, 'test');
374+
375+
// User 2 is graded in course 2.
376+
$gi2a->update_final_grade($u2->id, 10, 'test');
377+
378+
// User 3 is set as modifier.
379+
$gi1a->update_final_grade($u1->id, 1, 'test', '', FORMAT_MOODLE, $u3->id);
380+
381+
// User 4 is set as modifier, and creates history..
382+
$this->setUser($u4);
383+
$gi1a->update_final_grade($u2->id, 1, 'test');
384+
385+
// User 5 creates history, user 6 is the known modifier, and we delete the item.
386+
$this->setUser($u5);
387+
$gi2b->update_final_grade($u2->id, 1, 'test', '', FORMAT_PLAIN, $u6->id);
388+
$gi2b->delete();
389+
390+
$userlist = new \core_privacy\local\request\userlist($c1ctx, 'core_grades');
391+
provider::get_users_in_context($userlist);
392+
$course1userids = [$u1->id, $u2->id, $u3->id, $u4->id];
393+
$this->assertEquals($course1userids, $userlist->get_userids());
394+
395+
$userlist = new \core_privacy\local\request\userlist($c2ctx, 'core_grades');
396+
provider::get_users_in_context($userlist);
397+
$course2userids = [$u5->id, $u2->id];
398+
$this->assertEquals($course2userids, $userlist->get_userids());
399+
400+
$userlist = new \core_privacy\local\request\userlist($u2ctx, 'core_grades');
401+
provider::get_users_in_context($userlist);
402+
$this->assertEquals([$u2->id], $userlist->get_userids());
403+
}
404+
251405
public function test_delete_data_for_all_users_in_context() {
252406
global $DB;
253407
$dg = $this->getDataGenerator();
@@ -368,6 +522,80 @@ public function test_delete_data_for_user() {
368522
$this->assertTrue($DB->record_exists('grade_grades_history', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
369523
}
370524

525+
/**
526+
* Test deleting multiple users for a context works.
527+
*/
528+
public function test_delete_data_for_users() {
529+
global $DB;
530+
$dg = $this->getDataGenerator();
531+
532+
$c1 = $dg->create_course();
533+
$c2 = $dg->create_course();
534+
$u1 = $dg->create_user();
535+
$u2 = $dg->create_user();
536+
$u3 = $dg->create_user();
537+
$u4 = $dg->create_user();
538+
$u1ctx = context_user::instance($u1->id);
539+
$u2ctx = context_user::instance($u2->id);
540+
$c1ctx = context_course::instance($c1->id);
541+
$c2ctx = context_course::instance($c2->id);
542+
543+
// Create some stuff.
544+
$gi1a = new grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
545+
$gi1b = new grade_item($dg->create_grade_item(['courseid' => $c1->id]), false);
546+
$gi2a = new grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
547+
$gi2b = new grade_item($dg->create_grade_item(['courseid' => $c2->id]), false);
548+
549+
$gi1a->update_final_grade($u1->id, 1, 'test');
550+
$gi1a->update_final_grade($u2->id, 1, 'test');
551+
$gi1a->update_final_grade($u3->id, 1, 'test');
552+
$gi1b->update_final_grade($u1->id, 1, 'test');
553+
$gi1b->update_final_grade($u4->id, 1, 'test');
554+
$gi2a->update_final_grade($u1->id, 1, 'test');
555+
$gi2a->update_final_grade($u2->id, 1, 'test');
556+
$gi2a->update_final_grade($u4->id, 1, 'test');
557+
$gi2b->update_final_grade($u1->id, 1, 'test');
558+
$gi2b->update_final_grade($u2->id, 1, 'test');
559+
$gi2b->update_final_grade($u3->id, 1, 'test');
560+
$gi2b->delete();
561+
562+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
563+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
564+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u3->id, 'itemid' => $gi1a->id]));
565+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
566+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi1b->id]));
567+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
568+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
569+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi2a->id]));
570+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2b->id]));
571+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2b->id]));
572+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u3->id, 'itemid' => $gi2b->id]));
573+
574+
$userlist = new \core_privacy\local\request\approved_userlist($c1ctx, 'core_grades', [$u1->id, $u2->id]);
575+
provider::delete_data_for_users($userlist);
576+
577+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
578+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
579+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u3->id, 'itemid' => $gi1a->id]));
580+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
581+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi1b->id]));
582+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
583+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
584+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi2a->id]));
585+
586+
$userlist = new \core_privacy\local\request\approved_userlist($c2ctx, 'core_grades', [$u2->id, $u4->id]);
587+
provider::delete_data_for_users($userlist);
588+
589+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1a->id]));
590+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi1a->id]));
591+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u3->id, 'itemid' => $gi1a->id]));
592+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi1b->id]));
593+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi1b->id]));
594+
$this->assertTrue($DB->record_exists('grade_grades', ['userid' => $u1->id, 'itemid' => $gi2a->id]));
595+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u2->id, 'itemid' => $gi2a->id]));
596+
$this->assertFalse($DB->record_exists('grade_grades', ['userid' => $u4->id, 'itemid' => $gi2a->id]));
597+
}
598+
371599
public function test_export_data_for_user_about_grades_and_history() {
372600
global $DB;
373601
$dg = $this->getDataGenerator();

0 commit comments

Comments
 (0)