You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2659 lines
108 KiB
2659 lines
108 KiB
2 years ago
|
<?php
|
||
|
// This file is part of Moodle - http://moodle.org/
|
||
|
//
|
||
|
// Moodle is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU General Public License as published by
|
||
|
// the Free Software Foundation, either version 3 of the License, or
|
||
|
// (at your option) any later version.
|
||
|
//
|
||
|
// Moodle is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU General Public License
|
||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
/**
|
||
|
* Expired contexts tests.
|
||
|
*
|
||
|
* @package tool_dataprivacy
|
||
|
* @copyright 2018 David Monllao
|
||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||
|
*/
|
||
|
|
||
|
use tool_dataprivacy\api;
|
||
|
use tool_dataprivacy\data_registry;
|
||
|
use tool_dataprivacy\expired_context;
|
||
|
use tool_dataprivacy\purpose;
|
||
|
use tool_dataprivacy\purpose_override;
|
||
|
use tool_dataprivacy\category;
|
||
|
use tool_dataprivacy\contextlevel;
|
||
|
use tool_dataprivacy\expired_contexts_manager;
|
||
|
|
||
|
defined('MOODLE_INTERNAL') || die();
|
||
|
global $CFG;
|
||
|
|
||
|
/**
|
||
|
* Expired contexts tests.
|
||
|
*
|
||
|
* @package tool_dataprivacy
|
||
|
* @copyright 2018 David Monllao
|
||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||
|
*/
|
||
|
class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||
|
|
||
|
/**
|
||
|
* Setup the basics with the specified retention period.
|
||
|
*
|
||
|
* @param string $system Retention policy for the system.
|
||
|
* @param string $user Retention policy for users.
|
||
|
* @param string $course Retention policy for courses.
|
||
|
* @param string $activity Retention policy for activities.
|
||
|
*/
|
||
|
protected function setup_basics(string $system, string $user, string $course = null, string $activity = null) : \stdClass {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = (object) [
|
||
|
'system' => $this->create_and_set_purpose_for_contextlevel($system, CONTEXT_SYSTEM),
|
||
|
'user' => $this->create_and_set_purpose_for_contextlevel($user, CONTEXT_USER),
|
||
|
];
|
||
|
|
||
|
if (null !== $course) {
|
||
|
$purposes->course = $this->create_and_set_purpose_for_contextlevel($course, CONTEXT_COURSE);
|
||
|
}
|
||
|
|
||
|
if (null !== $activity) {
|
||
|
$purposes->activity = $this->create_and_set_purpose_for_contextlevel($activity, CONTEXT_MODULE);
|
||
|
}
|
||
|
|
||
|
return $purposes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a retention period and set it for the specified context level.
|
||
|
*
|
||
|
* @param string $retention
|
||
|
* @param int $contextlevel
|
||
|
* @return purpose
|
||
|
*/
|
||
|
protected function create_and_set_purpose_for_contextlevel(string $retention, int $contextlevel) : purpose {
|
||
|
$purpose = new purpose(0, (object) [
|
||
|
'name' => 'Test purpose ' . rand(1, 1000),
|
||
|
'retentionperiod' => $retention,
|
||
|
'lawfulbases' => 'gdpr_art_6_1_a',
|
||
|
]);
|
||
|
$purpose->create();
|
||
|
|
||
|
$cat = new category(0, (object) ['name' => 'Test category']);
|
||
|
$cat->create();
|
||
|
|
||
|
if ($contextlevel <= CONTEXT_USER) {
|
||
|
$record = (object) [
|
||
|
'purposeid' => $purpose->get('id'),
|
||
|
'categoryid' => $cat->get('id'),
|
||
|
'contextlevel' => $contextlevel,
|
||
|
];
|
||
|
api::set_contextlevel($record);
|
||
|
} else {
|
||
|
list($purposevar, ) = data_registry::var_names_from_context(
|
||
|
\context_helper::get_class_for_level($contextlevel)
|
||
|
);
|
||
|
set_config($purposevar, $purpose->get('id'), 'tool_dataprivacy');
|
||
|
}
|
||
|
|
||
|
return $purpose;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with no lastaccess is not flagged for deletion.
|
||
|
*/
|
||
|
public function test_flag_not_setup() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user();
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with no lastaccess is not flagged for deletion.
|
||
|
*/
|
||
|
public function test_flag_user_no_lastaccess() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user();
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a recent lastaccess is not flagged for deletion.
|
||
|
*/
|
||
|
public function test_flag_user_recent_lastaccess() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time()]);
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a lastaccess in the past is flagged for deletion.
|
||
|
*/
|
||
|
public function test_flag_user_past_lastaccess() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
// Although there is a block in the user context, everything in the user context is regarded as one.
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(1, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a lastaccess in the past but active enrolments is not flagged for deletion.
|
||
|
*/
|
||
|
public function test_flag_user_past_lastaccess_still_enrolled() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time(), 'enddate' => time() + YEARSECS]);
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
$otheruser = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($otheruser->id, $course->id, 'student');
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a lastaccess in the past and no active enrolments is flagged for deletion.
|
||
|
*/
|
||
|
public function test_flag_user_update_existing() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'P5Y');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $usercontext->id,
|
||
|
'defaultexpired' => 0,
|
||
|
'status' => expired_context::STATUS_EXPIRED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
$this->assertEquals(0, $expiredcontext->get('defaultexpired'));
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(1, $flaggedusers);
|
||
|
|
||
|
// The user context will now have expired.
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
$this->assertEquals(1, $updatedcontext->get('defaultexpired'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a lastaccess in the past and expired enrolments.
|
||
|
*/
|
||
|
public function test_flag_user_past_lastaccess_unexpired_past_enrolment() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'P1Y');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
$otheruser = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($otheruser->id, $course->id, 'student');
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a lastaccess in the past and expired enrolments.
|
||
|
*/
|
||
|
public function test_flag_user_past_override_role() {
|
||
|
global $DB;
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
$systemcontext = \context_system::instance();
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||
|
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->user->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'P5Y',
|
||
|
]);
|
||
|
$override->save();
|
||
|
role_assign($role->id, $user->id, $systemcontext->id);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
|
||
|
$expiredrecord = expired_context::get_record(['contextid' => $usercontext->id]);
|
||
|
$this->assertFalse($expiredrecord);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a lastaccess in the past and expired enrolments.
|
||
|
*/
|
||
|
public function test_flag_user_past_lastaccess_expired_enrolled() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
$otheruser = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($otheruser->id, $course->id, 'student');
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(1, $flaggedcourses);
|
||
|
$this->assertEquals(1, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a lastaccess in the past and enrolments without a course end date are respected
|
||
|
* correctly.
|
||
|
*/
|
||
|
public function test_flag_user_past_lastaccess_missing_enddate_required() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course();
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
$otheruser = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($otheruser->id, $course->id, 'student');
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Ensure that course end dates are not required.
|
||
|
set_config('requireallenddatesforuserdeletion', 1, 'tool_dataprivacy');
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a lastaccess in the past and enrolments without a course end date are respected
|
||
|
* correctly when the end date is not required.
|
||
|
*/
|
||
|
public function test_flag_user_past_lastaccess_missing_enddate_not_required() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course();
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
$otheruser = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($otheruser->id, $course->id, 'student');
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Ensure that course end dates are required.
|
||
|
set_config('requireallenddatesforuserdeletion', 0, 'tool_dataprivacy');
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(1, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a recent lastaccess is not flagged for deletion.
|
||
|
*/
|
||
|
public function test_flag_user_recent_lastaccess_existing_record() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time()]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $usercontext->id,
|
||
|
'status' => expired_context::STATUS_EXPIRED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
|
||
|
$this->expectException('dml_missing_record_exception');
|
||
|
new expired_context($expiredcontext->get('id'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a recent lastaccess is not flagged for deletion.
|
||
|
*/
|
||
|
public function test_flag_user_retention_changed() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - DAYSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(1, $flaggedusers);
|
||
|
|
||
|
$expiredcontext = expired_context::get_record(['contextid' => $usercontext->id]);
|
||
|
$this->assertNotFalse($expiredcontext);
|
||
|
|
||
|
// Increase the retention period to 5 years.
|
||
|
$purposes->user->set('retentionperiod', 'P5Y');
|
||
|
$purposes->user->save();
|
||
|
|
||
|
// Re-run the expiry job - the previously flagged user will be removed because the retention period has been increased.
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
|
||
|
// The expiry record will now have been removed.
|
||
|
$this->expectException('dml_missing_record_exception');
|
||
|
new expired_context($expiredcontext->get('id'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a historically expired expired block record child is cleaned up.
|
||
|
*/
|
||
|
public function test_flag_user_historic_block_unapproved() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - DAYSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$blockcontext = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Create an existing expired_context which has not been approved for the block.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $blockcontext->id,
|
||
|
'status' => expired_context::STATUS_EXPIRED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(1, $flaggedusers);
|
||
|
|
||
|
$expiredblockcontext = expired_context::get_record(['contextid' => $blockcontext->id]);
|
||
|
$this->assertFalse($expiredblockcontext);
|
||
|
|
||
|
$expiredusercontext = expired_context::get_record(['contextid' => $usercontext->id]);
|
||
|
$this->assertNotFalse($expiredusercontext);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a block which has a default retention period which has not expired, is still expired.
|
||
|
*/
|
||
|
public function test_flag_user_historic_unexpired_child() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
$this->create_and_set_purpose_for_contextlevel('P5Y', CONTEXT_BLOCK);
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - DAYSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$blockcontext = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(1, $flaggedusers);
|
||
|
|
||
|
$expiredcontext = expired_context::get_record(['contextid' => $usercontext->id]);
|
||
|
$this->assertNotFalse($expiredcontext);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course with no end date is not flagged.
|
||
|
*/
|
||
|
public function test_flag_course_no_enddate() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course();
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course with an end date in the distant past, but a child which is unexpired is not flagged.
|
||
|
*/
|
||
|
public function test_flag_course_past_enddate_future_child() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'P5Y');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course with an end date in the distant past is flagged.
|
||
|
*/
|
||
|
public function test_flag_course_past_enddate() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(2, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course with an end date in the distant past is flagged.
|
||
|
*/
|
||
|
public function test_flag_course_past_enddate_multiple() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course1 = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$forum1 = $this->getDataGenerator()->create_module('forum', ['course' => $course1->id]);
|
||
|
|
||
|
$course2 = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$forum2 = $this->getDataGenerator()->create_module('forum', ['course' => $course2->id]);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(4, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course with an end date in the future is not flagged.
|
||
|
*/
|
||
|
public function test_flag_course_future_enddate() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course(['enddate' => time() + YEARSECS]);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course with an end date in the future is not flagged.
|
||
|
*/
|
||
|
public function test_flag_course_recent_unexpired_enddate() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course(['enddate' => time() - 1]);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course with an end date in the distant past is flagged, taking into account any purpose override
|
||
|
*/
|
||
|
public function test_flag_course_past_enddate_with_override_unexpired_role() {
|
||
|
global $DB;
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'editingteacher']);
|
||
|
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'P5Y',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * DAYSECS),
|
||
|
'enddate' => time() - DAYSECS,
|
||
|
]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(1, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
|
||
|
$expiredrecord = expired_context::get_record(['contextid' => $coursecontext->id]);
|
||
|
$this->assertEmpty($expiredrecord->get('expiredroles'));
|
||
|
|
||
|
$unexpiredroles = $expiredrecord->get('unexpiredroles');
|
||
|
$this->assertCount(1, $unexpiredroles);
|
||
|
$this->assertContains($role->id, $unexpiredroles);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course with an end date in the distant past is flagged, and any expired role is ignored.
|
||
|
*/
|
||
|
public function test_flag_course_past_enddate_with_override_expired_role() {
|
||
|
global $DB;
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'student']);
|
||
|
|
||
|
// The role has a much shorter retention, but both should match.
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'PT1M',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * DAYSECS),
|
||
|
'enddate' => time() - DAYSECS,
|
||
|
]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(1, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
|
||
|
$expiredrecord = expired_context::get_record(['contextid' => $coursecontext->id]);
|
||
|
$this->assertEmpty($expiredrecord->get('expiredroles'));
|
||
|
$this->assertEmpty($expiredrecord->get('unexpiredroles'));
|
||
|
$this->assertTrue((bool) $expiredrecord->get('defaultexpired'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that where a course has explicitly expired one role, but that role is explicitly not expired in a child
|
||
|
* context, does not have the parent context role expired.
|
||
|
*/
|
||
|
public function test_flag_course_override_expiredwith_override_unexpired_on_child() {
|
||
|
global $DB;
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('P1Y', 'P1Y', 'P1Y');
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'editingteacher']);
|
||
|
|
||
|
(new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'PT1S',
|
||
|
]))->save();
|
||
|
|
||
|
$modpurpose = new purpose(0, (object) [
|
||
|
'name' => 'Module purpose',
|
||
|
'retentionperiod' => 'PT1S',
|
||
|
'lawfulbases' => 'gdpr_art_6_1_a',
|
||
|
]);
|
||
|
$modpurpose->create();
|
||
|
|
||
|
(new purpose_override(0, (object) [
|
||
|
'purposeid' => $modpurpose->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'P5Y',
|
||
|
]))->save();
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * DAYSECS),
|
||
|
'enddate' => time() - DAYSECS,
|
||
|
]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
$cm = get_coursemodule_from_instance('forum', $forum->id);
|
||
|
$forumcontext = \context_module::instance($cm->id);
|
||
|
|
||
|
api::set_context_instance((object) [
|
||
|
'contextid' => $forumcontext->id,
|
||
|
'purposeid' => $modpurpose->get('id'),
|
||
|
'categoryid' => 0,
|
||
|
]);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(1, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
|
||
|
// The course will not be expired as the default expiry has not passed, and the explicit role override has been
|
||
|
// removed due to the child non-expiry.
|
||
|
$expiredrecord = expired_context::get_record(['contextid' => $coursecontext->id]);
|
||
|
$this->assertFalse($expiredrecord);
|
||
|
|
||
|
// The forum will have an expiry for all _but_ the overridden role.
|
||
|
$expiredrecord = expired_context::get_record(['contextid' => $forumcontext->id]);
|
||
|
$this->assertEmpty($expiredrecord->get('expiredroles'));
|
||
|
|
||
|
// The teacher is not expired.
|
||
|
$unexpiredroles = $expiredrecord->get('unexpiredroles');
|
||
|
$this->assertCount(1, $unexpiredroles);
|
||
|
$this->assertContains($role->id, $unexpiredroles);
|
||
|
$this->assertTrue((bool) $expiredrecord->get('defaultexpired'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user context previously flagged as approved is not removed if the user has any unexpired roles.
|
||
|
*/
|
||
|
public function test_process_user_context_with_override_unexpired_role() {
|
||
|
global $DB;
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
$systemcontext = \context_system::instance();
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||
|
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->user->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'P5Y',
|
||
|
]);
|
||
|
$override->save();
|
||
|
role_assign($role->id, $user->id, $systemcontext->id);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $usercontext->id,
|
||
|
'defaultexpired' => 1,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->add_unexpiredroles([$role->id]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_users_in_context',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_users_in_context');
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$this->expectException('dml_missing_record_exception');
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a module context previously flagged as approved is removed with appropriate unexpiredroles kept.
|
||
|
*/
|
||
|
public function test_process_course_context_with_override_unexpired_role() {
|
||
|
global $DB;
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'editingteacher']);
|
||
|
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'P5Y',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
$cm = get_coursemodule_from_instance('forum', $forum->id);
|
||
|
$forumcontext = \context_module::instance($cm->id);
|
||
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
|
||
|
|
||
|
$student = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($student->id, $course->id, 'student');
|
||
|
$generator->create_discussion((object) [
|
||
|
'course' => $forum->course,
|
||
|
'forum' => $forum->id,
|
||
|
'userid' => $student->id,
|
||
|
]);
|
||
|
|
||
|
$teacher = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
|
||
|
$generator->create_discussion((object) [
|
||
|
'course' => $forum->course,
|
||
|
'forum' => $forum->id,
|
||
|
'userid' => $teacher->id,
|
||
|
]);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $forumcontext->id,
|
||
|
'defaultexpired' => 1,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->add_unexpiredroles([$role->id]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_users_in_context',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
$mockprivacymanager
|
||
|
->expects($this->once())
|
||
|
->method('delete_data_for_users_in_context')
|
||
|
->with($this->callback(function($userlist) use ($student, $teacher) {
|
||
|
$forumlist = $userlist->get_userlist_for_component('mod_forum');
|
||
|
$userids = $forumlist->get_userids();
|
||
|
$this->assertCount(1, $userids);
|
||
|
$this->assertContains($student->id, $userids);
|
||
|
$this->assertNotContains($teacher->id, $userids);
|
||
|
return true;
|
||
|
}));
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(1, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a module context previously flagged as approved is removed with appropriate expiredroles kept.
|
||
|
*/
|
||
|
public function test_process_course_context_with_override_expired_role() {
|
||
|
global $DB;
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P5Y');
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'student']);
|
||
|
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'PT1M',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
$cm = get_coursemodule_from_instance('forum', $forum->id);
|
||
|
$forumcontext = \context_module::instance($cm->id);
|
||
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
|
||
|
|
||
|
$student = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($student->id, $course->id, 'student');
|
||
|
$generator->create_discussion((object) [
|
||
|
'course' => $forum->course,
|
||
|
'forum' => $forum->id,
|
||
|
'userid' => $student->id,
|
||
|
]);
|
||
|
|
||
|
$teacher = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
|
||
|
$generator->create_discussion((object) [
|
||
|
'course' => $forum->course,
|
||
|
'forum' => $forum->id,
|
||
|
'userid' => $teacher->id,
|
||
|
]);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $forumcontext->id,
|
||
|
'defaultexpired' => 0,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->add_expiredroles([$role->id]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_users_in_context',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
$mockprivacymanager
|
||
|
->expects($this->once())
|
||
|
->method('delete_data_for_users_in_context')
|
||
|
->with($this->callback(function($userlist) use ($student, $teacher) {
|
||
|
$forumlist = $userlist->get_userlist_for_component('mod_forum');
|
||
|
$userids = $forumlist->get_userids();
|
||
|
$this->assertCount(1, $userids);
|
||
|
$this->assertContains($student->id, $userids);
|
||
|
$this->assertNotContains($teacher->id, $userids);
|
||
|
return true;
|
||
|
}));
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(1, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a module context previously flagged as approved is removed with appropriate expiredroles kept.
|
||
|
*/
|
||
|
public function test_process_course_context_with_user_in_both_lists() {
|
||
|
global $DB;
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P5Y');
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'student']);
|
||
|
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'PT1M',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
$cm = get_coursemodule_from_instance('forum', $forum->id);
|
||
|
$forumcontext = \context_module::instance($cm->id);
|
||
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
|
||
|
|
||
|
$teacher = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
|
||
|
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'student');
|
||
|
$generator->create_discussion((object) [
|
||
|
'course' => $forum->course,
|
||
|
'forum' => $forum->id,
|
||
|
'userid' => $teacher->id,
|
||
|
]);
|
||
|
|
||
|
$student = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($student->id, $course->id, 'student');
|
||
|
$generator->create_discussion((object) [
|
||
|
'course' => $forum->course,
|
||
|
'forum' => $forum->id,
|
||
|
'userid' => $student->id,
|
||
|
]);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $forumcontext->id,
|
||
|
'defaultexpired' => 0,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->add_expiredroles([$role->id]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_users_in_context',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
$mockprivacymanager
|
||
|
->expects($this->once())
|
||
|
->method('delete_data_for_users_in_context')
|
||
|
->with($this->callback(function($userlist) use ($student, $teacher) {
|
||
|
$forumlist = $userlist->get_userlist_for_component('mod_forum');
|
||
|
$userids = $forumlist->get_userids();
|
||
|
$this->assertCount(1, $userids);
|
||
|
$this->assertContains($student->id, $userids);
|
||
|
$this->assertNotContains($teacher->id, $userids);
|
||
|
return true;
|
||
|
}));
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(1, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a module context previously flagged as approved is removed with appropriate expiredroles kept.
|
||
|
*/
|
||
|
public function test_process_course_context_with_user_in_both_lists_expired() {
|
||
|
global $DB;
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P5Y');
|
||
|
|
||
|
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $studentrole->id,
|
||
|
'retentionperiod' => 'PT1M',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']);
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $teacherrole->id,
|
||
|
'retentionperiod' => 'PT1M',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
$cm = get_coursemodule_from_instance('forum', $forum->id);
|
||
|
$forumcontext = \context_module::instance($cm->id);
|
||
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
|
||
|
|
||
|
$teacher = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
|
||
|
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'student');
|
||
|
$generator->create_discussion((object) [
|
||
|
'course' => $forum->course,
|
||
|
'forum' => $forum->id,
|
||
|
'userid' => $teacher->id,
|
||
|
]);
|
||
|
|
||
|
$student = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($student->id, $course->id, 'student');
|
||
|
$generator->create_discussion((object) [
|
||
|
'course' => $forum->course,
|
||
|
'forum' => $forum->id,
|
||
|
'userid' => $student->id,
|
||
|
]);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $forumcontext->id,
|
||
|
'defaultexpired' => 0,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->add_expiredroles([$studentrole->id, $teacherrole->id]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_users_in_context',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
$mockprivacymanager
|
||
|
->expects($this->once())
|
||
|
->method('delete_data_for_users_in_context')
|
||
|
->with($this->callback(function($userlist) use ($student, $teacher) {
|
||
|
$forumlist = $userlist->get_userlist_for_component('mod_forum');
|
||
|
$userids = $forumlist->get_userids();
|
||
|
$this->assertCount(2, $userids);
|
||
|
$this->assertContains($student->id, $userids);
|
||
|
$this->assertContains($teacher->id, $userids);
|
||
|
return true;
|
||
|
}));
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(1, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a site not setup will not process anything.
|
||
|
*/
|
||
|
public function test_process_not_setup() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $usercontext->id,
|
||
|
'status' => expired_context::STATUS_EXPIRED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with no lastaccess is not flagged for deletion.
|
||
|
*/
|
||
|
public function test_process_none_approved() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $usercontext->id,
|
||
|
'status' => expired_context::STATUS_EXPIRED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with no lastaccess is not flagged for deletion.
|
||
|
*/
|
||
|
public function test_process_no_context() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => -1,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$this->expectException('dml_missing_record_exception');
|
||
|
new expired_context($expiredcontext->get('id'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user context previously flagged as approved is removed.
|
||
|
*/
|
||
|
public function test_process_user_context() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$blockcontext = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $usercontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->atLeastOnce())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->exactly(2))
|
||
|
->method('delete_data_for_all_users_in_context')
|
||
|
->withConsecutive(
|
||
|
[$blockcontext],
|
||
|
[$usercontext]
|
||
|
);
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(1, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
|
||
|
// Flag all expired contexts again.
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(0, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
|
||
|
// Ensure that the deleted context record is still present.
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course context previously flagged as approved is removed.
|
||
|
*/
|
||
|
public function test_process_course_context() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $coursecontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->once())->method('delete_data_for_all_users_in_context');
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(1, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user context previously flagged as approved is not removed if the user then logs in.
|
||
|
*/
|
||
|
public function test_process_user_context_logged_in_after_approval() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $usercontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
// Now bump the user's last login time.
|
||
|
$this->setUser($user);
|
||
|
user_accesstime_log();
|
||
|
$this->setUser();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$this->expectException('dml_missing_record_exception');
|
||
|
new expired_context($expiredcontext->get('id'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user context previously flagged as approved is not removed if the purpose has changed.
|
||
|
*/
|
||
|
public function test_process_user_context_changed_after_approved() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$context = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $usercontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
// Now make the user a site admin.
|
||
|
$admins = explode(',', get_config('moodle', 'siteadmins'));
|
||
|
$admins[] = $user->id;
|
||
|
set_config('siteadmins', implode(',', $admins));
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$this->expectException('dml_missing_record_exception');
|
||
|
new expired_context($expiredcontext->get('id'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a historically expired expired block record child is cleaned up.
|
||
|
*/
|
||
|
public function test_process_user_historic_block_unapproved() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - DAYSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$blockcontext = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Create an expired_context for the user.
|
||
|
$expiredusercontext = new expired_context(0, (object) [
|
||
|
'contextid' => $usercontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredusercontext->save();
|
||
|
|
||
|
// Create an existing expired_context which has not been approved for the block.
|
||
|
$expiredblockcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $blockcontext->id,
|
||
|
'status' => expired_context::STATUS_EXPIRED,
|
||
|
]);
|
||
|
$expiredblockcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->atLeastOnce())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->exactly(2))
|
||
|
->method('delete_data_for_all_users_in_context')
|
||
|
->withConsecutive(
|
||
|
[$blockcontext],
|
||
|
[$usercontext]
|
||
|
);
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(1, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredusercontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a user with a block which has a default retention period which has not expired, is still expired.
|
||
|
*/
|
||
|
public function test_process_user_historic_unexpired_child() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
$this->create_and_set_purpose_for_contextlevel('P5Y', CONTEXT_BLOCK);
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - DAYSECS]);
|
||
|
$usercontext = \context_user::instance($user->id);
|
||
|
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$blockcontext = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Create an expired_context for the user.
|
||
|
$expiredusercontext = new expired_context(0, (object) [
|
||
|
'contextid' => $usercontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredusercontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->atLeastOnce())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->exactly(2))
|
||
|
->method('delete_data_for_all_users_in_context')
|
||
|
->withConsecutive(
|
||
|
[$blockcontext],
|
||
|
[$usercontext]
|
||
|
);
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(1, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredusercontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course context previously flagged as approved for deletion which now has an unflagged child, is
|
||
|
* updated.
|
||
|
*/
|
||
|
public function test_process_course_context_updated() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $coursecontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
|
||
|
// Changing the retention period to a longer period will remove the expired_context record.
|
||
|
$purposes->activity->set('retentionperiod', 'P5Y');
|
||
|
$purposes->activity->save();
|
||
|
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$this->expectException('dml_missing_record_exception');
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course context previously flagged as approved for deletion which now has an unflagged child, is
|
||
|
* updated.
|
||
|
*/
|
||
|
public function test_process_course_context_outstanding_children() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
|
||
|
// Create an existing expired_context.
|
||
|
$expiredcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $coursecontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredcontext->get('id'));
|
||
|
|
||
|
// No change - we just can't process it until the children have finished.
|
||
|
$this->assertEquals(expired_context::STATUS_APPROVED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course context previously flagged as approved for deletion which now has an unflagged child, is
|
||
|
* updated.
|
||
|
*/
|
||
|
public function test_process_course_context_pending_children() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
$cm = get_coursemodule_from_instance('forum', $forum->id);
|
||
|
$forumcontext = \context_module::instance($cm->id);
|
||
|
|
||
|
// Create an existing expired_context for the course.
|
||
|
$expiredcoursecontext = new expired_context(0, (object) [
|
||
|
'contextid' => $coursecontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcoursecontext->save();
|
||
|
|
||
|
// And for the forum.
|
||
|
$expiredforumcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $forumcontext->id,
|
||
|
'status' => expired_context::STATUS_EXPIRED,
|
||
|
]);
|
||
|
$expiredforumcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_all_users_in_context');
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(0, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredcoursecontext->get('id'));
|
||
|
|
||
|
// No change - we just can't process it until the children have finished.
|
||
|
$this->assertEquals(expired_context::STATUS_APPROVED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that a course context previously flagged as approved for deletion which now has an unflagged child, is
|
||
|
* updated.
|
||
|
*/
|
||
|
public function test_process_course_context_approved_children() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
$cm = get_coursemodule_from_instance('forum', $forum->id);
|
||
|
$forumcontext = \context_module::instance($cm->id);
|
||
|
|
||
|
// Create an existing expired_context for the course.
|
||
|
$expiredcoursecontext = new expired_context(0, (object) [
|
||
|
'contextid' => $coursecontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredcoursecontext->save();
|
||
|
|
||
|
// And for the forum.
|
||
|
$expiredforumcontext = new expired_context(0, (object) [
|
||
|
'contextid' => $forumcontext->id,
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
]);
|
||
|
$expiredforumcontext->save();
|
||
|
|
||
|
$mockprivacymanager = $this->getMockBuilder(\core_privacy\manager::class)
|
||
|
->setMethods([
|
||
|
'delete_data_for_user',
|
||
|
'delete_data_for_all_users_in_context',
|
||
|
])
|
||
|
->getMock();
|
||
|
$mockprivacymanager->expects($this->never())->method('delete_data_for_user');
|
||
|
$mockprivacymanager->expects($this->exactly(2))
|
||
|
->method('delete_data_for_all_users_in_context')
|
||
|
->withConsecutive(
|
||
|
[$forumcontext],
|
||
|
[$coursecontext]
|
||
|
);
|
||
|
|
||
|
$manager = $this->getMockBuilder(\tool_dataprivacy\expired_contexts_manager::class)
|
||
|
->setMethods(['get_privacy_manager'])
|
||
|
->getMock();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
|
||
|
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||
|
|
||
|
// Initially only the forum will be processed.
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(1, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
$updatedcontext = new expired_context($expiredforumcontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
|
||
|
// The course won't have been processed yet.
|
||
|
$updatedcontext = new expired_context($expiredcoursecontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_APPROVED, $updatedcontext->get('status'));
|
||
|
|
||
|
// A subsequent run will cause the course to processed as it is no longer dependent upon the child contexts.
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(1, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
$updatedcontext = new expired_context($expiredcoursecontext->get('id'));
|
||
|
$this->assertEquals(expired_context::STATUS_CLEANED, $updatedcontext->get('status'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test that the can_process_deletion function returns expected results.
|
||
|
*
|
||
|
* @dataProvider can_process_deletion_provider
|
||
|
* @param int $status
|
||
|
* @param bool $expected
|
||
|
*/
|
||
|
public function test_can_process_deletion($status, $expected) {
|
||
|
$purpose = new expired_context(0, (object) [
|
||
|
'status' => $status,
|
||
|
|
||
|
'contextid' => \context_system::instance()->id,
|
||
|
]);
|
||
|
|
||
|
$this->assertEquals($expected, $purpose->can_process_deletion());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Data provider for the can_process_deletion tests.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function can_process_deletion_provider() : array {
|
||
|
return [
|
||
|
'Pending' => [
|
||
|
expired_context::STATUS_EXPIRED,
|
||
|
false,
|
||
|
],
|
||
|
'Approved' => [
|
||
|
expired_context::STATUS_APPROVED,
|
||
|
true,
|
||
|
],
|
||
|
'Complete' => [
|
||
|
expired_context::STATUS_CLEANED,
|
||
|
false,
|
||
|
],
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test that the is_complete function returns expected results.
|
||
|
*
|
||
|
* @dataProvider is_complete_provider
|
||
|
* @param int $status
|
||
|
* @param bool $expected
|
||
|
*/
|
||
|
public function test_is_complete($status, $expected) {
|
||
|
$purpose = new expired_context(0, (object) [
|
||
|
'status' => $status,
|
||
|
'contextid' => \context_system::instance()->id,
|
||
|
]);
|
||
|
|
||
|
$this->assertEquals($expected, $purpose->is_complete());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Data provider for the is_complete tests.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function is_complete_provider() : array {
|
||
|
return [
|
||
|
'Pending' => [
|
||
|
expired_context::STATUS_EXPIRED,
|
||
|
false,
|
||
|
],
|
||
|
'Approved' => [
|
||
|
expired_context::STATUS_APPROVED,
|
||
|
false,
|
||
|
],
|
||
|
'Complete' => [
|
||
|
expired_context::STATUS_CLEANED,
|
||
|
true,
|
||
|
],
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test that the is_fully_expired function returns expected results.
|
||
|
*
|
||
|
* @dataProvider is_fully_expired_provider
|
||
|
* @param array $record
|
||
|
* @param bool $expected
|
||
|
*/
|
||
|
public function test_is_fully_expired($record, $expected) {
|
||
|
$purpose = new expired_context(0, (object) $record);
|
||
|
|
||
|
$this->assertEquals($expected, $purpose->is_fully_expired());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Data provider for the is_fully_expired tests.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function is_fully_expired_provider() : array {
|
||
|
return [
|
||
|
'Fully expired' => [
|
||
|
[
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
'defaultexpired' => 1,
|
||
|
],
|
||
|
true,
|
||
|
],
|
||
|
'Unexpired roles present' => [
|
||
|
[
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
'defaultexpired' => 1,
|
||
|
'unexpiredroles' => json_encode([1]),
|
||
|
],
|
||
|
false,
|
||
|
],
|
||
|
'Only some expired roles present' => [
|
||
|
[
|
||
|
'status' => expired_context::STATUS_APPROVED,
|
||
|
'defaultexpired' => 0,
|
||
|
'expiredroles' => json_encode([1]),
|
||
|
],
|
||
|
false,
|
||
|
],
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that any orphaned records are removed once the context has been removed.
|
||
|
*/
|
||
|
public function test_orphaned_records_are_cleared() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course([
|
||
|
'startdate' => time() - (2 * YEARSECS),
|
||
|
'enddate' => time() - YEARSECS,
|
||
|
]);
|
||
|
$context = \context_course::instance($course->id);
|
||
|
|
||
|
// Flag all expired contexts.
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
$manager->set_progress(new \null_progress_trace());
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
|
||
|
$this->assertEquals(1, $flaggedcourses);
|
||
|
$this->assertEquals(0, $flaggedusers);
|
||
|
|
||
|
// Ensure that the record currently exists.
|
||
|
$expiredcontext = expired_context::get_record(['contextid' => $context->id]);
|
||
|
$this->assertNotFalse($expiredcontext);
|
||
|
|
||
|
// Approve it.
|
||
|
$expiredcontext->set('status', expired_context::STATUS_APPROVED)->save();
|
||
|
|
||
|
// Process deletions.
|
||
|
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||
|
|
||
|
$this->assertEquals(1, $processedcourses);
|
||
|
$this->assertEquals(0, $processedusers);
|
||
|
|
||
|
// Ensure that the record still exists.
|
||
|
$expiredcontext = expired_context::get_record(['contextid' => $context->id]);
|
||
|
$this->assertNotFalse($expiredcontext);
|
||
|
|
||
|
// Remove the actual course.
|
||
|
delete_course($course->id, false);
|
||
|
|
||
|
// The record will still exist until we flag it again.
|
||
|
$expiredcontext = expired_context::get_record(['contextid' => $context->id]);
|
||
|
$this->assertNotFalse($expiredcontext);
|
||
|
|
||
|
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||
|
$expiredcontext = expired_context::get_record(['contextid' => $context->id]);
|
||
|
$this->assertFalse($expiredcontext);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that the progres tracer works as expected out of the box.
|
||
|
*/
|
||
|
public function test_progress_tracer_default() {
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
|
||
|
$rc = new \ReflectionClass(\tool_dataprivacy\expired_contexts_manager::class);
|
||
|
$rcm = $rc->getMethod('get_progress');
|
||
|
|
||
|
$rcm->setAccessible(true);
|
||
|
$this->assertInstanceOf(\text_progress_trace::class, $rcm->invoke($manager));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that the progres tracer works as expected when given a specific traer.
|
||
|
*/
|
||
|
public function test_progress_tracer_set() {
|
||
|
$manager = new \tool_dataprivacy\expired_contexts_manager();
|
||
|
$mytrace = new \null_progress_trace();
|
||
|
$manager->set_progress($mytrace);
|
||
|
|
||
|
$rc = new \ReflectionClass(\tool_dataprivacy\expired_contexts_manager::class);
|
||
|
$rcm = $rc->getMethod('get_progress');
|
||
|
|
||
|
$rcm->setAccessible(true);
|
||
|
$this->assertSame($mytrace, $rcm->invoke($manager));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates an HTML block on a user.
|
||
|
*
|
||
|
* @param string $title
|
||
|
* @param string $body
|
||
|
* @param string $format
|
||
|
* @return \block_instance
|
||
|
*/
|
||
|
protected function create_user_block($title, $body, $format) {
|
||
|
global $USER;
|
||
|
|
||
|
$configdata = (object) [
|
||
|
'title' => $title,
|
||
|
'text' => [
|
||
|
'itemid' => 19,
|
||
|
'text' => $body,
|
||
|
'format' => $format,
|
||
|
],
|
||
|
];
|
||
|
|
||
|
$this->create_block($this->construct_user_page($USER));
|
||
|
$block = $this->get_last_block_on_page($this->construct_user_page($USER));
|
||
|
$block = block_instance('html', $block->instance);
|
||
|
$block->instance_config_save((object) $configdata);
|
||
|
|
||
|
return $block;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates an HTML block on a page.
|
||
|
*
|
||
|
* @param \page $page Page
|
||
|
*/
|
||
|
protected function create_block($page) {
|
||
|
$page->blocks->add_block_at_end_of_default_region('html');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructs a Page object for the User Dashboard.
|
||
|
*
|
||
|
* @param \stdClass $user User to create Dashboard for.
|
||
|
* @return \moodle_page
|
||
|
*/
|
||
|
protected function construct_user_page(\stdClass $user) {
|
||
|
$page = new \moodle_page();
|
||
|
$page->set_context(\context_user::instance($user->id));
|
||
|
$page->set_pagelayout('mydashboard');
|
||
|
$page->set_pagetype('my-index');
|
||
|
$page->blocks->load_blocks();
|
||
|
return $page;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the last block on the page.
|
||
|
*
|
||
|
* @param \page $page Page
|
||
|
* @return \block_html Block instance object
|
||
|
*/
|
||
|
protected function get_last_block_on_page($page) {
|
||
|
$blocks = $page->blocks->get_blocks_for_region($page->blocks->get_default_region());
|
||
|
$block = end($blocks);
|
||
|
|
||
|
return $block;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with the system context.
|
||
|
*/
|
||
|
public function test_is_context_expired_system() {
|
||
|
$this->resetAfterTest();
|
||
|
$this->setup_basics('PT1H', 'PT1H', 'P1D');
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired(\context_system::instance()));
|
||
|
$this->assertFalse(
|
||
|
expired_contexts_manager::is_context_expired_or_unprotected_for_user(\context_system::instance(), $user));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with a block in the user context.
|
||
|
*
|
||
|
* Children of a user context always follow the user expiry rather than any context level defaults (e.g. at the
|
||
|
* block level.
|
||
|
*/
|
||
|
public function test_is_context_expired_user_block() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1D');
|
||
|
$purposes->block = $this->create_and_set_purpose_for_contextlevel('P5Y', CONTEXT_BLOCK);
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$this->setUser($user);
|
||
|
$block = $this->create_user_block('Title', 'Content', FORMAT_PLAIN);
|
||
|
$blockcontext = \context_block::instance($block->instance->id);
|
||
|
$this->setUser();
|
||
|
|
||
|
// Protected flags have no bearing on expiry of user subcontexts.
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired($blockcontext));
|
||
|
|
||
|
$purposes->block->set('protected', 1)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($blockcontext, $user));
|
||
|
|
||
|
$purposes->block->set('protected', 0)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($blockcontext, $user));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with the front page course.
|
||
|
*/
|
||
|
public function test_is_context_expired_frontpage() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1D');
|
||
|
|
||
|
$frontcourse = get_site();
|
||
|
$frontcoursecontext = \context_course::instance($frontcourse->id);
|
||
|
|
||
|
$sitenews = $this->getDataGenerator()->create_module('forum', ['course' => $frontcourse->id]);
|
||
|
$cm = get_coursemodule_from_instance('forum', $sitenews->id);
|
||
|
$sitenewscontext = \context_module::instance($cm->id);
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($frontcoursecontext));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($sitenewscontext));
|
||
|
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($frontcoursecontext, $user));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($sitenewscontext, $user));
|
||
|
|
||
|
// Protecting the course contextlevel does not impact the front page.
|
||
|
$purposes->course->set('protected', 1)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($frontcoursecontext, $user));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($sitenewscontext, $user));
|
||
|
|
||
|
// Protecting the system contextlevel affects the front page, too.
|
||
|
$purposes->system->set('protected', 1)->save();
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($frontcoursecontext, $user));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($sitenewscontext, $user));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with an expired course.
|
||
|
*/
|
||
|
public function test_is_context_expired_course_expired() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1D');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time()]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||
|
|
||
|
$purposes->course->set('protected', 1)->save();
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
|
||
|
$purposes->course->set('protected', 0)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with an unexpired course.
|
||
|
*/
|
||
|
public function test_is_context_expired_course_unexpired() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1D');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired($coursecontext));
|
||
|
|
||
|
$purposes->course->set('protected', 1)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
|
||
|
$purposes->course->set('protected', 0)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with an unexpired course and a child context in the course which is protected.
|
||
|
*
|
||
|
* When a child context has a specific purpose set, then that purpose should be respected with respect to the
|
||
|
* course.
|
||
|
*
|
||
|
* If the course is still within the expiry period for the child context, then that child's protected flag should be
|
||
|
* respected, even when the course may have expired.
|
||
|
*/
|
||
|
public function test_is_child_context_expired_course_unexpired_with_child() {
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1D', 'P1D');
|
||
|
$purposes->course->set('protected', 0)->save();
|
||
|
$purposes->activity->set('protected', 1)->save();
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() + WEEKSECS]);
|
||
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
||
|
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$cm = get_coursemodule_from_instance('forum', $forum->id);
|
||
|
$forumcontext = \context_module::instance($cm->id);
|
||
|
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($forumcontext));
|
||
|
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($forumcontext, $user));
|
||
|
|
||
|
$purposes->activity->set('protected', 0)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($forumcontext, $user));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with an expired course which has role overrides.
|
||
|
*/
|
||
|
public function test_is_context_expired_course_expired_override() {
|
||
|
global $DB;
|
||
|
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$systemcontext = \context_system::instance();
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'P5Y',
|
||
|
]);
|
||
|
$override->save();
|
||
|
role_assign($role->id, $user->id, $systemcontext->id);
|
||
|
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||
|
|
||
|
$purposes->course->set('protected', 1)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
|
||
|
$purposes->course->set('protected', 0)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with an expired course which has role overrides.
|
||
|
*/
|
||
|
public function test_is_context_expired_course_expired_override_parent() {
|
||
|
global $DB;
|
||
|
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$systemcontext = \context_system::instance();
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->system->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'P5Y',
|
||
|
]);
|
||
|
$override->save();
|
||
|
role_assign($role->id, $user->id, $systemcontext->id);
|
||
|
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||
|
|
||
|
// The user override applies to this user. THIs means that the default expiry has no effect.
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
|
||
|
$purposes->system->set('protected', 1)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
|
||
|
$purposes->system->set('protected', 0)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
|
||
|
$override->set('protected', 1)->save();
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
|
||
|
$purposes->system->set('protected', 1)->save();
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
|
||
|
$purposes->system->set('protected', 0)->save();
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with an expired course which has role overrides but the user
|
||
|
* does not hold the role.
|
||
|
*/
|
||
|
public function test_is_context_expired_course_expired_override_parent_no_role() {
|
||
|
global $DB;
|
||
|
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1H', 'PT1H');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$systemcontext = \context_system::instance();
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->system->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'P5Y',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
// This context is not _fully _ expired.
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with an unexpired course which has role overrides.
|
||
|
*/
|
||
|
public function test_is_context_expired_course_expired_override_inverse() {
|
||
|
global $DB;
|
||
|
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('P1Y', 'P1Y');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$systemcontext = \context_system::instance();
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'student']);
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->system->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'PT1S',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
|
||
|
// This context is not _fully _ expired.
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with an unexpired course which has role overrides.
|
||
|
*/
|
||
|
public function test_is_context_expired_course_expired_override_inverse_parent() {
|
||
|
global $DB;
|
||
|
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('P1Y', 'P1Y');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$systemcontext = \context_system::instance();
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->system->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'PT1S',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
role_assign($role->id, $user->id, $systemcontext->id);
|
||
|
|
||
|
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||
|
role_unassign($studentrole->id, $user->id, $coursecontext->id);
|
||
|
|
||
|
// This context is not _fully _ expired.
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test the is_context_expired functions when supplied with an unexpired course which has role overrides.
|
||
|
*/
|
||
|
public function test_is_context_expired_course_expired_override_inverse_parent_not_assigned() {
|
||
|
global $DB;
|
||
|
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('P1Y', 'P1Y');
|
||
|
|
||
|
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$systemcontext = \context_system::instance();
|
||
|
|
||
|
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->system->get('id'),
|
||
|
'roleid' => $role->id,
|
||
|
'retentionperiod' => 'PT1S',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
// Enrol the user in the course without any role.
|
||
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||
|
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||
|
role_unassign($studentrole->id, $user->id, $coursecontext->id);
|
||
|
|
||
|
// This context is not _fully _ expired.
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that context expired checks for a specific user taken into account roles.
|
||
|
*/
|
||
|
public function test_is_context_expired_or_unprotected_for_user_role_mixtures_protected() {
|
||
|
global $DB;
|
||
|
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('PT1S', 'PT1S', 'PT1S');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - DAYSECS]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$systemcontext = \context_system::instance();
|
||
|
|
||
|
$roles = $DB->get_records_menu('role', [], 'id', 'shortname, id');
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $roles['manager'],
|
||
|
'retentionperiod' => 'P1W',
|
||
|
'protected' => 1,
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$s = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($s->id, $course->id, 'student');
|
||
|
|
||
|
$t = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($t->id, $course->id, 'teacher');
|
||
|
|
||
|
$sm = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($sm->id, $course->id, 'student');
|
||
|
role_assign($roles['manager'], $sm->id, $coursecontext->id);
|
||
|
|
||
|
$m = $this->getDataGenerator()->create_user();
|
||
|
role_assign($roles['manager'], $m->id, $coursecontext->id);
|
||
|
|
||
|
$tm = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($t->id, $course->id, 'teacher');
|
||
|
role_assign($roles['manager'], $tm->id, $coursecontext->id);
|
||
|
|
||
|
// The context should only be expired for users who are not a manager.
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $s));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $t));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $sm));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $tm));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $m));
|
||
|
|
||
|
$override->set('protected', 0)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $s));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $t));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $sm));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $tm));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $m));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that context expired checks for a specific user taken into account roles when retention is inversed.
|
||
|
*/
|
||
|
public function test_is_context_expired_or_unprotected_for_user_role_mixtures_protected_inverse() {
|
||
|
global $DB;
|
||
|
|
||
|
$this->resetAfterTest();
|
||
|
|
||
|
$purposes = $this->setup_basics('P5Y', 'P5Y', 'P5Y');
|
||
|
|
||
|
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - DAYSECS]);
|
||
|
$coursecontext = \context_course::instance($course->id);
|
||
|
$systemcontext = \context_system::instance();
|
||
|
|
||
|
$roles = $DB->get_records_menu('role', [], 'id', 'shortname, id');
|
||
|
$override = new purpose_override(0, (object) [
|
||
|
'purposeid' => $purposes->course->get('id'),
|
||
|
'roleid' => $roles['student'],
|
||
|
'retentionperiod' => 'PT1S',
|
||
|
]);
|
||
|
$override->save();
|
||
|
|
||
|
$s = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($s->id, $course->id, 'student');
|
||
|
|
||
|
$t = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($t->id, $course->id, 'teacher');
|
||
|
|
||
|
$sm = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($sm->id, $course->id, 'student');
|
||
|
role_assign($roles['manager'], $sm->id, $coursecontext->id);
|
||
|
|
||
|
$m = $this->getDataGenerator()->create_user();
|
||
|
role_assign($roles['manager'], $m->id, $coursecontext->id);
|
||
|
|
||
|
$tm = $this->getDataGenerator()->create_user();
|
||
|
$this->getDataGenerator()->enrol_user($t->id, $course->id, 'teacher');
|
||
|
role_assign($roles['manager'], $tm->id, $coursecontext->id);
|
||
|
|
||
|
// The context should only be expired for users who are only a student.
|
||
|
$purposes->course->set('protected', 1)->save();
|
||
|
$override->set('protected', 1)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $s));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $t));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $sm));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $tm));
|
||
|
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $m));
|
||
|
|
||
|
$purposes->course->set('protected', 0)->save();
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $s));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $t));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $sm));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $tm));
|
||
|
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $m));
|
||
|
}
|
||
|
}
|