MDL-63497 mod_data: Add support for removal of context users
This issue is a part of the MDL-62560 Epic.
This commit is contained in:
committed by
Andrew Nicols
parent
8ba35e317c
commit
3c0c280882
@@ -26,9 +26,11 @@ namespace mod_data\privacy;
|
||||
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\approved_userlist;
|
||||
use core_privacy\local\request\contextlist;
|
||||
use core_privacy\local\request\helper;
|
||||
use core_privacy\local\request\transform;
|
||||
use core_privacy\local\request\userlist;
|
||||
use core_privacy\local\request\writer;
|
||||
use core_privacy\manager;
|
||||
|
||||
@@ -45,6 +47,9 @@ class provider implements
|
||||
// This plugin stores personal data.
|
||||
\core_privacy\local\metadata\provider,
|
||||
|
||||
// This plugin is capable of determining which users have data within it.
|
||||
\core_privacy\local\request\core_userlist_provider,
|
||||
|
||||
// This plugin is a core_user_data_provider.
|
||||
\core_privacy\local\request\plugin\provider {
|
||||
|
||||
@@ -126,6 +131,72 @@ class provider implements
|
||||
return $contextlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of users who have data within a context.
|
||||
*
|
||||
* @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(userlist $userlist) {
|
||||
$context = $userlist->get_context();
|
||||
|
||||
if (!is_a($context, \context_module::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find users with data records.
|
||||
$sql = "SELECT dr.userid
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {data} d ON d.id = cm.instance
|
||||
JOIN {data_records} dr ON dr.dataid = d.id
|
||||
WHERE c.id = :contextid";
|
||||
|
||||
$params = [
|
||||
'modname' => 'data',
|
||||
'contextid' => $context->id,
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
];
|
||||
|
||||
$userlist->add_from_sql('userid', $sql, $params);
|
||||
|
||||
// Find users with comments.
|
||||
$sql = "SELECT dr.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {data} d ON d.id = cm.instance
|
||||
JOIN {data_records} dr ON dr.dataid = d.id
|
||||
WHERE c.id = :contextid";
|
||||
|
||||
$params = [
|
||||
'modname' => 'data',
|
||||
'contextid' => $context->id,
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
];
|
||||
|
||||
\core_comment\privacy\provider::get_users_in_context_from_sql(
|
||||
$userlist, 'com', 'mod_data', 'database_entry', $sql, $params);
|
||||
|
||||
// Find users with ratings.
|
||||
$sql = "SELECT dr.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {data} d ON d.id = cm.instance
|
||||
JOIN {data_records} dr ON dr.dataid = d.id
|
||||
WHERE c.id = :contextid";
|
||||
|
||||
$params = [
|
||||
'modname' => 'data',
|
||||
'contextid' => $context->id,
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
];
|
||||
|
||||
\core_rating\privacy\provider::get_users_in_context_from_sql($userlist, 'rat', 'mod_data', 'entry', $sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object from all fields in the $record where key starts with $prefix
|
||||
*
|
||||
@@ -385,6 +456,51 @@ class provider implements
|
||||
// We do not delete ratings made by this user on other records because it may change grades.
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(approved_userlist $userlist) {
|
||||
global $DB;
|
||||
|
||||
$context = $userlist->get_context();
|
||||
$recordstobedeleted = [];
|
||||
list($userinsql, $userinparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
|
||||
|
||||
$sql = "SELECT " . self::sql_fields() . "
|
||||
FROM {context} ctx
|
||||
JOIN {course_modules} cm ON cm.id = ctx.instanceid
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {data} d ON d.id = cm.instance
|
||||
JOIN {data_records} dr ON dr.dataid = d.id AND dr.userid {$userinsql}
|
||||
LEFT JOIN {data_content} dc ON dc.recordid = dr.id
|
||||
LEFT JOIN {data_fields} df ON df.id = dc.fieldid
|
||||
WHERE ctx.id = :ctxid AND ctx.contextlevel = :contextlevel
|
||||
ORDER BY dr.id";
|
||||
|
||||
$params = [
|
||||
'ctxid' => $context->id,
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'modname' => 'data',
|
||||
];
|
||||
$params += $userinparams;
|
||||
|
||||
$rs = $DB->get_recordset_sql($sql, $params);
|
||||
foreach ($rs as $row) {
|
||||
self::mark_data_content_for_deletion($context, $row);
|
||||
$recordstobedeleted[$row->recordid] = $row->recordid;
|
||||
}
|
||||
$rs->close();
|
||||
|
||||
self::delete_data_records($context, $recordstobedeleted);
|
||||
|
||||
// Additionally remove comments these users made on other entries.
|
||||
\core_comment\privacy\provider::delete_comments_for_users($userlist, 'mod_data', 'database_entry');
|
||||
|
||||
// We do not delete ratings made by users on other records because it may change grades.
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a data_record/data_content for deletion
|
||||
*
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\approved_userlist;
|
||||
use mod_data\privacy\provider;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
@@ -39,6 +40,8 @@ class mod_data_privacy_provider_testcase extends \core_privacy\tests\provider_te
|
||||
protected $student;
|
||||
/** @var stdClass The student object. */
|
||||
protected $student2;
|
||||
/** @var stdClass The student object. */
|
||||
protected $student3;
|
||||
|
||||
/** @var stdClass The data object. */
|
||||
protected $datamodule;
|
||||
@@ -84,9 +87,11 @@ class mod_data_privacy_provider_testcase extends \core_privacy\tests\provider_te
|
||||
// Create a student.
|
||||
$student1 = $generator->create_user();
|
||||
$student2 = $generator->create_user();
|
||||
$student3 = $generator->create_user();
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||||
$generator->enrol_user($student1->id, $course->id, $studentrole->id);
|
||||
$generator->enrol_user($student2->id, $course->id, $studentrole->id);
|
||||
$generator->enrol_user($student3->id, $course->id, $studentrole->id);
|
||||
|
||||
// Add records.
|
||||
$this->setUser($student1);
|
||||
@@ -98,8 +103,12 @@ class mod_data_privacy_provider_testcase extends \core_privacy\tests\provider_te
|
||||
$this->generate_data_record($datamodule);
|
||||
$this->generate_data_record($datamodule);
|
||||
|
||||
$this->setUser($student3);
|
||||
$this->generate_data_record($datamodule);
|
||||
|
||||
$this->student = $student1;
|
||||
$this->student2 = $student2;
|
||||
$this->student3 = $student3;
|
||||
$this->datamodule = $datamodule;
|
||||
$this->course = $course;
|
||||
}
|
||||
@@ -177,6 +186,27 @@ class mod_data_privacy_provider_testcase extends \core_privacy\tests\provider_te
|
||||
$this->assertEquals($cmcontext->id, $contextforuser->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for provider::get_users_in_context().
|
||||
*/
|
||||
public function test_get_users_in_context() {
|
||||
$component = 'mod_data';
|
||||
$cm = get_coursemodule_from_instance('data', $this->datamodule->id);
|
||||
$cmcontext = context_module::instance($cm->id);
|
||||
|
||||
$userlist = new \core_privacy\local\request\userlist($cmcontext, $component);
|
||||
provider::get_users_in_context($userlist);
|
||||
|
||||
$this->assertCount(3, $userlist);
|
||||
|
||||
$expected = [$this->student->id, $this->student2->id, $this->student3->id];
|
||||
$actual = $userlist->get_userids();
|
||||
sort($expected);
|
||||
sort($actual);
|
||||
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get test privacy writer
|
||||
*
|
||||
@@ -240,4 +270,46 @@ class mod_data_privacy_provider_testcase extends \core_privacy\tests\provider_te
|
||||
provider::export_user_data($appctxt);
|
||||
$this->assertFalse($this->get_writer($cmcontext)->has_any_data());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for provider::delete_data_for_users().
|
||||
*/
|
||||
public function test_delete_data_for_users() {
|
||||
$cm = get_coursemodule_from_instance('data', $this->datamodule->id);
|
||||
$cmcontext = context_module::instance($cm->id);
|
||||
$userstodelete = [$this->student->id, $this->student2->id];
|
||||
|
||||
// Ensure student, student 2 and student 3 have data before being deleted.
|
||||
$appctxt = new \core_privacy\local\request\approved_contextlist($this->student, 'mod_data', [$cmcontext->id]);
|
||||
provider::export_user_data($appctxt);
|
||||
$this->assertTrue($this->get_writer($cmcontext)->has_any_data());
|
||||
|
||||
$appctxt = new \core_privacy\local\request\approved_contextlist($this->student2, 'mod_data', [$cmcontext->id]);
|
||||
provider::export_user_data($appctxt);
|
||||
$this->assertTrue($this->get_writer($cmcontext)->has_any_data());
|
||||
|
||||
// Delete data for student 1 and 2.
|
||||
$approvedlist = new approved_userlist($cmcontext, 'mod_data', $userstodelete);
|
||||
provider::delete_data_for_users($approvedlist);
|
||||
|
||||
// Reset the writer so it doesn't contain the data from before deletion.
|
||||
\core_privacy\local\request\writer::reset();
|
||||
|
||||
// Ensure data is now deleted for student and student 2.
|
||||
$appctxt = new \core_privacy\local\request\approved_contextlist($this->student, 'mod_data', [$cmcontext->id]);
|
||||
provider::export_user_data($appctxt);
|
||||
|
||||
$this->assertFalse($this->get_writer($cmcontext)->has_any_data());
|
||||
|
||||
$appctxt = new \core_privacy\local\request\approved_contextlist($this->student2, 'mod_data', [$cmcontext->id]);
|
||||
provider::export_user_data($appctxt);
|
||||
|
||||
$this->assertFalse($this->get_writer($cmcontext)->has_any_data());
|
||||
|
||||
// Ensure data still intact for student 3.
|
||||
$appctxt = new \core_privacy\local\request\approved_contextlist($this->student3, 'mod_data', [$cmcontext->id]);
|
||||
provider::export_user_data($appctxt);
|
||||
|
||||
$this->assertTrue($this->get_writer($cmcontext)->has_any_data());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user