Skip to content

Commit

Permalink
MDL-63401 tool_dataprivacy: Rewrite expired deletion handling
Browse files Browse the repository at this point in the history
This change rewrites the way in which expiry is calculated and addresses
a number of closely related issues:

Users can customise, and add blocks with data to, their dashboard.  When
a user had done so, the user could be flagged for deletion, but the
blocks in their Dashboard were subject to the default block retention
policy. In addition there is no way to override the retention policy for
user blocks.

This change modifies the structure of the expiry mechanism:
- to consider any subcontext of the user context to be a part of the user
  context during calculation. User child contexts are not the property
  of the system, and should not be treated separately.
- the way in which the context expiry mechanism worked was to select
  use a multiple different managers based solely on the context level.
  Because of the use of user blocks, this proved to be unreliable as
  blocks has been attributed purely to courses.
  This has been changed to a single manager which is aware of hierarchy
  and deletions as a whole.
- to prepare for upcoming work relating to more detailed expiry
  mechanisms, a new expiry_info class is introduced and used to
  merge the expiry of child contexts into a working in-memory view.

This changeset includes extensive unit tests.
  • Loading branch information
andrewnicols committed Oct 8, 2018
1 parent 0cd43cc commit 62165df
Show file tree
Hide file tree
Showing 12 changed files with 2,090 additions and 631 deletions.
30 changes: 1 addition & 29 deletions admin/tool/dataprivacy/classes/api.php
Expand Up @@ -926,7 +926,7 @@ public static function get_effective_context_category(\context $context, $forced
* @param int $forcedvalue Use this purposeid value as if this was this context instance purpose.
* @return purpose|false
*/
public static function get_effective_context_purpose(\context $context, $forcedvalue=false) {
public static function get_effective_context_purpose(\context $context, $forcedvalue = false) {
if (!data_registry::defaults_set()) {
return false;
}
Expand Down Expand Up @@ -964,34 +964,6 @@ public static function get_effective_contextlevel_purpose($contextlevel, $forced
return data_registry::get_effective_contextlevel_value($contextlevel, 'purpose', $forcedvalue);
}

/**
* Creates an expired context record for the provided context id.
*
* @param int $contextid
* @return \tool_dataprivacy\expired_context
*/
public static function create_expired_context($contextid) {
$record = (object)[
'contextid' => $contextid,
'status' => expired_context::STATUS_EXPIRED,
];
$expiredctx = new expired_context(0, $record);
$expiredctx->save();

return $expiredctx;
}

/**
* Deletes an expired context record.
*
* @param int $id The tool_dataprivacy_ctxexpire id.
* @return bool True on success.
*/
public static function delete_expired_context($id) {
$expiredcontext = new expired_context($id);
return $expiredcontext->delete();
}

/**
* Updates the status of an expired context.
*
Expand Down
37 changes: 23 additions & 14 deletions admin/tool/dataprivacy/classes/data_registry.php
Expand Up @@ -191,14 +191,14 @@ public static function get_subject_scope(\context $context) {
* @param int|false $forcedvalue Use this value as if this was this context instance value.
* @return persistent|false It return a 'purpose' instance or a 'category' instance, depending on $element
*/
public static function get_effective_context_value(\context $context, $element, $forcedvalue=false) {
public static function get_effective_context_value(\context $context, $element, $forcedvalue = false) {

if ($element !== 'purpose' && $element !== 'category') {
throw new coding_exception('Only \'purpose\' and \'category\' are supported.');
}
$fieldname = $element . 'id';

if ($forcedvalue === false) {
if (empty($forcedvalue)) {
$instance = context_instance::get_record_by_contextid($context->id, false);

if (!$instance) {
Expand All @@ -217,20 +217,29 @@ public static function get_effective_context_value(\context $context, $element,
// The effective value varies depending on the context level.
if ($context->contextlevel == CONTEXT_USER) {
// Use the context level value as we don't allow people to set specific instances values.
return self::get_effective_contextlevel_value($context->contextlevel, $element);
} else {
// Check if we need to pass the plugin name of an activity.
$forplugin = '';
if ($context->contextlevel == CONTEXT_MODULE) {
list($course, $cm) = get_course_and_cm_from_cmid($context->instanceid);
$forplugin = $cm->modname;
return self::get_effective_contextlevel_value(CONTEXT_USER, $element);
}

$parents = $context->get_parent_contexts(true);
foreach ($parents as $parent) {
if ($parent->contextlevel == CONTEXT_USER) {
// Use the context level value as we don't allow people to set specific instances values.
return self::get_effective_contextlevel_value(CONTEXT_USER, $element);
}
// Use the default context level value.
list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category(
$context->contextlevel, false, false, $forplugin
);
return self::get_element_instance($element, $$fieldname);
}

// Check if we need to pass the plugin name of an activity.
$forplugin = '';
if ($context->contextlevel == CONTEXT_MODULE) {
list($course, $cm) = get_course_and_cm_from_cmid($context->instanceid);
$forplugin = $cm->modname;
}
// Use the default context level value.
list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category(
$context->contextlevel, false, false, $forplugin
);

return self::get_element_instance($element, $$fieldname);
}

// Specific value for this context instance.
Expand Down
47 changes: 47 additions & 0 deletions admin/tool/dataprivacy/classes/expired_context.php
Expand Up @@ -159,4 +159,51 @@ public static function get_record_count_by_contextlevel($contextlevel = null, $s

return $DB->count_records_sql($sql, $params);
}

/**
* Create a new expired_context based on the context, and expiry_info object.
*
* @param \context $context
* @param expiry_info $info
* @return expired_context
*/
public static function create_from_expiry_info(\context $context, expiry_info $info) {
$record = (object) [
'contextid' => $context->id,
'status' => self::STATUS_EXPIRED,
];

$expiredcontext = new static(0, $record);
$expiredcontext->save();

return $expiredcontext;
}

/**
* Update the expired_context from an expiry_info object which relates to this context.
*
* @param expiry_info $info
* @return $this
*/
public function update_from_expiry_info(expiry_info $info) {
return $this;
}

/**
* Check whether this expired_context record is in a state ready for deletion to actually take place.
*
* @return bool
*/
public function can_process_deletion() {
return ($this->get('status') == self::STATUS_APPROVED);
}

/**
* Check whether this expired_context record has already been cleaned.
*
* @return bool
*/
public function is_complete() {
return ($this->get('status') == self::STATUS_CLEANED);
}
}

0 comments on commit 62165df

Please sign in to comment.