Skip to content

Commit

Permalink
MDL-63497 mod_lesson: Add support for removal of context users
Browse files Browse the repository at this point in the history
This issue is a part of the MDL-62560 Epic.
  • Loading branch information
mickhawkins authored and David Monllao committed Oct 22, 2018
1 parent e917288 commit 630a05c
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 0 deletions.
88 changes: 88 additions & 0 deletions mod/lesson/classes/privacy/provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
use stdClass;
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/lesson/locallib.php');
Expand All @@ -51,6 +53,7 @@
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\plugin\provider,
\core_privacy\local\request\user_preference_provider {

Expand Down Expand Up @@ -166,6 +169,54 @@ public static function get_contexts_for_userid(int $userid) : \core_privacy\loca
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 = [
'lesson' => 'lesson',
'modulelevel' => CONTEXT_MODULE,
'contextid' => $context->id,
];

// Mapping of lesson tables which may contain user data.
$joins = [
'lesson_attempts',
'lesson_branch',
'lesson_grades',
'lesson_overrides',
'lesson_timer',
];

foreach ($joins as $join) {
$sql = "
SELECT lx.userid
FROM {lesson} l
JOIN {modules} m
ON m.name = :lesson
JOIN {course_modules} cm
ON cm.instance = l.id
AND cm.module = m.id
JOIN {context} ctx
ON ctx.instanceid = cm.id
AND ctx.contextlevel = :modulelevel
JOIN {{$join}} lx
ON lx.lessonid = l.id
WHERE ctx.id = :contextid";

$userlist->add_from_sql('userid', $sql, $params);
}
}

/**
* Export all user data for the specified user, in the specified contexts.
*
Expand Down Expand Up @@ -445,6 +496,43 @@ public static function delete_data_for_user(approved_contextlist $contextlist) {
$DB->delete_records_select('lesson_overrides', $sql, $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();
$lessonid = static::get_lesson_id_from_context($context);
$userids = $userlist->get_userids();

if (empty($lessonid)) {
return;
}

// Prepare the SQL we'll need below.
list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$sql = "lessonid = :lessonid AND userid {$insql}";
$params = array_merge($inparams, ['lessonid' => $lessonid]);

// Delete the attempt files.
$fs = get_file_storage();
$recordset = $DB->get_recordset_select('lesson_attempts', $sql, $params, '', 'id, lessonid');
foreach ($recordset as $record) {
$fs->delete_area_files($context->id, 'mod_lesson', 'essay_responses', $record->id);
}
$recordset->close();

// Delete all the things.
$DB->delete_records_select('lesson_attempts', $sql, $params);
$DB->delete_records_select('lesson_branch', $sql, $params);
$DB->delete_records_select('lesson_grades', $sql, $params);
$DB->delete_records_select('lesson_timer', $sql, $params);
$DB->delete_records_select('lesson_overrides', $sql, $params);
}

/**
* Get a survey ID from its context.
*
Expand Down
133 changes: 133 additions & 0 deletions mod/lesson/tests/privacy_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,60 @@ public function test_get_contexts_for_userid() {
$this->assertTrue(in_array($cm3ctx->id, $contextids));
}

/*
* Test for provider::get_users_in_context().
*/
public function test_get_users_in_context() {
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
$component = 'mod_lesson';

$u1 = $dg->create_user();
$u2 = $dg->create_user();
$u3 = $dg->create_user();
$u4 = $dg->create_user();
$u5 = $dg->create_user();
$u6 = $dg->create_user();

$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);

$cm1ctx = context_module::instance($cm1->cmid);
$cm2ctx = context_module::instance($cm2->cmid);

$this->create_attempt($cm1, $u1);
$this->create_grade($cm1, $u2);
$this->create_timer($cm1, $u3);
$this->create_branch($cm1, $u4);
$this->create_override($cm1, $u5);

