MDL-63510 mod_survey: 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-03 12:18:11 +08:00 committed by David Monllao
parent 6d6e4b7a51
commit bd0b6d8693
2 changed files with 208 additions and 0 deletions

View File

@ -31,8 +31,10 @@ use context_helper;
use context_module;
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\helper;
use core_privacy\local\request\transform;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;
require_once($CFG->dirroot . '/mod/survey/lib.php');
@ -47,6 +49,7 @@ require_once($CFG->dirroot . '/mod/survey/lib.php');
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\plugin\provider {
/**
@ -113,6 +116,61 @@ 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;
}
$params = [
'survey' => 'survey',
'modulelevel' => CONTEXT_MODULE,
'contextid' => $context->id,
];
$sql = "
SELECT sa.userid
FROM {survey} s
JOIN {modules} m
ON m.name = :survey
JOIN {course_modules} cm
ON cm.instance = s.id
AND cm.module = m.id
JOIN {context} ctx
ON ctx.instanceid = cm.id
AND ctx.contextlevel = :modulelevel
JOIN {survey_answers} sa
ON sa.survey = s.id
WHERE ctx.id = :contextid
AND s.template <> 0";
$userlist->add_from_sql('userid', $sql, $params);
$sql = "
SELECT sy.userid
FROM {survey} s
JOIN {modules} m
ON m.name = :survey
JOIN {course_modules} cm
ON cm.instance = s.id
AND cm.module = m.id
JOIN {context} ctx
ON ctx.instanceid = cm.id
AND ctx.contextlevel = :modulelevel
JOIN {survey_analysis} sy
ON sy.survey = s.id
WHERE ctx.id = :contextid
AND s.template <> 0";
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Export all user data for the specified user, in the specified contexts.
*
@ -275,6 +333,44 @@ class provider implements
$DB->delete_records_select('survey_analysis', "survey $insql AND userid = :userid", $params);
}
/**
* 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();
if ($context->contextlevel != CONTEXT_MODULE) {
return;
}
// Fetch the survey ID.
$sql = "
SELECT s.id
FROM {survey} s
JOIN {modules} m
ON m.name = :survey
JOIN {course_modules} cm
ON cm.instance = s.id
AND cm.module = m.id
WHERE cm.id = :cmid";
$params = [
'survey' => 'survey',
'cmid' => $context->instanceid,
];
$surveyid = $DB->get_field_sql($sql, $params);
$userids = $userlist->get_userids();
// Delete all the things.
list($insql, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$params['surveyid'] = $surveyid;
$DB->delete_records_select('survey_answers', "survey = :surveyid AND userid {$insql}", $params);
$DB->delete_records_select('survey_analysis', "survey = :surveyid AND userid {$insql}", $params);
}
/**
* Get a survey ID from its context.
*

View File

@ -29,6 +29,7 @@ global $CFG;
use core_privacy\tests\provider_testcase;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
use mod_survey\privacy\provider;
@ -85,6 +86,59 @@ class mod_survey_privacy_testcase extends provider_testcase {
$this->assertTrue(in_array(context_module::instance($cm1c->cmid)->id, $contextids));
}
/**
* Test for provider::test_get_users_in_context().
*/
public function test_get_users_in_context() {
$dg = $this->getDataGenerator();
$component = 'mod_survey';
$c1 = $dg->create_course();
$c2 = $dg->create_course();
$cm1a = $dg->create_module('survey', ['template' => 1, 'course' => $c1]);
$cm1b = $dg->create_module('survey', ['template' => 2, 'course' => $c1]);
$cm2 = $dg->create_module('survey', ['template' => 1, 'course' => $c2]);
$cm1acontext = context_module::instance($cm1a->cmid);
$cm1bcontext = context_module::instance($cm1b->cmid);
$cm2context = context_module::instance($cm2->cmid);
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$bothusers = [$u1->id, $u2->id];
sort($bothusers);
$this->create_answer($cm1a->id, 1, $u1->id);
$this->create_answer($cm1b->id, 1, $u1->id);
$this->create_answer($cm1b->id, 1, $u2->id);
$this->create_answer($cm2->id, 1, $u2->id);
$this->create_analysis($cm2->id, $u1->id);
// Cm1a should only contain u1.
$userlist = new \core_privacy\local\request\userlist($cm1acontext, $component);
provider::get_users_in_context($userlist);
$this->assertCount(1, $userlist);
$this->assertEquals([$u1->id], $userlist->get_userids());
// Cm1b should contain u1 and u2 (both have answers).
$userlist = new \core_privacy\local\request\userlist($cm1bcontext, $component);
provider::get_users_in_context($userlist);
$this->assertCount(2, $userlist);
$actual = $userlist->get_userids();
sort($actual);
$this->assertEquals($bothusers, $actual);
// Cm2 should contain u1 (analysis) and u2 (answer).
$userlist = new \core_privacy\local\request\userlist($cm2context, $component);
provider::get_users_in_context($userlist);
$this->assertCount(2, $userlist);
$actual = $userlist->get_userids();
sort($actual);
$this->assertEquals($bothusers, $actual);
}
public function test_delete_data_for_all_users_in_context() {
global $DB;
$dg = $this->getDataGenerator();
@ -190,6 +244,64 @@ class mod_survey_privacy_testcase extends provider_testcase {
$this->assertTrue($DB->record_exists('survey_analysis', ['userid' => $u2->id, 'survey' => $cm1c->id]));
}
/**
* Test for provider::delete_data_for_users().
*/
public function test_delete_data_for_users() {
global $DB;
$dg = $this->getDataGenerator();
$component = 'mod_survey';
$c1 = $dg->create_course();
$cm1a = $dg->create_module('survey', ['template' => 1, 'course' => $c1]);
$cm1b = $dg->create_module('survey', ['template' => 2, 'course' => $c1]);
$cm1c = $dg->create_module('survey', ['template' => 2, 'course' => $c1]);
$cm1acontext = context_module::instance($cm1a->cmid);
$cm1bcontext = context_module::instance($cm1b->cmid);
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$this->create_answer($cm1a->id, 1, $u1->id);
$this->create_answer($cm1a->id, 1, $u2->id);
$this->create_analysis($cm1a->id, $u1->id);
$this->create_analysis($cm1a->id, $u2->id);
$this->create_answer($cm1b->id, 1, $u2->id);
$this->create_analysis($cm1b->id, $u1->id);
$this->create_answer($cm1c->id, 1, $u1->id);
$this->create_analysis($cm1c->id, $u2->id);
// Confirm data exists before deletion.
$this->assertTrue($DB->record_exists('survey_answers', ['userid' => $u1->id, 'survey' => $cm1a->id]));
$this->assertTrue($DB->record_exists('survey_answers', ['userid' => $u1->id, 'survey' => $cm1c->id]));
$this->assertTrue($DB->record_exists('survey_answers', ['userid' => $u2->id, 'survey' => $cm1a->id]));
$this->assertTrue($DB->record_exists('survey_answers', ['userid' => $u2->id, 'survey' => $cm1b->id]));
$this->assertTrue($DB->record_exists('survey_analysis', ['userid' => $u1->id, 'survey' => $cm1a->id]));
$this->assertTrue($DB->record_exists('survey_analysis', ['userid' => $u1->id, 'survey' => $cm1b->id]));
$this->assertTrue($DB->record_exists('survey_analysis', ['userid' => $u2->id, 'survey' => $cm1a->id]));
$this->assertTrue($DB->record_exists('survey_analysis', ['userid' => $u2->id, 'survey' => $cm1c->id]));
// Ensure only approved user data is deleted.
$approveduserids = [$u1->id];
$approvedlist = new approved_userlist($cm1acontext, $component, $approveduserids);
provider::delete_data_for_users($approvedlist);
$this->assertFalse($DB->record_exists('survey_answers', ['userid' => $u1->id, 'survey' => $cm1a->id]));
$this->assertFalse($DB->record_exists('survey_analysis', ['userid' => $u1->id, 'survey' => $cm1a->id]));
$this->assertTrue($DB->record_exists('survey_answers', ['userid' => $u2->id, 'survey' => $cm1a->id]));
$this->assertTrue($DB->record_exists('survey_analysis', ['userid' => $u2->id, 'survey' => $cm1a->id]));
$approveduserids = [$u1->id, $u2->id];
$approvedlist = new approved_userlist($cm1bcontext, $component, $approveduserids);
provider::delete_data_for_users($approvedlist);
$this->assertFalse($DB->record_exists('survey_answers', ['survey' => $cm1b->id]));
$this->assertFalse($DB->record_exists('survey_analysis', ['survey' => $cm1b->id]));
$this->assertTrue($DB->record_exists('survey_answers', ['userid' => $u1->id, 'survey' => $cm1c->id]));
$this->assertTrue($DB->record_exists('survey_analysis', ['userid' => $u2->id, 'survey' => $cm1c->id]));
}
public function test_export_data_for_user() {
global $DB;
$dg = $this->getDataGenerator();