mirror of
https://github.com/moodle/moodle.git
synced 2025-08-05 00:46:50 +02:00
MDL-63497 mod_feedback: Add support for removal of context users
This issue is a part of the MDL-62560 Epic.
This commit is contained in:
parent
55d1ef377c
commit
e917288971
2 changed files with 214 additions and 0 deletions
|
@ -31,9 +31,11 @@ use context_helper;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use core_privacy\local\metadata\collection;
|
use core_privacy\local\metadata\collection;
|
||||||
use core_privacy\local\request\approved_contextlist;
|
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\contextlist;
|
||||||
use core_privacy\local\request\helper;
|
use core_privacy\local\request\helper;
|
||||||
use core_privacy\local\request\transform;
|
use core_privacy\local\request\transform;
|
||||||
|
use core_privacy\local\request\userlist;
|
||||||
use core_privacy\local\request\writer;
|
use core_privacy\local\request\writer;
|
||||||
|
|
||||||
require_once($CFG->dirroot . '/mod/feedback/lib.php');
|
require_once($CFG->dirroot . '/mod/feedback/lib.php');
|
||||||
|
@ -48,6 +50,7 @@ require_once($CFG->dirroot . '/mod/feedback/lib.php');
|
||||||
*/
|
*/
|
||||||
class provider implements
|
class provider implements
|
||||||
\core_privacy\local\metadata\provider,
|
\core_privacy\local\metadata\provider,
|
||||||
|
\core_privacy\local\request\core_userlist_provider,
|
||||||
\core_privacy\local\request\plugin\provider {
|
\core_privacy\local\request\plugin\provider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,6 +105,38 @@ class provider implements
|
||||||
return $contextlist;
|
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 feedback entries.
|
||||||
|
$sql = "
|
||||||
|
SELECT fc.userid
|
||||||
|
FROM {%s} fc
|
||||||
|
JOIN {modules} m
|
||||||
|
ON m.name = :feedback
|
||||||
|
JOIN {course_modules} cm
|
||||||
|
ON cm.instance = fc.feedback
|
||||||
|
AND cm.module = m.id
|
||||||
|
JOIN {context} ctx
|
||||||
|
ON ctx.instanceid = cm.id
|
||||||
|
AND ctx.contextlevel = :modlevel
|
||||||
|
WHERE ctx.id = :contextid";
|
||||||
|
$params = ['feedback' => 'feedback', 'modlevel' => CONTEXT_MODULE, 'contextid' => $context->id];
|
||||||
|
|
||||||
|
$userlist->add_from_sql('userid', sprintf($sql, 'feedback_completed'), $params);
|
||||||
|
$userlist->add_from_sql('userid', sprintf($sql, 'feedback_completedtmp'), $params);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export all user data for the specified user, in the specified contexts.
|
* Export all user data for the specified user, in the specified contexts.
|
||||||
*
|
*
|
||||||
|
@ -272,6 +307,48 @@ class provider implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
$userids = $userlist->get_userids();
|
||||||
|
|
||||||
|
// Prepare SQL to gather all completed IDs.
|
||||||
|
list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
|
||||||
|
$completedsql = "
|
||||||
|
SELECT fc.id
|
||||||
|
FROM {%s} fc
|
||||||
|
JOIN {modules} m
|
||||||
|
ON m.name = :feedback
|
||||||
|
JOIN {course_modules} cm
|
||||||
|
ON cm.instance = fc.feedback
|
||||||
|
AND cm.module = m.id
|
||||||
|
WHERE cm.id = :instanceid
|
||||||
|
AND fc.userid $insql";
|
||||||
|
$completedparams = array_merge($inparams, ['instanceid' => $context->instanceid, 'feedback' => 'feedback']);
|
||||||
|
|
||||||
|
// Delete all submissions in progress.
|
||||||
|
$completedtmpids = $DB->get_fieldset_sql(sprintf($completedsql, 'feedback_completedtmp'), $completedparams);
|
||||||
|
if (!empty($completedtmpids)) {
|
||||||
|
list($insql, $inparams) = $DB->get_in_or_equal($completedtmpids, SQL_PARAMS_NAMED);
|
||||||
|
$DB->delete_records_select('feedback_valuetmp', "completed $insql", $inparams);
|
||||||
|
$DB->delete_records_select('feedback_completedtmp', "id $insql", $inparams);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all final submissions.
|
||||||
|
$completedids = $DB->get_fieldset_sql(sprintf($completedsql, 'feedback_completed'), $completedparams);
|
||||||
|
if (!empty($completedids)) {
|
||||||
|
list($insql, $inparams) = $DB->get_in_or_equal($completedids, SQL_PARAMS_NAMED);
|
||||||
|
$DB->delete_records_select('feedback_value', "completed $insql", $inparams);
|
||||||
|
$DB->delete_records_select('feedback_completed', "id $insql", $inparams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract an item record from a database record.
|
* Extract an item record from a database record.
|
||||||
*
|
*
|
||||||
|
|
|
@ -115,6 +115,83 @@ class mod_feedback_privacy_testcase extends provider_testcase {
|
||||||
$this->assertTrue(in_array(context_module::instance($cm2c->cmid)->id, $contextids));
|
$this->assertTrue(in_array(context_module::instance($cm2c->cmid)->id, $contextids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getting the users in a context.
|
||||||
|
*/
|
||||||
|
public function test_get_users_in_context() {
|
||||||
|
global $DB;
|
||||||
|
$dg = $this->getDataGenerator();
|
||||||
|
$fg = $dg->get_plugin_generator('mod_feedback');
|
||||||
|
$component = 'mod_feedback';
|
||||||
|
|
||||||
|
$c1 = $dg->create_course();
|
||||||
|
$c2 = $dg->create_course();
|
||||||
|
$cm0 = $dg->create_module('feedback', ['course' => SITEID]);
|
||||||
|
$cm1a = $dg->create_module('feedback', ['course' => $c1, 'anonymous' => FEEDBACK_ANONYMOUS_NO]);
|
||||||
|
$cm1b = $dg->create_module('feedback', ['course' => $c1]);
|
||||||
|
$cm2 = $dg->create_module('feedback', ['course' => $c2]);
|
||||||
|
|
||||||
|
$u1 = $dg->create_user();
|
||||||
|
$u2 = $dg->create_user();
|
||||||
|
|
||||||
|
foreach ([$cm0, $cm1a, $cm1b, $cm2] as $feedback) {
|
||||||
|
$i1 = $fg->create_item_numeric($feedback);
|
||||||
|
$i2 = $fg->create_item_multichoice($feedback);
|
||||||
|
$answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
|
||||||
|
|
||||||
|
if ($feedback == $cm1b) {
|
||||||
|
$this->create_submission_with_answers($feedback, $u2, $answers);
|
||||||
|
} else {
|
||||||
|
$this->create_submission_with_answers($feedback, $u1, $answers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsaved submission for u2 in cm1a.
|
||||||
|
$feedback = $cm1a;
|
||||||
|
$i1 = $fg->create_item_numeric($feedback);
|
||||||
|
$i2 = $fg->create_item_multichoice($feedback);
|
||||||
|
$answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
|
||||||
|
$this->create_tmp_submission_with_answers($feedback, $u2, $answers);
|
||||||
|
|
||||||
|
// Only u1 in cm0.
|
||||||
|
$context = context_module::instance($cm0->cmid);
|
||||||
|
$userlist = new \core_privacy\local\request\userlist($context, $component);
|
||||||
|
provider::get_users_in_context($userlist);
|
||||||
|
|
||||||
|
$this->assertCount(1, $userlist);
|
||||||
|
$this->assertEquals([$u1->id], $userlist->get_userids());
|
||||||
|
|
||||||
|
$context = context_module::instance($cm1a->cmid);
|
||||||
|
$userlist = new \core_privacy\local\request\userlist($context, $component);
|
||||||
|
provider::get_users_in_context($userlist);
|
||||||
|
|
||||||
|
// Two submissions in cm1a: saved for u1, unsaved for u2.
|
||||||
|
$this->assertCount(2, $userlist);
|
||||||
|
|
||||||
|
$expected = [$u1->id, $u2->id];
|
||||||
|
$actual = $userlist->get_userids();
|
||||||
|
sort($expected);
|
||||||
|
sort($actual);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $actual);
|
||||||
|
|
||||||
|
// Only u2 in cm1b.
|
||||||
|
$context = context_module::instance($cm1b->cmid);
|
||||||
|
$userlist = new \core_privacy\local\request\userlist($context, $component);
|
||||||
|
provider::get_users_in_context($userlist);
|
||||||
|
|
||||||
|
$this->assertCount(1, $userlist);
|
||||||
|
$this->assertEquals([$u2->id], $userlist->get_userids());
|
||||||
|
|
||||||
|
// Only u1 in cm2.
|
||||||
|
$context = context_module::instance($cm2->cmid);
|
||||||
|
$userlist = new \core_privacy\local\request\userlist($context, $component);
|
||||||
|
provider::get_users_in_context($userlist);
|
||||||
|
|
||||||
|
$this->assertCount(1, $userlist);
|
||||||
|
$this->assertEquals([$u1->id], $userlist->get_userids());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test deleting user data.
|
* Test deleting user data.
|
||||||
*/
|
*/
|
||||||
|
@ -169,6 +246,66 @@ class mod_feedback_privacy_testcase extends provider_testcase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test deleting data within a context for an approved userlist.
|
||||||
|
*/
|
||||||
|
public function test_delete_data_for_users() {
|
||||||
|
global $DB;
|
||||||
|
$dg = $this->getDataGenerator();
|
||||||
|
$fg = $dg->get_plugin_generator('mod_feedback');
|
||||||
|
|
||||||
|
$c1 = $dg->create_course();
|
||||||
|
$c2 = $dg->create_course();
|
||||||
|
$cm0 = $dg->create_module('feedback', ['course' => SITEID]);
|
||||||
|
$cm1 = $dg->create_module('feedback', ['course' => $c1, 'anonymous' => FEEDBACK_ANONYMOUS_NO]);
|
||||||
|
$cm2 = $dg->create_module('feedback', ['course' => $c2]);
|
||||||
|
$context0 = context_module::instance($cm0->cmid);
|
||||||
|
$context1 = context_module::instance($cm1->cmid);
|
||||||
|
|
||||||
|
$u1 = $dg->create_user();
|
||||||
|
$u2 = $dg->create_user();
|
||||||
|
|
||||||
|
// Create a bunch of data.
|
||||||
|
foreach ([$cm0, $cm1, $cm2] as $feedback) {
|
||||||
|
$i1 = $fg->create_item_numeric($feedback);
|
||||||
|
$i2 = $fg->create_item_multichoice($feedback);
|
||||||
|
$answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
|
||||||
|
|
||||||
|
$this->create_submission_with_answers($feedback, $u1, $answers);
|
||||||
|
$this->create_tmp_submission_with_answers($feedback, $u1, $answers);
|
||||||
|
|
||||||
|
$this->create_submission_with_answers($feedback, $u2, $answers);
|
||||||
|
$this->create_tmp_submission_with_answers($feedback, $u2, $answers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete u1 from cm0, ensure u2 data is retained.
|
||||||
|
$approveduserlist = new core_privacy\local\request\approved_userlist($context0, 'mod_feedback', [$u1->id]);
|
||||||
|
provider::delete_data_for_users($approveduserlist);
|
||||||
|
|
||||||
|
$this->assert_no_feedback_data_for_user($cm0, $u1);
|
||||||
|
$this->assert_feedback_data_for_user($cm0, $u2);
|
||||||
|
$this->assert_feedback_tmp_data_for_user($cm0, $u2);
|
||||||
|
|
||||||
|
// Ensure cm1 unaffected by cm1 deletes.
|
||||||
|
$this->assert_feedback_data_for_user($cm1, $u1);
|
||||||
|
$this->assert_feedback_tmp_data_for_user($cm1, $u1);
|
||||||
|
$this->assert_feedback_data_for_user($cm1, $u2);
|
||||||
|
$this->assert_feedback_tmp_data_for_user($cm1, $u2);
|
||||||
|
|
||||||
|
// Delete u1 and u2 from cm1, ensure no data is retained.
|
||||||
|
$approveduserlist = new core_privacy\local\request\approved_userlist($context1, 'mod_feedback', [$u1->id, $u2->id]);
|
||||||
|
provider::delete_data_for_users($approveduserlist);
|
||||||
|
|
||||||
|
$this->assert_no_feedback_data_for_user($cm1, $u1);
|
||||||
|
$this->assert_no_feedback_data_for_user($cm1, $u2);
|
||||||
|
|
||||||
|
// Ensure cm2 is unaffected by any of the deletes.
|
||||||
|
$this->assert_feedback_data_for_user($cm2, $u1);
|
||||||
|
$this->assert_feedback_tmp_data_for_user($cm2, $u1);
|
||||||
|
$this->assert_feedback_data_for_user($cm2, $u2);
|
||||||
|
$this->assert_feedback_tmp_data_for_user($cm2, $u2);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test deleting a whole context.
|
* Test deleting a whole context.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue