Skip to content

Commit

Permalink
MDL-62147 portfolio: add missing tables to privacy provider
Browse files Browse the repository at this point in the history
  • Loading branch information
marinaglancy committed May 16, 2018
1 parent 3a20fb2 commit f2f7255
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 11 deletions.
10 changes: 10 additions & 0 deletions lang/en/portfolio.php
Expand Up @@ -170,6 +170,16 @@
$string['privacy:metadata:name'] = 'Name of the preference.';
$string['privacy:metadata:instance'] = 'Identifier for the portfolio.';
$string['privacy:metadata:instancesummary'] = 'This stores portfolio both instances and preferences for the portfolios user is using.';
$string['privacy:metadata:portfolio_log'] = 'Log of portfolio transfers (used to later check for duplicates)';
$string['privacy:metadata:portfolio_log:caller_class'] = 'Name of the class used to create the transfer';
$string['privacy:metadata:portfolio_log:caller_component'] = 'Component name responsible for exporting';
$string['privacy:metadata:portfolio_log:time'] = 'Time of transfer (in the case of a queued transfer this is the time the actual transfer ran, not when the user started)';
$string['privacy:metadata:portfolio_log:userid'] = 'User who exported content';
$string['privacy:metadata:portfolio_tempdata'] = 'Stores temporary data for portfolio exports, cleaned by cron after one day';
$string['privacy:metadata:portfolio_tempdata:data'] = 'Export data';
$string['privacy:metadata:portfolio_tempdata:expirytime'] = 'Time this record will expire';
$string['privacy:metadata:portfolio_tempdata:instance'] = 'Portfolio plugin instance being used';
$string['privacy:metadata:portfolio_tempdata:userid'] = 'User performing export';
$string['privacy:metadata:value'] = 'Value for the preference';
$string['privacy:metadata:userid'] = 'The user Identifier.';
$string['privacy:path'] = 'Portfolio instances';
Expand Down
92 changes: 83 additions & 9 deletions portfolio/classes/privacy/provider.php
Expand Up @@ -29,6 +29,7 @@
use core_privacy\local\request\context;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\transform;

