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:
Michael Hawkins
2018-10-17 13:12:18 +08:00
committed by Andrew Nicols
parent 8ba35e317c
commit 3c0c280882
2 changed files with 188 additions and 0 deletions
+116
View File
@@ -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
*
+72
View File
@@ -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());
}
}