Skip to content

Commit

Permalink
MDL-63897 dataprivacy: Kill the preprocess stage
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Nov 9, 2018
1 parent b627565 commit b838d85
Show file tree
Hide file tree
Showing 22 changed files with 249 additions and 876 deletions.
168 changes: 23 additions & 145 deletions admin/tool/dataprivacy/classes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
use stdClass;
use tool_dataprivacy\external\data_request_exporter;
use tool_dataprivacy\local\helper;
use tool_dataprivacy\task\initiate_data_request_task;
use tool_dataprivacy\task\process_data_request_task;
use tool_dataprivacy\data_request;

Expand All @@ -65,9 +64,6 @@ class api {
/** Newly submitted and we haven't yet started finding out where they have data. */
const DATAREQUEST_STATUS_PENDING = 0;

/** Newly submitted and we have started to find the location of data. */
const DATAREQUEST_STATUS_PREPROCESSING = 1;

/** Metadata ready and awaiting review and approval by the Data Protection officer. */
const DATAREQUEST_STATUS_AWAITING_APPROVAL = 2;

Expand Down Expand Up @@ -249,7 +245,7 @@ public static function create_data_request($foruser, $type, $comments = '',
// The user making the request.
$datarequest->set('requestedby', $requestinguser);
// Set status.
$datarequest->set('status', self::DATAREQUEST_STATUS_PENDING);
$datarequest->set('status', self::DATAREQUEST_STATUS_AWAITING_APPROVAL);
// Set request type.
$datarequest->set('type', $type);
// Set request comments.
Expand All @@ -260,11 +256,6 @@ public static function create_data_request($foruser, $type, $comments = '',
// Store subject access request.
$datarequest->create();

// Fire an ad hoc task to initiate the data request process.
$task = new initiate_data_request_task();
$task->set_custom_data(['requestid' => $datarequest->get('id')]);
manager::queue_adhoc_task($task, true);

return $datarequest;
}

Expand Down Expand Up @@ -603,11 +594,6 @@ public static function approve_data_request($requestid) {
// Update the status and the DPO.
$result = self::update_request_status($requestid, self::DATAREQUEST_STATUS_APPROVED, $USER->id);

// Approve all the contexts attached to the request.
// Currently, approving the request implicitly approves all associated contexts, but this may change in future, allowing
// users to selectively approve certain contexts only.
self::update_request_contexts_with_status($requestid, contextlist_context::STATUS_APPROVED);

// Fire an ad hoc task to initiate the data request process.
$task = new process_data_request_task();
$task->set_custom_data(['requestid' => $requestid]);
Expand Down Expand Up @@ -1066,147 +1052,39 @@ public static function set_expired_context_status(expired_context $expiredctx, $
}

/**
* Adds the contexts from the contextlist_collection to the request with the status provided.
* Finds all contextlists having at least one approved context, and returns them as in a contextlist_collection.
*
* @param contextlist_collection $clcollection a collection of contextlists for all components.
* @param int $requestid the id of the request.
* @param int $status the status to set the contexts to.
* @param contextlist_collection $collection The collection of unapproved contextlist objects.
* @param \stdClass $foruser The target user
* @param int $type The purpose of the collection
* @return contextlist_collection The collection of approved_contextlist objects.
*/
public static function add_request_contexts_with_status(contextlist_collection $clcollection, int $requestid, int $status) {
$request = new data_request($requestid);
$user = \core_user::get_user($request->get('userid'));
foreach ($clcollection as $contextlist) {
// Convert the \core_privacy\local\request\contextlist into a contextlist persistent and store it.
$clp = \tool_dataprivacy\contextlist::from_contextlist($contextlist);
$clp->create();
$contextlistid = $clp->get('id');

// Store the associated contexts in the contextlist.
foreach ($contextlist->get_contextids() as $contextid) {
if ($request->get('type') == static::DATAREQUEST_TYPE_DELETE) {
$context = \context::instance_by_id($contextid);
$purpose = static::get_effective_context_purpose($context);
public static function get_approved_contextlist_collection_for_collection(contextlist_collection $collection,
\stdClass $foruser, int $type) : contextlist_collection {

// Create the approved contextlist collection object.
$approvedcollection = new contextlist_collection($collection->get_userid());

foreach ($collection as $contextlist) {
$contextids = [];
foreach ($contextlist as $context) {
if (self::DATAREQUEST_TYPE_DELETE == $type) {
// Data can only be deleted from it if the context is either expired, or unprotected.
if (!expired_contexts_manager::is_context_expired_or_unprotected_for_user($context, $user)) {
$purpose = static::get_effective_context_purpose($context);
if (!expired_contexts_manager::is_context_expired_or_unprotected_for_user($context, $foruser)) {
continue;
}
}

$context = new contextlist_context();
$context->set('contextid', $contextid)
->set('contextlistid', $contextlistid)
->set('status', $status)
->create();
}

// Create the relation to the request.
$requestcontextlist = request_contextlist::create_relation($requestid, $contextlistid);
$requestcontextlist->create();
}
}

/**
* Sets the status of all contexts associated with the request.
*
* @param int $requestid the requestid to which the contexts belong.
* @param int $status the status to set to.
* @throws \dml_exception if the requestid is invalid.
* @throws \moodle_exception if the status is invalid.
*/
public static function update_request_contexts_with_status(int $requestid, int $status) {
// Validate contextlist_context status using the persistent's attribute validation.
$contextlistcontext = new contextlist_context();
$contextlistcontext->set('status', $status);
if (array_key_exists('status', $contextlistcontext->get_errors())) {
throw new moodle_exception("Invalid contextlist_context status: $status");
}

// Validate requestid using the persistent's record validation.
// A dml_exception is thrown if the record is missing.
$datarequest = new data_request($requestid);

// Bulk update the status of the request contexts.
global $DB;

$select = "SELECT ctx.id as id
FROM {" . request_contextlist::TABLE . "} rcl
JOIN {" . contextlist::TABLE . "} cl ON rcl.contextlistid = cl.id
JOIN {" . contextlist_context::TABLE . "} ctx ON cl.id = ctx.contextlistid
WHERE rcl.requestid = ?";

// Fetch records IDs to be updated and update by chunks, if applicable (limit of 1000 records per update).
$limit = 1000;
$idstoupdate = $DB->get_fieldset_sql($select, [$requestid]);
$count = count($idstoupdate);
$idchunks = $idstoupdate;
if ($count > $limit) {
$idchunks = array_chunk($idstoupdate, $limit);
}
$transaction = $DB->start_delegated_transaction();
$initialparams = [$status];
foreach ($idchunks as $chunk) {
list($insql, $inparams) = $DB->get_in_or_equal($chunk);
$update = "UPDATE {" . contextlist_context::TABLE . "}
SET status = ?
WHERE id $insql";
$params = array_merge($initialparams, $inparams);
$DB->execute($update, $params);
}
$transaction->allow_commit();
}

/**
* Finds all request contextlists having at least on approved context, and returns them as in a contextlist_collection.
*
* @param data_request $request the data request with which the contextlists are associated.
* @return contextlist_collection the collection of approved_contextlist objects.
*/
public static function get_approved_contextlist_collection_for_request(data_request $request) : contextlist_collection {
$foruser = core_user::get_user($request->get('userid'));

// Fetch all approved contextlists and create the core_privacy\local\request\contextlist objects here.
global $DB;
$sql = "SELECT cl.component, ctx.contextid
FROM {" . request_contextlist::TABLE . "} rcl
JOIN {" . contextlist::TABLE . "} cl ON rcl.contextlistid = cl.id
JOIN {" . contextlist_context::TABLE . "} ctx ON cl.id = ctx.contextlistid
WHERE rcl.requestid = ?
AND ctx.status = ?
ORDER BY cl.component, ctx.contextid";

// Create the approved contextlist collection object.
$lastcomponent = null;
$approvedcollection = new contextlist_collection($foruser->id);

$rs = $DB->get_recordset_sql($sql, [$request->get('id'), contextlist_context::STATUS_APPROVED]);
foreach ($rs as $record) {
// If we encounter a new component, and we've built up contexts for the last, then add the approved_contextlist for the
// last (the one we've just finished with) and reset the context array for the next one.
if ($lastcomponent != $record->component) {
if (!empty($contexts)) {
$approvedcollection->add_contextlist(new approved_contextlist($foruser, $lastcomponent, $contexts));
}
$contexts = [];
$contextids[] = $context->id;
}

if ($request->get('type') == static::DATAREQUEST_TYPE_DELETE) {
$context = \context::instance_by_id($record->contextid);
$purpose = static::get_effective_context_purpose($context);
// Data can only be deleted from it if the context is either expired, or unprotected.
if (!expired_contexts_manager::is_context_expired_or_unprotected_for_user($context, $foruser)) {
continue;
}
// The data for the last component contextlist won't have been written yet, so write it now.
if (!empty($contextids)) {
$approvedcollection->add_contextlist(
new approved_contextlist($foruser, $contextlist->get_component(), $contextids)
);
}

$contexts[] = $record->contextid;
$lastcomponent = $record->component;
}
$rs->close();

// The data for the last component contextlist won't have been written yet, so write it now.
if (!empty($contexts)) {
$approvedcollection->add_contextlist(new approved_contextlist($foruser, $lastcomponent, $contexts));
}

return $approvedcollection;
Expand Down
64 changes: 0 additions & 64 deletions admin/tool/dataprivacy/classes/contextlist.php

This file was deleted.

74 changes: 0 additions & 74 deletions admin/tool/dataprivacy/classes/contextlist_context.php

This file was deleted.

3 changes: 1 addition & 2 deletions admin/tool/dataprivacy/classes/data_request.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,9 @@ protected static function define_properties() {
'type' => PARAM_INT
],
'status' => [
'default' => api::DATAREQUEST_STATUS_PENDING,
'default' => api::DATAREQUEST_STATUS_AWAITING_APPROVAL,
'choices' => [
api::DATAREQUEST_STATUS_PENDING,
api::DATAREQUEST_STATUS_PREPROCESSING,
api::DATAREQUEST_STATUS_AWAITING_APPROVAL,
api::DATAREQUEST_STATUS_APPROVED,
api::DATAREQUEST_STATUS_PROCESSING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,6 @@ protected function get_other_values(renderer_base $output) {
// Request can be manually completed for general enquiry requests.
$values['canmarkcomplete'] = $requesttype == api::DATAREQUEST_TYPE_OTHERS;
break;
case api::DATAREQUEST_STATUS_PREPROCESSING:
$values['statuslabelclass'] = 'label-default';
break;
case api::DATAREQUEST_STATUS_AWAITING_APPROVAL:
$values['statuslabelclass'] = 'label-info';
// DPO can review the request once it's ready.
Expand Down
1 change: 0 additions & 1 deletion admin/tool/dataprivacy/classes/local/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ public static function get_request_status_string($status) {
public static function get_request_statuses() {
return [
api::DATAREQUEST_STATUS_PENDING => get_string('statuspending', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_PREPROCESSING => get_string('statuspreprocessing', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_AWAITING_APPROVAL => get_string('statusawaitingapproval', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_APPROVED => get_string('statusapproved', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_PROCESSING => get_string('statusprocessing', 'tool_dataprivacy'),
Expand Down
Loading

0 comments on commit b838d85

Please sign in to comment.