/**
* Provider for the portfolio API.
Expand Down Expand Up @@ -56,6 +57,22 @@ public static function get_metadata(collection $collection) : collection {
'name' => 'privacy:metadata:name',
'value' => 'privacy:metadata:value'
], 'privacy:metadata:instancesummary');

$collection->add_database_table('portfolio_log', [
'userid' => 'privacy:metadata:portfolio_log:userid',
'time' => 'privacy:metadata:portfolio_log:time',
'caller_class' => 'privacy:metadata:portfolio_log:caller_class',
'caller_component' => 'privacy:metadata:portfolio_log:caller_component',
], 'privacy:metadata:portfolio_log');

// Temporary data is not exported/deleted in privacy API. It is cleaned by cron.
$collection->add_database_table('portfolio_tempdata', [
'data' => 'privacy:metadata:portfolio_tempdata:data',
'expirytime' => 'privacy:metadata:portfolio_tempdata:expirytime',
'userid' => 'privacy:metadata:portfolio_tempdata:userid',
'instance' => 'privacy:metadata:portfolio_tempdata:instance',
], 'privacy:metadata:portfolio_tempdata');

$collection->add_plugintype_link('portfolio', [], 'privacy:metadata');
return $collection;
}
Expand All @@ -69,9 +86,11 @@ public static function get_metadata(collection $collection) : collection {
public static function get_contexts_for_userid(int $userid) : contextlist {
$sql = "SELECT ctx.id
FROM {context} ctx
JOIN {portfolio_instance_user} piu ON ctx.instanceid = piu.userid AND ctx.contextlevel = :usercontext
WHERE piu.userid = :userid";
$params = ['userid' => $userid, 'usercontext' => CONTEXT_USER];
WHERE ctx.instanceid = :userid AND ctx.contextlevel = :usercontext
AND (EXISTS (SELECT 1 FROM {portfolio_instance_user} WHERE userid = :userid1) OR
EXISTS (SELECT 1 FROM {portfolio_log} WHERE userid = :userid2))
";
$params = ['userid' => $userid, 'usercontext' => CONTEXT_USER, 'userid1' => $userid, 'userid2' => $userid];
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
return $contextlist;
Expand All @@ -95,16 +114,63 @@ public static function export_user_data(approved_contextlist $contextlist) {
}
});

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

$usercontext = array_shift($correctusercontext);

$sql = "SELECT pi.id AS instanceid, pi.name,
piu.id AS preferenceid, piu.name AS preference, piu.value,
pl.id AS logid, pl.time AS logtime, pl.caller_class, pl.caller_file,
pl.caller_component, pl.returnurl, pl.continueurl
FROM {portfolio_instance} pi
LEFT JOIN {portfolio_instance_user} piu ON piu.instance = pi.id AND piu.userid = :userid1
LEFT JOIN {portfolio_log} pl ON pl.portfolio = pi.id AND pl.userid = :userid2
WHERE piu.id IS NOT NULL OR pl.id IS NOT NULL";
$params = ['userid1' => $usercontext->instanceid, 'userid2' => $usercontext->instanceid];
$instances = [];
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $record) {
$instances += [$record->name =>
(object)[
'name' => $record->name,
'preferences' => [],
'logs' => [],
]
];
if ($record->preferenceid) {
$instances[$record->name]->preferences[$record->preferenceid] = (object)[
'name' => $record->preference,
'value' => $record->value,
];
}
if ($record->logid) {
$instances[$record->name]->logs[$record->logid] = (object)[
'time' => transform::datetime($record->logtime),
'caller_class' => $record->caller_class,
'caller_file' => $record->caller_file,
'caller_component' => $record->caller_component,
'returnurl' => $record->returnurl,
'continueurl' => $record->continueurl
];
}
}
$rs->close();

$sql = "SELECT pi.name, piu.name AS preference, piu.value
FROM {portfolio_instance_user} piu
JOIN {portfolio_instance} pi ON piu.instance = pi.id
WHERE piu.userid = :userid";
$params = ['userid' => $usercontext->instanceid];
$instances = $DB->get_records_sql($sql, $params);
if (!empty($instances)) {
foreach ($instances as &$instance) {
if (!empty($instance->preferences)) {
$instance->preferences = array_values($instance->preferences);
} else {
unset($instance->preferences);
}
if (!empty($instance->logs)) {
$instance->logs = array_values($instance->logs);
} else {
unset($instance->logs);
}
}
\core_privacy\local\request\writer::with_context($contextlist->current())->export_data(
[get_string('privacy:path', 'portfolio')], (object) $instances);
}
Expand All @@ -120,6 +186,8 @@ public static function delete_data_for_all_users_in_context(\context $context) {
// Context could be anything, BEWARE!
if ($context->contextlevel == CONTEXT_USER) {
$DB->delete_records('portfolio_instance_user', ['userid' => $context->instanceid]);
$DB->delete_records('portfolio_tempdata', ['userid' => $context->instanceid]);
$DB->delete_records('portfolio_log', ['userid' => $context->instanceid]);
}
}

Expand All @@ -141,9 +209,15 @@ public static function delete_data_for_user(approved_contextlist $contextlist) {
}
});

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

$usercontext = array_shift($correctusercontext);

$DB->delete_records('portfolio_instance_user', ['userid' => $usercontext->instanceid]);
$DB->delete_records('portfolio_tempdata', ['userid' => $usercontext->instanceid]);
$DB->delete_records('portfolio_log', ['userid' => $usercontext->instanceid]);
}

/**
Expand Down
26 changes: 24 additions & 2 deletions portfolio/tests/privacy_provider_test.php
Expand Up @@ -47,6 +47,22 @@ protected function create_portfolio_data($plugin, $name, $user, $preference, $va
'value' => $value
];
$DB->insert_record('portfolio_instance_user', $userinstance);

$DB->insert_record('portfolio_log', [
'portfolio' => $portfolioinstance->id,
'userid' => $user->id,
'caller_class' => 'forum_portfolio_caller',
'caller_component' => 'mod_forum',
'time' => time(),
]);

$DB->insert_record('portfolio_log', [
'portfolio' => $portfolioinstance->id,
'userid' => $user->id,
'caller_class' => 'workshop_portfolio_caller',
'caller_component' => 'mod_workshop',
'time' => time(),
]);
}

/**
Expand All @@ -57,9 +73,11 @@ public function test_get_metadata() {
$collection = \core_portfolio\privacy\provider::get_metadata($collection);
$this->assertNotEmpty($collection);
$items = $collection->get_collection();
$this->assertEquals(2, count($items));
$this->assertEquals(4, count($items));
$this->assertInstanceOf(\core_privacy\local\metadata\types\database_table::class, $items[0]);
$this->assertInstanceOf(\core_privacy\local\metadata\types\plugintype_link::class, $items[1]);
$this->assertInstanceOf(\core_privacy\local\metadata\types\database_table::class, $items[1]);
$this->assertInstanceOf(\core_privacy\local\metadata\types\database_table::class, $items[2]);
$this->assertInstanceOf(\core_privacy\local\metadata\types\plugintype_link::class, $items[3]);
}

/**
Expand Down Expand Up @@ -105,13 +123,15 @@ public function test_delete_data_for_all_users_in_context() {
\core_portfolio\privacy\provider::delete_data_for_all_users_in_context($systemcontext);
$records = $DB->get_records('portfolio_instance_user');
$this->assertCount(2, $records);
$this->assertCount(4, $DB->get_records('portfolio_log'));
$context = context_user::instance($user1->id);
\core_portfolio\privacy\provider::delete_data_for_all_users_in_context($context);
$records = $DB->get_records('portfolio_instance_user');
// Only one entry should remain for user 2.
$this->assertCount(1, $records);
$data = array_shift($records);
$this->assertEquals($user2->id, $data->userid);
$this->assertCount(2, $DB->get_records('portfolio_log'));
}

/**
Expand All @@ -128,6 +148,7 @@ public function test_delete_data_for_user() {

$records = $DB->get_records('portfolio_instance_user');
$this->assertCount(2, $records);
$this->assertCount(4, $DB->get_records('portfolio_log'));

$context = context_user::instance($user1->id);
$contextlist = new \core_privacy\local\request\approved_contextlist($user1, 'core_portfolio', [$context->id]);
Expand All @@ -137,5 +158,6 @@ public function test_delete_data_for_user() {
$this->assertCount(1, $records);
$data = array_shift($records);
$this->assertEquals($user2->id, $data->userid);
$this->assertCount(2, $DB->get_records('portfolio_log'));
}
}

0 comments on commit f2f7255

Please sign in to comment.