$this->create_attempt($cm2, $u6);
$this->create_grade($cm2, $u6);
$this->create_timer($cm2, $u6);
$this->create_branch($cm2, $u6);
$this->create_override($cm2, $u6);

$context = context_module::instance($cm1->cmid);
$userlist = new \core_privacy\local\request\userlist($context, $component);
provider::get_users_in_context($userlist);
$userids = $userlist->get_userids();

$this->assertCount(5, $userids);
$expected = [$u1->id, $u2->id, $u3->id, $u4->id, $u5->id];
$actual = $userids;
sort($expected);
sort($actual);
$this->assertEquals($expected, $actual);

$context = context_module::instance($cm2->cmid);
$userlist = new \core_privacy\local\request\userlist($context, $component);
provider::get_users_in_context($userlist);
$userids = $userlist->get_userids();

$this->assertCount(1, $userids);
$this->assertEquals([$u6->id], $userids);
}

public function test_delete_data_for_all_users_in_context() {
global $DB;
$dg = $this->getDataGenerator();
Expand Down Expand Up @@ -293,6 +347,85 @@ public function test_delete_data_for_user() {
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
}

/*
* Test for provider::delete_data_for_users().
*/
public function test_delete_data_for_users() {
global $DB;
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();

$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);
$cm3 = $dg->create_module('lesson', ['course' => $c1]);
$context1 = context_module::instance($cm1->cmid);
$context3 = context_module::instance($cm3->cmid);

$this->create_attempt($cm1, $u1);
$this->create_grade($cm1, $u1);
$this->create_timer($cm1, $u1);
$this->create_branch($cm1, $u1);
$this->create_override($cm1, $u1);
$this->create_attempt($cm1, $u2);
$this->create_grade($cm1, $u2);
$this->create_timer($cm1, $u2);
$this->create_branch($cm1, $u2);
$this->create_override($cm1, $u2);

$this->create_attempt($cm2, $u1);
$this->create_grade($cm2, $u1);
$this->create_timer($cm2, $u1);
$this->create_branch($cm2, $u1);
$this->create_override($cm2, $u1);
$this->create_attempt($cm2, $u2);
$this->create_grade($cm2, $u2);
$this->create_timer($cm2, $u2);
$this->create_branch($cm2, $u2);
$this->create_override($cm2, $u2);

$assertnochange = function($user, $cm) use ($DB) {
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $user->id, 'lessonid' => $cm->id]));
};

$assertdeleted = function($user, $cm) use ($DB) {
$this->assertFalse($DB->record_exists('lesson_attempts', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertFalse($DB->record_exists('lesson_grades', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertFalse($DB->record_exists('lesson_timer', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertFalse($DB->record_exists('lesson_branch', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertFalse($DB->record_exists('lesson_overrides', ['userid' => $user->id, 'lessonid' => $cm->id]));
};

// Confirm existing state.
$assertnochange($u1, $cm1);
$assertnochange($u1, $cm2);
$assertnochange($u2, $cm1);
$assertnochange($u2, $cm2);

// Delete another module: no change.
$approveduserlist = new core_privacy\local\request\approved_userlist($context3, 'mod_lesson', [$u1->id]);
provider::delete_data_for_users($approveduserlist);

$assertnochange($u1, $cm1);
$assertnochange($u1, $cm2);
$assertnochange($u2, $cm1);
$assertnochange($u2, $cm2);

// Delete cm1 for u1: no change for u2 and in cm2.
$approveduserlist = new core_privacy\local\request\approved_userlist($context1, 'mod_lesson', [$u1->id]);
provider::delete_data_for_users($approveduserlist);

$assertdeleted($u1, $cm1);
$assertnochange($u1, $cm2);
$assertnochange($u2, $cm1);
$assertnochange($u2, $cm2);
}

public function test_export_data_for_user_overrides() {
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
Expand Down

0 comments on commit 630a05c

Please sign in to comment.