Skip to content
Snippets Groups Projects
Unverified Commit 0158ff5c authored by Justus Dieckmann's avatar Justus Dieckmann Committed by GitHub
Browse files

Merge pull request #91 from justusdieckmann/feature/privacy

Implementing Privacy
parents 289fcb48 35572f5d
Branches
Tags
No related merge requests found
...@@ -164,7 +164,11 @@ class interaction_remaining_table extends interaction_table { ...@@ -164,7 +164,11 @@ class interaction_remaining_table extends interaction_table {
} }
// Otherwise, show latest action commited by user. // Otherwise, show latest action commited by user.
global $CFG; global $CFG;
if ($row->userid == -1) {
$userlink = get_string("anonymous_user", 'tool_lifecycle');
} else {
$userlink = \html_writer::link($CFG->wwwroot . '/user/profile.php?id=' . $row->userid, fullname($row)); $userlink = \html_writer::link($CFG->wwwroot . '/user/profile.php?id=' . $row->userid, fullname($row));
}
$interactionlib = lib_manager::get_step_interactionlib($row->subpluginname); $interactionlib = lib_manager::get_step_interactionlib($row->subpluginname);
return $interactionlib->get_action_string($row->action, $userlink); return $interactionlib->get_action_string($row->action, $userlink);
} }
......
<?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/>.
/**
* Privacy Subsystem implementation for tool_lifecycle.
*
* @package tool_lifecycle
* @copyright 2019 Justus Dieckmann WWU
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_lifecycle\privacy;
use context;
use context_system;
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;
use tool_lifecycle\local\manager\step_manager;
use tool_lifecycle\local\manager\workflow_manager;
defined('MOODLE_INTERNAL') || die();
/**
* Implementation of the privacy subsystem plugin provider for the Life Cycle tool.
*
* @copyright 2019 Justus Dieckmann WWU
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
// This plugin has data.
\core_privacy\local\metadata\provider,
// This plugin currently implements the original plugin_provider interface.
\core_privacy\local\request\plugin\provider,
// This plugin is capable of determining which users have data within it.
\core_privacy\local\request\core_userlist_provider {
/**
* Returns meta data about this system.
*
* @param collection $collection The initialised collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_database_table('tool_lifecycle_action_log',
array(
'processid' => 'privacy:metadata:tool_lifecycle_action_log:processid',
'workflowid' => 'privacy:metadata:tool_lifecycle_action_log:workflowid',
'courseid' => 'privacy:metadata:tool_lifecycle_action_log:courseid',
'stepindex' => 'privacy:metadata:tool_lifecycle_action_log:stepindex',
'time' => 'privacy:metadata:tool_lifecycle_action_log:time',
'userid' => 'privacy:metadata:tool_lifecycle_action_log:userid',
'action' => 'privacy:metadata:tool_lifecycle_action_log:action'
),
'privacy:metadata:tool_lifecycle_action_log');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
global $DB;
$contextlist = new contextlist();
if ($DB->record_exists('tool_lifecycle_action_log', array('userid' => $userid))) {
$contextlist->add_system_context();
}
return $contextlist;
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
foreach ($contextlist->get_contexts() as $context) {
if ($context instanceof context_system) {
$records = $DB->get_records('tool_lifecycle_action_log', array('userid' => $contextlist->get_user()->id));
$writer = writer::with_context($contextlist->current());
foreach ($records as $record) {
$step = step_manager::get_step_instance_by_workflow_index($record->workflowid, $record->stepindex);
$workflow = workflow_manager::get_workflow($record->workflowid);
$record->course = get_course($record->courseid)->fullname;
$record->step = $step->instancename;
$record->workflow = $workflow->displaytitle;
$subcontext = ['tool_lifecycle', 'action_log', "process_$record->processid", $step->instancename,
"action_$record->action"];
$writer->export_data($subcontext, $record);
}
}
}
}
/**
* Delete all data for all users in the specified context.
*
* @param context $context The specific context to delete data for.
*/
public static function delete_data_for_all_users_in_context(context $context) {
global $DB;
if ($context instanceof context_system) {
$sql = "UPDATE {tool_lifecycle_action_log}
SET userid = -1";
$DB->execute($sql);
}
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
global $DB;
foreach ($contextlist->get_contexts() as $context) {
if ($context instanceof context_system) {
$sql = "UPDATE {tool_lifecycle_action_log}
SET userid = -1
WHERE userid = :userid";
$DB->execute($sql, array('userid' => $contextlist->get_user()->id));
}
}
}
/**
* Get the list of users who have data within a context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if ($context instanceof context_system) {
$sql = "SELECT userid
FROM {tool_lifecycle_action_log}";
$userlist->add_from_sql('userid', $sql, array());
}
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
global $DB;
$context = $userlist->get_context();
if ($context instanceof context_system) {
list($insql, $params) = $DB->get_in_or_equal($userlist->get_userids());
$sql = "UPDATE {tool_lifecycle_action_log}
SET userid = -1
WHERE userid " . $insql;
$DB->execute($sql, $params);
}
}
}
...@@ -163,6 +163,7 @@ $string['manual_trigger_process_existed'] = 'A workflow for this course already ...@@ -163,6 +163,7 @@ $string['manual_trigger_process_existed'] = 'A workflow for this course already
$string['coursename'] = 'Course name'; $string['coursename'] = 'Course name';
$string['lastaction'] = 'Last action on'; $string['lastaction'] = 'Last action on';
$string['anonymous_user'] = 'Anonymous User';
$string['workflow_started'] = 'Workflow started.'; $string['workflow_started'] = 'Workflow started.';
$string['workflow_is_running'] = 'Workflow is running.'; $string['workflow_is_running'] = 'Workflow is running.';
...@@ -176,3 +177,13 @@ $string['restore_trigger_does_not_exist'] = 'The trigger {$a} is not installed, ...@@ -176,3 +177,13 @@ $string['restore_trigger_does_not_exist'] = 'The trigger {$a} is not installed,
$string['process_triggered_event'] = 'A process has been triggered'; $string['process_triggered_event'] = 'A process has been triggered';
$string['process_proceeded_event'] = 'A process has been proceeded'; $string['process_proceeded_event'] = 'A process has been proceeded';
$string['process_rollback_event'] = 'A process has been rolled back'; $string['process_rollback_event'] = 'A process has been rolled back';
// Privacy API
$string['privacy:metadata:tool_lifecycle_action_log'] = 'A log of actions done by course managers.';
$string['privacy:metadata:tool_lifecycle_action_log:processid'] = 'ID of the Process the action was done in.';
$string['privacy:metadata:tool_lifecycle_action_log:workflowid'] = 'ID of the Workflow the action was done in.';
$string['privacy:metadata:tool_lifecycle_action_log:courseid'] = 'ID of the Course the action was done for';
$string['privacy:metadata:tool_lifecycle_action_log:stepindex'] = 'Index of the Step in the Workflow, the action was done for.';
$string['privacy:metadata:tool_lifecycle_action_log:time'] = 'Time when the action was done.';
$string['privacy:metadata:tool_lifecycle_action_log:userid'] = 'ID of the user that did the action.';
$string['privacy:metadata:tool_lifecycle_action_log:action'] = 'Identifier of the action that was done.';
<?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/>.
/**
* Tests Privacy Implementation
* @package tool_lifecycle
* @category test
* @group tool_lifecycle
* @copyright 2019 Justus Dieckmann WWU
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;
use core_privacy\tests\provider_testcase;
use core_privacy\tests\request\approved_contextlist;
use tool_lifecycle\action;
use tool_lifecycle\local\entity\step_subplugin;
use tool_lifecycle\local\entity\workflow;
use tool_lifecycle\local\manager\interaction_manager;
use tool_lifecycle\local\manager\step_manager;
use tool_lifecycle\local\manager\workflow_manager;
use tool_lifecycle\privacy\provider;
use tool_lifecycle\processor;
/**
* Tests Privacy Implementation
* @package tool_lifecycle
* @category test
* @group tool_lifecycle
* @copyright 2019 Justus Dieckmann WWU
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_lifecycle_privacy_test extends provider_testcase {
/** Icon of the manual trigger. */
const MANUAL_TRIGGER1_ICON = 't/up';
/** Display name of the manual trigger. */
const MANUAL_TRIGGER1_DISPLAYNAME = 'Up';
/** Capability of the manual trigger. */
const MANUAL_TRIGGER1_CAPABILITY = 'moodle/course:manageactivities';
/** @var string Action string for triggering to keep a course from Email step. */
const ACTION_KEEP = 'keep';
/** @var workflow $workflow Workflow of this test. */
private $workflow;
/** @var tool_lifecycle_generator $generator Instance of the test generator. */
private $generator;
/** @var step_subplugin $emailstep Instance of the Email step */
private $emailstep;
/**
* Setup the testcase.
* @throws coding_exception
*/
public function setUp() {
global $USER;
// We do not need a sesskey check in theses tests.
$USER->ignoresesskey = true;
$this->resetAfterTest();
$this->generator = $this->getDataGenerator()->get_plugin_generator('tool_lifecycle');
$settings = new stdClass();
$settings->icon = self::MANUAL_TRIGGER1_ICON;
$settings->displayname = self::MANUAL_TRIGGER1_DISPLAYNAME;
$settings->capability = self::MANUAL_TRIGGER1_CAPABILITY;
$this->workflow = $this->generator->create_manual_workflow($settings);
workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow->id);
$this->emailstep = $this->generator->create_step("instance2", "email", $this->workflow->id);
}
public function test_get_contexts_for_userid() {
$c1 = $this->getDataGenerator()->create_course();
$c2 = $this->getDataGenerator()->create_course();
$u1 = $this->getDataGenerator()->create_user();
$this->setUser($u1);
$contextlist = provider::get_contexts_for_userid($u1->id);
$this->assertEquals(0, $contextlist->count());
$p1 = $this->generator->create_process($c1->id, $this->workflow->id);
$p2 = $this->generator->create_process($c2->id, $this->workflow->id);
$processor = new processor();
$processor->process_courses();
interaction_manager::handle_interaction($this->emailstep->id, $p1->id, self::ACTION_KEEP);
interaction_manager::handle_interaction($this->emailstep->id, $p2->id, self::ACTION_KEEP);
$contextlist = provider::get_contexts_for_userid($u1->id);
$this->assertEquals(1, $contextlist->count());
$this->assertTrue($contextlist->current() instanceof context_system);
}
public function test_export_user_data() {
$c1 = $this->getDataGenerator()->create_course();
$c2 = $this->getDataGenerator()->create_course();
$u1 = $this->getDataGenerator()->create_user();
$this->setUser($u1);
$p1 = $this->generator->create_process($c1->id, $this->workflow->id);
$p2 = $this->generator->create_process($c2->id, $this->workflow->id);
$processor = new processor();
$processor->process_courses();
interaction_manager::handle_interaction($this->emailstep->id, $p1->id, self::ACTION_KEEP);
interaction_manager::handle_interaction($this->emailstep->id, $p2->id, self::ACTION_KEEP);
$contextlist = new approved_contextlist($u1, 'tool_lifecycle', [context_system::instance()->id]);
provider::export_user_data($contextlist);
$writer = writer::with_context(context_system::instance());
$step = step_manager::get_step_instance_by_workflow_index($this->workflow->id, 1);
$subcontext = ['tool_lifecycle', 'action_log', "process_$p1->id", $step->instancename,
"action_" . self::ACTION_KEEP];
$data1 = $writer->get_data($subcontext);
$this->assertEquals($u1->id, $data1->userid);
$this->assertEquals(self::ACTION_KEEP, $data1->action);
$subcontext = ['tool_lifecycle', 'action_log', "process_$p2->id", $step->instancename,
"action_" . self::ACTION_KEEP];
$data2 = $writer->get_data($subcontext);
$this->assertEquals($u1->id, $data2->userid);
$this->assertEquals(self::ACTION_KEEP, $data2->action);
}
public function test_delete_data_for_all_users_in_context() {
global $DB;
$c1 = $this->getDataGenerator()->create_course();
$u1 = $this->getDataGenerator()->create_user();
$this->setUser($u1);
$p1 = $this->generator->create_process($c1->id, $this->workflow->id);
$processor = new processor();
$processor->process_courses();
interaction_manager::handle_interaction($this->emailstep->id, $p1->id, self::ACTION_KEEP);
provider::delete_data_for_all_users_in_context(context_system::instance());
$this->assertFalse($DB->record_exists_select('tool_lifecycle_action_log', 'userid != -1'));
}
public function test_delete_data_for_user() {
global $DB;
$c1 = $this->getDataGenerator()->create_course();
$c2 = $this->getDataGenerator()->create_course();
$u1 = $this->getDataGenerator()->create_user();
$u2 = $this->getDataGenerator()->create_user();
$p1 = $this->generator->create_process($c1->id, $this->workflow->id);
$p2 = $this->generator->create_process($c2->id, $this->workflow->id);
$processor = new processor();
$processor->process_courses();
$this->setUser($u1);
interaction_manager::handle_interaction($this->emailstep->id, $p1->id, self::ACTION_KEEP);
$this->setUser($u2);
interaction_manager::handle_interaction($this->emailstep->id, $p2->id, self::ACTION_KEEP);
$contextlist = new approved_contextlist($u1, 'tool_lifecycle', [1]);
provider::delete_data_for_user($contextlist);
$this->assertEquals(0, $DB->count_records_select('tool_lifecycle_action_log', "userid = $u1->id"));
$this->assertEquals(1, $DB->count_records_select('tool_lifecycle_action_log', "userid = $u2->id"));
$this->assertEquals(1, $DB->count_records_select('tool_lifecycle_action_log', "userid = -1"));
}
public function test_get_users_in_context() {
$c1 = $this->getDataGenerator()->create_course();
$c2 = $this->getDataGenerator()->create_course();
$u1 = $this->getDataGenerator()->create_user();
$p1 = $this->generator->create_process($c1->id, $this->workflow->id);
$p2 = $this->generator->create_process($c2->id, $this->workflow->id);
$processor = new processor();
$processor->process_courses();
$this->setUser($u1);
interaction_manager::handle_interaction($this->emailstep->id, $p1->id, self::ACTION_KEEP);
interaction_manager::handle_interaction($this->emailstep->id, $p2->id, self::ACTION_KEEP);
$userlist = new userlist(context_system::instance(), 'tool_lifecycle');
provider::get_users_in_context($userlist);
$this->assertEquals(1, $userlist->count());
$this->assertEquals($u1->id, $userlist->current()->id);
}
public function test_delete_data_for_users() {
global $DB;
$c1 = $this->getDataGenerator()->create_course();
$c2 = $this->getDataGenerator()->create_course();
$u1 = $this->getDataGenerator()->create_user();
$u2 = $this->getDataGenerator()->create_user();
$proc1 = $this->generator->create_process($c1->id, $this->workflow->id);
$proc2 = $this->generator->create_process($c2->id, $this->workflow->id);
$this->setUser($u1);
$processor = new processor();
$processor->process_courses();
interaction_manager::handle_interaction($this->emailstep->id, $proc1->id, self::ACTION_KEEP);
$this->setUser($u2);
interaction_manager::handle_interaction($this->emailstep->id, $proc2->id, self::ACTION_KEEP);
$userlist = new approved_userlist(context_system::instance(), 'tool_lifecycle', [$u1->id]);
provider::delete_data_for_users($userlist);
$this->assertEquals(0, $DB->count_records_select('tool_lifecycle_action_log', "userid = $u1->id"));
$this->assertEquals(1, $DB->count_records_select('tool_lifecycle_action_log', "userid = $u2->id"));
$this->assertEquals(1, $DB->count_records_select('tool_lifecycle_action_log', "userid = -1"));
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment