From 72d2bbf706201c057c13d7b1efa38837dd5f0ef8 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 23 Mar 2018 12:52:58 +0000 Subject: [PATCH 01/21] By yanniboi: Started building out task entities. --- gdpr.module | 2 +- gdpr.test | 6 +- modules/gdpr_tasks/gdpr_tasks.admin.inc | 135 ++++++++++ modules/gdpr_tasks/gdpr_tasks.info | 10 + modules/gdpr_tasks/gdpr_tasks.install | 87 +++++++ modules/gdpr_tasks/gdpr_tasks.module | 235 ++++++++++++++++++ modules/gdpr_tasks/gdpr_tasks.pages.inc | 39 +++ modules/gdpr_tasks/src/Entity/GDPRTask.php | 89 +++++++ .../src/Entity/GDPRTaskController.php | 8 + .../src/Entity/GDPRTaskInterface.php | 11 + 10 files changed, 618 insertions(+), 4 deletions(-) create mode 100644 modules/gdpr_tasks/gdpr_tasks.admin.inc create mode 100644 modules/gdpr_tasks/gdpr_tasks.info create mode 100644 modules/gdpr_tasks/gdpr_tasks.install create mode 100644 modules/gdpr_tasks/gdpr_tasks.module create mode 100644 modules/gdpr_tasks/gdpr_tasks.pages.inc create mode 100644 modules/gdpr_tasks/src/Entity/GDPRTask.php create mode 100644 modules/gdpr_tasks/src/Entity/GDPRTaskController.php create mode 100644 modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php diff --git a/gdpr.module b/gdpr.module index 10d0674..4ccd93f 100644 --- a/gdpr.module +++ b/gdpr.module @@ -31,7 +31,7 @@ function gdpr_menu() { 'file path' => drupal_get_path('module', 'system'), ]; - $items['user/%user/collected_data'] = [ + $items['user/%user/gdpr'] = [ 'title' => 'All your data', 'page callback' => 'gdpr_collected_user_data', 'page arguments' => [1], diff --git a/gdpr.test b/gdpr.test index 8a6e33b..609d590 100644 --- a/gdpr.test +++ b/gdpr.test @@ -48,7 +48,7 @@ class GdprUserCollectedDataPageTest extends DrupalWebTestCase { * Tests that anonymous users do not have access to these pages. */ public function testAnonymousUserShouldNotHaveAccess() { - $this->drupalGet(sprintf('user/%d/collected_data', $this->subject->uid)); + $this->drupalGet(sprintf('user/%d/gdpr', $this->subject->uid)); $this->assertResponse(403); } @@ -57,7 +57,7 @@ class GdprUserCollectedDataPageTest extends DrupalWebTestCase { */ public function testUsersShouldHaveAccessToOwnPage() { $this->drupalLogin($this->subject); - $this->drupalGet(sprintf('user/%d/collected_data', $this->subject->uid)); + $this->drupalGet(sprintf('user/%d/gdpr', $this->subject->uid)); $this->assertResponse(200); } @@ -66,7 +66,7 @@ class GdprUserCollectedDataPageTest extends DrupalWebTestCase { */ public function testUsersShouldNotHaveAccessToOtherPages() { $this->drupalLogin($this->observer); - $this->drupalGet(sprintf('user/%d/collected_data', $this->subject->uid)); + $this->drupalGet(sprintf('user/%d/gdpr', $this->subject->uid)); $this->assertResponse(403); } diff --git a/modules/gdpr_tasks/gdpr_tasks.admin.inc b/modules/gdpr_tasks/gdpr_tasks.admin.inc new file mode 100644 index 0000000..4c2dc73 --- /dev/null +++ b/modules/gdpr_tasks/gdpr_tasks.admin.inc @@ -0,0 +1,135 @@ + $bundle) { + // Get the bundle edit link, add it to a table row. + $link = l(t('edit'), $bundle['admin']['real path']); + $rows[] = array($bundle['label'], $link); + } + // Format the table array. This is a pretty simple table, you could include + // links to the field management pages for each bundle too. + $output = array( + '#theme' => 'table', + '#header' => $headers, + '#rows' => $rows, + ); + return $output; +} + +/** + * Edit form for task bundles. + */ +function gdpr_task_type_form($form, &$form_state, $bundle = array(), $op = 'edit') { + + // @todo Do we need to edit bundles? + $form['message'] = array( + '#type' => 'markup', + '#markup' => 'Editing of GDPR Task types is not currently supported.' + ); + + return $form; +} + + +/** + * Form builder for the tasks overview administration form. + */ +function gdpr_task_admin_overview($form, &$form_state) { + // Load the tasks that need to be displayed. + $header = array( + 'task' => array('data' => t('Task'), 'field' => 'id'), + 'user' => array('data' => t('User'), 'field' => 'user_id'), + 'type' => array('data' => t('Type'), 'field' => 'type'), + 'created' => array('data' => t('Updated'), 'field' => 'created'), + 'operations' => array('data' => t('Operations')), + ); + + $query = db_select('gdpr_task', 't')->extend('PagerDefault')->extend('TableSort'); + $result = $query + ->fields('t', array('id', 'user_id', 'type', 'created')) + ->limit(50) + ->orderByHeader($header) + ->execute(); + + $ids = array(); + + foreach ($result as $row) { + $ids[] = $row->id; + } + /* @var GDPRTask[] $tasks */ + $tasks = gdpr_task_load_multiple($ids); + + // Build a table listing the appropriate tasks. + $options = array(); + $destination = drupal_get_destination(); + + foreach ($tasks as $task) { + $options[$task->id] = array( + 'task' => array( + 'data' => array( + '#type' => 'link', + '#title' => $task->label(), + '#href' => 'admin/content/task/' . $task->id, + ), + ), + 'user' => theme('username', array('account' => user_load($task->user_id))), + 'type' => array( + 'data' => $task->bundleLabel(), + ), + 'created' => format_date($task->created, 'short'), + 'operations' => array( + 'data' => array( + '#type' => 'link', + '#title' => t('edit'), + '#href' => 'admin/content/task/' . $task->id . '/edit', + '#options' => array('query' => $destination), + ), + ), + ); + } + + $form['tasks'] = array( + '#type' => 'tableselect', + '#header' => $header, + '#options' => $options, + '#empty' => t('No tasks available.'), + ); + + $form['pager'] = array('#theme' => 'pager'); + + return $form; +} + +/** + * Example entity display page. + */ +function gdpr_task_page($task) { + $build = array(); + + field_attach_prepare_view('gdpr_task', array($task->id => $task), 'full'); + + return $build; +} + +/** + * Example bundle edit page. + */ +function gdpr_task_edit($task) { + $build = array(); + $form_state = array(); + field_attach_form('gdpr_task', $task, $build, $form_state); + + return $build; +} \ No newline at end of file diff --git a/modules/gdpr_tasks/gdpr_tasks.info b/modules/gdpr_tasks/gdpr_tasks.info new file mode 100644 index 0000000..41012fb --- /dev/null +++ b/modules/gdpr_tasks/gdpr_tasks.info @@ -0,0 +1,10 @@ +name = General Data Protection Regulation (GDPR) - Tasks +description = Allow tracking of SARs and Removal requests. +core = 7.x + +dependencies[] = gdpr +dependencies[] = entity + +files[] = src/Entity/GDPRTask.php +files[] = src/Entity/GDPRTaskInterface.php +files[] = src/Entity/GDPRTaskController.php diff --git a/modules/gdpr_tasks/gdpr_tasks.install b/modules/gdpr_tasks/gdpr_tasks.install new file mode 100644 index 0000000..edc60a7 --- /dev/null +++ b/modules/gdpr_tasks/gdpr_tasks.install @@ -0,0 +1,87 @@ + 'The base table for tasks.', + 'fields' => array( + 'id' => array( + 'description' => 'The primary identifier for a task.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE + ), + 'type' => array( + 'description' => 'The {gdpr_task_type} of this task.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '' + ), + 'language' => array( + 'description' => 'The {languages}.language of this task.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), + 'user_id' => array( + 'description' => 'The {users}.uid that requested this task.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'status' => array( + 'description' => 'The text status of this task.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '' + ), + 'created' => array( + 'description' => 'The Unix timestamp when the task was created.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'changed' => array( + 'description' => 'The Unix timestamp when the task was most recently saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'complete' => array( + 'description' => 'The Unix timestamp when the task was completed.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'processed_by' => array( + 'description' => 'The {users}.uid that processed this task.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('id'), + 'foreign keys' => array( + 'task_author' => array( + 'table' => 'users', + 'columns' => array('user_id' => 'uid'), + ), + 'task_processor' => array( + 'table' => 'users', + 'columns' => array('user_id' => 'uid'), + ), + ), + ); + + return $schema; +} \ No newline at end of file diff --git a/modules/gdpr_tasks/gdpr_tasks.module b/modules/gdpr_tasks/gdpr_tasks.module new file mode 100644 index 0000000..3ecf625 --- /dev/null +++ b/modules/gdpr_tasks/gdpr_tasks.module @@ -0,0 +1,235 @@ + array( + 'label' => t('Task'), + 'base table' => 'gdpr_task', + 'entity class' => 'GDPRTask', + 'controller class' => 'GDPRTaskController', + 'entity keys' => array( + 'id' => 'id', + 'bundle' => 'type', + 'label' => 'id', + 'language' => 'language', + ), + 'bundle keys' => array( + 'bundle' => 'type', + ), + 'bundles' => array( + 'gdpr_remove' => array( + 'label' => 'Removal Request', + 'admin' => array( + 'path' => 'admin/structure/gdpr-tasks/manage/%', + 'real path' => 'admin/structure/gdpr-tasks/manage/gdpr_remove', + 'bundle argument' => 4, + 'access arguments' => array('administer task entities'), + ), + ), + 'gdpr_sar' => array( + 'label' => 'SARs Request', + 'admin' => array( + 'path' => 'admin/structure/gdpr-tasks/manage/%', + 'real path' => 'admin/structure/gdpr-tasks/manage/gdpr_sar', + 'bundle argument' => 4, + 'access arguments' => array('administer task entities'), + ), + ), + ), + 'fieldable' => TRUE, + ), + ); +} + +/** + * Implements hook_menu(). + */ +function gdpr_tasks_menu() { + // Task bundle menu items. + $items['admin/structure/gdpr-tasks'] = array( + 'title' => 'GDPR Task Types', + 'access arguments' => array('administer task entities'), + 'page callback' => 'gdpr_task_type_list', + 'file' => 'gdpr_tasks.admin.inc', + ); + $items['admin/structure/gdpr-tasks/list'] = array( // As above. + 'title' => 'List', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + $items['admin/structure/gdpr-tasks/manage/%'] = array( + 'title' => 'Edit task type', + 'access arguments' => array('administer task entities'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('gdpr_task_type_form', 4), + 'type' => MENU_CALLBACK, + 'file' => 'gdpr_tasks.admin.inc', + ); + $items['admin/structure/gdpr-tasks/manage/%/edit'] = array( // As above. + 'title' => 'Edit', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + + // Task entity menu items. + $items['admin/content/task'] = array( + 'title' => 'Tasks', + 'description' => 'List and edit GDPR Tasks.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('gdpr_task_admin_overview'), + 'access arguments' => array('view gdpr tasks'), + 'type' => MENU_LOCAL_TASK | MENU_NORMAL_ITEM, + 'file' => 'gdpr_tasks.admin.inc', + ); + $items['admin/content/task/%gdpr_task'] = array( + 'title callback' => 'gdpr_task_title', + 'title arguments' => array(3), + 'access arguments' => array('view gdpr tasks'), + 'page callback' => 'gdpr_task_page', + 'page arguments' => array(3), + 'file' => 'gdpr_tasks.admin.inc', + ); + $items['admin/content/task/%gdpr_task/view'] = array( // As above. + 'title' => 'View', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + $items['admin/content/task/%gdpr_task/edit'] = array( //page to edit your entities. + 'title' => 'Edit', + 'title callback' => 'entity_label', + 'title arguments' => array('gdpr_task', 3), + 'access arguments' => array('edit gdpr tasks'), + 'page callback' => 'entity_ui_get_form', + 'page arguments' => array('gdpr_task', 3), + 'type' => MENU_LOCAL_TASK, + ); + + // User task menu items. + $items['user/%user/gdpr/list'] = array( + 'title' => 'Summary', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + + $items['user/%user/gdpr/requests'] = array( + 'title' => 'My data requests', + 'access arguments' => array('view own gdpr tasks'), + 'page callback' => 'gdpr_task_user_request', + 'page arguments' => array(1), + 'type' => MENU_LOCAL_TASK, + 'file' => 'gdpr_tasks.pages.inc', + ); + + $items['user/%user/gdpr/requests/gdpr_remove/add'] = array( + 'title' => 'Request data removal', + 'page callback' => 'gdpr_tasks_request', + 'page arguments' => array(1, 4), + 'access arguments' => array('request gdpr tasks'), + 'type' => MENU_LOCAL_ACTION, + 'file' => 'gdpr_tasks.pages.inc', + ); + $items['user/%user/gdpr/requests/gdpr_sar/add'] = array( + 'title' => 'Request data export', + 'page callback' => 'gdpr_tasks_request', + 'page arguments' => array(1, 4), + 'access arguments' => array('request gdpr tasks'), + 'type' => MENU_LOCAL_ACTION, + 'file' => 'gdpr_tasks.pages.inc', + ); + return $items; +} + +/** + * Implements hook_permission(). + */ +function gdpr_tasks_permission() { + $perms = array( + 'administer task entities' => array( + 'title' => t('Administer task entities'), + 'restrict access' => TRUE, + ), + 'view gdpr tasks' => array( + 'title' => t('View gdpr tasks'), + 'description' => t(''), + ), + 'view own gdpr tasks' => array( + 'title' => t('View you own gdpr tasks'), + 'description' => t(''), + ), + 'edit gdpr tasks' => array( + 'title' => t('Edit gdpr tasks'), + 'description' => t(''), + ), + 'request gdpr tasks' => array( + 'title' => t('Edit gdpr tasks'), + 'description' => t(''), + ), + ); + + return $perms; +} + +/** + * Load a GDPR Task entity. + * + * @param $id + * The id of the task. + * + * @return GDPRTask|null + * The fully loaded task entity if available. + */ +function gdpr_task_load($id) { + return entity_load_single('gdpr_task', $id); +} + +/** + * Load tasks from the database. + * + * @param $ids + * An array of task IDs. + * @param $conditions + * (deprecated) An associative array of conditions on the {gdpr_task} + * table, where the keys are the database fields and the values are the + * values those fields must have. Instead, it is preferable to use + * EntityFieldQuery to retrieve a list of entity IDs loadable by + * this function. + * @param $reset + * Whether to reset the internal static entity cache. + * + * @return + * An array of task objects, indexed by task ID. + * + * @see entity_load() + * @see EntityFieldQuery + */ +function gdpr_task_load_multiple($ids = FALSE, $conditions = array(), $reset = FALSE) { + return entity_load('gdpr_task', $ids, $conditions, $reset); +} + + +function gdpr_tasks_get_user_tasks($user, $gdpr_task_type = NULL) { + $query = db_select('gdpr_task', 't') + ->fields('t', array('id')) + ->condition('user_id', $user->uid); + + if ($gdpr_task_type) { + $query->condition('type', $gdpr_task_type); + } + + $result = $query->execute() + ->fetchAssoc(); + + if (!empty($result)) { + return gdpr_task_load_multiple($result); + } + + return $result; +} \ No newline at end of file diff --git a/modules/gdpr_tasks/gdpr_tasks.pages.inc b/modules/gdpr_tasks/gdpr_tasks.pages.inc new file mode 100644 index 0000000..52ecc02 --- /dev/null +++ b/modules/gdpr_tasks/gdpr_tasks.pages.inc @@ -0,0 +1,39 @@ + array( + '#markup' => 'Make data access requests.' + ), + ); +} + +/** + * Request page for user. + */ +function gdpr_tasks_request($user, $gdpr_task_type) { + $tasks = gdpr_tasks_get_user_tasks($user, $gdpr_task_type); + + if (!empty($tasks)) { + drupal_set_message('You already have a pending task.', 'warning'); + } + else { + $values = [ + 'type' => $gdpr_task_type, + 'user_id' => $user->uid, + ]; + $task = entity_create('gdpr_task', $values); + $task->save(); + drupal_set_message('Your request has been logged.'); + } + + drupal_goto("user/{$user->uid}/gdpr/requests"); +} \ No newline at end of file diff --git a/modules/gdpr_tasks/src/Entity/GDPRTask.php b/modules/gdpr_tasks/src/Entity/GDPRTask.php new file mode 100644 index 0000000..105a0de --- /dev/null +++ b/modules/gdpr_tasks/src/Entity/GDPRTask.php @@ -0,0 +1,89 @@ +id}"; + } + + /** + * {@inheritdoc} + */ + public function bundleLabel() { + // Add in the translated specified label property. + return $this->entityInfo['bundles'][$this->bundle()]['label']; + } +} \ No newline at end of file diff --git a/modules/gdpr_tasks/src/Entity/GDPRTaskController.php b/modules/gdpr_tasks/src/Entity/GDPRTaskController.php new file mode 100644 index 0000000..2e1a4e3 --- /dev/null +++ b/modules/gdpr_tasks/src/Entity/GDPRTaskController.php @@ -0,0 +1,8 @@ + Date: Tue, 3 Apr 2018 11:47:18 +0000 Subject: [PATCH 02/21] By yanniboi: Task entity ui with bundles. --- modules/gdpr_tasks/gdpr_tasks.admin.inc | 113 +-------- modules/gdpr_tasks/gdpr_tasks.info | 3 + modules/gdpr_tasks/gdpr_tasks.install | 57 +++++ modules/gdpr_tasks/gdpr_tasks.module | 215 ++++++++++-------- modules/gdpr_tasks/src/Entity/GDPRTask.php | 5 + .../src/Entity/GDPRTaskInterface.php | 2 + .../gdpr_tasks/src/Entity/GDPRTaskType.php | 30 +++ .../src/Entity/GDPRTaskUIController.php | 70 ++++++ .../gdpr_tasks/templates/gdpr_task.tpl.php | 1 + 9 files changed, 297 insertions(+), 199 deletions(-) create mode 100644 modules/gdpr_tasks/src/Entity/GDPRTaskType.php create mode 100644 modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php create mode 100644 modules/gdpr_tasks/templates/gdpr_task.tpl.php diff --git a/modules/gdpr_tasks/gdpr_tasks.admin.inc b/modules/gdpr_tasks/gdpr_tasks.admin.inc index 4c2dc73..aa42d5c 100644 --- a/modules/gdpr_tasks/gdpr_tasks.admin.inc +++ b/modules/gdpr_tasks/gdpr_tasks.admin.inc @@ -5,29 +5,6 @@ * Administrative page callbacks for the GDPR Tasks module. */ -/** - * List all task bundles. - */ -function gdpr_task_type_list() { - // Call your hook_entity_info implementation, 'cause it includes all your bundles. - $task_info = gdpr_tasks_entity_info(); - $headers = array(t('Name'), t('Actions')); // Table Headers - $rows = array(); - foreach ($task_info['gdpr_task']['bundles'] as $name => $bundle) { - // Get the bundle edit link, add it to a table row. - $link = l(t('edit'), $bundle['admin']['real path']); - $rows[] = array($bundle['label'], $link); - } - // Format the table array. This is a pretty simple table, you could include - // links to the field management pages for each bundle too. - $output = array( - '#theme' => 'table', - '#header' => $headers, - '#rows' => $rows, - ); - return $output; -} - /** * Edit form for task bundles. */ @@ -42,94 +19,20 @@ function gdpr_task_type_form($form, &$form_state, $bundle = array(), $op = 'edit return $form; } - -/** - * Form builder for the tasks overview administration form. - */ -function gdpr_task_admin_overview($form, &$form_state) { - // Load the tasks that need to be displayed. - $header = array( - 'task' => array('data' => t('Task'), 'field' => 'id'), - 'user' => array('data' => t('User'), 'field' => 'user_id'), - 'type' => array('data' => t('Type'), 'field' => 'type'), - 'created' => array('data' => t('Updated'), 'field' => 'created'), - 'operations' => array('data' => t('Operations')), - ); - - $query = db_select('gdpr_task', 't')->extend('PagerDefault')->extend('TableSort'); - $result = $query - ->fields('t', array('id', 'user_id', 'type', 'created')) - ->limit(50) - ->orderByHeader($header) - ->execute(); - - $ids = array(); - - foreach ($result as $row) { - $ids[] = $row->id; - } - /* @var GDPRTask[] $tasks */ - $tasks = gdpr_task_load_multiple($ids); - - // Build a table listing the appropriate tasks. - $options = array(); - $destination = drupal_get_destination(); - - foreach ($tasks as $task) { - $options[$task->id] = array( - 'task' => array( - 'data' => array( - '#type' => 'link', - '#title' => $task->label(), - '#href' => 'admin/content/task/' . $task->id, - ), - ), - 'user' => theme('username', array('account' => user_load($task->user_id))), - 'type' => array( - 'data' => $task->bundleLabel(), - ), - 'created' => format_date($task->created, 'short'), - 'operations' => array( - 'data' => array( - '#type' => 'link', - '#title' => t('edit'), - '#href' => 'admin/content/task/' . $task->id . '/edit', - '#options' => array('query' => $destination), - ), - ), - ); - } - - $form['tasks'] = array( - '#type' => 'tableselect', - '#header' => $header, - '#options' => $options, - '#empty' => t('No tasks available.'), - ); - - $form['pager'] = array('#theme' => 'pager'); +function gdpr_task_form($form, &$form_state) { + field_attach_form('gdpr_task', $form_state['build_info']['args'][0], $form, $form_state); return $form; } -/** - * Example entity display page. - */ -function gdpr_task_page($task) { - $build = array(); - - field_attach_prepare_view('gdpr_task', array($task->id => $task), 'full'); +function gdpr_task_edit_gdpr_remove_form($form, &$form_state) { + $form = gdpr_task_form($form, $form_state); - return $build; + return $form; } -/** - * Example bundle edit page. - */ -function gdpr_task_edit($task) { - $build = array(); - $form_state = array(); - field_attach_form('gdpr_task', $task, $build, $form_state); +function gdpr_task_edit_gdpr_sars_form($form, &$form_state) { + $form = gdpr_task_form($form, $form_state); - return $build; + return $form; } \ No newline at end of file diff --git a/modules/gdpr_tasks/gdpr_tasks.info b/modules/gdpr_tasks/gdpr_tasks.info index 41012fb..87a5fe3 100644 --- a/modules/gdpr_tasks/gdpr_tasks.info +++ b/modules/gdpr_tasks/gdpr_tasks.info @@ -8,3 +8,6 @@ dependencies[] = entity files[] = src/Entity/GDPRTask.php files[] = src/Entity/GDPRTaskInterface.php files[] = src/Entity/GDPRTaskController.php +files[] = src/Entity/GDPRTaskUIController.php +files[] = src/Entity/GDPRTaskType.php +files[] = src/Entity/GDPRTaskTypeUIController.php diff --git a/modules/gdpr_tasks/gdpr_tasks.install b/modules/gdpr_tasks/gdpr_tasks.install index edc60a7..eb1fad6 100644 --- a/modules/gdpr_tasks/gdpr_tasks.install +++ b/modules/gdpr_tasks/gdpr_tasks.install @@ -83,5 +83,62 @@ function gdpr_tasks_schema() { ), ); + $schema['gdpr_task_type'] = array( + 'description' => 'Stores information about all defined profile types.', + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Primary Key: Unique profile type ID.', + ), + 'type' => array( + 'description' => 'The machine-readable name of this profile type.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + ), + 'label' => array( + 'description' => 'The human-readable name of this profile type.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + 'description' => 'The weight of this profile type in relation to others.', + ), + 'data' => array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + 'serialize' => TRUE, + 'description' => 'A serialized array of additional data related to this profile type.', + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + // Set the default to ENTITY_CUSTOM without using the constant as it is + // not safe to use it at this point. + 'default' => 0x01, + 'size' => 'tiny', + 'description' => 'The exportable status of the entity.', + ), + 'module' => array( + 'description' => 'The name of the providing module if the entity has been defined in code.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), + ), + 'primary key' => array('id'), + 'unique keys' => array( + 'type' => array('type'), + ), + ); + return $schema; } \ No newline at end of file diff --git a/modules/gdpr_tasks/gdpr_tasks.module b/modules/gdpr_tasks/gdpr_tasks.module index 3ecf625..df22733 100644 --- a/modules/gdpr_tasks/gdpr_tasks.module +++ b/modules/gdpr_tasks/gdpr_tasks.module @@ -9,116 +9,101 @@ * Implements hook_entity_info(). */ function gdpr_tasks_entity_info() { - return array( - 'gdpr_task' => array( - 'label' => t('Task'), - 'base table' => 'gdpr_task', - 'entity class' => 'GDPRTask', - 'controller class' => 'GDPRTaskController', - 'entity keys' => array( - 'id' => 'id', - 'bundle' => 'type', - 'label' => 'id', - 'language' => 'language', - ), - 'bundle keys' => array( - 'bundle' => 'type', - ), - 'bundles' => array( - 'gdpr_remove' => array( - 'label' => 'Removal Request', - 'admin' => array( - 'path' => 'admin/structure/gdpr-tasks/manage/%', - 'real path' => 'admin/structure/gdpr-tasks/manage/gdpr_remove', - 'bundle argument' => 4, - 'access arguments' => array('administer task entities'), - ), + $entities = array(); + + $entities['gdpr_task'] = array( + 'label' => t('Task'), + 'base table' => 'gdpr_task', + 'entity class' => 'GDPRTask', + 'controller class' => 'GDPRTaskController', + 'module' => 'gdpr_tasks', + 'admin ui' => array( + 'path' => 'admin/structure/gdpr-tasks', + 'file' => 'gdpr_tasks.admin.inc', + 'menu wildcard' => '%gdpr_task', + 'controller class' => 'GDPRTaskUIController', + ), + 'access callback' => 'gdpr_task_access', + 'entity keys' => array( + 'id' => 'id', + 'bundle' => 'type', + 'label' => 'id', + 'language' => 'language', + ), + 'bundle keys' => array( + 'bundle' => 'type', + ), + 'bundles' => array( + 'gdpr_remove' => array( + 'label' => 'Removal Request', + 'admin' => array( + 'path' => 'admin/structure/gdpr-tasks/manage/%', + 'real path' => 'admin/structure/gdpr-tasks/manage/gdpr_remove', + 'bundle argument' => 4, + 'access arguments' => array('administer task entities'), ), - 'gdpr_sar' => array( - 'label' => 'SARs Request', - 'admin' => array( - 'path' => 'admin/structure/gdpr-tasks/manage/%', - 'real path' => 'admin/structure/gdpr-tasks/manage/gdpr_sar', - 'bundle argument' => 4, - 'access arguments' => array('administer task entities'), - ), + ), + 'gdpr_sar' => array( + 'label' => 'SARs Request', + 'admin' => array( + 'path' => 'admin/structure/gdpr-tasks/manage/%', + 'real path' => 'admin/structure/gdpr-tasks/manage/gdpr_sar', + 'bundle argument' => 4, + 'access arguments' => array('administer task entities'), ), ), - 'fieldable' => TRUE, ), + 'fieldable' => TRUE, ); + + $entities['gdpr_task_type'] = array( + 'label' => t('Task type'), + 'plural label' => t('Task types'), + 'description' => t('Task types for GDPR Tasks.'), + 'entity class' => 'GDPRTaskType', + 'controller class' => 'EntityAPIControllerExportable', + 'base table' => 'gdpr_task_type', + 'fieldable' => FALSE, + 'bundle of' => 'gdpr_task', + 'exportable' => TRUE, + 'entity keys' => array( + 'id' => 'id', + 'name' => 'type', + 'label' => 'label', + ), + 'access callback' => 'gdpr_task_type_access', + 'module' => 'gdpr_tasks', + 'admin ui' => array( + 'path' => 'admin/structure/gdpr-tasks', + 'file' => 'gdpr_tasks.admin.inc', + 'controller class' => 'EntityDefaultUIController', + ), + ); + + return $entities; } /** - * Implements hook_menu(). + * Implements hook_theme(). */ -function gdpr_tasks_menu() { - // Task bundle menu items. - $items['admin/structure/gdpr-tasks'] = array( - 'title' => 'GDPR Task Types', - 'access arguments' => array('administer task entities'), - 'page callback' => 'gdpr_task_type_list', - 'file' => 'gdpr_tasks.admin.inc', - ); - $items['admin/structure/gdpr-tasks/list'] = array( // As above. - 'title' => 'List', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items['admin/structure/gdpr-tasks/manage/%'] = array( - 'title' => 'Edit task type', - 'access arguments' => array('administer task entities'), - 'page callback' => 'drupal_get_form', - 'page arguments' => array('gdpr_task_type_form', 4), - 'type' => MENU_CALLBACK, - 'file' => 'gdpr_tasks.admin.inc', - ); - $items['admin/structure/gdpr-tasks/manage/%/edit'] = array( // As above. - 'title' => 'Edit', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - - // Task entity menu items. - $items['admin/content/task'] = array( - 'title' => 'Tasks', - 'description' => 'List and edit GDPR Tasks.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('gdpr_task_admin_overview'), - 'access arguments' => array('view gdpr tasks'), - 'type' => MENU_LOCAL_TASK | MENU_NORMAL_ITEM, - 'file' => 'gdpr_tasks.admin.inc', - ); - $items['admin/content/task/%gdpr_task'] = array( - 'title callback' => 'gdpr_task_title', - 'title arguments' => array(3), - 'access arguments' => array('view gdpr tasks'), - 'page callback' => 'gdpr_task_page', - 'page arguments' => array(3), - 'file' => 'gdpr_tasks.admin.inc', - ); - $items['admin/content/task/%gdpr_task/view'] = array( // As above. - 'title' => 'View', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items['admin/content/task/%gdpr_task/edit'] = array( //page to edit your entities. - 'title' => 'Edit', - 'title callback' => 'entity_label', - 'title arguments' => array('gdpr_task', 3), - 'access arguments' => array('edit gdpr tasks'), - 'page callback' => 'entity_ui_get_form', - 'page arguments' => array('gdpr_task', 3), - 'type' => MENU_LOCAL_TASK, +function gdpr_tasks_theme() { + return array( + 'gdpr_task' => array( + 'render element' => 'elements', + 'template' => 'templates/gdpr_task', + ), ); +} - // User task menu items. +/** + * Implements hook_menu(). + */ +function gdpr_tasks_menu() { $items['user/%user/gdpr/list'] = array( 'title' => 'Summary', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); - $items['user/%user/gdpr/requests'] = array( 'title' => 'My data requests', 'access arguments' => array('view own gdpr tasks'), @@ -156,6 +141,10 @@ function gdpr_tasks_permission() { 'title' => t('Administer task entities'), 'restrict access' => TRUE, ), + 'administer task type entities' => array( + 'title' => t('Administer task entities'), + 'restrict access' => TRUE, + ), 'view gdpr tasks' => array( 'title' => t('View gdpr tasks'), 'description' => t(''), @@ -177,6 +166,44 @@ function gdpr_tasks_permission() { return $perms; } +/** + * Implements hook_default_gdpr_task_type(). + */ +function gdpr_tasks_default_gdpr_task_type() { + $types['gdpr_sar'] = new GDPRTaskType(array( + 'type' => 'gdpr_sar', + 'label' => t('SARs Request'), + 'weight' => 0, + 'locked' => TRUE, + )); + $types['gdpr_remove'] = new GDPRTaskType(array( + 'type' => 'gdpr_remove', + 'label' => t('Removal Request'), + 'weight' => 0, + 'locked' => TRUE, + )); + return $types; +} + +/** + * Access callback for task entities. + */ +function gdpr_task_access($op, $task = NULL, $account = NULL) { + // @todo Support other operations. + if (user_access('administer task entities', $account)) { + return TRUE; + } +} + +/** + * Access callback for task type entities. + */ +function gdpr_task_type_access($op, $task_type = NULL, $account = NULL) { + if (user_access('administer task type entities', $account)) { + return TRUE; + } +} + /** * Load a GDPR Task entity. * diff --git a/modules/gdpr_tasks/src/Entity/GDPRTask.php b/modules/gdpr_tasks/src/Entity/GDPRTask.php index 105a0de..4ecf306 100644 --- a/modules/gdpr_tasks/src/Entity/GDPRTask.php +++ b/modules/gdpr_tasks/src/Entity/GDPRTask.php @@ -64,6 +64,11 @@ class GDPRTask extends Entity implements GDPRTaskInterface { */ public $processed_by; + /** + * {@inheritdoc} + */ + protected $defaultLabel = TRUE; + /** * {@inheritdoc} diff --git a/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php b/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php index dacf3f5..edcebfc 100644 --- a/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php +++ b/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php @@ -4,8 +4,10 @@ * The Task entity class. */ interface GDPRTaskInterface extends EntityInterface { + /** * Gets the human readable label of the tasks bundle. */ public function bundleLabel(); + } \ No newline at end of file diff --git a/modules/gdpr_tasks/src/Entity/GDPRTaskType.php b/modules/gdpr_tasks/src/Entity/GDPRTaskType.php new file mode 100644 index 0000000..2ae0484 --- /dev/null +++ b/modules/gdpr_tasks/src/Entity/GDPRTaskType.php @@ -0,0 +1,30 @@ +label; + } + +} \ No newline at end of file diff --git a/modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php b/modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php new file mode 100644 index 0000000..9521ff3 --- /dev/null +++ b/modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php @@ -0,0 +1,70 @@ +entityInfo['plural label']) ? $this->entityInfo['plural label'] : $this->entityInfo['label'] . 's'; + + $items[$this->path . '/types'] = array( + 'title' => 'Task Types', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + + $items[$this->path . '/list'] = array( + 'title' => $plural_label, + 'page callback' => 'drupal_get_form', + 'page arguments' => array($this->entityType . '_overview_form', $this->entityType), + 'description' => 'Manage ' . $plural_label . '.', + 'access callback' => 'entity_access', + 'access arguments' => array('view', $this->entityType), + 'file' => 'includes/entity.ui.inc', + 'type' => MENU_LOCAL_TASK, + 'weight' => 10, + ); + + return $items; + } + + /** + * {@inheritdoc} + */ + protected function overviewTableHeaders($conditions, $rows, $additional_header = array()) { + $additional_header = array( + t('Type'), + t('Status'), + t('User'), + t('Requested'), + ); + return parent::overviewTableHeaders($conditions, $rows, $additional_header); + } + + /** + * {@inheritdoc} + */ + protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { + /* @var GDPRTask $entity */ + $additional_cols = array( + $entity->bundleLabel(), + $entity->status, + theme('username', array('account' => user_load($entity->user_id))), + format_date($entity->created, 'short'), + ); + $row = parent::overviewTableRow($conditions, $id, $entity, $additional_cols); + // @todo Fix hardcoded links. + $row[0] = l($entity->label(), $this->path . '/' . $id . '/view', array('query' => drupal_get_destination())); + $row[5] = l(t('edit'), $this->path . '/' . $id . '/edit', array('query' => drupal_get_destination())); + $row[6] = l(t('delete'), $this->path . '/' . $id . '/delete', array('query' => drupal_get_destination())); + + return $row; + } + +} diff --git a/modules/gdpr_tasks/templates/gdpr_task.tpl.php b/modules/gdpr_tasks/templates/gdpr_task.tpl.php new file mode 100644 index 0000000..b93914e --- /dev/null +++ b/modules/gdpr_tasks/templates/gdpr_task.tpl.php @@ -0,0 +1 @@ +

Task

\ No newline at end of file From 3bca9a4f1b8204949dcee0e54c7f090633d37112 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 13 Apr 2018 15:23:15 +0000 Subject: [PATCH 03/21] By yanniboi: Central GDPR menu item for all features. --- gdpr.info | 2 +- gdpr.install | 2 +- gdpr.module | 31 ++++++++++++++++--------- includes/gdpr.admin.inc | 50 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 includes/gdpr.admin.inc diff --git a/gdpr.info b/gdpr.info index 52f0108..d3adddf 100644 --- a/gdpr.info +++ b/gdpr.info @@ -2,6 +2,6 @@ name = General Data Protection Regulation (GDPR) description = Helps with making a site GDPR-compliant. core = 7.x dependencies[] = checklistapi -configure = admin/config/gdpr/checklist +configure = admin/gdpr/checklist package = General Data Protection Regulation files[] = gdpr.test diff --git a/gdpr.install b/gdpr.install index d5fc5e2..bb4cbec 100644 --- a/gdpr.install +++ b/gdpr.install @@ -21,7 +21,7 @@ function gdpr_requirements($phase) { 'title' => $t('GDPR Preparation'), 'value' => $t('Self assessment Checklist: @percent% done.', [ '@percent' => $percent, - '@url' => '/admin/config/gdpr/checklist', + '@url' => '/admin/gdpr/checklist', ]), 'severity' => REQUIREMENT_INFO, ]; diff --git a/gdpr.module b/gdpr.module index 4ccd93f..3b2ba25 100644 --- a/gdpr.module +++ b/gdpr.module @@ -19,16 +19,12 @@ function gdpr_help($path, $arg) { * Implements hook_menu(). */ function gdpr_menu() { - // Administration pages. - $items['admin/config/gdpr'] = [ + $items['admin/gdpr'] = [ 'title' => 'GDPR', - 'position' => 'left', - 'page callback' => 'system_admin_menu_block_page', - 'access arguments' => [ - 'administer site configuration', - ], - 'file' => 'system.admin.inc', - 'file path' => drupal_get_path('module', 'system'), + 'description' => 'Administer settings.', + 'page callback' => 'gdpr_dashboard_page', + 'access arguments' => array('administer gdpr settings'), + 'file' => 'includes/gdpr.admin.inc', ]; $items['user/%user/gdpr'] = [ @@ -39,6 +35,7 @@ function gdpr_menu() { 'access arguments' => [1], 'type' => MENU_LOCAL_TASK, ]; + return $items; } @@ -54,6 +51,20 @@ function gdpr_theme() { ]; } +/** + * Implements hook_permission(). + */ +function gdpr_permission() { + $perms = array( + 'administer gdpr settings' => array( + 'title' => t('Administer GDPR settings'), + 'restrict access' => TRUE, + ), + ); + + return $perms; +} + /** * Implements hook_checklistapi_checklist_info(). */ @@ -131,7 +142,7 @@ function gdpr_checklistapi_checklist_info() { $definitions['gdpr_checklist'] = [ '#title' => t('GDPR Checklist'), - '#path' => 'admin/config/gdpr/checklist', + '#path' => 'admin/gdpr/checklist', '#description' => t('GDPR Checklist'), '#help' => t('

Complete this checklist to make your site GDPR-compliant.

'), 'getting_started' => [ diff --git a/includes/gdpr.admin.inc b/includes/gdpr.admin.inc new file mode 100644 index 0000000..98b34a6 --- /dev/null +++ b/includes/gdpr.admin.inc @@ -0,0 +1,50 @@ +fetchAssoc()) { + $result = db_query(" + SELECT m.*, ml.* + FROM {menu_links} ml + INNER JOIN {menu_router} m ON ml.router_path = m.path + WHERE menu_name = :menu_name AND ml.mlid = :mlid AND hidden = 0", $admin, array('fetch' => PDO::FETCH_ASSOC)); + + foreach ($result as $item) { + _menu_link_translate($item); + if (!$item['access']) { + continue; + } + // The link description, either derived from 'description' in hook_menu() + // or customized via menu module is used as title attribute. + if (!empty($item['localized_options']['attributes']['title'])) { + $item['description'] = $item['localized_options']['attributes']['title']; + unset($item['localized_options']['attributes']['title']); + } + $block = $item; + $block['content'] = ''; + $block['content'] .= theme('admin_block_content', array('content' => system_admin_menu_block($item))); + if (!empty($block['content'])) { + $block['show'] = TRUE; + } + + // Prepare for sorting as in function _menu_tree_check_access(). + // The weight is offset so it is always positive, with a uniform 5-digits. + $blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $block; + } + } + if ($blocks) { + ksort($blocks); + return theme('admin_page', array('blocks' => $blocks)); + } + else { + return t('You do not have any pages yet.'); + } +} From 6385e69f4f59e70509d18f81a9bffc2a915180ab Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 13 Apr 2018 15:24:53 +0000 Subject: [PATCH 04/21] By yanniboi: GDPR Dump module with data sanitizers. --- modules/gdpr_dump/gdpr_dump.admin.inc | 7 ++ modules/gdpr_dump/gdpr_dump.info | 5 +- modules/gdpr_dump/gdpr_dump.install | 54 +++++++++ modules/gdpr_dump/gdpr_dump.module | 105 +++++++++++----- .../export_ui/gdpr_sanitizer_ui.class.php | 71 +++++++++++ .../plugins/export_ui/gdpr_sanitizer_ui.inc | 66 ++++++++++ .../GDPRSanitizerDate.class.php | 25 ++++ .../GDPRSanitizerEmail.class.php | 36 ++++++ .../GDPRSanitizerName.class.php | 42 +++++++ .../GDPRSanitizerText.class.php | 43 +++++++ .../GDPRSanitizerUsername.class.php | 34 ++++++ .../gdpr_sanitizer/gdpr_sanitizer_date.inc | 11 ++ .../gdpr_sanitizer/gdpr_sanitizer_email.inc | 10 ++ .../gdpr_sanitizer_long_text.inc | 12 ++ .../gdpr_sanitizer/gdpr_sanitizer_name.inc | 10 ++ .../gdpr_sanitizer_password.inc | 12 ++ .../gdpr_sanitizer/gdpr_sanitizer_text.inc | 11 ++ .../gdpr_sanitizer_username.inc | 10 ++ modules/gdpr_dump/src/GDPRUtilRandom.php | 90 ++++++++++++++ .../src/Plugins/GDPRSanitizerDefault.php | 114 ++++++++++++++++++ 20 files changed, 737 insertions(+), 31 deletions(-) create mode 100644 modules/gdpr_dump/gdpr_dump.admin.inc create mode 100644 modules/gdpr_dump/gdpr_dump.install create mode 100644 modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php create mode 100644 modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerEmail.class.php create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerText.class.php create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_email.inc create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_long_text.inc create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_name.inc create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_password.inc create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_text.inc create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_username.inc create mode 100644 modules/gdpr_dump/src/GDPRUtilRandom.php create mode 100644 modules/gdpr_dump/src/Plugins/GDPRSanitizerDefault.php diff --git a/modules/gdpr_dump/gdpr_dump.admin.inc b/modules/gdpr_dump/gdpr_dump.admin.inc new file mode 100644 index 0000000..2e9dc1e --- /dev/null +++ b/modules/gdpr_dump/gdpr_dump.admin.inc @@ -0,0 +1,7 @@ + 'Stores GDPR Sanitizers.', + 'export' => array( + 'key' => 'name', + 'key name' => 'Name', + 'admin_title' => 'label', + 'admin_description' => 'description', + 'primary key' => 'name', + 'identifier' => 'field', + 'default hook' => 'gdpr_dump_default_sanitizer', + 'object' => 'GDPRSanitizerDefault', + ), + 'fields' => array( + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'Machine name of sanitizer.', + ), + 'label' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'Label of sanitizer.', + ), + 'description' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'Description of sanitizer.', + ), + 'settings' => array( + 'type' => 'text', + 'size' => 'big', + 'description' => 'Additional settings.', + 'serialize' => TRUE, + ), + ), + 'primary key' => array('name'), + 'keys' => array( + 'enabled' => array('enabled'), + ) + ); + + return $schema; +} \ No newline at end of file diff --git a/modules/gdpr_dump/gdpr_dump.module b/modules/gdpr_dump/gdpr_dump.module index b2ca09f..7ec11ee 100644 --- a/modules/gdpr_dump/gdpr_dump.module +++ b/modules/gdpr_dump/gdpr_dump.module @@ -9,7 +9,7 @@ * Implements hook_menu(). */ function gdpr_dump_menu() { - $items['admin/config/gdpr/dump-settings'] = [ + $items['admin/gdpr/dump-settings'] = [ 'title' => 'SQL Dump settings', 'page callback' => 'drupal_get_form', 'page arguments' => ['gdpr_dump_settings_form'], @@ -36,7 +36,7 @@ function gdpr_dump_settings_form($form, &$form_state) { $plugins = []; // @todo: implement sanitize plugins - foreach (gdpr_dump_get_sanitizer_plugins() as $plugin_name => $plugin) { + foreach (gdpr_dump_get_gdpr_sanitizers() as $plugin_name => $plugin) { $plugins[$plugin_name] = $plugin['title']; } @@ -170,34 +170,6 @@ function gdpr_get_complete_schema() { return $schema; } -/** - * Implements hook_ctools_plugin_directory(). - */ -function gdpr_dump_ctools_plugin_directory($module, $plugin) { - if ($module == 'gdpr_dump' && \array_key_exists($plugin, gdpr_dump_ctools_plugin_type())) { - return 'plugins/' . $plugin; - } -} - -/** - * Implements hook_ctools_plugin_type(). - */ -function gdpr_dump_ctools_plugin_type() { - return [ - 'sanitizer' => [ - 'label' => 'sanitizer', - 'use hooks' => FALSE, - ], - ]; -} - -/** - * Helper function for listing gdpr sanitizer plugins. - */ -function gdpr_dump_get_sanitizer_plugins($id = NULL) { - return ctools_get_plugins('gdpr_dump', 'sanitizer', $id); -} - /** * Random word generator function. */ @@ -293,3 +265,76 @@ function gdpr_dump_service() { return $service; } + +/** + * Implements hook_ctools_plugin_type(). + */ +function gdpr_dump_ctools_plugin_type() { + $plugins['gdpr_sanitizer'] = array( + 'classes' => array('handler'), + 'child plugins' => TRUE, + 'use hooks' => TRUE, + ); + + return $plugins; +} + +function gdpr_dump_ctools_plugin_directory($owner, $plugin_type) { + if ($owner == 'gdpr_dump') { + return 'plugins/' . $plugin_type; + } + if ($owner == 'ctools' && $plugin_type == 'export_ui') { + return 'plugins/' . $plugin_type; + } +} + +/** + * Fetch metadata for all context plugins. + * + * @return array + * An array of arrays with information about all available panel contexts. + */ +function gdpr_dump_get_gdpr_sanitizers() { + ctools_include('plugins'); + + return ctools_get_plugins('gdpr_dump', 'gdpr_sanitizer'); +} + +function gdpr_dump_get_gdpr_sanitizer($plugin_id) { + ctools_include('plugins'); + + return ctools_get_plugins('gdpr_dump', 'gdpr_sanitizer', $plugin_id); +} + +/** + * Implements hook_gdpr_dump_default_field_data(). + * + * Default hook for building field data plugins. + */ +function gdpr_dump_gdpr_dump_default_sanitizer() { + $export = array(); + + $plugins = gdpr_dump_get_gdpr_sanitizers(); + + foreach ($plugins as $name => $plugin) { + $class = ctools_plugin_get_class($plugin, 'handler'); + $export[$name] = $class::create($plugin); + } + + return $export; +} + +/** + * Implements hook_permission(). + */ +function gdpr_dump_permission() { + $perms = array( + 'administer gdpr sanitizers' => array( + 'title' => t('Administer GDPR sanitizer settings'), + 'restrict access' => TRUE, + ), + ); + + return $perms; +} + diff --git a/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php new file mode 100644 index 0000000..1157a85 --- /dev/null +++ b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php @@ -0,0 +1,71 @@ +plugin['menu']['items']['add']); + // @todo Make sure import always overrides and never adds. +// $this->plugin['menu']['items']['import']['title'] = 'Override'; + parent::hook_menu($items); + } + + /** + * {@inheritdoc} + * + * @param GDPRFieldData $item + */ + public function list_build_row($item, &$form_state, $operations) { + parent::list_build_row($item, $form_state, $operations); + return; + + $name = $item->{$this->plugin['export']['key']}; + $ops = array_pop($this->rows[$name]['data']); + + $title = array('data' => check_plain($item->getSetting('label')), 'class' => array('ctools-export-ui-title')); + array_unshift($this->rows[$name]['data'], $title); + + $this->rows[$name]['data'][] = array('data' => check_plain($item->entity_type), 'class' => array('ctools-export-ui-entity-type')); + $this->rows[$name]['data'][] = array('data' => check_plain($item->entity_bundle), 'class' => array('ctools-export-ui-entity-bundle')); + $this->rows[$name]['data'][] = array('data' => check_plain($item->field_name), 'class' => array('ctools-export-ui-field-name')); + $this->rows[$name]['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rta', 'none')), 'class' => array('ctools-export-ui-rta')); + $this->rows[$name]['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rtf', 'none')), 'class' => array('ctools-export-ui-rtf')); + $this->rows[$name]['data'][] = $ops; + } + + /** + * {@inheritdoc} + */ + public function list_table_header() { + $header = parent::list_table_header(); + return $header; + + + $ops = array_pop($header); + + $title = array('data' => t('Label'), 'class' => array('ctools-export-ui-title')); + array_unshift($header, $title); + + $header[] = array('data' => t('Entity type'), 'class' => array('ctools-export-ui-entity-type')); + $header[] = array('data' => t('Entity bundle'), 'class' => array('ctools-export-ui-entity-bundle')); + $header[] = array('data' => t('Field name'), 'class' => array('ctools-export-ui-field-name')); + $header[] = array('data' => t('Right to access'), 'class' => array('ctools-export-ui-rta')); + $header[] = array('data' => t('Right to be forgotten'), 'class' => array('ctools-export-ui-rtf')); + $header[] = $ops; + return $header; + + } + +} \ No newline at end of file diff --git a/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc new file mode 100644 index 0000000..f1c32e9 --- /dev/null +++ b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc @@ -0,0 +1,66 @@ + 'gdpr_dump_sanitizers', + 'access' => 'administer gdpr sanitizers', + + 'menu' => array( + 'menu prefix' => 'admin/gdpr', + 'menu item' => 'sanitizer-list', + 'menu title' => 'GDPR Sanitizers', + 'menu description' => 'Configure GDPR Sanitizers.', + ), + + 'title singular' => t('sanitizer'), + 'title singular proper' => t('Sanitizer'), + 'title plural' => t('sanitizers'), + 'title plural proper' => t('Sanitizers'), + + 'form' => array( + 'settings' => 'gdpr_dump_sanitizer_export_ui_form', +// 'submit' => 'gdpr_dump_sanitizer_export_ui_form_submit', + ), + 'handler' => 'gdpr_sanitizer_ui', + 'strings' => array( + 'confirmation' => array( + 'revert' => array( + 'information' => t('This action will permanently remove any customizations made to this view.'), + 'success' => t('The view has been reverted.'), + ), + 'delete' => array( + 'information' => t('This action will permanently remove the view from your database.'), + 'success' => t('The view has been deleted.'), + ), + ), + ), +); + +/** + * Define the preset add/edit form. + */ +function gdpr_dump_sanitizer_export_ui_form(&$form, &$form_state) { + /* @var GDPRSanitizerDefault $sanitizer */ + $sanitizer = $form_state['item']; + + $form['settings'] = array( + '#type' => 'fieldset', + '#title' => t('Settings'), + '#tree' => TRUE, + ); + + $form['settings']['notes'] = [ + '#type' => 'textarea', + '#title' => t('Notes'), + '#default_value' => $sanitizer->getSetting('notes', ''), + ]; +} + +/** + * Define the submit function for the add/edit form. + */ +//function gdpr_dump_sanitizer_export_ui_form_submit(&$form, &$form_state) {} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php new file mode 100644 index 0000000..35d8e66 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php @@ -0,0 +1,25 @@ +word(self::EMAIL_LENGTH) . '@example.com'; + } +} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php new file mode 100644 index 0000000..fd6c664 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php @@ -0,0 +1,42 @@ + $random->word(rand(self::MIN_LENGTH, self::MAX_LENGTH)), + 'family' => $random->word(rand(self::MIN_LENGTH, self::MAX_LENGTH)), + ); + } +} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerText.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerText.class.php new file mode 100644 index 0000000..86489c0 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerText.class.php @@ -0,0 +1,43 @@ +info()); +// $max_length = $entity['settings']['max_length']; + } + + $value = ''; + + if (!empty($input)) { + // Generate a prefixed random string. + $rand = new GDPRUtilRandom(); + $value = "anon_" . $rand->string(4); + // If the value is too long, tirm it. + if (isset($max_length) && strlen($value) > $max_length) { + $value = substr($value, 0, $max_length); + } + } + return $value; + } +} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php new file mode 100644 index 0000000..a369c75 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php @@ -0,0 +1,34 @@ +name(self::NAME_LENGTH); + } +} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc new file mode 100644 index 0000000..8e78f0c --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc @@ -0,0 +1,11 @@ + array( + 'class' => 'GDPRSanitizerDate', + ), +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_email.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_email.inc new file mode 100644 index 0000000..ac66a45 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_email.inc @@ -0,0 +1,10 @@ + array( + 'class' => 'GDPRSanitizerEmail', + ), +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_long_text.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_long_text.inc new file mode 100644 index 0000000..9b4a681 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_long_text.inc @@ -0,0 +1,12 @@ + array( + 'class' => 'GDPRSanitizerDefault', + ), + 'name' => 'gdpr_sanitizer_long_text', + 'label' => 'Long text sanitizer', +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_name.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_name.inc new file mode 100644 index 0000000..bbeb2d3 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_name.inc @@ -0,0 +1,10 @@ + array( + 'class' => 'GDPRSanitizerName', + ), +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_password.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_password.inc new file mode 100644 index 0000000..88eb7af --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_password.inc @@ -0,0 +1,12 @@ + array( + 'class' => 'GDPRSanitizerDefault', + ), + 'name' => 'gdpr_sanitizer_password', + 'label' => 'Password sanitizer', +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_text.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_text.inc new file mode 100644 index 0000000..968f41e --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_text.inc @@ -0,0 +1,11 @@ + array( + 'class' => 'GDPRSanitizerText', + ), +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_username.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_username.inc new file mode 100644 index 0000000..d5e0fe9 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_username.inc @@ -0,0 +1,10 @@ + array( + 'class' => 'GDPRSanitizerUsername', + ), +); diff --git a/modules/gdpr_dump/src/GDPRUtilRandom.php b/modules/gdpr_dump/src/GDPRUtilRandom.php new file mode 100644 index 0000000..389c9c5 --- /dev/null +++ b/modules/gdpr_dump/src/GDPRUtilRandom.php @@ -0,0 +1,90 @@ +name = $plugin['name']; + } + + if (!empty($plugin['label'])) { + $sanitzer->label = $plugin['label']; + } + + return $sanitzer; + } + + /** + * Get a stored setting. + * + * @param string $setting + * The key of the setting to be fetched. + * @param mixed|null $default + * The default to be returned if not stored. + * + * @return mixed|null + * The field data setting. + */ + public function getSetting($setting, $default = NULL) { + if (isset($this->settings[$setting])) { + return $this->settings[$setting]; + } + + return $default; + } + + /** + * Get a stored setting. + * + * @param string $setting + * The key of the setting to be stored. + * @param mixed $value + * The value to be stored. + * + * @return $this + */ + public function setSetting($setting, $value) { + $this->settings[$setting] = $value; + return $this; + } + + /** + * Return the sanitized input. + * + * @var int|string $input + * The input. + * @var EntityMetadataWrapper|null $field + * The input. + * + * @return int|string + * The sanitized input. + */ + public function sanitize($input, $field = NULL) { + + return $input; + } + +} \ No newline at end of file From a7864707d643c3c2c2e81b0a7dd50269648dd578 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 13 Apr 2018 15:25:43 +0000 Subject: [PATCH 05/21] By yanniboi: GDPR Fields module for ctools plugins for field data discovery and traversal. --- modules/gdpr_fields/gdpr_fields.admin.inc | 7 + modules/gdpr_fields/gdpr_fields.info | 9 + modules/gdpr_fields/gdpr_fields.install | 59 ++++ modules/gdpr_fields/gdpr_fields.module | 261 ++++++++++++++++++ .../export_ui/gdpr_fields_ui.class.php | 67 +++++ .../plugins/export_ui/gdpr_fields_ui.inc | 155 +++++++++++ .../gdpr_data/gdpr_entity_field.class.php | 18 ++ .../plugins/gdpr_data/gdpr_entity_field.inc | 41 +++ .../gdpr_data/gdpr_entity_property.class.php | 18 ++ .../gdpr_data/gdpr_entity_property.inc | 71 +++++ .../relationships/entities_from_field.inc | 228 +++++++++++++++ .../relationships/entities_from_schema.inc | 158 +++++++++++ .../gdpr_fields/src/Plugins/GDPRFieldData.php | 136 +++++++++ 13 files changed, 1228 insertions(+) create mode 100644 modules/gdpr_fields/gdpr_fields.admin.inc create mode 100644 modules/gdpr_fields/gdpr_fields.info create mode 100644 modules/gdpr_fields/gdpr_fields.install create mode 100644 modules/gdpr_fields/gdpr_fields.module create mode 100644 modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php create mode 100644 modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc create mode 100644 modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.class.php create mode 100644 modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.inc create mode 100644 modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.class.php create mode 100644 modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc create mode 100644 modules/gdpr_fields/plugins/relationships/entities_from_field.inc create mode 100644 modules/gdpr_fields/plugins/relationships/entities_from_schema.inc create mode 100644 modules/gdpr_fields/src/Plugins/GDPRFieldData.php diff --git a/modules/gdpr_fields/gdpr_fields.admin.inc b/modules/gdpr_fields/gdpr_fields.admin.inc new file mode 100644 index 0000000..f8fad22 --- /dev/null +++ b/modules/gdpr_fields/gdpr_fields.admin.inc @@ -0,0 +1,7 @@ + 'Stores GDPR field data.', + 'export' => array( + 'key' => 'name', + 'key name' => 'Name', +// 'admin_title' => 'label', +// 'admin_description' => 'description', + 'primary key' => 'name', + 'identifier' => 'field', + 'default hook' => 'gdpr_fields_default_field_data', + 'object' => 'GDPRFieldData', + ), + 'fields' => array( + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'Machine name for field.', + ), + 'entity_type' => array( + 'type' => 'varchar', + 'length' => 32, + 'description' => 'Entity type of field.', + ), + 'entity_bundle' => array( + 'type' => 'varchar', + 'length' => 32, + 'description' => 'Entity bundle of field.', + ), + 'field_name' => array( + 'type' => 'varchar', + 'length' => 32, + 'description' => 'Field name of field.', + ), + 'settings' => array( + 'type' => 'text', + 'size' => 'big', + 'description' => 'Additional settings.', + 'serialize' => TRUE, + ), + ), + 'primary key' => array('name'), + 'keys' => array( + 'enabled' => array('enabled'), + ) + ); + + return $schema; +} \ No newline at end of file diff --git a/modules/gdpr_fields/gdpr_fields.module b/modules/gdpr_fields/gdpr_fields.module new file mode 100644 index 0000000..d292ecb --- /dev/null +++ b/modules/gdpr_fields/gdpr_fields.module @@ -0,0 +1,261 @@ + array('handler'), + 'child plugins' => TRUE, + 'use hooks' => TRUE, + ); + + return $plugins; +} + +function gdpr_fields_ctools_plugin_directory($owner, $plugin_type) { + if ($owner == 'gdpr_fields') { + return 'plugins/' . $plugin_type; + } + if ($owner == 'ctools' && $plugin_type == 'export_ui') { + return 'plugins/' . $plugin_type; + } + if ($owner == 'ctools' && $plugin_type == 'relationships') { + return 'plugins/' . $plugin_type; + } +} + +/** + * Fetch metadata for all context plugins. + * + * @return array + * An array of arrays with information about all available panel contexts. + */ +function gdpr_fields_get_contexts() { + ctools_include('plugins'); + ctools_include('export-ui'); + + +// dpm(ctools_get_plugins('ctools', 'export_ui')); +// dpm(gdpr_fields_get_gdpr_data()); +} + +/** + * Fetch metadata for all context plugins. + * + * @return array + * An array of arrays with information about all available panel contexts. + */ +function gdpr_fields_get_gdpr_data() { + ctools_include('plugins'); + + return ctools_get_plugins('gdpr_fields', 'gdpr_data'); +} + +/** + * Implements hook_menu(). + */ +function gdpr_fields_menu() { + +} + +/** + * Implements hook_gdpr_fields_default_field_data(). + * + * Default hook for building field data plugins. + */ +function gdpr_fields_gdpr_fields_default_field_data() { + $export = array(); + + $plugins = gdpr_fields_get_gdpr_data(); + + foreach ($plugins as $name => $plugin) { + $export[$name] = GDPRFieldData::createFromPlugin($plugin); + } + + return $export; +} + + +function gdpr_fields_exluded_entities($relationship) { + list($field, $from, $to) = explode('-', $relationship['name']); + + $exluded_source = array( + 'gdpr_task', + ); + $exluded_destination = array( + 'gdpr_task', + ); + + if (in_array($from, $exluded_source)) { + return TRUE; + } + + if (in_array($to, $exluded_destination)) { + return TRUE; + } + + return FALSE; +} + +function gdpr_fields_collect_gdpr_entities(&$entity_list, $entity_type, $entity) { + // Check for recursion. + list($entity_id, , $bundle) = entity_extract_ids($entity_type, $entity); + if (isset($entity_list[$entity_type][$bundle][$entity_id])) { + return; + } + + // Set entity. + $entity_list[$entity_type][$bundle][$entity_id] = $entity; + + ctools_include('context'); + ctools_include('plugins'); + + $context = ctools_context_create("entity:{$entity_type}", $entity); + + // Get available relationships from context. + $available_relationships = array(); + $relationships = ctools_get_relationships(); + + // Go through each relationship. + foreach ($relationships as $rid => $relationship) { + // For each relationship, see if there is a context that satisfies it. + if (ctools_context_filter(array($context), $relationship['required context'])) { + $available_relationships[$rid] = $relationship; + } + } + + // @todo Turn these into hooks. + $allowed_relationships = array( + 'entities_from_schema', + 'entities_from_field', + 'party_from_user', + 'attached_entity_from_party', + ); + + $forbidden_relationships = array( + 'entities_from_field' => 'gdpr_fields_exluded_entities', + 'entities_from_schema' => 'gdpr_fields_exluded_entities', + ); + + foreach ($available_relationships as $relationship_name => $relationship) { + if (is_numeric(strpos($relationship_name, ':'))) { + list($rel_plugin, $rel_plugin_name) = explode(':', $relationship_name); + } + else { + $rel_plugin = $relationship_name; + } + + if (!in_array($rel_plugin, $allowed_relationships)) { + continue; + } + + if (in_array($rel_plugin, array_keys($forbidden_relationships))) { + if ($forbidden_relationships[$rel_plugin]($relationship)) { + continue; + } + } + + // @todo can identifier be loaded elsewhere? + $relationship['identifier'] = $relationship['title']; + + $entity_contexts = gdpr_fields_get_contexts_from_relationship($relationship, $context); + + if (!is_array($entity_contexts)) { + $entity_contexts = array($entity_contexts); + } + + foreach ($entity_contexts as $entity_context) { + if (!empty($entity_context->data)) { + list($plugin_type, $entity_type) = explode(':', $entity_context->plugin); + gdpr_fields_collect_gdpr_entities($entity_list, $entity_type, $entity_context->data); + } + } + } +} + +/** + * Implements hook_permission(). + */ +function gdpr_fields_permission() { + $perms = array( + 'administer gdpr fields' => array( + 'title' => t('Administer GDPR field settings'), + 'restrict access' => TRUE, + ), + ); + + return $perms; +} + +/** + * Return a context from a relationship. + * + * @param array $relationship + * The configuration of a relationship. It must contain the following data: + * - name: The name of the relationship plugin being used. + * - relationship_settings: The configuration based upon the plugin forms. + * - identifier: The human readable identifier for this relationship, usually + * defined by the UI. + * - keyword: The keyword used for this relationship for substitutions. + * + * @param ctools_context $source_context + * The context this relationship is based upon. + * @param bool $placeholders + * If TRUE, placeholders are acceptable. + * + * @return ctools_context|null + * A context object if one can be loaded, otherwise NULL. + * + * @see ctools_context_get_relevant_relationships() + * @see ctools_context_get_context_from_relationships() + */ +function gdpr_fields_get_contexts_from_relationship($relationship, $source_context, $placeholders = FALSE) { + ctools_include('plugins'); + + // Attempt to get custom contexts function. + $function = ctools_plugin_load_function('ctools', 'relationships', $relationship['name'], 'contexts'); + + // Fall back on default context function. + if (!$function) { + $function = ctools_plugin_load_function('ctools', 'relationships', $relationship['name'], 'context'); + } + + if ($function) { + // Backward compatibility: Merge old style settings into new style: + if (!empty($relationship['relationship_settings'])) { + $relationship += $relationship['relationship_settings']; + unset($relationship['relationship_settings']); + } + + $contexts = $function($source_context, $relationship, $placeholders); + + + + if ($contexts && !is_array($contexts)) { + $contexts = array($contexts); + } + + if (!empty($contexts)) { + + foreach ($contexts as &$context) { + $context->identifier = $relationship['identifier']; + $context->page_title = isset($relationship['title']) ? $relationship['title'] : ''; + $context->keyword = $relationship['keyword']; + if (!empty($context->empty)) { + $context->placeholder = array( + 'type' => 'relationship', + 'conf' => $relationship, + ); + } + } + return $contexts; + } + } + return NULL; +} diff --git a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php new file mode 100644 index 0000000..8dcc1f3 --- /dev/null +++ b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php @@ -0,0 +1,67 @@ +plugin['menu']['items']['add']); + // @todo Make sure import always overrides and never adds. + $this->plugin['menu']['items']['import']['title'] = 'Override'; + parent::hook_menu($items); + } + + /** + * {@inheritdoc} + * + * @param GDPRFieldData $item + */ + public function list_build_row($item, &$form_state, $operations) { + parent::list_build_row($item, $form_state, $operations); + + $name = $item->{$this->plugin['export']['key']}; + $ops = array_pop($this->rows[$name]['data']); + + $title = array('data' => check_plain($item->getSetting('label')), 'class' => array('ctools-export-ui-title')); + array_unshift($this->rows[$name]['data'], $title); + + $this->rows[$name]['data'][] = array('data' => check_plain($item->entity_type), 'class' => array('ctools-export-ui-entity-type')); + $this->rows[$name]['data'][] = array('data' => check_plain($item->entity_bundle), 'class' => array('ctools-export-ui-entity-bundle')); + $this->rows[$name]['data'][] = array('data' => check_plain($item->field_name), 'class' => array('ctools-export-ui-field-name')); + $this->rows[$name]['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rta', 'none')), 'class' => array('ctools-export-ui-rta')); + $this->rows[$name]['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rtf', 'none')), 'class' => array('ctools-export-ui-rtf')); + $this->rows[$name]['data'][] = $ops; + } + + /** + * {@inheritdoc} + */ + public function list_table_header() { + $header = parent::list_table_header(); + $ops = array_pop($header); + + $title = array('data' => t('Label'), 'class' => array('ctools-export-ui-title')); + array_unshift($header, $title); + + $header[] = array('data' => t('Entity type'), 'class' => array('ctools-export-ui-entity-type')); + $header[] = array('data' => t('Entity bundle'), 'class' => array('ctools-export-ui-entity-bundle')); + $header[] = array('data' => t('Field name'), 'class' => array('ctools-export-ui-field-name')); + $header[] = array('data' => t('Right to access'), 'class' => array('ctools-export-ui-rta')); + $header[] = array('data' => t('Right to be forgotten'), 'class' => array('ctools-export-ui-rtf')); + $header[] = $ops; + return $header; + + } + +} \ No newline at end of file diff --git a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc new file mode 100644 index 0000000..1e13826 --- /dev/null +++ b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc @@ -0,0 +1,155 @@ + 'gdpr_fields_field_data', + 'access' => 'administer gdpr fields', + + 'menu' => array( + 'menu prefix' => 'admin/gdpr', + 'menu item' => 'field-list', + 'menu title' => 'GDPR Field config', + 'menu description' => 'Find and configure GDPR field data.', + ), + + 'title singular' => t('field'), + 'title singular proper' => t('Field'), + 'title plural' => t('field'), + 'title plural proper' => t('Fields'), + + 'form' => array( + 'settings' => 'gdpr_fields_field_data_export_ui_form', + 'submit' => 'gdpr_fields_field_data_export_ui_form_submit', + ), + 'handler' => 'gdpr_fields_ui', +// 'handler' => array( +// 'class' => 'gdpr_fields_ui', +// 'parent' => 'ctools_export_ui', +// ), + 'strings' => array( + 'confirmation' => array( + 'revert' => array( + 'information' => t('This action will permanently remove any customizations made to this view.'), + 'success' => t('The view has been reverted.'), + ), + 'delete' => array( + 'information' => t('This action will permanently remove the view from your database.'), + 'success' => t('The view has been deleted.'), + ), + ), + ), +); + +/** + * Define the preset add/edit form. + */ +function gdpr_fields_field_data_export_ui_form(&$form, &$form_state) { + /* @var GDPRFieldData $field_data */ + $field_data = $form_state['item']; + + $form['field'] = array( + '#type' => 'fieldset', + '#title' => t('Field info'), + '#tree' => TRUE, + ); + + $form['field']['entity_type'] = array( + '#type' => 'textfield', + '#title' => t('Entity type'), + '#default_value' => $field_data->entity_type, + '#disabled' => TRUE, + ); + + $form['field']['entity_bundle'] = array( + '#type' => 'textfield', + '#title' => t('Bundle'), + '#default_value' => $field_data->entity_bundle, + '#disabled' => TRUE, + ); + + $form['field']['field_name'] = array( + '#type' => 'textfield', + '#title' => t('Field name'), + '#default_value' => $field_data->field_name, + '#disabled' => TRUE, + ); + + $form['field']['label'] = array( + '#type' => 'textfield', + '#title' => t('Label'), + '#default_value' => $field_data->getSetting('label', ''), + '#disabled' => TRUE, + ); + + $form['field']['descriptions'] = array( + '#type' => 'textfield', + '#title' => t('Description'), + '#default_value' => $field_data->getSetting('description', ''), + '#disabled' => TRUE, + ); + + $form['settings'] = array( + '#type' => 'fieldset', + '#title' => t('Settings'), + '#tree' => TRUE, + ); + + $form['settings']['gdpr_fields_rta'] = [ + '#type' => 'select', + '#title' => t('Right to access'), + '#options' => [ + 'inc' => 'Included', + 'maybe' => 'Maybe', + 'no' => 'Not', + ], + '#default_value' => $field_data->getSetting('gdpr_fields_rta', 'no'), + ]; + + $form['settings']['gdpr_fields_rtf'] = [ + '#type' => 'select', + '#title' => t('Right to be forgotten'), + '#options' => [ + 'anonymise' => 'Anonymise', + 'remove' => 'Remove', + 'maybe' => 'Maybe', + 'no' => 'Not', + ], + '#default_value' => $field_data->getSetting('gdpr_fields_rtf', 'no'), + ]; + + // @todo Filter by relevance. + $sanitizer_options = array(); + foreach (ctools_export_load_object('gdpr_dump_sanitizers') as $sanitizer) { + $sanitizer_options[$sanitizer->name] = $sanitizer->label; + } + + $form['settings']['gdpr_fields_sanitizer'] = [ + '#type' => 'select', + '#title' => t('Sanitizer to use'), + '#options' => $sanitizer_options, + '#default_value' => $field_data->getSetting('gdpr_fields_sanitizer', ''), + '#states' => [ + 'visible' => [ + ':input[name="settings[gdpr_fields_rtf]"]' => ['value' => 'anonymise'], + ], + ], + ]; + + $form['settings']['notes'] = [ + '#type' => 'textarea', + '#title' => t('Notes'), + '#default_value' => $field_data->getSetting('gdpr_fields_notes', ''), + ]; +} + +/** + * Define the submit function for the add/edit form. + */ +function gdpr_fields_field_data_export_ui_form_submit(&$form, &$form_state) { + $form_state['values']['settings']['label'] = $form_state['values']['field']['label']; + $form_state['values']['settings']['description'] = $form_state['values']['field']['description']; +} \ No newline at end of file diff --git a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.class.php b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.class.php new file mode 100644 index 0000000..bf1e3d9 --- /dev/null +++ b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.class.php @@ -0,0 +1,18 @@ +plugin = $plugin; + } + +} diff --git a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.inc b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.inc new file mode 100644 index 0000000..fe69c2a --- /dev/null +++ b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.inc @@ -0,0 +1,41 @@ + array( + 'class' => 'gdpr_entity_field', + ), + 'get child' => 'gdpr_fields_gdpr_entity_field_get_child', + 'get children' => 'gdpr_fields_gdpr_entity_field_get_children', +); + + +function gdpr_fields_gdpr_entity_field_get_child($plugin, $parent, $child) { + $plugins = gdpr_fields_gdpr_entity_field_get_children($plugin, $parent); + return $plugins[$parent . ':' . $child]; +} + +function gdpr_fields_gdpr_entity_field_get_children($plugin, $parent) { + $instances = field_info_instances(); + $plugins = array(); + + foreach ($instances as $entity_type => $type_bundles) { + foreach ($type_bundles as $bundle => $bundle_instances) { + foreach ($bundle_instances as $field_name => $instance) { +// $field = field_info_field($field_name); + $name = "{$parent}:{$entity_type}|{$bundle}|{$field_name}"; + $child_plugin = $plugin; + $child_plugin['name'] = $name; + $child_plugin['label'] = $instance['label']; + $child_plugin['description'] = $instance['description']; + $plugins[$name] = $child_plugin; + } + } + } + + return $plugins; +} \ No newline at end of file diff --git a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.class.php b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.class.php new file mode 100644 index 0000000..d392064 --- /dev/null +++ b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.class.php @@ -0,0 +1,18 @@ +plugin = $plugin; + } + +} diff --git a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc new file mode 100644 index 0000000..3610792 --- /dev/null +++ b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc @@ -0,0 +1,71 @@ + FALSE, + 'handler' => array( + 'class' => 'gdpr_entity_property', + ), + 'get child' => 'gdpr_fields_gdpr_entity_property_get_child', + 'get children' => 'gdpr_fields_gdpr_entity_property_get_children', +); + +function gdpr_fields_gdpr_entity_property_get_child($plugin, $parent, $child) { + $plugins = gdpr_fields_gdpr_entity_property_get_children($plugin, $parent); + return $plugins[$parent . ':' . $child]; +} + +function gdpr_fields_gdpr_entity_property_get_children($plugin, $parent) { + $entities = entity_get_info(); + + $plugins = array(); + foreach (array_keys($entities) as $entity_type) { + + $info = entity_get_info($entity_type); + $property_info = entity_get_property_info($entity_type); + + if (empty($property_info)) { + continue; + } + + foreach (array_keys($info['bundles']) as $bundle) { + $properties = array(); + + // Default properties + foreach ($property_info['properties'] as $property_name => $property) { + $name = "{$parent}:{$entity_type}|{$bundle}|{$property_name}"; + $properties[$name] = $property; + } + + // Bundle properties + if (isset($property_info['bundles'][$bundle])) { + foreach ($property_info['bundles'][$bundle]['properties'] as $property_name => $property) { + $name = "{$parent}:{$entity_type}|{$bundle}|{$property_name}"; + $properties[$name] = $property; + } + } + + // Add plugins for properties. + foreach ($properties as $name => $property) { + if (!empty($property['field'])) { + continue; + } + $child_plugin = $plugin; + $child_plugin['name'] = $name; + $child_plugin['label'] = $property['label']; + $child_plugin['description'] = $property['description']; + // @todo Should computed properties be removed instead or disabled? + if (!empty($property['computed'])) { + $child_plugin['computed'] = TRUE; + } + $plugins[$name] = $child_plugin; + } + } + } + + return $plugins; +} \ No newline at end of file diff --git a/modules/gdpr_fields/plugins/relationships/entities_from_field.inc b/modules/gdpr_fields/plugins/relationships/entities_from_field.inc new file mode 100644 index 0000000..f4ee739 --- /dev/null +++ b/modules/gdpr_fields/plugins/relationships/entities_from_field.inc @@ -0,0 +1,228 @@ + t('Entity'), + 'description' => t('Creates an entity context from a foreign key on a field.'), + 'context' => 'gdpr_fields_entities_from_field_context', + 'edit form' => 'gdpr_fields_entities_from_field_edit_form', + 'get child' => 'gdpr_fields_entities_from_field_get_child', + 'get children' => 'gdpr_fields_entities_from_field_get_children', + 'defaults' => array('delta' => 0), + // 'no ui' => TRUE, +); + +function gdpr_fields_entities_from_field_get_child($plugin, $parent, $child) { + $plugins = gdpr_fields_entities_from_field_get_children($plugin, $parent); + return $plugins[$parent . ':' . $child]; +} + +function gdpr_fields_entities_from_field_get_children($parent_plugin, $parent) { + $cid = $parent_plugin['name'] . ':' . $parent; + $cache = &drupal_static(__FUNCTION__); + if (!empty($cache[$cid])) { + return $cache[$cid]; + } + + ctools_include('fields'); + $entities = entity_get_info(); + $plugins = array(); + $context_types = array(); + + // Get the schema information for every field. + $fields_info = field_info_fields(); + foreach ($fields_info as $field_name => $field) { + foreach ($field['bundles'] as $from_entity => $bundles) { + foreach ($bundles as $bundle) { + // There might be fields attached to bundles that are disabled (e.g. a + // module that declared a node's content type, is now disabled), but the + // field instance is still shown. + if (!empty($entities[$from_entity]['bundles'][$bundle])) { + $foreign_keys = ctools_field_foreign_keys($field_name); + + foreach ($foreign_keys as $key => $info) { + if (isset($info['table'])) { + foreach ($entities as $to_entity => $to_entity_info) { + $from_entity_info = $entities[$from_entity]; + // If somehow the bundle doesn't exist on the to-entity, + // skip. + if (!isset($from_entity_info['bundles'][$bundle])) { + continue; + } + + if (isset($to_entity_info['base table']) && $to_entity_info['base table'] == $info['table'] && array_keys($info['columns'], $to_entity_info['entity keys']['id'])) { + $name = $field_name . '-' . $from_entity . '-' . $to_entity; + $plugin_id = $parent . ':' . $name; + + // Record the bundle for later. + $context_types[$plugin_id]['types'][$bundle] = $from_entity_info['bundles'][$bundle]['label']; + + // We check for every bundle; this plugin may already have + // been created, so don't recreate it. + if (!isset($plugins[$plugin_id])) { + $plugin = $parent_plugin; + $replacements = array( + '@to_entity' => $to_entity_info['label'], + '@from_entity' => $from_entity_info['label'], + '@field_name' => $field_name, + '@field_label' => ctools_field_label($field_name), + ); + $plugin['title'] = t('@to_entity from @from_entity (on @from_entity: @field_label [@field_name])', $replacements); + $plugin['keyword'] = $to_entity; + $plugin['context name'] = $name; + $plugin['name'] = $plugin_id; + $plugin['description'] = t('Creates a @to_entity context from @from_entity using the @field_name field on @from_entity.', $replacements); + $plugin['from entity'] = $from_entity; + $plugin['to entity'] = $to_entity; + $plugin['field name'] = $field_name; + $plugin['join key'] = $key; + $plugin['source key'] = current(array_keys($info['columns'])); + $plugin['reverse'] = FALSE; + $plugin['parent'] = $parent; + + $plugins[$plugin_id] = $plugin; + + // Build the reverse + $plugin = $parent_plugin; + $name = $field_name . '-' . $to_entity . '-' . $from_entity; + $plugin_id = $parent . ':' . $name; + + $plugin['title'] = t('@from_entity from @to_entity (on @from_entity: @field_name)', $replacements); + $plugin['keyword'] = $to_entity; + $plugin['context name'] = $name; + $plugin['name'] = $plugin_id; + $plugin['description'] = t('Creates a @from_entity context from @to_entity using the @field_name field on @from_entity.', $replacements); + + $plugin['from entity'] = $from_entity; + $plugin['to entity'] = $to_entity; + $plugin['field name'] = $field_name; + $plugin['reverse'] = TRUE; + $plugin['parent'] = $parent; + + // Since we can't apply restrictions on the reverse relationship + // we just add the required context here. + $plugin['required context'] = new ctools_context_required($to_entity_info['label'], $to_entity); + +// $plugin_entities = array( +// 'to' => array($from_entity => $from_entity_info), +// 'from' => array($to_entity => $to_entity_info) +// ); +// drupal_alter('ctools_entity_context', $plugin, $plugin_entities, $plugin_id); + + $plugins[$plugin_id] = $plugin; + } + } + } + } + } + } + } + } + } + + foreach ($context_types as $key => $context) { + list($parent, $plugin_name) = explode(':', $key); + list($field_name, $from_entity, $to_entity) = explode('-', $plugin_name); + + $from_entity_info = $entities[$from_entity]; +// $to_entity_info = $entities[$to_entity]; + + $plugins[$key]['required context'] = new ctools_context_required($from_entity_info['label'], $from_entity, array('type' => array_keys($context['types']))); + +// $plugin_entities = array( +// 'to' => array($to_entity => $to_entity_info), +// 'from' => array($from_entity => $from_entity_info), +// ); +// drupal_alter('ctools_entity_context', $plugins[$key], $plugin_entities, $key); + } +// drupal_alter('ctools_entity_contexts', $plugins); + + $cache[$cid] = $plugins; + return $plugins; +} + +/** + * Return a new context based on an existing context. + */ +function gdpr_fields_entities_from_field_context($context, $conf) { + // Perform access check on current logged in user. + global $user; + // Clone user object so account can be passed by value to access callback. + $account = clone $user; + + $delta = !empty($conf['delta']) ? intval($conf['delta']) : 0; + $plugin = $conf['name']; + list($plugin, $plugin_name) = explode(':', $plugin); + list($field_name, $from_entity, $to_entity) = explode('-', $plugin_name); + // If unset it wants a generic, unfilled context, which is just NULL. + $entity_info = entity_get_info($from_entity); + if (empty($context->data) || !isset($context->data->{$entity_info['entity keys']['id']})) { + return ctools_context_create_empty('entity:' . $to_entity, NULL); + } + + if (isset($context->data->{$entity_info['entity keys']['id']})) { + // Load the entity. + $id = $context->data->{$entity_info['entity keys']['id']}; + $entity = entity_load($from_entity, array($id)); + $entity = $entity[$id]; + if ($items = field_get_items($from_entity, $entity, $field_name)) { + if (isset($items[$delta])) { + ctools_include('fields'); + $to_entity_info = entity_get_info($to_entity); + + $plugin_info = ctools_get_relationship($conf['name']); + $to_entity_id = $items[$delta][$plugin_info['source key']]; + $loaded_to_entity = entity_load($to_entity, array($to_entity_id)); + $loaded_to_entity = array_shift($loaded_to_entity); + + // Pass current user account and entity type to access callback. + if (isset($to_entity_info['access callback']) && function_exists($to_entity_info['access callback']) && !call_user_func($to_entity_info['access callback'], 'view', $loaded_to_entity, $account, $to_entity)) { + return ctools_context_create_empty('entity:' . $to_entity, NULL); + } + else { + // Send it to ctools. + return ctools_context_create('entity:' . $to_entity, $to_entity_id); + } + } + else { + // In case that delta was empty. + return ctools_context_create_empty('entity:' . $to_entity, NULL); + } + } + } +} + +function gdpr_fields_entities_from_field_edit_form($form, &$form_state) { + $field = field_info_field($form_state['plugin']['field name']); + $conf = $form_state['conf']; + + if ($field && $field['cardinality'] != 1) { + if ($field['cardinality'] == -1) { + $form['delta'] = array( + '#type' => 'textfield', + '#title' => t('Delta'), + '#description' => t('The relationship can only create one context, but multiple items can be related. Please select which one. Since this can have unlimited items, type in the number you want. The first one will be 0.'), + '#default_value' => !empty($conf['delta']) ? $conf['delta'] : 0, + ); + } + else { + $form['delta'] = array( + '#type' => 'select', + '#title' => t('Delta'), + '#description' => t('The relationship can only create one context, but multiple items can be related. Please select which one.'), + '#options' => range(1, $field['cardinality']), + '#default_value' => !empty($conf['delta']) ? $conf['delta'] : 0, + ); + } + } + + return $form; +} diff --git a/modules/gdpr_fields/plugins/relationships/entities_from_schema.inc b/modules/gdpr_fields/plugins/relationships/entities_from_schema.inc new file mode 100644 index 0000000..8659a00 --- /dev/null +++ b/modules/gdpr_fields/plugins/relationships/entities_from_schema.inc @@ -0,0 +1,158 @@ + t('GDPR Entities'), + 'description' => t('Creates an entity context from a foreign key on a field.'), + 'context' => 'gdpr_fields_entities_from_schema_context', + 'get child' => 'gdpr_fields_entities_from_schema_get_child', + 'get children' => 'gdpr_fields_entities_from_schema_get_children', +// 'no ui' => TRUE, +); + +function gdpr_fields_entities_from_schema_get_child($plugin, $parent, $child) { + $plugins = gdpr_fields_entities_from_schema_get_children($plugin, $parent); + return $plugins[$parent . ':' . $child]; +} + +function gdpr_fields_entities_from_schema_get_children($parent_plugin, $parent) { + $entities = entity_get_info(); + $plugins = array(); + + foreach (module_implements('entity_info') as $module) { + module_load_install($module); + $schemas = drupal_get_schema(); + + foreach ($entities as $from_entity => $from_entity_info) { + if (empty($from_entity_info['base table'])) { + continue; + } + + $table = $from_entity_info['base table']; + if (isset($schemas[$table]['foreign keys'])) { + foreach ($schemas[$table]['foreign keys'] as $relationship => $info) { + foreach ($entities as $to_entity => $to_entity_info) { + if (empty($info['table'])) { + continue; + } + + if (isset($to_entity_info['base table']) && $info['table'] == $to_entity_info['base table'] && in_array($to_entity_info['entity keys']['id'], $info['columns'])) { + $this_col = gdpr_fields_entities_from_schema_columns_filter($info['columns'], $to_entity_info['entity keys']['id']); + + // Set up our t() replacements as we reuse these. + $replacements = array( + '@relationship' => $relationship, + '@base_table' => $table, + '@to_entity' => $to_entity_info['label'], + '@from_entity' => $from_entity_info['label'], + ); + + $name = $this_col . '-' . $from_entity . '-' . $to_entity; + $plugin_id = $parent . ':' . $name; + $plugin = $parent_plugin; + + $plugin['title'] = t('@to_entity from @from_entity (on @base_table.@relationship)', $replacements); + $plugin['keyword'] = $to_entity; + $plugin['context name'] = $name; + $plugin['name'] = $plugin_id; + $plugin['description'] = t('Builds a relationship from a @from_entity to a @to_entity using the @base_table.@relationship field.', $replacements); + $plugin['reverse'] = FALSE; + + $plugin['required context'] = new ctools_context_required($from_entity_info['label'], $from_entity); + +// $plugin_entities = array('to' => array($to_entity => $to_entity_info), 'from' => array($from_entity => $from_entity_info)); +// drupal_alter('ctools_entity_context', $plugin, $plugin_entities, $plugin_id); + $plugins[$plugin_id] = $plugin; + + // Add the relation in the reverse direction. + $name = $this_col . '-' . $to_entity . '-' . $from_entity; + $plugin_id = $parent . ':' . $name; + $plugin = $parent_plugin; + + $plugin['title'] = t('@from_entity from @to_entity (on @base_table.@relationship)', $replacements); + $plugin['keyword'] = $to_entity; + $plugin['context name'] = $name; + $plugin['name'] = $plugin_id; + $plugin['description'] = t('Builds a relationship from a @to_entity to a @from_entity using the @base_table.@relationship field.', $replacements); + $plugin['reverse'] = TRUE; + $plugin['base_table'] = $table; + $plugin['base_table_key'] = key($info['columns']); + $plugin['base_table_id'] = $from_entity_info['entity keys']['id']; + + $plugin['required context'] = new ctools_context_required($to_entity_info['label'], $to_entity); + +// $plugin_entities = array('to' => array($from_entity => $from_entity_info), 'from' => array($to_entity => $to_entity_info)); +// drupal_alter('ctools_entity_context', $plugin, $plugin_entities, $plugin_id); + $plugins[$plugin_id] = $plugin; + + } + } + } + } + } + } +// drupal_alter('ctools_entity_contexts', $plugins); + return $plugins; +} + +function gdpr_fields_entities_from_schema_columns_filter($columns, $value) { + foreach ($columns as $this_col => $that_col) { + if ($value == $that_col) { + return $this_col; + } + } +} + +/** + * Return a new context based on an existing context. + */ +function gdpr_fields_entities_from_schema_context($context, $conf) { + + $plugin = $conf['name']; + list($plugin, $plugin_name) = explode(':', $plugin); + list($this_col, $from_entity, $to_entity) = explode('-', $plugin_name); + // If unset it wants a generic, unfilled context, which is just NULL. + $entity_info = entity_get_info($from_entity); + if (empty($context->data) || !isset($context->data->{$entity_info['entity keys']['id']})) { + return ctools_context_create_empty('entity:' . $to_entity); + } + + if (isset($context->data->{$entity_info['entity keys']['id']})) { + // Load the entity. + $id = $context->data->{$entity_info['entity keys']['id']}; + + if (!$conf['reverse']) { + $entity = entity_load($from_entity, array($id)); + $entity = $entity[$id]; + if (isset($entity->$this_col)) { + $to_entity_id = $entity->$this_col; + // Send it to ctools. + return ctools_context_create('entity:' . $to_entity, $to_entity_id); + } + } + else { + + $result = db_select($conf['base_table'], 'base') + ->fields('base', array($conf['base_table_id'])) + ->condition($conf['base_table_key'], $id, '=') + // @todo figure out performance improvement for more contexts. + ->range(0,1) + ->execute(); + + $contexts = array(); + while($record = $result->fetchAssoc()) { + $contexts[] = ctools_context_create('entity:' . $to_entity, $record[$conf['base_table_id']]); + } + + return $contexts; + } + } +} diff --git a/modules/gdpr_fields/src/Plugins/GDPRFieldData.php b/modules/gdpr_fields/src/Plugins/GDPRFieldData.php new file mode 100644 index 0000000..0448efd --- /dev/null +++ b/modules/gdpr_fields/src/Plugins/GDPRFieldData.php @@ -0,0 +1,136 @@ +entity_type = $entity_type; + $field->entity_bundle = $entity_bundle; + $field->field_name = $field_name; + $field->name = $name; + + // @todo Should computed properties be removed instead or disabled? + if (!empty($plugin['computed'])) { + $field->disabled = TRUE; + } + +// $field->name = $plugin['name']; + if (isset($plugin['label'])) { + $field->label = $plugin['label']; + $field->setSetting('label', $plugin['label']); + } + if (isset($plugin['description'])) { + $field->description = $plugin['label']; + $field->setSetting('description', $plugin['description']); + } + + return $field; + } + + /** + * Get a stored setting. + * + * @param string $setting + * The key of the setting to be fetched. + * @param mixed|null $default + * The default to be returned if not stored. + * + * @return mixed|null + * The field data setting. + */ + public function getSetting($setting, $default = NULL) { + if (isset($this->settings[$setting])) { + return $this->settings[$setting]; + } + + return $default; + } + + /** + * Get a stored setting. + * + * @param string $setting + * The key of the setting to be stored. + * @param mixed $value + * The value to be stored. + * + * @return $this + */ + public function setSetting($setting, $value) { + $this->settings[$setting] = $value; + return $this; + } + + + public function getValue($user) { + return $user->name; + } + +} From b4b7a18cd10881a7a99abe2d5660578a767c7379 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 13 Apr 2018 15:26:20 +0000 Subject: [PATCH 06/21] By yanniboi: Added task processing for removal and export as well as views integration. --- modules/gdpr_tasks/gdpr_tasks.admin.inc | 371 +++++++++++++++++- modules/gdpr_tasks/gdpr_tasks.info | 4 +- modules/gdpr_tasks/gdpr_tasks.install | 167 +++++++- modules/gdpr_tasks/gdpr_tasks.module | 347 +++++++++++++++- modules/gdpr_tasks/gdpr_tasks.pages.inc | 19 +- modules/gdpr_tasks/gdpr_tasks.views.inc | 43 ++ .../gdpr_tasks/gdpr_tasks.views_default.inc | 86 ++++ modules/gdpr_tasks/src/Anonymizer.php | 302 ++++++++++++++ modules/gdpr_tasks/src/Entity/GDPRTask.php | 7 + .../src/Entity/GDPRTaskController.php | 17 + .../src/Entity/GDPRTaskInterface.php | 5 + .../src/Entity/GDPRTaskUIController.php | 73 +++- .../gdpr_tasks/templates/gdpr_task.tpl.php | 49 ++- 13 files changed, 1463 insertions(+), 27 deletions(-) create mode 100644 modules/gdpr_tasks/gdpr_tasks.views.inc create mode 100644 modules/gdpr_tasks/gdpr_tasks.views_default.inc create mode 100644 modules/gdpr_tasks/src/Anonymizer.php diff --git a/modules/gdpr_tasks/gdpr_tasks.admin.inc b/modules/gdpr_tasks/gdpr_tasks.admin.inc index aa42d5c..a4a6a58 100644 --- a/modules/gdpr_tasks/gdpr_tasks.admin.inc +++ b/modules/gdpr_tasks/gdpr_tasks.admin.inc @@ -2,7 +2,7 @@ /** * @file - * Administrative page callbacks for the GDPR Tasks module. + * Administrative page and form callbacks for the GDPR Tasks module. */ /** @@ -19,20 +19,383 @@ function gdpr_task_type_form($form, &$form_state, $bundle = array(), $op = 'edit return $form; } +/** + * Form callback for all task bundles. + */ function gdpr_task_form($form, &$form_state) { - field_attach_form('gdpr_task', $form_state['build_info']['args'][0], $form, $form_state); + $task = $form_state['task']; + field_attach_form('gdpr_task', $task, $form, $form_state); + + if ($task->user_id == $task->requested_by) { + $form['gdpr_tasks_notes']['#access'] = FALSE; + } + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + '#weight' => 40, + ); return $form; } +/** + * Validate handler for all task bundles. + */ +function gdpr_task_form_validate($form, &$form_state) { + $task = $form_state['task']; + field_attach_validate('gdpr_task', $task); +} + +/** + * Submit handler for all task bundles. + */ +function gdpr_task_form_submit($form, &$form_state) { + global $user; + + /* @var GDPRTask $task */ + $task = $form_state['task']; + + // General form submission. + field_attach_submit('gdpr_task', $task, $form, $form_state); + + // Process and close the task. + $task->processed_by = $user->uid; + drupal_set_message(t('Task has been processed.')); + $task->status = 'closed'; + $task->save(); + + // Send confirmation email. + gdpr_tasks_send_mail('task_processed', $task); +} + +/** + * Form callback for removal tasks. + */ function gdpr_task_edit_gdpr_remove_form($form, &$form_state) { + $task = $form_state['task'] = $form_state['build_info']['args'][0]; $form = gdpr_task_form($form, $form_state); + $header_table = array( + 'Name', + 'Data', + 'Notes', + 'Right to access', + ); + $rows = gdpr_tasks_collect_rtf_data(user_load($task->user_id)); + + $data_table = array( + '#theme' => 'table', + '#header' => $header_table, + '#rows' => $rows, + '#caption' => 'Export data', + ); + + $form['data'] = array( + '#markup' => drupal_render($data_table), + ); + + $form['actions']['submit']['#value'] = t('Remove and Anonymise Data'); + $form['actions']['submit']['#name'] = 'remove'; + + if ($task->status == 'closed') { +// $form['actions']['#access'] = FALSE; + } + return $form; } -function gdpr_task_edit_gdpr_sars_form($form, &$form_state) { +/** + * Form callback for export tasks. + */ +function gdpr_task_edit_gdpr_sar_form($form, &$form_state) { + $task = $form_state['task'] = $form_state['build_info']['args'][0]; $form = gdpr_task_form($form, $form_state); + // Disable export field form element. + $form['gdpr_tasks_sar_export']['#disabled'] = TRUE; + + + $header_table = array( + 'Name', + 'Data', + 'Notes', + 'Right to access', + ); + $rows = gdpr_tasks_collect_rta_data(user_load($task->user_id)); + + $data_table = array( + '#theme' => 'table', + '#header' => $header_table, + '#rows' => $rows, + '#caption' => 'Export data', + ); + + $form['data'] = array( + '#markup' => drupal_render($data_table), + ); + + $form['actions']['submit']['#value'] = t('Process'); + $form['actions']['submit']['#name'] = 'export'; + + + if ($task->status == 'closed') { + $form['gdpr_tasks_manual_data']['#disabled'] = TRUE; + $form['actions']['#access'] = FALSE; + } + return $form; -} \ No newline at end of file +} + +/** + * Validate handler for removal tasks. + */ +function gdpr_task_edit_gdpr_remove_form_validate($form, &$form_state) { + gdpr_task_form_validate($form, $form_state); +} + +/** + * Validate handler for export tasks. + */ +function gdpr_task_edit_gdpr_sar_form_validate($form, &$form_state) { + gdpr_task_form_validate($form, $form_state); +} + +/** + * Submit handler for removal tasks. + */ +function gdpr_task_edit_gdpr_remove_form_submit($form, &$form_state) { + $anonymizer = new Anonymizer(); + $task = $form_state['task']; + $errors = $anonymizer->run($task); + + // Copy log to form_state. + $form_state['values']['gdpr_tasks_removal_log'] = $task->removal_log; + + if (empty($errors)) { + gdpr_task_form_submit($form, $form_state); + } + else { + dpm($errors); + $form_state['rebuild'] = TRUE; + } + +} + +/** + * Submit handler for export tasks. + */ +function gdpr_task_edit_gdpr_sar_form_submit($form, &$form_state) { + gdpr_task_form_submit($form, $form_state); + + // Process the export. + /* @var GDPRTask $task */ + $task = $form_state['task']; + $manual = $form_state['values']['gdpr_tasks_manual_data'][LANGUAGE_NONE][0]['value']; + + // @todo add getOwner method to Task. + $data = gdpr_tasks_collect_rta_data(user_load($task->user_id)); + + $inc = []; + foreach ($data as $key => $values) { + $rta = $values['rta']; + unset($values['rat']); + if ($rta == 'inc') { + $inc[$key] = $values; + } + } + + $file = $task->wrapper()->gdpr_tasks_sar_export->file->value(); + $file_name = $file->filename; + $file_uri = $file->uri; + $dirname = str_replace($file_name, '', $file_uri); + + $destination = gdpr_tasks_update_task_csv($inc, $dirname); + $export = file_get_contents($destination); + + $export .= $manual; + + // @todo Add headers to csv export. + file_save_data($export, $file_uri, FILE_EXISTS_REPLACE); +} + +/** + * Config form for automated emails for task requests. + */ +function gdpr_tasks_email_settings($form, &$form_state) { + $form['gdpr_tasks_emails'] = array('#tree' => TRUE); + $form['gdpr_tasks_emails']['emails'] = array( + '#type' => 'vertical_tabs', + ); + + $emails = variable_get('gdpr_tasks_emails', array()); + $tokens = array('site', 'gdpr_task'); + + $title = t('Request requested (by user)'); + $description = t('This email is sent when a task is requested by a user.'); + $form['gdpr_tasks_emails'] += gdpr_tasks_email_settings_subform('task_requested_self', $title, $description, $emails, $tokens); + + $title = t('Request requested (by staff)'); + $description = t('This email is sent when a task is requested by a staff member or administrator.'); + $form['gdpr_tasks_emails'] += gdpr_tasks_email_settings_subform('task_requested_other', $title, $description, $emails, $tokens); + + $title = t('Task processed'); + $description = t('This email is sent when a task has been prcessed by a staff member or administrator.'); + $form['gdpr_tasks_emails'] += gdpr_tasks_email_settings_subform('task_processed', $title, $description, $emails, $tokens); + + + // Make sure anything not exposed is preserved. + foreach ($emails as $key => $value) { + if (!isset($form['gdpr_tasks_emails'][$key])) { + $form['gdpr_tasks_emails'][$key] = array( + '#type' => 'value', + '#value' => $value, + ); + } + } + + $form['gdpr_tasks_emails_from'] = array( + '#type' => 'textfield', + '#title' => t('Email from address'), + '#description' => t('Leave blank to use the site wide email address.'), + '#default_value' => variable_get('gdpr_tasks_emails_from', NULL), + ); + + $form['#validate'][] = 'gdpr_tasks_email_settings_validate'; + $form['#submit'][] = 'gdpr_tasks_email_settings_submit'; + return system_settings_form($form); +} + +/** + * Validation handler for gdpr_tasks_email_settings(). + */ +function gdpr_tasks_email_settings_validate(&$form, &$form_state) { + foreach (element_children($form['gdpr_tasks_emails']) as $key) { + // Skip our vertical tabs. + if ($key == 'emails') { + continue; + } + + $element = $form['gdpr_tasks_emails'][$key]; + + // If enabled, check we have our required values. + $enabled = drupal_array_get_nested_value($form_state['values'], $element['enabled']['#parents']); + if (!empty($enabled) && !empty($element['enabled']['#commerce_booking_team_email_dependents'])) { + foreach ($element['enabled']['#commerce_booking_team_email_dependents'] as $array_parents) { + // Get hold of the sub element we are requiring. + $sub_element = drupal_array_get_nested_value($element, $array_parents); + if (!$sub_element) { + continue; + } + + // Get hold of it's value and check it. Show an error if it's empty. + $value = drupal_array_get_nested_value($form_state['values'], $sub_element['#parents']); + if (empty($value)) { + $error = t('%title is required if %set is enabled.', array( + '%title' => $sub_element['#title'], + '%set' => $element['#title'], + )); + form_error($sub_element, $error); + } + } + } + } +} + +/** + * Submission handler for gdpr_tasks_email_settings(). + */ +function gdpr_tasks_email_settings_submit(&$form, &$form_state) { + // Remove the vertical tabs hidden element. + unset($form_state['values']['gdpr_tasks_emails']['emails']); +} + +/** + * Build the form elements for a particular + * + * @param $key string + * The form key for the element. + * @param $title string + * The translated title for this email. + * @param $description string + * The translated description for this email. + * @param $settings array + * An array of settings for this email. + * @param array $tokens + * An optional array of tokens which are supported for this email. + * + * @return array + * A fieldset form element array. + */ +function gdpr_tasks_email_settings_subform($key, $title, $description, $settings = array(), $tokens = array()) { + // Pull the relevant key out of the settings. + $settings = isset($settings[$key]) ? $settings[$key] : array(); + + // Build our fieldset. + $element = array( + '#type' => 'fieldset', + '#collapsible' => TRUE, + '#group' => 'gdpr_tasks_emails][emails', + '#title' => $title, + '#description' => $description, + '#commerce_booking_teams_email' => TRUE, + ); + + // Allow this email to be enabled/disabled. + $element['enabled'] = array( + '#type' => 'checkbox', + '#title' => t('Enable %title', array('%title' => $title)), + '#default_value' => !empty($settings['enabled']), + '#commerce_booking_team_email_dependents' => array(array('email', 'subject'), array('email', 'body', 'value')), + ); + + $element['email'] = array( + '#type' => 'container', + '#states' => array( + 'visible' => array( + ":input[name=\"gdpr_tasks_emails[{$key}][enabled]\"]" => array('checked' => TRUE), + ), + ), + '#parents' => array('gdpr_tasks_emails', $key), + ); + + // If we have tokens, output some help information. + if (!empty($tokens)) { + $element['email']['tokens'] = array( + '#theme' => 'token_tree_link', + '#token_types' => $tokens, + ); + + } + + // Subject line. + $element['email']['subject'] = array( + '#type' => 'textfield', + '#title' => t('Subject'), + '#default_value' => isset($settings['subject']) ? $settings['subject'] : NULL, + '#maxlength' => 180, + '#states' => array( + 'required' => array( + ":input[name=\"gdpr_tasks_emails[{$key}][enabled]\"]" => array('checked' => TRUE), + ), + ), + ); + + // Body with format. + $element['email']['body'] = array( + '#type' => 'text_format', + '#title' => t('Body'), + '#rows' => 15, + '#format' => isset($settings['body']['format']) ? $settings['body']['format'] : NULL, + '#default_value' => isset($settings['body']['value']) ? $settings['body']['value'] : NULL, + '#states' => array( + 'required' => array( + ":input[name=\"gdpr_tasks_emails[{$key}][enabled]\"]" => array('checked' => TRUE), + ), + ), + ); + + // Return with our key. + return array($key => $element); +} diff --git a/modules/gdpr_tasks/gdpr_tasks.info b/modules/gdpr_tasks/gdpr_tasks.info index 87a5fe3..fc160dc 100644 --- a/modules/gdpr_tasks/gdpr_tasks.info +++ b/modules/gdpr_tasks/gdpr_tasks.info @@ -3,11 +3,13 @@ description = Allow tracking of SARs and Removal requests. core = 7.x dependencies[] = gdpr +dependencies[] = gdpr_fields dependencies[] = entity +dependencies[] = views +files[] = src/Anonymizer.php files[] = src/Entity/GDPRTask.php files[] = src/Entity/GDPRTaskInterface.php files[] = src/Entity/GDPRTaskController.php files[] = src/Entity/GDPRTaskUIController.php files[] = src/Entity/GDPRTaskType.php -files[] = src/Entity/GDPRTaskTypeUIController.php diff --git a/modules/gdpr_tasks/gdpr_tasks.install b/modules/gdpr_tasks/gdpr_tasks.install index eb1fad6..c54bb7b 100644 --- a/modules/gdpr_tasks/gdpr_tasks.install +++ b/modules/gdpr_tasks/gdpr_tasks.install @@ -33,7 +33,7 @@ function gdpr_tasks_schema() { 'default' => '', ), 'user_id' => array( - 'description' => 'The {users}.uid that requested this task.', + 'description' => 'The {users}.uid that this task is for.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, @@ -63,6 +63,12 @@ function gdpr_tasks_schema() { 'not null' => TRUE, 'default' => 0, ), + 'requested_by' => array( + 'description' => 'The {users}.uid that requested this task.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), 'processed_by' => array( 'description' => 'The {users}.uid that processed this task.', 'type' => 'int', @@ -141,4 +147,161 @@ function gdpr_tasks_schema() { ); return $schema; -} \ No newline at end of file +} + +/** + * Implements hook_schema(). + */ +function gdpr_tasks_install() { + $t = get_t(); + + // Add an export field to sar task. + if (!field_info_field('gdpr_tasks_sar_export')) { + $field = array( + 'field_name' => 'gdpr_tasks_sar_export', + 'type' => 'file', + 'locked' => TRUE, + 'settings' => array( + 'display_field' => FALSE, + 'uri_scheme' => 'private', + ), + 'cardinality' => 1, + ); + field_create_field($field); + } + + if (!field_info_instance('gdpr_task', 'gdpr_tasks_sar_export', 'gdpr_sar')) { + $instance = array( + 'label' => $t('Data Export'), + 'field_name' => 'gdpr_tasks_sar_export', + 'entity_type' => 'gdpr_task', + 'bundle' => 'gdpr_sar', + 'required' => TRUE, + 'widget' => array( + 'type' => 'file_generic', + ), + 'settings' => array( + 'file_display' => 'gdpr-exports', + 'file_extensions' => 'csv', + ), + 'display' => array( + 'default' => array( + 'type' => 'hidden', + ), + ), + ); + field_create_instance($instance); + } + + // Add an manual data override field to sar task. + if (!field_info_field('gdpr_tasks_manual_data')) { + $field = array( + 'field_name' => 'gdpr_tasks_manual_data', + 'type' => 'text_long', + 'locked' => TRUE, + 'cardinality' => 1, + ); + field_create_field($field); + } + if (!field_info_instance('gdpr_task', 'gdpr_tasks_manual_data', 'gdpr_sar')) { + $instance = array( + 'label' => $t('Data Override'), + 'field_name' => 'gdpr_tasks_manual_data', + 'entity_type' => 'gdpr_task', + 'bundle' => 'gdpr_sar', + 'required' => FALSE, + 'widget' => array( + 'type' => 'text_textarea', + ), + 'display' => array( + 'default' => array( + 'type' => 'hidden', + ), + ), + ); + field_create_instance($instance); + } + + // Add an manual data override field to sar task. + if (!field_info_field('gdpr_tasks_removal_log')) { + $field = array( + 'field_name' => 'gdpr_tasks_removal_log', + 'type' => 'text_long', + 'locked' => TRUE, + 'cardinality' => 1, + ); + field_create_field($field); + } + if (!field_info_instance('gdpr_task', 'gdpr_tasks_removal_log', 'gdpr_remove')) { + $instance = array( + 'label' => $t('Removal Log'), + 'field_name' => 'gdpr_tasks_removal_log', + 'entity_type' => 'gdpr_task', + 'bundle' => 'gdpr_remove', + 'required' => FALSE, + 'widget' => array( + 'type' => 'text_textarea', + ), + 'display' => array( + 'default' => array( + 'type' => 'hidden', + ), + ), + ); + field_create_instance($instance); + } + + // Add a notes field to all tasks. + if (!field_info_field('gdpr_tasks_notes')) { + $field = array( + 'field_name' => 'gdpr_tasks_notes', + 'type' => 'text_long', + 'locked' => TRUE, + 'cardinality' => 1, + ); + field_create_field($field); + } + foreach (array('gdpr_remove', 'gdpr_sar') as $bundle) { + if (!field_info_instance('gdpr_task', 'gdpr_tasks_notes', $bundle)) { + $instance = array( + 'label' => $t('Notes'), + 'field_name' => 'gdpr_tasks_notes', + 'entity_type' => 'gdpr_task', + 'bundle' => $bundle, + 'required' => FALSE, + 'widget' => array( + 'type' => 'text_textarea', + ), + 'display' => array( + 'default' => array( + 'type' => 'hidden', + ), + ), + ); + field_create_instance($instance); + } + } + + // Set default email values. + // @todo Make email content more useful. + $default_emails = array( + 'task_requested_self' => array( + 'enabled' => TRUE, + 'subject' => $t('Request submitted'), + 'body' => array( + 'value' => $t('A task has been requested.'), + ), + ), + 'task_requested_other' => array( + 'enabled' => FALSE, + ), + 'task_processed' => array( + 'enabled' => TRUE, + 'subject' => $t('Request complete'), + 'body' => array( + 'value' => $t('Your requested task has been completed.'), + ), + ), + ); + variable_set('gdpr_tasks_emails', $default_emails); +} diff --git a/modules/gdpr_tasks/gdpr_tasks.module b/modules/gdpr_tasks/gdpr_tasks.module index df22733..e15f21f 100644 --- a/modules/gdpr_tasks/gdpr_tasks.module +++ b/modules/gdpr_tasks/gdpr_tasks.module @@ -112,7 +112,6 @@ function gdpr_tasks_menu() { 'type' => MENU_LOCAL_TASK, 'file' => 'gdpr_tasks.pages.inc', ); - $items['user/%user/gdpr/requests/gdpr_remove/add'] = array( 'title' => 'Request data removal', 'page callback' => 'gdpr_tasks_request', @@ -129,6 +128,16 @@ function gdpr_tasks_menu() { 'type' => MENU_LOCAL_ACTION, 'file' => 'gdpr_tasks.pages.inc', ); + + $items['admin/gdpr/task-email'] = array( + 'title' => 'Task Emails', + 'description' => 'Configure email templates to be sent for task requests.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('gdpr_tasks_email_settings'), + 'access arguments' => array('administer gdpr settings'), + 'file' => 'gdpr_tasks.admin.inc', + 'weight' => 99, + ); return $items; } @@ -136,6 +145,7 @@ function gdpr_tasks_menu() { * Implements hook_permission(). */ function gdpr_tasks_permission() { + // @todo IMPORTANT!! Permission review. $perms = array( 'administer task entities' => array( 'title' => t('Administer task entities'), @@ -145,20 +155,28 @@ function gdpr_tasks_permission() { 'title' => t('Administer task entities'), 'restrict access' => TRUE, ), + 'process gdpr tasks' => array( + 'title' => t('Process GDPR tasks'), + 'restrict access' => TRUE, + ), 'view gdpr tasks' => array( - 'title' => t('View gdpr tasks'), + 'title' => t('View GDPR tasks'), 'description' => t(''), ), 'view own gdpr tasks' => array( - 'title' => t('View you own gdpr tasks'), + 'title' => t('View you own GDPR tasks'), 'description' => t(''), ), 'edit gdpr tasks' => array( - 'title' => t('Edit gdpr tasks'), + 'title' => t('Edit GDPR tasks'), 'description' => t(''), ), 'request gdpr tasks' => array( - 'title' => t('Edit gdpr tasks'), + 'title' => t('Request new GDPR tasks for themselves'), + 'description' => t(''), + ), + 'request any gdpr tasks' => array( + 'title' => t('Request new GDPR tasks for anyone'), 'description' => t(''), ), ); @@ -166,6 +184,13 @@ function gdpr_tasks_permission() { return $perms; } +/** + * Implements hook_views_api(). + */ +function gdpr_tasks_views_api($module, $api) { + return array('version' => 2); +} + /** * Implements hook_default_gdpr_task_type(). */ @@ -185,6 +210,19 @@ function gdpr_tasks_default_gdpr_task_type() { return $types; } +/** + * Implements hook_mail(). + */ +function gdpr_tasks_mail($key, &$message, $params) { + $context = $params['context']; + $options = array('clear' => TRUE); + + $message['to'] = token_replace($message['to'], $context, $options); + $message['subject'] = token_replace($context['subject'], $context, $options); + $message['body'][] = token_replace($context['body'], $context, $options); + $message['params']['plaintext'] = token_replace($params['plaintext'], $context, $options); +} + /** * Access callback for task entities. */ @@ -193,6 +231,10 @@ function gdpr_task_access($op, $task = NULL, $account = NULL) { if (user_access('administer task entities', $account)) { return TRUE; } + + if (user_access('process gdpr tasks', $account)) { + return TRUE; + } } /** @@ -259,4 +301,297 @@ function gdpr_tasks_get_user_tasks($user, $gdpr_task_type = NULL) { } return $result; -} \ No newline at end of file +} + +/** + * Implements hook_entity_load(). + * + * TEMP function for api testing. + */ +function gdpr_tasks_entity_load($entities, $type) { + if ($type != 'gdpr_task') { + return; + } + return; + + foreach ($entities as $entity) { + if ($entity->type != 'gdpr_sar') { + continue; + } + + + ctools_include('context'); + ctools_include('plugins'); + + + $context = ctools_context_create('entity:user', $entity->user_id); + $available_relationships = ctools_context_get_relevant_relationships(array($context)); + + // @todo work out how to configure any required entity_properties. + if (isset($available_relationships['entity_property'])) { + unset($available_relationships['entity_property']); + } + + if (isset($available_relationships['user_category_edit_form_from_user'])) { + unset($available_relationships['user_category_edit_form_from_user']); + } + + foreach (array_keys($available_relationships) as $relationship) { + $relationship = ctools_get_relationship($relationship); + // @todo can identifier be loaded elsewhere? + $relationship['identifier'] = $relationship['title']; + + $entity_contexts = gdpr_fields_get_contexts_from_relationship($relationship, $context); + + if (!is_array($entity_contexts)) { + $entity_contexts = array($entity_contexts); + } + + foreach ($entity_contexts as $entity_context) { + if (!empty($entity_context->data)) { +// dpm($entity_context->data); + } + } + } + } +} + + +/** + * Implements hook_entity_load(). + */ +function gdpr_tasks_entity_presave($entity, $type) { + if ($type != 'gdpr_task') { + return; + } + + if ($entity->type != 'gdpr_sar') { + return; + } + + + if (empty($entity->gdpr_tasks_sar_export)) { + $wrapper = entity_metadata_wrapper('gdpr_task', $entity); + + $info = field_info_field('gdpr_tasks_sar_export'); + $instance = field_info_instance('gdpr_task', 'gdpr_tasks_sar_export', 'gdpr_sar'); + + $dirname = $info['settings']['uri_scheme'] . '://' . $instance['settings']['file_display']; + + $data = gdpr_tasks_collect_rta_data(user_load($entity->user_id)); + + $inc = []; + $maybe = []; + foreach ($data as $key => $values) { + $rta = $values['rta']; + unset($values['rta']); + $inc[$key] = $values; + + if ($rta == 'maybe') { + $maybe[$key] = $values; + } + } + + + $destination = gdpr_tasks_update_task_csv($inc, $dirname); + $export = file_get_contents($destination); + + // Generate a file entity. + // @todo Add headers to csv export. + $file = file_save_data($export, $destination, FILE_EXISTS_REPLACE); + + $wrapper->gdpr_tasks_sar_export->file = $file; + + $temp = gdpr_tasks_update_task_csv($maybe, $dirname); + $entity->gdpr_tasks_manual_data[LANGUAGE_NONE][0]['value'] = file_get_contents($temp); + } + +} + +function gdpr_tasks_update_task_csv($data, $dirname = 'private://') { + // Prepare destination. + file_prepare_directory($dirname, FILE_CREATE_DIRECTORY); + + // Generate a file entity. + $random = new GDPRUtilRandom(); + $destination = $dirname . '/' . $random->name(10) . '.csv'; + + // Update csv with actual data. + $fp = fopen($destination, 'w'); + foreach($data as $line){ + fputcsv($fp, $line); + } + fclose($fp); + + return $destination; +} + +/** + * @param $user + */ +function gdpr_tasks_collect_rta_data($user, $include_plugins = FALSE) { + ctools_include('plugins'); + ctools_include('export'); + + $plugins = ctools_export_load_object('gdpr_fields_field_data'); + $fields = array(); + + $gdpr_entities = array(); + gdpr_fields_collect_gdpr_entities($gdpr_entities, 'user', $user); + + foreach ($gdpr_entities as $entity_type => $bundles) { + foreach ($bundles as $entity_bundle => $entities) { + foreach ($entities as $entity) { + foreach ($entity as $key => $value) { + $plugin_name = "{$entity_type}|{$entity_bundle}|{$key}"; + $wrapper = entity_metadata_wrapper($entity_type, $entity); + + if (isset($plugins[$plugin_name])) { + $field = $plugins[$plugin_name]; + + /* @var GDPRFieldData $field */ + if ($field->disabled) { + continue; + } + + if ($field->getSetting('gdpr_fields_rta')) { + // @todo Better data rendering. + if (is_array($value)) { + $value = ''; + if ($wrapper->{$key} instanceof EntityValueWrapper) { + $value .= ' ' . $wrapper->{$key}->value(); + } + elseif (!empty($wrapper->{$key}->value()) && $wrapper->{$key} instanceof EntityStructureWrapper) { + foreach (array_keys($wrapper->{$key}->value()) as $sub_key) { + $list = $wrapper->{$key}->getPropertyInfo(); + if (!empty($list) && in_array($sub_key, array_keys($list)) && $wrapper->{$key}->{$sub_key} instanceof EntityValueWrapper) { + $value .= ' ' . $wrapper->{$key}->{$sub_key}->value(); + } + } + } + } + $data = array( + 'label' => $field->getSetting('label'), + 'value' => (string) $value, + 'notes' => $field->getSetting('notes'), + 'rta' => $field->getSetting('gdpr_fields_rta'), + ); + + if ($include_plugins) { + $data['plugin'] = $field; + } + $fields[$plugin_name] = $data; + } + } + } + } + } + } + + return $fields; +} + + +/** + * @param $user + */ +function gdpr_tasks_collect_rtf_data($user, $include_plugins = FALSE) { + ctools_include('plugins'); + ctools_include('export'); + + $plugins = ctools_export_load_object('gdpr_fields_field_data'); + $fields = array(); + + $gdpr_entities = array(); + gdpr_fields_collect_gdpr_entities($gdpr_entities, 'user', $user); + + foreach ($gdpr_entities as $entity_type => $bundles) { + foreach ($bundles as $entity_bundle => $entities) { + foreach ($entities as $entity) { + foreach ($entity as $key => $value) { + $plugin_name = "{$entity_type}|{$entity_bundle}|{$key}"; + $wrapper = entity_metadata_wrapper($entity_type, $entity); + + if (isset($plugins[$plugin_name])) { + $field = $plugins[$plugin_name]; + + /* @var GDPRFieldData $field */ + if ($field->disabled) { + continue; + } + + if ($field->getSetting('gdpr_fields_rtf')) { + // @todo Better data rendering. + if (is_array($value)) { + $value = ''; + if ($wrapper->{$key} instanceof EntityValueWrapper) { + $value .= ' ' . $wrapper->{$key}->value(); + } + elseif (!empty($wrapper->{$key}->value()) && $wrapper->{$key} instanceof EntityStructureWrapper) { + foreach (array_keys($wrapper->{$key}->value()) as $sub_key) { + $list = $wrapper->{$key}->getPropertyInfo(); + if (!empty($list) && in_array($sub_key, array_keys($list)) && $wrapper->{$key}->{$sub_key} instanceof EntityValueWrapper) { + $value .= ' ' . $wrapper->{$key}->{$sub_key}->value(); + } + } + } + } + $data = array( + 'label' => $field->getSetting('label'), + 'value' => (string) $value, + 'notes' => $field->getSetting('notes'), + 'rtf' => $field->getSetting('gdpr_fields_rtf'), + ); + + if ($include_plugins) { + $data['plugin'] = $field; + } + $fields[$plugin_name] = $data; + } + } + } + } + } + } + + return $fields; +} + +/** + * Helper function to send emails notifications to users. + * + * @param string $key + * The email key to send. + * @param GDPRTask $task + * The wrapped ticket entity. + * @param array $tokens + * Optional array of tokens suitable for token_replace(). + * gdpr_task is be added automatically. + */ +function gdpr_tasks_send_mail($key, GDPRTask $task, array $tokens = array()) { + $emails = variable_get('gdpr_tasks_emails', array()); + + // Allow other modules to alter the task emails. + drupal_alter('gdpr_tasks_send_mail', $emails, $key, $ticket, $tokens); + + if (empty($emails[$key]['enabled'])) { + return; + } + $email = $emails[$key]; + + // Gather our params. + global $language; + $tokens['gdpr_task'] = $task; + $params = array( + 'context' => array( + 'subject' => $email['subject'], + 'body' => check_markup($email['body']['value'], $email['body']['format'], $language->language, TRUE), + ) + $tokens, + 'attachments' => NULL, + 'plaintext' => NULL, + ); + + $from = variable_get('gdpr_tasks_emails_from', NULL); + $to = $task->getOwner()->mail; + drupal_mail('gdpr_tasks', 'gdpr_tasks_' . $key, $to, $language, $params, $from); +} diff --git a/modules/gdpr_tasks/gdpr_tasks.pages.inc b/modules/gdpr_tasks/gdpr_tasks.pages.inc index 52ecc02..c6583ec 100644 --- a/modules/gdpr_tasks/gdpr_tasks.pages.inc +++ b/modules/gdpr_tasks/gdpr_tasks.pages.inc @@ -19,21 +19,32 @@ function gdpr_task_user_request($user) { /** * Request page for user. */ -function gdpr_tasks_request($user, $gdpr_task_type) { - $tasks = gdpr_tasks_get_user_tasks($user, $gdpr_task_type); +function gdpr_tasks_request($account, $gdpr_task_type) { + $tasks = gdpr_tasks_get_user_tasks($account, $gdpr_task_type); if (!empty($tasks)) { drupal_set_message('You already have a pending task.', 'warning'); } else { + global $user; + $values = [ 'type' => $gdpr_task_type, - 'user_id' => $user->uid, + 'user_id' => $account->uid, + 'requested_by' => $user->uid, ]; $task = entity_create('gdpr_task', $values); $task->save(); + + // Send confirmation email to user. + if ($task->requested_by == $task->user_id) { + gdpr_tasks_send_mail('task_requested_self', $task); + } + else { + gdpr_tasks_send_mail('task_requested_other', $task); + } drupal_set_message('Your request has been logged.'); } - drupal_goto("user/{$user->uid}/gdpr/requests"); + drupal_goto("user/{$account->uid}/gdpr/requests"); } \ No newline at end of file diff --git a/modules/gdpr_tasks/gdpr_tasks.views.inc b/modules/gdpr_tasks/gdpr_tasks.views.inc new file mode 100644 index 0000000..cb1e022 --- /dev/null +++ b/modules/gdpr_tasks/gdpr_tasks.views.inc @@ -0,0 +1,43 @@ + t('Created date'), + 'help' => t('The date the task was requested.'), + 'field' => array( + 'handler' => 'views_handler_field_date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'handler' => 'views_handler_sort_date', + ), + 'filter' => array( + 'handler' => 'views_handler_filter_date', + ), + ); + + $data['gdpr_task']['changed'] = array( + 'title' => t('Changed date'), + 'help' => t('The date the task was last updated.'), + 'field' => array( + 'handler' => 'views_handler_field_date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'handler' => 'views_handler_sort_date', + ), + 'filter' => array( + 'handler' => 'views_handler_filter_date', + ), + ); +} diff --git a/modules/gdpr_tasks/gdpr_tasks.views_default.inc b/modules/gdpr_tasks/gdpr_tasks.views_default.inc new file mode 100644 index 0000000..d7209a9 --- /dev/null +++ b/modules/gdpr_tasks/gdpr_tasks.views_default.inc @@ -0,0 +1,86 @@ +name = 'gdpr_tasks_my_data_requests'; + $view->description = ''; + $view->tag = 'default'; + $view->base_table = 'gdpr_task'; + $view->human_name = 'GDRP User Data Requests'; + $view->core = 7; + $view->api_version = '3.0'; + $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ + + /* Display: Master */ + $handler = $view->new_display('default', 'Master', 'default'); + $handler->display->display_options['title'] = 'GDRP User Data Requests'; + $handler->display->display_options['use_more_always'] = FALSE; + $handler->display->display_options['access']['type'] = 'perm'; + $handler->display->display_options['access']['perm'] = 'view gdpr tasks'; + $handler->display->display_options['cache']['type'] = 'none'; + $handler->display->display_options['query']['type'] = 'views_query'; + $handler->display->display_options['exposed_form']['type'] = 'basic'; + $handler->display->display_options['pager']['type'] = 'none'; + $handler->display->display_options['style_plugin'] = 'table'; + /* Field: Task: Created date */ + $handler->display->display_options['fields']['created']['id'] = 'created'; + $handler->display->display_options['fields']['created']['table'] = 'gdpr_task'; + $handler->display->display_options['fields']['created']['field'] = 'created'; + $handler->display->display_options['fields']['created']['label'] = 'Requested date'; + $handler->display->display_options['fields']['created']['date_format'] = 'short'; + $handler->display->display_options['fields']['created']['second_date_format'] = 'panopoly_time'; + /* Field: Task: Data Export */ + $handler->display->display_options['fields']['gdpr_tasks_sar_export']['id'] = 'gdpr_tasks_sar_export'; + $handler->display->display_options['fields']['gdpr_tasks_sar_export']['table'] = 'field_data_gdpr_tasks_sar_export'; + $handler->display->display_options['fields']['gdpr_tasks_sar_export']['field'] = 'gdpr_tasks_sar_export'; + $handler->display->display_options['fields']['gdpr_tasks_sar_export']['click_sort_column'] = 'fid'; + $handler->display->display_options['fields']['gdpr_tasks_sar_export']['type'] = 'file_download_link'; + $handler->display->display_options['fields']['gdpr_tasks_sar_export']['settings'] = array( + 'text' => 'Download', + ); + /* Contextual filter: Task: User_id */ + $handler->display->display_options['arguments']['user_id']['id'] = 'user_id'; + $handler->display->display_options['arguments']['user_id']['table'] = 'gdpr_task'; + $handler->display->display_options['arguments']['user_id']['field'] = 'user_id'; + $handler->display->display_options['arguments']['user_id']['default_action'] = 'default'; + $handler->display->display_options['arguments']['user_id']['default_argument_type'] = 'user'; + $handler->display->display_options['arguments']['user_id']['default_argument_options']['user'] = FALSE; + $handler->display->display_options['arguments']['user_id']['summary']['number_of_records'] = '0'; + $handler->display->display_options['arguments']['user_id']['summary']['format'] = 'default_summary'; + $handler->display->display_options['arguments']['user_id']['summary_options']['items_per_page'] = '25'; + /* Filter criterion: Task: Type */ + $handler->display->display_options['filters']['type']['id'] = 'type'; + $handler->display->display_options['filters']['type']['table'] = 'gdpr_task'; + $handler->display->display_options['filters']['type']['field'] = 'type'; + $handler->display->display_options['filters']['type']['value'] = array( + 'gdpr_sar' => 'gdpr_sar', + ); + /* Filter criterion: Task: Status */ + $handler->display->display_options['filters']['status']['id'] = 'status'; + $handler->display->display_options['filters']['status']['table'] = 'gdpr_task'; + $handler->display->display_options['filters']['status']['field'] = 'status'; + $handler->display->display_options['filters']['status']['operator'] = '='; + $handler->display->display_options['filters']['status']['value'] = 'closed'; + + /* Display: Page */ + $handler = $view->new_display('page', 'Page', 'page'); + $handler->display->display_options['path'] = 'user/%/gdpr/requests'; + $handler->display->display_options['menu']['type'] = 'tab'; + $handler->display->display_options['menu']['title'] = 'My data requests'; + $handler->display->display_options['menu']['weight'] = '0'; + $handler->display->display_options['menu']['context'] = 0; + $handler->display->display_options['menu']['context_only_inline'] = 0; + $views['gdpr_tasks_my_data_requests'] = $view; + + return $views; +} diff --git a/modules/gdpr_tasks/src/Anonymizer.php b/modules/gdpr_tasks/src/Anonymizer.php new file mode 100644 index 0000000..4784391 --- /dev/null +++ b/modules/gdpr_tasks/src/Anonymizer.php @@ -0,0 +1,302 @@ +getOwner(); + + $errors = []; + $entities = []; + $successes = []; + $failures = []; + $log = []; + + if (!$this->checkExportDirectoryExists()) { + $errors[] = 'An export directory has not been set. Please set this under Configuration -> GDPR -> Right to be Forgotten'; + } + + foreach (gdpr_tasks_collect_rtf_data($user, TRUE) as $plugin_id => $data) { + $plugin = $data['plugin']; + unset($data['plugin']); + + $this->plugins[$plugin->entity_type][$plugin_id] = $plugin; + $this->plugin_data[$plugin->entity_type][$plugin_id] = $data; + } + gdpr_fields_collect_gdpr_entities($entities, 'user', $user); + + foreach ($entities as $entity_type => $bundles) { + foreach ($bundles as $entity_bundle => $entities) { + foreach ($entities as $bundle_entity_id => $bundle_entity) { + + // Re-load a fresh copy of the bundle entity from storage so we don't + // end up modifying any other references to the entity in memory. + $bundle_entity = entity_load_unchanged($entity_type, $bundle_entity_id); + $entity_success = TRUE; + + foreach ($this->getFieldsToProcess($entity_type, $bundle_entity) as $field_info) { + $mode = $field_info['mode']; + + $success = TRUE; + $msg = NULL; + $sanitizer = ''; + + if ($mode == 'anonymise') { + list($success, $msg, $sanitizer) = $this->anonymize($field_info, $bundle_entity); + } + elseif ($mode == 'remove') { + list($success, $msg) = $this->remove($field_info, $bundle_entity); + } + + if ($success === TRUE) { + $log[] = 'success'; + $log[] = [ + 'entity_id' => $bundle_entity_id, + 'entity_type' => $entity_type . '.' . $entity_bundle, + 'field_name' => $field_info['field'], + 'action' => $mode, + 'sanitizer' => $sanitizer, + ]; + } + else { + // Could not anonymize/remove field. Record to errors list. + // Prevent entity from being saved. + $entity_success = FALSE; + $errors[] = $msg; + $log[] = 'error'; + $log[] = [ + 'error' => $msg, + 'entity_id' => $bundle_entity_id, + 'entity_type' => $entity_type . '.' . $entity_bundle, + 'field_name' => $field_info['field'], + 'action' => $mode, + 'sanitizer' => $sanitizer, + ]; + } + } + + if ($entity_success) { + $successes[$entity_type][$bundle_entity_id] = $bundle_entity; + } + else { + $failures[] = $bundle_entity; + } + } + } + } + + // @todo Better log field. + $task->wrapper()->gdpr_tasks_removal_log = json_encode($log); + + if (count($failures) === 0) { + $tx = db_transaction(); + + try { + /* @var EntityInterface $entity */ + foreach ($successes as $entity_type => $entities) { + foreach ($entities as $entity) { + entity_save($entity_type, $entity); + } + } + // Re-fetch the user so we see any changes that were made. + $user = entity_load_unchanged('user', $task->user_id); + user_save($user, array('status' => 0)); + + // @todo Write a log to file system. +// $this->writeLogToFile($task, $log); + } + catch (\Exception $e) { + $tx->rollback(); + $errors[] = $e->getMessage(); + } + } + + return $errors; + } + + /** + * Removes the field value. + * + * @param string $field_info + * The current field to process. + * @param EntityInterface|stdClass $entity + * The current field to process. + * + * @return array + * First element is success boolean, second element is the error message. + */ + private function remove($field_info, $entity) { + try { + /* @var EntityDrupalWrapper $wrapper */ + $wrapper = entity_metadata_wrapper($field_info['entity_type'], $entity); + + /* @var EntityMetadataWrapper $field */ + $field = $wrapper->{$field_info['field']}; + $this->clearField($field); + return [TRUE, NULL]; + } + catch (Exception $e) { + return [FALSE, $e->getMessage()]; + } + } + + /** + * Removes the field value. + * + * @param EntityMetadataWrapper $field + * The current field to process. + * + * @return array + * First element is success boolean, second element is the error message. + */ + protected function clearField($field) { + if ($field instanceof EntityValueWrapper) { + $field->set(NULL); + } + elseif ($field instanceof EntityStructureWrapper) { + $list = $field->getPropertyInfo(); + if (!empty($list) && !empty($field->value())) { + foreach (array_keys($field->value()) as $key) { + if (!empty($list) && in_array($key, array_keys($list))) { + $sub_field = $field->{$key}; + $this->clearField($sub_field); + } + } + } + } + } + + /** + * Runs anonymize functionality against a field. + * + * @param string $field_info + * The field to anonymise. + * @param $entity + * The parent entity. + * + * @return array + * First element is success boolean, second element is the error message. + */ + private function anonymize($field_info, $entity) { + $sanitizer_id = $this->getSanitizerId($field_info, $entity); + + if (!$sanitizer_id) { + return [ + FALSE, + "Could not anonymize field {$field_info['field']}. Please consider changing this field from 'anonymize' to 'remove', or register a custom sanitizer.", + NULL, + ]; + } + + try { + $plugin = gdpr_dump_get_gdpr_sanitizer($sanitizer_id); + $class = ctools_plugin_get_class($plugin, 'handler'); + /* @var GDPRSanitizerDefault $sanitizer */ + $sanitizer = $class::create($plugin); + + $wrapper = entity_metadata_wrapper($field_info['entity_type'], $entity); + + $wrapper->{$field_info['field']} = $sanitizer->sanitize($field_info['value'], $wrapper->{$field_info['field']}); + return [TRUE, NULL, $sanitizer_id]; + } + catch (\Exception $e) { + return [FALSE, $e->getMessage(), NULL]; + } + } + + + /** + * Checks that the export directory has been set. + * + * @return bool + * Indicates whether the export directory has been configured and exists. + */ + private function checkExportDirectoryExists() { + // @todo Configure export directory. + $directory = 'private://gdpr-export'; + + return !empty($directory) && file_prepare_directory($directory, FILE_CREATE_DIRECTORY); + } + + /** + * Gets fields to anonymize/remove. + * + * @param $entity + * The entity to anonymise. + * + * @return array + * Array containing metadata about the entity. + * Elements are entity_type, bundle, field, value, mode and plugin. + */ + private function getFieldsToProcess($entity_type, $entity) { + if (!isset($this->plugins[$entity_type])) { + return array(); + } + + $plugins = $this->plugins[$entity_type]; + list(, , $bundle) = entity_extract_ids($entity_type, $entity); + + // Get fields for entity. + $fields = []; + foreach ($entity as $field_id => $field) { + $plugin_name = "{$entity_type}|{$bundle}|{$field_id}"; + + if (isset($plugins[$plugin_name])) { + $plugin = $plugins[$plugin_name]; + $data = $this->plugin_data[$entity_type][$plugin_name]; + + if (!empty($data['rtf']) && $data['rtf'] !== 'no') { + $fields[] = array( + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'field' => $field_id, + 'value' => $data['value'], + 'mode' => $data['rtf'], + 'plugin' => $plugin, + ); + } + } + } + + return $fields; + } + + /** + * Gets the ID of the sanitizer plugin to use on this field. + * + * @param string $field_info + * The field to anonymise. + * @param $entity + * The parent entity. + * + * @return string + * The sanitizer ID or null. + */ + private function getSanitizerId($field_info, $entity) { + // First check if this field has a sanitizer defined. + $sanitizer = $field_info['plugin']->settings['gdpr_fields_sanitizer']; + + // @todo Allow sanitizers to fall back to type selection relevant for the field type. + if (!$sanitizer) { + $sanitizer = 'gdpr_sanitizer_text'; + } + return $sanitizer; + } + +} diff --git a/modules/gdpr_tasks/src/Entity/GDPRTask.php b/modules/gdpr_tasks/src/Entity/GDPRTask.php index 4ecf306..237ae2a 100644 --- a/modules/gdpr_tasks/src/Entity/GDPRTask.php +++ b/modules/gdpr_tasks/src/Entity/GDPRTask.php @@ -77,6 +77,13 @@ public function __construct($values = array()) { parent::__construct($values, 'gdpr_task'); } + /** + * {@inheritdoc} + */ + public function getOwner() { + return user_load($this->user_id); + } + /** * {@inheritdoc} */ diff --git a/modules/gdpr_tasks/src/Entity/GDPRTaskController.php b/modules/gdpr_tasks/src/Entity/GDPRTaskController.php index 2e1a4e3..2611cbf 100644 --- a/modules/gdpr_tasks/src/Entity/GDPRTaskController.php +++ b/modules/gdpr_tasks/src/Entity/GDPRTaskController.php @@ -5,4 +5,21 @@ */ class GDPRTaskController extends EntityAPIControllerExportable { + /** + * {@inheritdoc} + */ + public function create(array $values = array()) { + $values += array('status' => 'requested'); + $values += array('created' => REQUEST_TIME); + + $task = parent::create($values); + return $task; + } + + public function save($entity, DatabaseTransaction $transaction = NULL) { + $entity->changed = REQUEST_TIME; + return parent::save($entity, $transaction); + } + + } \ No newline at end of file diff --git a/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php b/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php index edcebfc..301f6cc 100644 --- a/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php +++ b/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php @@ -10,4 +10,9 @@ interface GDPRTaskInterface extends EntityInterface { */ public function bundleLabel(); + /** + * Gets the user that the task belongs to. + */ + public function getOwner(); + } \ No newline at end of file diff --git a/modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php b/modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php index 9521ff3..882f5e1 100644 --- a/modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php +++ b/modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php @@ -13,13 +13,7 @@ public function hook_menu() { // Set this on the object so classes that extend hook_menu() can use it. $plural_label = isset($this->entityInfo['plural label']) ? $this->entityInfo['plural label'] : $this->entityInfo['label'] . 's'; - $items[$this->path . '/types'] = array( - 'title' => 'Task Types', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - - $items[$this->path . '/list'] = array( + $items['admin/gdpr/task-list'] = array( 'title' => $plural_label, 'page callback' => 'drupal_get_form', 'page arguments' => array($this->entityType . '_overview_form', $this->entityType), @@ -27,13 +21,70 @@ public function hook_menu() { 'access callback' => 'entity_access', 'access arguments' => array('view', $this->entityType), 'file' => 'includes/entity.ui.inc', - 'type' => MENU_LOCAL_TASK, 'weight' => 10, ); return $items; } + /** + * Generates the render array for a overview tables for different statuses. + * + * @param $conditions + * An array of conditions as needed by entity_load(). + + * @return array + * A renderable array. + */ + public function overviewTable($conditions = array()) { + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', $this->entityType); + $query->propertyOrderBy('created'); + + // Add all conditions to query. + foreach ($conditions as $key => $value) { + $query->propertyCondition($key, $value); + } + + if ($this->overviewPagerLimit) { + $query->pager($this->overviewPagerLimit); + } + + $results = $query->execute(); + + $ids = isset($results[$this->entityType]) ? array_keys($results[$this->entityType]) : array(); + $entities = $ids ? entity_load($this->entityType, $ids) : array(); + ksort($entities); + + // Always show at least requested and complete tables. + $rows = array( + 'requested' => array(), + 'complete' => array(), + ); + foreach ($entities as $entity) { + $rows[$entity->status][] = $this->overviewTableRow($conditions, entity_id($this->entityType, $entity), $entity); + } + + $render = array(); + foreach ($rows as $status => $status_rows) { + $render[$status] = array( + '#theme' => 'table', + '#header' => $this->overviewTableHeaders($conditions, $status_rows), + '#rows' => $status_rows, + '#caption' => t('Tasks with status - @status', array('@status' => ucfirst($status))), + '#empty' => t('No tasks.'), + '#weight' => 3, + ); + + // @todo Find a better way to order statuses. + if ($status == 'requested') { + $render[$status]['#weight'] = 0; + } + } + + return $render; + } + /** * {@inheritdoc} */ @@ -52,11 +103,15 @@ protected function overviewTableHeaders($conditions, $rows, $additional_header = */ protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { /* @var GDPRTask $entity */ + + $time_diff = REQUEST_TIME - $entity->created; + $created_ago = t('%time ago', array('%time' => format_interval($time_diff, 1))); + $additional_cols = array( $entity->bundleLabel(), $entity->status, theme('username', array('account' => user_load($entity->user_id))), - format_date($entity->created, 'short'), + format_date($entity->created, 'short') . ' - ' . $created_ago, ); $row = parent::overviewTableRow($conditions, $id, $entity, $additional_cols); // @todo Fix hardcoded links. diff --git a/modules/gdpr_tasks/templates/gdpr_task.tpl.php b/modules/gdpr_tasks/templates/gdpr_task.tpl.php index b93914e..3c9524f 100644 --- a/modules/gdpr_tasks/templates/gdpr_task.tpl.php +++ b/modules/gdpr_tasks/templates/gdpr_task.tpl.php @@ -1 +1,48 @@ -

Task

\ No newline at end of file + +
> + + + > + + + + + + + + +
> + +
+
From 3d94b41342fbe71a5b290dd850d34d93c8a04c91 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 16 Apr 2018 16:32:00 +0000 Subject: [PATCH 07/21] By yanniboi: Added default field exports for some user fields. --- .../default_fields/user.user.mail.field.php | 16 ++++++++++++++++ .../default_fields/user.user.name.field.php | 16 ++++++++++++++++ .../default_fields/user.user.roles.field.php | 16 ++++++++++++++++ .../default_fields/user.user.status.field.php | 15 +++++++++++++++ modules/gdpr_fields/gdpr_fields.install | 5 +++++ modules/gdpr_fields/gdpr_fields.module | 11 ++++++++++- .../plugins/export_ui/gdpr_fields_ui.inc | 10 +++++++--- .../gdpr_fields/src/Plugins/GDPRFieldData.php | 9 ++++++++- modules/gdpr_tasks/gdpr_tasks.admin.inc | 2 +- modules/gdpr_tasks/gdpr_tasks.install | 6 +++++- modules/gdpr_tasks/src/Anonymizer.php | 13 ++++++++++++- modules/gdpr_tasks/src/Entity/GDPRTask.php | 7 +++++++ 12 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 modules/gdpr_fields/default_fields/user.user.mail.field.php create mode 100644 modules/gdpr_fields/default_fields/user.user.name.field.php create mode 100644 modules/gdpr_fields/default_fields/user.user.roles.field.php create mode 100644 modules/gdpr_fields/default_fields/user.user.status.field.php diff --git a/modules/gdpr_fields/default_fields/user.user.mail.field.php b/modules/gdpr_fields/default_fields/user.user.mail.field.php new file mode 100644 index 0000000..ec10aed --- /dev/null +++ b/modules/gdpr_fields/default_fields/user.user.mail.field.php @@ -0,0 +1,16 @@ +disabled = FALSE; /* Edit this to true to make a default field disabled initially */ +$field->name = 'user|user|mail'; +$field->plugin_type = 'gdpr_entity_property'; +$field->entity_type = 'user'; +$field->entity_bundle = 'user'; +$field->field_name = 'mail'; +$field->settings = array( + 'gdpr_fields_rta' => 'inc', + 'gdpr_fields_rtf' => 'anonymise', + 'gdpr_fields_sanitizer' => 'gdpr_sanitizer_email', + 'notes' => '', + 'label' => 'Email', + 'description' => NULL, +); diff --git a/modules/gdpr_fields/default_fields/user.user.name.field.php b/modules/gdpr_fields/default_fields/user.user.name.field.php new file mode 100644 index 0000000..87dcb5b --- /dev/null +++ b/modules/gdpr_fields/default_fields/user.user.name.field.php @@ -0,0 +1,16 @@ +disabled = FALSE; /* Edit this to true to make a default field disabled initially */ +$field->name = 'user|user|name'; +$field->plugin_type = 'gdpr_entity_property'; +$field->entity_type = 'user'; +$field->entity_bundle = 'user'; +$field->field_name = 'name'; +$field->settings = array( + 'gdpr_fields_rta' => 'maybe', + 'gdpr_fields_rtf' => 'anonymise', + 'gdpr_fields_sanitizer' => 'gdpr_sanitizer_username', + 'notes' => '', + 'label' => 'Name', + 'description' => NULL, +); diff --git a/modules/gdpr_fields/default_fields/user.user.roles.field.php b/modules/gdpr_fields/default_fields/user.user.roles.field.php new file mode 100644 index 0000000..aebdb9b --- /dev/null +++ b/modules/gdpr_fields/default_fields/user.user.roles.field.php @@ -0,0 +1,16 @@ +disabled = FALSE; /* Edit this to true to make a default field disabled initially */ +$field->name = 'user|user|roles'; +$field->plugin_type = 'gdpr_entity_property'; +$field->entity_type = 'user'; +$field->entity_bundle = 'user'; +$field->field_name = 'roles'; +$field->settings = array( + 'gdpr_fields_rta' => 'maybe', + 'gdpr_fields_rtf' => 'remove', + 'gdpr_fields_sanitizer' => 'gdpr_sanitizer_text', + 'notes' => '', + 'label' => 'User roles', + 'description' => NULL, +); diff --git a/modules/gdpr_fields/default_fields/user.user.status.field.php b/modules/gdpr_fields/default_fields/user.user.status.field.php new file mode 100644 index 0000000..f476db7 --- /dev/null +++ b/modules/gdpr_fields/default_fields/user.user.status.field.php @@ -0,0 +1,15 @@ +disabled = FALSE; /* Edit this to true to make a default field disabled initially */ +$field->name = 'user|user|status'; +$field->plugin_type = 'gdpr_entity_property'; +$field->entity_type = 'user'; +$field->entity_bundle = 'user'; +$field->field_name = 'status'; +$field->settings = array( + 'gdpr_fields_rta' => 'no', + 'gdpr_fields_rtf' => 'remove', + 'gdpr_fields_sanitizer' => 'gdpr_sanitizer_text', + 'notes' => '', + 'label' => 'Status', +); diff --git a/modules/gdpr_fields/gdpr_fields.install b/modules/gdpr_fields/gdpr_fields.install index d9da047..b589333 100644 --- a/modules/gdpr_fields/gdpr_fields.install +++ b/modules/gdpr_fields/gdpr_fields.install @@ -27,6 +27,11 @@ function gdpr_fields_schema() { 'length' => 255, 'description' => 'Machine name for field.', ), + 'plugin_type' => array( + 'type' => 'varchar', + 'length' => 32, + 'description' => 'Plugin type of the field.', + ), 'entity_type' => array( 'type' => 'varchar', 'length' => 32, diff --git a/modules/gdpr_fields/gdpr_fields.module b/modules/gdpr_fields/gdpr_fields.module index d292ecb..42f589f 100644 --- a/modules/gdpr_fields/gdpr_fields.module +++ b/modules/gdpr_fields/gdpr_fields.module @@ -73,11 +73,20 @@ function gdpr_fields_gdpr_fields_default_field_data() { $export = array(); $plugins = gdpr_fields_get_gdpr_data(); - foreach ($plugins as $name => $plugin) { $export[$name] = GDPRFieldData::createFromPlugin($plugin); } + // Scan fields directory for default files. + $files = file_scan_directory(dirname(__FILE__) . '/default_fields', '/\.field.php/', array('key' => 'name')); + foreach ($files as $file) { + $field = new GDPRFieldData(); + if ((include $file->uri) == 1) { + $name = $field->plugin_type . ':' . $field->name; + $export[$name] = $field; + } + } + return $export; } diff --git a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc index 1e13826..f74d1d9 100644 --- a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc +++ b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc @@ -150,6 +150,10 @@ function gdpr_fields_field_data_export_ui_form(&$form, &$form_state) { * Define the submit function for the add/edit form. */ function gdpr_fields_field_data_export_ui_form_submit(&$form, &$form_state) { - $form_state['values']['settings']['label'] = $form_state['values']['field']['label']; - $form_state['values']['settings']['description'] = $form_state['values']['field']['description']; -} \ No newline at end of file + if (isset($form_state['values']['field']['label'])) { + $form_state['values']['settings']['label'] = $form_state['values']['field']['label']; + } + if (isset($form_state['values']['field']['description'])) { + $form_state['values']['settings']['description'] = $form_state['values']['field']['description']; + } +} diff --git a/modules/gdpr_fields/src/Plugins/GDPRFieldData.php b/modules/gdpr_fields/src/Plugins/GDPRFieldData.php index 0448efd..100e92d 100644 --- a/modules/gdpr_fields/src/Plugins/GDPRFieldData.php +++ b/modules/gdpr_fields/src/Plugins/GDPRFieldData.php @@ -16,6 +16,12 @@ class GDPRFieldData { */ public $name; + /** + * Plugin type of field. + * @var string + */ + public $plugin_type; + /** * Human readable name for the field. * @var string @@ -69,12 +75,13 @@ class GDPRFieldData { public static function createFromPlugin(array $plugin) { $field = new static(); - list($type, $name) = explode(':', $plugin['name']); + list($plugin_type, $name) = explode(':', $plugin['name']); list($entity_type, $entity_bundle, $field_name) = explode('|', $name); $field->entity_type = $entity_type; $field->entity_bundle = $entity_bundle; $field->field_name = $field_name; $field->name = $name; + $field->plugin_type = $plugin_type; // @todo Should computed properties be removed instead or disabled? if (!empty($plugin['computed'])) { diff --git a/modules/gdpr_tasks/gdpr_tasks.admin.inc b/modules/gdpr_tasks/gdpr_tasks.admin.inc index a4a6a58..b7e6aba 100644 --- a/modules/gdpr_tasks/gdpr_tasks.admin.inc +++ b/modules/gdpr_tasks/gdpr_tasks.admin.inc @@ -171,7 +171,7 @@ function gdpr_task_edit_gdpr_remove_form_submit($form, &$form_state) { $errors = $anonymizer->run($task); // Copy log to form_state. - $form_state['values']['gdpr_tasks_removal_log'] = $task->removal_log; + $form_state['values']['gdpr_tasks_removal_log'] = $task->gdpr_tasks_removal_log; if (empty($errors)) { gdpr_task_form_submit($form, $form_state); diff --git a/modules/gdpr_tasks/gdpr_tasks.install b/modules/gdpr_tasks/gdpr_tasks.install index c54bb7b..609e5a9 100644 --- a/modules/gdpr_tasks/gdpr_tasks.install +++ b/modules/gdpr_tasks/gdpr_tasks.install @@ -82,9 +82,13 @@ function gdpr_tasks_schema() { 'table' => 'users', 'columns' => array('user_id' => 'uid'), ), + 'task_requester' => array( + 'table' => 'users', + 'columns' => array('requested_by' => 'uid'), + ), 'task_processor' => array( 'table' => 'users', - 'columns' => array('user_id' => 'uid'), + 'columns' => array('processed_by' => 'uid'), ), ), ); diff --git a/modules/gdpr_tasks/src/Anonymizer.php b/modules/gdpr_tasks/src/Anonymizer.php index 4784391..778b921 100644 --- a/modules/gdpr_tasks/src/Anonymizer.php +++ b/modules/gdpr_tasks/src/Anonymizer.php @@ -167,7 +167,18 @@ private function remove($field_info, $entity) { */ protected function clearField($field) { if ($field instanceof EntityValueWrapper) { - $field->set(NULL); + // @todo Add any other types that come up. + switch ($field->info()['type']) { + case 'text': + $field->set(''); + break; + + default: + $field->set(NULL); + } + } + elseif ($field instanceof EntityListWrapper) { + $field->set(array()); } elseif ($field instanceof EntityStructureWrapper) { $list = $field->getPropertyInfo(); diff --git a/modules/gdpr_tasks/src/Entity/GDPRTask.php b/modules/gdpr_tasks/src/Entity/GDPRTask.php index 237ae2a..397076f 100644 --- a/modules/gdpr_tasks/src/Entity/GDPRTask.php +++ b/modules/gdpr_tasks/src/Entity/GDPRTask.php @@ -57,6 +57,13 @@ class GDPRTask extends Entity implements GDPRTaskInterface { */ public $complete; + /** + * The users id that requested this task. + * + * @var integer + */ + public $requested_by; + /** * The users id that processed this task. * From 3f762d509304f76eeabe9d7fe86501a961a977c7 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 16 Apr 2018 16:33:05 +0000 Subject: [PATCH 08/21] By yanniboi: Added export log module to track gdpr views data exports. --- .../gdpr_export_log/gdpr_export_log.admin.inc | 147 +++++++++++ modules/gdpr_export_log/gdpr_export_log.info | 12 + .../gdpr_export_log/gdpr_export_log.install | 231 ++++++++++++++++++ .../gdpr_export_log/gdpr_export_log.module | 168 +++++++++++++ .../gdpr_export_log/gdpr_export_log.views.inc | 55 +++++ .../src/Entity/GDPRExportLog.php | 143 +++++++++++ .../src/Entity/GDPRExportLogController.php | 53 ++++ .../src/Entity/GDPRExportLogInterface.php | 8 + .../src/Entity/GDPRExportLogUIController.php | 118 +++++++++ ...ws_plugin_display_extender_data_export.inc | 91 +++++++ 10 files changed, 1026 insertions(+) create mode 100644 modules/gdpr_export_log/gdpr_export_log.admin.inc create mode 100644 modules/gdpr_export_log/gdpr_export_log.info create mode 100644 modules/gdpr_export_log/gdpr_export_log.install create mode 100644 modules/gdpr_export_log/gdpr_export_log.module create mode 100644 modules/gdpr_export_log/gdpr_export_log.views.inc create mode 100644 modules/gdpr_export_log/src/Entity/GDPRExportLog.php create mode 100644 modules/gdpr_export_log/src/Entity/GDPRExportLogController.php create mode 100644 modules/gdpr_export_log/src/Entity/GDPRExportLogInterface.php create mode 100644 modules/gdpr_export_log/src/Entity/GDPRExportLogUIController.php create mode 100644 modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc diff --git a/modules/gdpr_export_log/gdpr_export_log.admin.inc b/modules/gdpr_export_log/gdpr_export_log.admin.inc new file mode 100644 index 0000000..907c0c6 --- /dev/null +++ b/modules/gdpr_export_log/gdpr_export_log.admin.inc @@ -0,0 +1,147 @@ +is_new)) { + $form['info'] = array('#markup' => t('Manually creating new log entries is not supported.')); + return $form; + } + + field_attach_form('gdpr_export_log', $log, $form, $form_state); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + '#submit' => array('gdpr_export_log_form_submit_save'), + '#weight' => 10, + ); + + $form['actions']['remove'] = array( + '#type' => 'submit', + '#value' => t('Remove export'), + '#submit' => array( + 'gdpr_export_log_form_submit_remove', + 'gdpr_export_log_form_submit_save', + ), + '#weight' => 40, + ); + + if ($log->status == 'removed') { + $form['actions']['#access'] = FALSE; + } + + return $form; +} + +/** + * Validate handler for logs form. + */ +function gdpr_export_log_form_validate($form, &$form_state) { + $log = $form_state['export_log']; + field_attach_validate('gdpr_export_log', $log); +} + +/** + * Submit handler for logs form. + */ +function gdpr_export_log_form_submit_save($form, &$form_state) { + /* @var GDPRExportLog $log */ + $log = $form_state['export_log']; + + // General form submission. + field_attach_submit('gdpr_export_log', $log, $form, $form_state); + + // Process and close the task. + $log->save(); + drupal_set_message(t('Log has been updated.')); +} + +/** + * Submit handler for logs form when removing . + */ +function gdpr_export_log_form_submit_remove($form, &$form_state) { +// global $user; + + /* @var GDPRExportLog $log */ + $log = $form_state['export_log']; + + // General form submission. + field_attach_submit('gdpr_export_log', $log, $form, $form_state); + + // Process and close the task. + drupal_set_message(t('Export has been marked as removed.')); + $log->status = 'removed'; + $log->removed = REQUEST_TIME; +} + + +function gdpr_export_log_export_approval($name, $display_id) { + $args = func_get_args(); + // Remove $name and $display_id from the arguments. + array_shift($args); + array_shift($args); + + // Load the view and render it. + if ($view = views_get_view($name)) { + $view->set_display($display_id); + return drupal_get_form('gdpr_export_log_export_approval_form', $view); + + } + + // Fallback; if we get here no view was found or handler was not valid. + return MENU_NOT_FOUND; +} + +function gdpr_export_log_export_approval_form($form, &$form_state, $view) { + global $user; + $values = array( + 'export' => $view->name, + 'exported_by' => $user->uid, + ); + $log = $form_state['export_log'] = entity_create('gdpr_export_log', $values); + + field_attach_form('gdpr_export_log', $log, $form, $form_state); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Export'), + '#weight' => 10, + ); + + return $form; +} + +/** + * Submit handler for logs form. + */ +function gdpr_export_log_export_approval_form_submit($form, &$form_state) { + /* @var GDPRExportLog $log */ + $log = $form_state['export_log']; + + // General form submission. + field_attach_submit('gdpr_export_log', $log, $form, $form_state); + + // Process and close the task. + $log->save(); + + $options = array(); + if (isset($_GET['destination'])) { + $options['query'] = drupal_get_destination(); + unset($_GET['destination']); + } + $options['query']['gdpr_export_log'] = $log->identifier(); + + drupal_goto(current_path() . '/gdpr_approved', $options); +} \ No newline at end of file diff --git a/modules/gdpr_export_log/gdpr_export_log.info b/modules/gdpr_export_log/gdpr_export_log.info new file mode 100644 index 0000000..02c682a --- /dev/null +++ b/modules/gdpr_export_log/gdpr_export_log.info @@ -0,0 +1,12 @@ +name = General Data Protection Regulation (GDPR) - Exports Log +description = Logs view exports and audit trails. +core = 7.x + +dependencies[] = gdpr +dependencies[] = entity + +files[] = src/Entity/GDPRExportLog.php +files[] = src/Entity/GDPRExportLogInterface.php +files[] = src/Entity/GDPRExportLogController.php +files[] = src/Entity/GDPRExportLogUIController.php +files[] = src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc \ No newline at end of file diff --git a/modules/gdpr_export_log/gdpr_export_log.install b/modules/gdpr_export_log/gdpr_export_log.install new file mode 100644 index 0000000..2702a8d --- /dev/null +++ b/modules/gdpr_export_log/gdpr_export_log.install @@ -0,0 +1,231 @@ + 'The base table for export logs.', + 'fields' => array( + 'id' => array( + 'description' => 'The primary identifier for this export log.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE + ), + 'language' => array( + 'description' => 'The {languages}.language of this export log.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), + 'export' => array( + 'description' => 'The {views_view}.name of this export log.', + 'type' => 'varchar', + 'length' => 128, + 'default' => '' + ), + 'exported_by' => array( + 'description' => 'The {users}.uid that exported the export.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'removed_by' => array( + 'description' => 'The {users}.uid that removed the export.', + 'type' => 'int', + ), + 'status' => array( + 'description' => 'The text status of this export log.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '' + ), + 'created' => array( + 'description' => 'The Unix timestamp when this export log was created.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'changed' => array( + 'description' => 'The Unix timestamp when this export log was most recently saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'removed' => array( + 'description' => 'The Unix timestamp when this export log was removed.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('id'), + 'foreign keys' => array( + 'log_exporter' => array( + 'table' => 'users', + 'columns' => array('exported_by' => 'uid'), + ), + 'log_remover' => array( + 'table' => 'users', + 'columns' => array('removed_by' => 'uid'), + ), + ), + ); + + $schema['gdpr_export_log_uid'] = array( + 'description' => 'Stores the users included in an export.', + 'fields' => array( + 'export_id' => array( + 'description' => 'The {gdpr_export_log}.id of the related export log.', + 'type' => 'int', + 'not null' => TRUE, + ), + 'uid' => array( + 'description' => 'The {users}.uid of the user included in the log.', + 'type' => 'int', + 'not null' => TRUE, + ), + 'forgotten_by' => array( + 'description' => 'The {users}.uid of the user who removed this user from log.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'forgotten' => array( + 'description' => 'Whether the user has been removed from the log.', + 'type' => 'int', + 'size' => 'tiny', + ), + ), + 'foreign keys' => array( + 'export' => array( + 'table' => 'gdpr_export_log', + 'columns' => array('export_id' => 'id'), + ), + 'user' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), + 'user_remover' => array( + 'table' => 'users', + 'columns' => array('forgotten_by' => 'uid'), + ), + ), + ); + + return $schema; +} + +/** + * Implements hook_schema(). + */ +function gdpr_export_log_install() { + $t = get_t(); + + // Add an export location field. + if (!field_info_field('gdpr_export_log_destination')) { + $field = array( + 'field_name' => 'gdpr_export_log_destination', + 'type' => 'text', + 'locked' => TRUE, + 'cardinality' => 1, + ); + field_create_field($field); + } + if (!field_info_instance('gdpr_export_log', 'gdpr_export_log_destination', 'gdpr_export_log')) { + $instance = array( + 'label' => $t('Export storage destination'), + 'description' => $t('Fill in the location where the export will be saved.'), + 'field_name' => 'gdpr_export_log_destination', + 'entity_type' => 'gdpr_export_log', + 'bundle' => 'gdpr_export_log', + 'required' => TRUE, + 'widget' => array( + 'type' => 'text_textfield', + ), + 'display' => array( + 'default' => array( + 'type' => 'text_default', + ), + ), + ); + field_create_instance($instance); + } + + // Add an export reason field. + if (!field_info_field('gdpr_export_log_reason')) { + $field = array( + 'field_name' => 'gdpr_export_log_reason', + 'type' => 'text_long', + 'locked' => TRUE, + 'cardinality' => 1, + ); + field_create_field($field); + } + if (!field_info_instance('gdpr_export_log', 'gdpr_export_log_reason', 'gdpr_export_log')) { + $instance = array( + 'label' => $t('Reason for export'), + 'description' => $t('Fill in the reason why the export is dealt with.'), + 'field_name' => 'gdpr_export_log_reason', + 'entity_type' => 'gdpr_export_log', + 'bundle' => 'gdpr_export_log', + 'required' => TRUE, + 'widget' => array( + 'type' => 'text_textarea', + ), + 'display' => array( + 'default' => array( + 'type' => 'text_default', + ), + ), + ); + field_create_instance($instance); + } + + // Add an export lifetime field. + if (!field_info_field('gdpr_export_log_lifetime')) { + $field = array( + 'field_name' => 'gdpr_export_log_lifetime', + 'type' => 'number_integer', + 'locked' => TRUE, + 'cardinality' => 1, + ); + field_create_field($field); + } + if (!field_info_instance('gdpr_export_log', 'gdpr_export_log_lifetime', 'gdpr_export_log')) { + $instance = array( + 'label' => $t('Export lifetime'), + 'description' => $t('Fill in the length (in days) that the export should live for.'), + 'field_name' => 'gdpr_export_log_lifetime', + 'entity_type' => 'gdpr_export_log', + 'bundle' => 'gdpr_export_log', + 'required' => TRUE, + 'default_value' => array( + array( + 'value' => 90, + ), + ), + 'widget' => array( + 'type' => 'number', + ), + 'display' => array( + 'default' => array( + 'type' => 'number_integer', + ), + ), + 'settings' => array( + 'suffix' => 'days', + ), + ); + field_create_instance($instance); + } + +} diff --git a/modules/gdpr_export_log/gdpr_export_log.module b/modules/gdpr_export_log/gdpr_export_log.module new file mode 100644 index 0000000..2a4f829 --- /dev/null +++ b/modules/gdpr_export_log/gdpr_export_log.module @@ -0,0 +1,168 @@ + t('Export Log'), + 'base table' => 'gdpr_export_log', + 'entity class' => 'GDPRExportLog', + 'controller class' => 'GDPRExportLogController', + 'module' => 'gdpr_export_log', + 'admin ui' => array( + 'path' => 'admin/structure/gdpr-export-log', + 'file' => 'gdpr_export_log.admin.inc', + 'menu wildcard' => '%gdpr_export_log', + 'controller class' => 'GDPRExportLogUIController', + ), + 'bundles' => array( + 'gdpr_export_log' => array( + 'label' => t('Export Log'), + 'admin' => array( + 'path' => 'admin/structure/gdpr-export-log', + 'access arguments' => array('administer export log entities'), + ), + ), + ), + 'access callback' => 'gdpr_export_log_access', + 'entity keys' => array( + 'id' => 'id', + 'label' => 'id', + 'language' => 'language', + ), + 'fieldable' => TRUE, + ); + + return $entities; +} + +/** + * Implements hook_permission(). + */ +function gdpr_export_log_permission() { + // @todo IMPORTANT!! Permission review. + return array( + 'administer export log entities' => array( + 'title' => t('Administer Export Log entities'), + 'restrict access' => TRUE, + ), + 'view gdpr export logs' => array( + 'title' => t('View GDPR export logs'), + ), + 'edit gdpr export logs' => array( + 'title' => t('Edit GDPR export logs'), + ), + ); +} + +/** + * Implements hook_views_api(). + */ +function gdpr_export_log_views_api($module, $api) { + return array( + 'api' => 3, + 'path' => drupal_get_path('module', 'gdpr_export_log'), + ); +} + +/** + * Implement hook_menu_alter(). + */ +function gdpr_export_log_menu_alter(&$items) { + $views = views_get_applicable_views('uses views_data_export'); + foreach ($views as $data) { + list($view, $display_id) = $data; + $view->set_display($display_id); + $include = FALSE; + + if (isset($view->display_handler)) { + $include = $view->display_handler->get_option('gdpr_export_log_audit'); + } + + if ($include) { + $result = $view->execute_hook_menu($display_id, $items); + if (is_array($result)) { + foreach (array_keys($result) as $path) { + if (isset($items[$path])) { + $items[$path . '/gdpr_approved'] = $items[$path]; + $items[$path]['page callback'] = 'gdpr_export_log_export_approval'; + $items[$path]['module'] = 'gdpr_export_log'; + $items[$path]['file'] = 'gdpr_export_log.admin.inc'; + } + } + } + } + } + + // Save memory: Destroy those views. + foreach ($views as $data) { + list($view, $display_id) = $data; + $view->destroy(); + } +} + +/** + * Implements hook_module_implements_alter(). + */ +function gdpr_export_log_module_implements_alter(&$implementations, $hook) { + if ($hook == 'menu_alter') { + $group = $implementations['gdpr_export_log']; + unset($implementations['gdpr_export_log']); + $implementations['gdpr_export_log'] = $group; + } +} + +/** + * Load a GDPR Export Log entity. + * + * @param $id + * The id of the log. + * + * @return GDPRExportLog|null + * The fully loaded log entity if available. + */ +function gdpr_export_log_load($id) { + return entity_load_single('gdpr_export_log', $id); +} + +/** + * Load logs from the database. + * + * @param $ids + * An array of log IDs. + * @param $conditions + * (deprecated) An associative array of conditions on the {gdpr_export_log} + * table, where the keys are the database fields and the values are the + * values those fields must have. Instead, it is preferable to use + * EntityFieldQuery to retrieve a list of entity IDs loadable by + * this function. + * @param $reset + * Whether to reset the internal static entity cache. + * + * @return GDPRExportLog[] + * An array of log objects, indexed by log ID. + * + * @see entity_load() + * @see EntityFieldQuery + */ +function gdpr_export_log_load_multiple($ids = FALSE, $conditions = array(), $reset = FALSE) { + return entity_load('gdpr_export_log', $ids, $conditions, $reset); +} + +/** + * Access callback for export log entities. + */ +function gdpr_export_log_access($op, $export_log = NULL, $account = NULL) { + // @todo Support other operations. + if (user_access('administer export log entities', $account)) { + return TRUE; + } +} diff --git a/modules/gdpr_export_log/gdpr_export_log.views.inc b/modules/gdpr_export_log/gdpr_export_log.views.inc new file mode 100644 index 0000000..17bcecb --- /dev/null +++ b/modules/gdpr_export_log/gdpr_export_log.views.inc @@ -0,0 +1,55 @@ +result as $data) { + if (isset($data->uid)) { + $uids[$data->uid] = $data->uid; + } + } + + if (!empty($uids)) { + // Set log uids. + $log->setUsers($uids); + $log->save(); + } +} + +/** + * Implements hook_views_plugins(). + */ +function gdpr_export_log_views_plugins() { + return array( + 'display_extender' => array( + 'gdpr_views_data_export' => array( + 'title' => t(''), + 'help' => t(''), + 'path' => drupal_get_path('module', 'gdpr_export_log') . '/src/Plugin/views/display_extender', + 'handler' => 'gdpr_export_log_views_plugin_display_extender_data_export', + ), + ), + ); +} + +/** + * Implements hook_views_plugins_alter(). + */ +function gdpr_export_log_views_plugins_alter(&$plugins) { + if (isset($plugins['display']['views_data_export'])) { + $plugins['display']['views_data_export']['uses views_data_export'] = TRUE; + } +} \ No newline at end of file diff --git a/modules/gdpr_export_log/src/Entity/GDPRExportLog.php b/modules/gdpr_export_log/src/Entity/GDPRExportLog.php new file mode 100644 index 0000000..3bdc9ed --- /dev/null +++ b/modules/gdpr_export_log/src/Entity/GDPRExportLog.php @@ -0,0 +1,143 @@ +uids)) { + $this->init(); + } + } + + public function init() { + $uids = db_select('gdpr_export_log_uid', 'u') + ->fields('u') + ->condition('export_id', $this->identifier()) + ->execute() + ->fetchAllAssoc('uid', PDO::FETCH_ASSOC); + + $this->setUsers($uids); + } + + /** + * {@inheritdoc} + */ + protected function defaultLabel() { + $view = views_get_view($this->export); + return "Log {$this->id} - {$view->get_human_name()}"; + } + + /** + * @return array + */ + protected function defaultUserData() { + return array( + 'export_id' => $this->identifier(), + ); + } + + /** + * @return array + */ + public function getUsers() { + return $this->uids; + } + + /** + * @param array $uids + * @return $this + */ + public function setUsers(array $uids) { + $user_data = array(); + + foreach ($uids as $uid => $data) { + if (is_array($data)) { + if (!isset($data['uid'])) { + $data['uid'] = $uid; + } + } + else { + $data = array('uid' => $uid); + } + $data += $this->defaultUserData(); + $user_data[$data['uid']] = $data; + } + + $this->uids = $user_data; + return $this; + } + +} diff --git a/modules/gdpr_export_log/src/Entity/GDPRExportLogController.php b/modules/gdpr_export_log/src/Entity/GDPRExportLogController.php new file mode 100644 index 0000000..f0e5988 --- /dev/null +++ b/modules/gdpr_export_log/src/Entity/GDPRExportLogController.php @@ -0,0 +1,53 @@ + 'exported'); + $values += array('created' => REQUEST_TIME); + + $task = parent::create($values); + return $task; + } + +// public function load($ids = array(), $conditions = array()) { +// /* @var GDPRExportLog[] $entities */ +// $entities = parent::load($ids, $conditions); +// +// foreach ($entities as $entity) { +// $entity->init(); +// } +// +// return $entities; +// } + + + /** + * {@inheritdoc} + */ + public function save($entity, DatabaseTransaction $transaction = NULL) { + /* @var GDPRExportLog $entity */ + $entity->changed = REQUEST_TIME; + $return = parent::save($entity, $transaction); + + foreach ($entity->getUsers() as $uid_data) { + db_merge('gdpr_export_log_uid') + ->key(array( + 'export_id' => $entity->identifier(), + 'uid' => $uid_data['uid'], + )) + ->fields($uid_data) + ->execute(); + } + + return $return; + } + + +} \ No newline at end of file diff --git a/modules/gdpr_export_log/src/Entity/GDPRExportLogInterface.php b/modules/gdpr_export_log/src/Entity/GDPRExportLogInterface.php new file mode 100644 index 0000000..721c2a4 --- /dev/null +++ b/modules/gdpr_export_log/src/Entity/GDPRExportLogInterface.php @@ -0,0 +1,8 @@ +path . '/add']); + + return $items; + } + + /** + * Generates the render array for a overview tables for different statuses. + * + * @param $conditions + * An array of conditions as needed by entity_load(). + + * @return array + * A renderable array. + */ +// public function overviewTable($conditions = array()) { +// $query = new EntityFieldQuery(); +// $query->entityCondition('entity_type', $this->entityType); +// $query->propertyOrderBy('created'); +// +// // Add all conditions to query. +// foreach ($conditions as $key => $value) { +// $query->propertyCondition($key, $value); +// } +// +// if ($this->overviewPagerLimit) { +// $query->pager($this->overviewPagerLimit); +// } +// +// $results = $query->execute(); +// +// $ids = isset($results[$this->entityType]) ? array_keys($results[$this->entityType]) : array(); +// $entities = $ids ? entity_load($this->entityType, $ids) : array(); +// ksort($entities); +// +// // Always show at least requested and complete tables. +// $rows = array( +// 'requested' => array(), +// 'complete' => array(), +// ); +// foreach ($entities as $entity) { +// $rows[$entity->status][] = $this->overviewTableRow($conditions, entity_id($this->entityType, $entity), $entity); +// } +// +// $render = array(); +// foreach ($rows as $status => $status_rows) { +// $render[$status] = array( +// '#theme' => 'table', +// '#header' => $this->overviewTableHeaders($conditions, $status_rows), +// '#rows' => $status_rows, +// '#caption' => t('Tasks with status - @status', array('@status' => ucfirst($status))), +// '#empty' => t('No tasks.'), +// '#weight' => 3, +// ); +// +// // @todo Find a better way to order statuses. +// if ($status == 'requested') { +// $render[$status]['#weight'] = 0; +// } +// } +// +// return $render; +// } + + /** + * {@inheritdoc} + */ + protected function overviewTableHeaders($conditions, $rows, $additional_header = array()) { + $additional_header = array( + t('User count'), + t('Status'), + t('Exported by'), + t('Exported'), + t('Lifetime'), + ); + return parent::overviewTableHeaders($conditions, $rows, $additional_header); + } + + /** + * {@inheritdoc} + */ + protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { + /* @var GDPRExportLog $entity */ + + // $time_left = $entity->created + ($entity->getLifetime('U')) + $time_left = $entity->created + ($entity->wrapper()->gdpr_export_log_lifetime->value() * 60 * 60 * 24) - REQUEST_TIME; + $left_to_go = t('%time to go', array('%time' => format_interval($time_left, 2))); + + + $additional_cols = array( + count($entity->getUsers()), + $entity->status, + theme('username', array('account' => user_load($entity->exported_by))), + format_date($entity->created, 'short'), + $left_to_go, + ); + $row = parent::overviewTableRow($conditions, $id, $entity, $additional_cols); + // @todo Fix hardcoded links. +// $row[0] = l($entity->label(), $this->path . '/' . $id . '/view', array('query' => drupal_get_destination())); + $row[0] = $entity->label(); + + return $row; + } + +} diff --git a/modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc b/modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc new file mode 100644 index 0000000..54720f9 --- /dev/null +++ b/modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc @@ -0,0 +1,91 @@ +display instanceof views_data_export_plugin_display_export) { + return FALSE; + } + + return TRUE; + } + + /** + * {@inheritdoc} + */ + function option_definition() { + $options = array(); + + if ($this->applies()) { + $options['gdpr_export_log_audit'] = array('default' => FALSE); + } + + return $options; + } + + /** + * {@inheritdoc} + */ + function options_definition_alter(&$options) { + if ($this->applies()) { + $options['gdpr_export_log_audit'] = array('default' => FALSE); + } + } + + /** + * {@inheritdoc} + */ + function options_form(&$form, &$form_state) { + if ($this->applies()) { + if (substr($form['#section'], -21) == 'gdpr_export_log_audit') { + $form['gdpr_export_log_audit'] = array( + '#type' => 'checkbox', + '#title' => t('Audit exports for GDPR'), + '#description' => t('Include this export in a GDPR audit log.'), + '#default_value' => $this->display->get_option('gdpr_export_log_audit'), + ); + } + } + } + + /** + * {@inheritdoc} + */ + function options_submit(&$form, &$form_state) { + if ($this->applies()) { + if (isset($form['options']['gdpr_export_log_audit'])) { + $this->display->set_option('gdpr_export_log_audit', $form_state['values']['gdpr_export_log_audit']); + } + } + } + + /** + * {@inheritdoc} + */ + function options_summary(&$categories, &$options) { + if ($this->applies()) { + $value = $this->display->get_option('gdpr_export_log_audit'); + $options['gdpr_export_log_audit'] = array( + 'category' => 'page', + 'title' => 'GDPR Audit', + 'value' => $value ? t('Include') : t("Don't include"), + ); + } + } + +} From 9d4e8e0c0eed7e8af73e9cfe497f7be52dd52f2e Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 20 Apr 2018 10:48:36 +0100 Subject: [PATCH 09/21] By yanniboi: Fix php coding standard. --- gdpr.install | 10 +- gdpr.module | 266 +++++++++--------- .../plugins/export_ui/gdpr_sanitizer_ui.inc | 4 +- modules/gdpr_dump/src/GDPRUtilRandom.php | 6 +- modules/gdpr_fields/gdpr_fields.module | 7 + .../export_ui/gdpr_fields_ui.class.php | 57 +++- .../plugins/export_ui/gdpr_fields_ui.inc | 36 +-- .../gdpr_data/gdpr_entity_property.inc | 4 +- .../relationships/entities_from_field.inc | 70 +++-- modules/gdpr_tasks/gdpr_tasks.admin.inc | 2 +- modules/gdpr_tasks/gdpr_tasks.module | 33 ++- modules/gdpr_tasks/gdpr_tasks.pages.inc | 4 +- templates/gdpr-user-data-page.tpl.php | 14 +- 13 files changed, 297 insertions(+), 216 deletions(-) diff --git a/gdpr.install b/gdpr.install index bb4cbec..ed88117 100644 --- a/gdpr.install +++ b/gdpr.install @@ -9,7 +9,7 @@ * Implements hook_requirements(). */ function gdpr_requirements($phase) { - $requirements = []; + $requirements = array(); $t = get_t(); if ($phase === 'runtime' && $definitions = checklistapi_get_checklist_info()) { @@ -17,14 +17,14 @@ function gdpr_requirements($phase) { if (isset($definitions[$id]) && $checklist = checklistapi_checklist_load($id)) { $percent = round($checklist->getPercentComplete()); - $requirements['gdpr_status'] = [ + $requirements['gdpr_status'] = array( 'title' => $t('GDPR Preparation'), - 'value' => $t('Self assessment Checklist: @percent% done.', [ + 'value' => $t('Self assessment Checklist: @percent% done.', array( '@percent' => $percent, '@url' => '/admin/gdpr/checklist', - ]), + )), 'severity' => REQUIREMENT_INFO, - ]; + ); } } diff --git a/gdpr.module b/gdpr.module index 3b2ba25..4885424 100644 --- a/gdpr.module +++ b/gdpr.module @@ -19,22 +19,22 @@ function gdpr_help($path, $arg) { * Implements hook_menu(). */ function gdpr_menu() { - $items['admin/gdpr'] = [ + $items['admin/gdpr'] = array( 'title' => 'GDPR', 'description' => 'Administer settings.', 'page callback' => 'gdpr_dashboard_page', 'access arguments' => array('administer gdpr settings'), 'file' => 'includes/gdpr.admin.inc', - ]; + ); - $items['user/%user/gdpr'] = [ + $items['user/%user/gdpr'] = array( 'title' => 'All your data', 'page callback' => 'gdpr_collected_user_data', - 'page arguments' => [1], + 'page arguments' => array(1), 'access callback' => 'gdpr_collected_user_data_access', - 'access arguments' => [1], + 'access arguments' => array(1), 'type' => MENU_LOCAL_TASK, - ]; + ); return $items; } @@ -43,12 +43,12 @@ function gdpr_menu() { * Implements hook_theme(). */ function gdpr_theme() { - return [ - 'user_data_page' => [ + return array( + 'user_data_page' => array( 'template' => 'gdpr-user-data-page', 'path' => drupal_get_path('module', 'gdpr') . '/templates', - ], - ]; + ), + ); } /** @@ -69,14 +69,14 @@ function gdpr_permission() { * Implements hook_checklistapi_checklist_info(). */ function gdpr_checklistapi_checklist_info() { - $definitions = []; + $definitions = array(); $content = gdpr_search_content(); if (!empty($content)) { - $description = [ + $description = array( '#theme' => 'item_list', - ]; + ); foreach ($content as $node) { @@ -90,26 +90,27 @@ function gdpr_checklistapi_checklist_info() { } if ($node->status) { - $status = [ + $status = array( '#prefix' => '', '#suffix' => '', '#markup' => t('published'), - ]; + ); + $is_published = TRUE; } else { - $status = [ + $status = array( '#prefix' => '', '#suffix' => '', '#markup' => t('not published'), - ]; + ); } - $options = [ + $options = array( 'absolute' => TRUE, - 'attributes' => [ + 'attributes' => array( 'target' => '_blank', - ], - ]; + ), + ); $mlids = db_select('menu_links', 'ml') ->fields('ml', ['mlid']) @@ -117,7 +118,7 @@ function gdpr_checklistapi_checklist_info() { ->execute() ->fetchCol(); - $menu_list = []; + $menu_list = array(); if (!empty($mlids)) { foreach ($mlids as $mlid) { $link = menu_link_load($mlid); @@ -125,13 +126,13 @@ function gdpr_checklistapi_checklist_info() { } } - $in_menu_text = !empty($menu_list) ? t('in menu: @menus', ['@menus' => implode(', ', $menu_list)]) : ''; + $in_menu_text = !empty($menu_list) ? t('in menu: @menus', array('@menus' => implode(', ', $menu_list))) : ''; - $description['#items'][] = t('!title (!status) @menu', [ + $description['#items'][] = t('!title (!status) @menu', array( '!title' => l($node->title, '/node/' . $node->nid, $options), '!status' => drupal_render($status), '@menu' => $in_menu_text, - ]); + )); } $content_discovery_description = drupal_render($description); @@ -140,128 +141,128 @@ function gdpr_checklistapi_checklist_info() { $content_discovery_description = t('No nodes with the following terms have been found: "Privacy Policy", "Terms of Use", "About us" or "Impressum".'); } - $definitions['gdpr_checklist'] = [ + $definitions['gdpr_checklist'] = array( '#title' => t('GDPR Checklist'), '#path' => 'admin/gdpr/checklist', '#description' => t('GDPR Checklist'), '#help' => t('

Complete this checklist to make your site GDPR-compliant.

'), - 'getting_started' => [ + 'getting_started' => array( '#title' => t('Getting Started'), '#description' => t("

To begin your self-assessment process, it's highly recommended to take the following steps:

"), - 'responsability_agreement' => [ + 'responsability_agreement' => array( '#title' => t("Yes, I agree that by using the GDPR module, I'm still accountable for personal data handling performed on the site."), '#description' => t('Responsibility Agreement: before the site owner starts the checklist process, they should acknowledge that installing and using this module pack does not mean sharing responsibility. Neither the Drupal Community nor module maintainers can guarantee full compliance with the GDPR regulations in case of a potential control.'), - ], - 'recommended_resources' => [ + ), + 'recommended_resources' => array( '#title' => t('Yes, I have read at least one of the following recommended resources from the list below'), - 'wiki_page' => [ + 'wiki_page' => array( '#text' => t('GDPR Wikipedia page'), '#path' => t('https://en.wikipedia.org/wiki/General_Data_Protection_Regulation'), - ], - 'summary' => [ + ), + 'summary' => array( '#text' => t('Summary of the GDPR regulation'), '#path' => t('http://eur-lex.europa.eu/legal-content/EN/LSU/?uri=CELEX:32016R0679#document1'), - ], - 'legislation' => [ + ), + 'legislation' => array( '#text' => t('The GDPR legislation'), '#path' => t('http://eur-lex.europa.eu/eli/reg/2016/679/oj'), - ], - ], - ], - 'policies' => [ + ), + ), + ), + 'policies' => array( '#title' => t('Policies and other measures'), '#description' => t('

Modules or libraries that already implement some of the needed features.

'), - 'data1' => [ + 'data1' => array( '#title' => t('Cookie policy'), '#description' => t('User needs to be informed when your site uses cookies to collect data.'), - 'handbook_page' => [ + 'handbook_page' => array( '#text' => t('EU Cookie Compliance-module'), '#path' => 'https://www.drupal.org/project/eu_cookie_compliance', - ], - ], - ], - 'content_related_suggestions' => [ + ), + ), + ), + 'content_related_suggestions' => array( '#title' => t('Content related suggestions'), '#description' => t('

Automated search performed on uploaded content of site.

') . $content_discovery_description, - 'privacy_policy_page' => [ + 'privacy_policy_page' => array( '#title' => t('I have checked through the uploaded content and verified that a page containing Privacy Policy exists and has been published to this site.'), '#description' => t('No nodes with the following terms have been found: "Privacy Policy", "Terms of Use", "About us" or "Impressum". Please verify manually that such content of similar titles exists and has been published.'), - ], - 'privacy_policy_published' => [ + ), + 'privacy_policy_published' => array( '#title' => t('I confirm the existing "Privacy Policy" has been published.'), '#description' => t('This GDPR module does not automatically affect publishing status of site content. Therefore, even if such information has been uploaded (regarding the previous point), then you must make sure that this content is available to visitors.'), - ], - 'privacy_policy_in_menu' => [ + ), + 'privacy_policy_in_menu' => array( '#title' => t('I confirm the published "Privacy Policy" is included in at least one menu of the site.'), '#description' => t("To ensure best practice, it's strongly recommended to divulge personal data handling guidelines on the site by including a link to the guidelines page in a generally displayed menu in clear view on the site layout."), - ], - ], - 'site_features' => [ + ), + ), + 'site_features' => array( '#title' => t('Site feature related suggestions'), '#description' => t('This group of checkpoints addresses various actions to be taken regarding site features.'), - 'external_traffic_measurement' => [ + 'external_traffic_measurement' => array( '#title' => t("Yes, I'm aware there are external traffic measurement and user tracking services integrated on this site"), '#description' => t('As the owner and/or maintainer of the site I have, to the best of my knowledge, taken the necessary measures to set up these 3rd-party data collecting tools to ensure maximum compliance with GDPR regulations.') . _get_module_list('tracking'), - ], - 'social_media_connections' => [ + ), + 'social_media_connections' => array( '#title' => t("Yes, I'm aware there are connections established between this site and some social media platforms"), '#description' => t('As many social media platforms collects identifiable personal data, I acknowledge that the owner of this site is responsible for setting up these connections to ensure maximum compliance with GDPR regulations.') . _get_module_list('social'), - ], - 'module_data_collection' => [ + ), + 'module_data_collection' => array( '#title' => t('I confirm that all Drupal core, community, and custom modules have been revised as to not gather any unnecessary personal data of site visitors'), '#description' => t('Owner and/or maintainer of the site has to ensure that the modules listed below have been revised as to gather only necessary (i.e. not needed for provision of service) personal data of site visitors.') . _get_module_list('collect'), - ], - 'user_role_permissions' => [ + ), + 'user_role_permissions' => array( '#title' => t('I have examined all aspects of displayed personal data, associated with this site, to ensure that all users can access only permitted types of information according to their role'), '#description' => t('Consider every use case in which Drupal fetches information from its database and exposes it to external parties. Bear in mind all possible scenarios extending beyond webpages, eg. RSS feeds, metadata and outgoing e-mail templates sent by the site.'), - ], - ], - 'configuration' => [ + ), + ), + 'configuration' => array( '#title' => t('Configuration'), '#description' => t('

Things you can can do by just making changes in your site configuration

'), - 'cancel_account' => [ + 'cancel_account' => array( '#title' => t('Allow users to cancel their own user account'), - 'permission' => [ + 'permission' => array( '#text' => t('Permissions for "Cancel own user account"'), '#path' => '/admin/people/permissions', - '#options' => [ + '#options' => array( 'fragment' => 'module-user', - ], - ], - ], - 'delete_data' => [ + ), + ), + ), + 'delete_data' => array( '#title' => t('Users need to be able to request for removal of all their personal data.'), - 'account_config' => [ + 'account_config' => array( '#text' => t('Account configuration for "Delete the account and make its content belong to the Anonymous user"'), '#path' => '/admin/config/people/accounts', - ], - ], - ], - 'beyond_website_management' => [ + ), + ), + ), + 'beyond_website_management' => array( '#title' => t('Beyond website management'), '#description' => t('The scope of GDPR is not clearly defined, stretching far beyond IT and legal aspects.'), - 'legal_adviser_consulted' => [ + 'legal_adviser_consulted' => array( '#title' => t('I have found and consulted with a legal adviser'), '#description' => t('It is strongly suggested to have a complete overview of the internal data handling workflows within the organisation represented by the website. An experienced legal adviser can guide your organisation through this transformation process.'), - ], - 'gdpr_modules' => [ + ), + 'gdpr_modules' => array( '#title' => t('I have enabled all features of the GDPR module'), '#description' => _get_gdpr_module_description(), - ], - 'notice_upon_breach' => [ + ), + 'notice_upon_breach' => array( '#title' => t('I declare that I am able to notify the supervisory authority within 72 hours in the case of a personal data breach'), '#description' => t('Referring to Article 33 of the regulation, site owner/maintainer as a data controller must have a contingency plan in place in the event of any personal data breach incident.'), - ], - 'logging_responsability' => [ + ), + 'logging_responsability' => array( '#title' => t('I confirm that I have understood that the responsibility of personal data logging is dependent on the size of my organisation as described below:'), '#description' => _get_logging_responsability_description(), - ], - ], - ]; + ), + ), + ); return $definitions; } @@ -277,9 +278,9 @@ function gdpr_checklistapi_checklist_info() { function gdpr_collected_user_data(\stdClass $user) { drupal_set_title(t('Data stored about you')); $user_data = gdpr_get_user_data($user); - $output = theme('user_data_page', [ + $output = theme('user_data_page', array( 'user_data' => $user_data, - ]); + )); return $output; } @@ -351,13 +352,13 @@ function gdpr_star_value($value) { * HTML markup string */ function _get_logging_responsability_description() { - $logging_items = [ + $logging_items = array( '#theme' => 'item_list', - '#items' => [ + '#items' => array( t('Has fewer than 250 employees the processing it carries out is likely no result in a risk to the rights and freedoms of data subjects, the processing is only occasional and the processing does not includes special categories of data (Article 30/5) and as such my organisation is not required to create records of processing acitivities'), t('My organisation has 250 or more employees and records of processing activities have been prepared according to Article 30 of the regulation'), - ], - ]; + ), + ); return drupal_render($logging_items); } @@ -371,55 +372,55 @@ function _get_logging_responsability_description() { function _get_gdpr_module_description() { $all_modules = _get_modules(); - $gdpr_module_names = ['gdpr_dump']; + $gdpr_module_names = array('gdpr_dump'); - $gdpr_modules_list = [ + $gdpr_modules_list = array( '#theme' => 'item_list', - '#items' => [], - ]; + '#items' => array(), + ); $module_statuses = _get_module_statuses(); - $color_classes = [ + $color_classes = array( -1 => 'admin-missing', 0 => 'admin-disabled', 1 => 'admin-enabled', - ]; + ); foreach ($gdpr_module_names as $module_name) { if (isset($all_modules[$module_name])) { if (!$all_modules[$module_name]['status']) { $status_class = $color_classes[$all_modules[$module_name]['status']]; - $status = [ + $status = array( '#prefix' => '', '#suffix' => '', '#markup' => $module_statuses[$all_modules[$module_name]['status']], - ]; + ); - $gdpr_modules_list['#items'][] = t('@title (!status)', [ + $gdpr_modules_list['#items'][] = t('@title (!status)', array( '@title' => $all_modules[$module_name]['name'], '!status' => drupal_render($status), - ]); + )); } } else { $status_class = $color_classes[-1]; - $status = [ + $status = array( '#prefix' => '', '#suffix' => '', '#markup' => $module_statuses[-1], - ]; + ); - $gdpr_modules_list['#items'][] = t('@title (!status)', [ + $gdpr_modules_list['#items'][] = t('@title (!status)', array( '@title' => $module_name, '!status' => drupal_render($status), - ]); + )); } } $gdpr_modules = drupal_render($gdpr_modules_list); - $options = ['absolute' => TRUE]; + $options = array('absolute' => TRUE); $link = l(t('Enable them on Modules admin page'), '/admin/modules', $options); if (!empty($gdpr_modules_list['#items'])) { @@ -438,11 +439,11 @@ function _get_gdpr_module_description() { * Module statuses array */ function _get_module_statuses() { - return [ + return array( -1 => t('missing'), 0 => t('disabled'), 1 => t('enabled'), - ]; + ); } /** @@ -463,10 +464,13 @@ function gdpr_search_content() { ->propertyCondition('title', $pattern, 'RLIKE') ->execute(); - if (!empty($results['node'])) { - $nids = array_keys($results['node']); - $nodes = node_load_multiple($nids); - } + if (!empty($results['node'])) { + $nids = array_keys($results['node']); + $nodes = node_load_multiple($nids); + return $nodes; + } + else { + return array(); } return $nodes; @@ -485,33 +489,33 @@ function _get_module_list($type) { $c_modules = _get_module_list_by_type($type); - $modules_list = [ + $modules_list = array( '#theme' => 'item_list', - '#items' => [], - ]; + '#items' => array(), + ); $module_statuses = _get_module_statuses(); $all_modules = _get_modules(); - $color_classes = [ + $color_classes = array( -1 => 'admin-enabled', 0 => 'admin-enabled', 1 => 'admin-missing', - ]; + ); foreach ($c_modules as $c_module) { if (isset($all_modules[$c_module])) { $status_class = $color_classes[$all_modules[$c_module]['status']]; - $status = [ + $status = array( '#prefix' => '', '#suffix' => '', '#markup' => $module_statuses[$all_modules[$c_module]['status']], - ]; + ); - $modules_list['#items'][] = t('@title (!status)', [ + $modules_list['#items'][] = t('@title (!status)', array( '@title' => $all_modules[$c_module]['name'], '!status' => drupal_render($status), - ]); + )); } } @@ -528,18 +532,18 @@ function _get_module_list($type) { * Array of module machine_names */ function _get_module_list_by_type($type) { - $list = []; + $list = array(); switch ($type) { case 'collect': - $list = [ + $list = array( 'contact', 'webform', 'geoip', - ]; + ); break; case 'tracking': - $list = [ + $list = array( 'google_analytics', 'google_tag', 'piwik', @@ -548,18 +552,18 @@ function _get_module_list_by_type($type) { 'site_verify', 'adsense', 'yandex_metrics', - ]; + ); break; case 'social': - $list = [ + $list = array( 'oauth', 'sharethis', 'addtoany', 'cas', 'fb_likebox', 'easy_social', - ]; + ); break; } return $list; @@ -579,13 +583,13 @@ function _get_modules() { } else { $modules_list_raw = system_rebuild_module_data(); - $modules_list = []; + $modules_list = array(); foreach ($modules_list_raw as $module) { - $modules_list[$module->name] = [ + $modules_list[$module->name] = array( 'status' => $module->status, 'name' => $module->info['name'], - ]; + ); } cache_set('modules_list', $modules_list); return $modules_list; diff --git a/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc index f1c32e9..d7e476f 100644 --- a/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc +++ b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc @@ -53,11 +53,11 @@ function gdpr_dump_sanitizer_export_ui_form(&$form, &$form_state) { '#tree' => TRUE, ); - $form['settings']['notes'] = [ + $form['settings']['notes'] = array( '#type' => 'textarea', '#title' => t('Notes'), '#default_value' => $sanitizer->getSetting('notes', ''), - ]; + ); } /** diff --git a/modules/gdpr_dump/src/GDPRUtilRandom.php b/modules/gdpr_dump/src/GDPRUtilRandom.php index 389c9c5..1c851f6 100644 --- a/modules/gdpr_dump/src/GDPRUtilRandom.php +++ b/modules/gdpr_dump/src/GDPRUtilRandom.php @@ -70,11 +70,11 @@ public function name($length = 8) { public function word($length) { mt_srand((double) microtime() * 1000000); - $vowels = ["a", "e", "i", "o", "u"]; - $cons = ["b", "c", "d", "g", "h", "j", "k", "l", "m", "n", "p", "r", "s", "t", "u", "v", "w", "tr", + $vowels = array("a", "e", "i", "o", "u"); + $cons = array("b", "c", "d", "g", "h", "j", "k", "l", "m", "n", "p", "r", "s", "t", "u", "v", "w", "tr", "cr", "br", "fr", "th", "dr", "ch", "ph", "wr", "st", "sp", "sw", "pr", "sl", "cl", "sh", - ]; + ); $num_vowels = count($vowels); $num_cons = count($cons); diff --git a/modules/gdpr_fields/gdpr_fields.module b/modules/gdpr_fields/gdpr_fields.module index 42f589f..9b0310e 100644 --- a/modules/gdpr_fields/gdpr_fields.module +++ b/modules/gdpr_fields/gdpr_fields.module @@ -134,6 +134,10 @@ function gdpr_fields_collect_gdpr_entities(&$entity_list, $entity_type, $entity) // Go through each relationship. foreach ($relationships as $rid => $relationship) { // For each relationship, see if there is a context that satisfies it. + if (!$context) { + var_dump($context); + var_dump($entity_type); + } if (ctools_context_filter(array($context), $relationship['required context'])) { $available_relationships[$rid] = $relationship; } @@ -182,6 +186,9 @@ function gdpr_fields_collect_gdpr_entities(&$entity_list, $entity_type, $entity) foreach ($entity_contexts as $entity_context) { if (!empty($entity_context->data)) { list($plugin_type, $entity_type) = explode(':', $entity_context->plugin); + if (!$entity_type) { + var_dump($entity_context->plugin); + } gdpr_fields_collect_gdpr_entities($entity_list, $entity_type, $entity_context->data); } } diff --git a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php index 8dcc1f3..76d7f20 100644 --- a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php +++ b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php @@ -31,17 +31,24 @@ public function list_build_row($item, &$form_state, $operations) { parent::list_build_row($item, $form_state, $operations); $name = $item->{$this->plugin['export']['key']}; - $ops = array_pop($this->rows[$name]['data']); + $row = $this->rows[$name]; + unset($this->rows[$name]); + + $ops = array_pop($row['data']); $title = array('data' => check_plain($item->getSetting('label')), 'class' => array('ctools-export-ui-title')); - array_unshift($this->rows[$name]['data'], $title); - - $this->rows[$name]['data'][] = array('data' => check_plain($item->entity_type), 'class' => array('ctools-export-ui-entity-type')); - $this->rows[$name]['data'][] = array('data' => check_plain($item->entity_bundle), 'class' => array('ctools-export-ui-entity-bundle')); - $this->rows[$name]['data'][] = array('data' => check_plain($item->field_name), 'class' => array('ctools-export-ui-field-name')); - $this->rows[$name]['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rta', 'none')), 'class' => array('ctools-export-ui-rta')); - $this->rows[$name]['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rtf', 'none')), 'class' => array('ctools-export-ui-rtf')); - $this->rows[$name]['data'][] = $ops; + array_unshift($row['data'], $title); + + unset($row['data'][1]); + +// $row['data'][] = array('data' => check_plain($item->entity_type), 'class' => array('ctools-export-ui-entity-type')); +// $row['data'][] = array('data' => check_plain($item->entity_bundle), 'class' => array('ctools-export-ui-entity-bundle')); +// $row['data'][] = array('data' => check_plain($item->field_name), 'class' => array('ctools-export-ui-field-name')); + $row['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rta', 'none')), 'class' => array('ctools-export-ui-rta')); + $row['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rtf', 'none')), 'class' => array('ctools-export-ui-rtf')); + $row['data'][] = $ops; + +// $this->rows[$item->entity_type][$item->entity_bundle][$name] = $row; } /** @@ -54,9 +61,11 @@ public function list_table_header() { $title = array('data' => t('Label'), 'class' => array('ctools-export-ui-title')); array_unshift($header, $title); - $header[] = array('data' => t('Entity type'), 'class' => array('ctools-export-ui-entity-type')); - $header[] = array('data' => t('Entity bundle'), 'class' => array('ctools-export-ui-entity-bundle')); - $header[] = array('data' => t('Field name'), 'class' => array('ctools-export-ui-field-name')); + unset($header[1]); + +// $header[] = array('data' => t('Entity type'), 'class' => array('ctools-export-ui-entity-type')); +// $header[] = array('data' => t('Entity bundle'), 'class' => array('ctools-export-ui-entity-bundle')); +// $header[] = array('data' => t('Field name'), 'class' => array('ctools-export-ui-field-name')); $header[] = array('data' => t('Right to access'), 'class' => array('ctools-export-ui-rta')); $header[] = array('data' => t('Right to be forgotten'), 'class' => array('ctools-export-ui-rtf')); $header[] = $ops; @@ -64,4 +73,28 @@ public function list_table_header() { } + /** + * {@inheritdoc} + */ + public function list_render(&$form_state) { + $tables = ''; + + foreach ($this->rows as $entity_type => $entities) { + dpm($entity_type); + foreach ($entities as $bundle => $rows) { + $table = array( + 'header' => $this->list_table_header(), + 'rows' => $rows, + 'attributes' => array('id' => 'ctools-export-ui-list-items'), + 'empty' => $this->plugin['strings']['message']['no items'], + ); + $tables .= theme('table', $table); + } + } + + + return ''; + return $tables; + } + } \ No newline at end of file diff --git a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc index f74d1d9..5187fe6 100644 --- a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc +++ b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc @@ -98,28 +98,28 @@ function gdpr_fields_field_data_export_ui_form(&$form, &$form_state) { '#tree' => TRUE, ); - $form['settings']['gdpr_fields_rta'] = [ + $form['settings']['gdpr_fields_rta'] = array( '#type' => 'select', '#title' => t('Right to access'), - '#options' => [ + '#options' => array( 'inc' => 'Included', 'maybe' => 'Maybe', 'no' => 'Not', - ], + ), '#default_value' => $field_data->getSetting('gdpr_fields_rta', 'no'), - ]; + ); - $form['settings']['gdpr_fields_rtf'] = [ + $form['settings']['gdpr_fields_rtf'] = array( '#type' => 'select', '#title' => t('Right to be forgotten'), - '#options' => [ + '#options' => array( 'anonymise' => 'Anonymise', 'remove' => 'Remove', 'maybe' => 'Maybe', 'no' => 'Not', - ], + ), '#default_value' => $field_data->getSetting('gdpr_fields_rtf', 'no'), - ]; + ); // @todo Filter by relevance. $sanitizer_options = array(); @@ -127,23 +127,23 @@ function gdpr_fields_field_data_export_ui_form(&$form, &$form_state) { $sanitizer_options[$sanitizer->name] = $sanitizer->label; } - $form['settings']['gdpr_fields_sanitizer'] = [ + $form['settings']['gdpr_fields_sanitizer'] = array( '#type' => 'select', '#title' => t('Sanitizer to use'), '#options' => $sanitizer_options, '#default_value' => $field_data->getSetting('gdpr_fields_sanitizer', ''), - '#states' => [ - 'visible' => [ - ':input[name="settings[gdpr_fields_rtf]"]' => ['value' => 'anonymise'], - ], - ], - ]; - - $form['settings']['notes'] = [ + '#states' => array( + 'visible' => array( + ':input[name="settings[gdpr_fields_rtf]"]' => array('value' => 'anonymise'), + ), + ), + ); + + $form['settings']['notes'] = array( '#type' => 'textarea', '#title' => t('Notes'), '#default_value' => $field_data->getSetting('gdpr_fields_notes', ''), - ]; + ); } /** diff --git a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc index 3610792..e47d347 100644 --- a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc +++ b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc @@ -57,7 +57,9 @@ function gdpr_fields_gdpr_entity_property_get_children($plugin, $parent) { $child_plugin = $plugin; $child_plugin['name'] = $name; $child_plugin['label'] = $property['label']; - $child_plugin['description'] = $property['description']; + + $child_plugin['description'] = isset($property['description']) ? $property['description'] : ''; + // @todo Should computed properties be removed instead or disabled? if (!empty($property['computed'])) { $child_plugin['computed'] = TRUE; diff --git a/modules/gdpr_fields/plugins/relationships/entities_from_field.inc b/modules/gdpr_fields/plugins/relationships/entities_from_field.inc index f4ee739..a095a6c 100644 --- a/modules/gdpr_fields/plugins/relationships/entities_from_field.inc +++ b/modules/gdpr_fields/plugins/relationships/entities_from_field.inc @@ -95,7 +95,7 @@ function gdpr_fields_entities_from_field_get_children($parent_plugin, $parent) { $name = $field_name . '-' . $to_entity . '-' . $from_entity; $plugin_id = $parent . ':' . $name; - $plugin['title'] = t('@from_entity from @to_entity (on @from_entity: @field_name)', $replacements); + $plugin['title'] = t('@from_entity from @to_entity (on @from_entity: @field_label [@field_name])', $replacements); $plugin['keyword'] = $to_entity; $plugin['context name'] = $name; $plugin['name'] = $plugin_id; @@ -171,32 +171,58 @@ function gdpr_fields_entities_from_field_context($context, $conf) { if (isset($context->data->{$entity_info['entity keys']['id']})) { // Load the entity. $id = $context->data->{$entity_info['entity keys']['id']}; - $entity = entity_load($from_entity, array($id)); - $entity = $entity[$id]; - if ($items = field_get_items($from_entity, $entity, $field_name)) { - if (isset($items[$delta])) { - ctools_include('fields'); - $to_entity_info = entity_get_info($to_entity); - - $plugin_info = ctools_get_relationship($conf['name']); - $to_entity_id = $items[$delta][$plugin_info['source key']]; - $loaded_to_entity = entity_load($to_entity, array($to_entity_id)); - $loaded_to_entity = array_shift($loaded_to_entity); - - // Pass current user account and entity type to access callback. - if (isset($to_entity_info['access callback']) && function_exists($to_entity_info['access callback']) && !call_user_func($to_entity_info['access callback'], 'view', $loaded_to_entity, $account, $to_entity)) { - return ctools_context_create_empty('entity:' . $to_entity, NULL); + + if (!$conf['reverse']) { + $entity = entity_load($from_entity, array($id)); + $entity = $entity[$id]; + if ($items = field_get_items($from_entity, $entity, $field_name)) { + if (isset($items[$delta])) { + ctools_include('fields'); + $to_entity_info = entity_get_info($to_entity); + + $plugin_info = ctools_get_relationship($conf['name']); + + if (!isset($plugin_info['source key'])) { + dpm($conf['name']); + dpm($plugin_info); + } + + $to_entity_id = $items[$delta][$plugin_info['source key']]; + $loaded_to_entity = entity_load($to_entity, array($to_entity_id)); + $loaded_to_entity = array_shift($loaded_to_entity); + + // Pass current user account and entity type to access callback. + if (isset($to_entity_info['access callback']) && function_exists($to_entity_info['access callback']) && !call_user_func($to_entity_info['access callback'], 'view', $loaded_to_entity, $account, $to_entity)) { + return ctools_context_create_empty('entity:' . $to_entity, NULL); + } + else { + // Send it to ctools. + return ctools_context_create('entity:' . $to_entity, $to_entity_id); + } } else { - // Send it to ctools. - return ctools_context_create('entity:' . $to_entity, $to_entity_id); + // In case that delta was empty. + return ctools_context_create_empty('entity:' . $to_entity, NULL); } } - else { - // In case that delta was empty. - return ctools_context_create_empty('entity:' . $to_entity, NULL); - } } + else { + // @todo Do reverse context. +// $result = db_select($conf['base_table'], 'base') +// ->fields('base', array($conf['base_table_id'])) +// ->condition($conf['base_table_key'], $id, '=') +// // @todo figure out performance improvement for more contexts. +// ->range(0,1) +// ->execute(); +// +// $contexts = array(); +// while($record = $result->fetchAssoc()) { +// $contexts[] = ctools_context_create('entity:' . $to_entity, $record[$conf['base_table_id']]); +// } +// +// return $contexts; + } + } } diff --git a/modules/gdpr_tasks/gdpr_tasks.admin.inc b/modules/gdpr_tasks/gdpr_tasks.admin.inc index b7e6aba..f4aed4e 100644 --- a/modules/gdpr_tasks/gdpr_tasks.admin.inc +++ b/modules/gdpr_tasks/gdpr_tasks.admin.inc @@ -197,7 +197,7 @@ function gdpr_task_edit_gdpr_sar_form_submit($form, &$form_state) { // @todo add getOwner method to Task. $data = gdpr_tasks_collect_rta_data(user_load($task->user_id)); - $inc = []; + $inc = array(); foreach ($data as $key => $values) { $rta = $values['rta']; unset($values['rat']); diff --git a/modules/gdpr_tasks/gdpr_tasks.module b/modules/gdpr_tasks/gdpr_tasks.module index e15f21f..3dc29de 100644 --- a/modules/gdpr_tasks/gdpr_tasks.module +++ b/modules/gdpr_tasks/gdpr_tasks.module @@ -380,8 +380,8 @@ function gdpr_tasks_entity_presave($entity, $type) { $data = gdpr_tasks_collect_rta_data(user_load($entity->user_id)); - $inc = []; - $maybe = []; + $inc = array(); + $maybe = array(); foreach ($data as $key => $values) { $rta = $values['rta']; unset($values['rta']); @@ -439,6 +439,8 @@ function gdpr_tasks_collect_rta_data($user, $include_plugins = FALSE) { $gdpr_entities = array(); gdpr_fields_collect_gdpr_entities($gdpr_entities, 'user', $user); + dpm($gdpr_entities); + foreach ($gdpr_entities as $entity_type => $bundles) { foreach ($bundles as $entity_bundle => $entities) { foreach ($entities as $entity) { @@ -461,11 +463,14 @@ function gdpr_tasks_collect_rta_data($user, $include_plugins = FALSE) { if ($wrapper->{$key} instanceof EntityValueWrapper) { $value .= ' ' . $wrapper->{$key}->value(); } - elseif (!empty($wrapper->{$key}->value()) && $wrapper->{$key} instanceof EntityStructureWrapper) { - foreach (array_keys($wrapper->{$key}->value()) as $sub_key) { - $list = $wrapper->{$key}->getPropertyInfo(); - if (!empty($list) && in_array($sub_key, array_keys($list)) && $wrapper->{$key}->{$sub_key} instanceof EntityValueWrapper) { - $value .= ' ' . $wrapper->{$key}->{$sub_key}->value(); + elseif ($wrapper->{$key} instanceof EntityStructureWrapper) { + $key_data = $wrapper->{$key}->value(); + if (!empty($key_data)) { + foreach (array_keys($key_data) as $sub_key) { + $list = $wrapper->{$key}->getPropertyInfo(); + if (!empty($list) && in_array($sub_key, array_keys($list)) && $wrapper->{$key}->{$sub_key} instanceof EntityValueWrapper) { + $value .= ' ' . $wrapper->{$key}->{$sub_key}->value(); + } } } } @@ -527,11 +532,15 @@ function gdpr_tasks_collect_rtf_data($user, $include_plugins = FALSE) { if ($wrapper->{$key} instanceof EntityValueWrapper) { $value .= ' ' . $wrapper->{$key}->value(); } - elseif (!empty($wrapper->{$key}->value()) && $wrapper->{$key} instanceof EntityStructureWrapper) { - foreach (array_keys($wrapper->{$key}->value()) as $sub_key) { - $list = $wrapper->{$key}->getPropertyInfo(); - if (!empty($list) && in_array($sub_key, array_keys($list)) && $wrapper->{$key}->{$sub_key} instanceof EntityValueWrapper) { - $value .= ' ' . $wrapper->{$key}->{$sub_key}->value(); + + elseif ($wrapper->{$key} instanceof EntityStructureWrapper) { + $key_data = $wrapper->{$key}->value(); + if (!empty($key_data)) { + foreach (array_keys($key_data) as $sub_key) { + $list = $wrapper->{$key}->getPropertyInfo(); + if (!empty($list) && in_array($sub_key, array_keys($list)) && $wrapper->{$key}->{$sub_key} instanceof EntityValueWrapper) { + $value .= ' ' . $wrapper->{$key}->{$sub_key}->value(); + } } } } diff --git a/modules/gdpr_tasks/gdpr_tasks.pages.inc b/modules/gdpr_tasks/gdpr_tasks.pages.inc index c6583ec..d71f4a0 100644 --- a/modules/gdpr_tasks/gdpr_tasks.pages.inc +++ b/modules/gdpr_tasks/gdpr_tasks.pages.inc @@ -28,11 +28,11 @@ function gdpr_tasks_request($account, $gdpr_task_type) { else { global $user; - $values = [ + $values = array( 'type' => $gdpr_task_type, 'user_id' => $account->uid, 'requested_by' => $user->uid, - ]; + ); $task = entity_create('gdpr_task', $values); $task->save(); diff --git a/templates/gdpr-user-data-page.tpl.php b/templates/gdpr-user-data-page.tpl.php index 6aac020..fdad013 100644 --- a/templates/gdpr-user-data-page.tpl.php +++ b/templates/gdpr-user-data-page.tpl.php @@ -5,14 +5,14 @@ * Renders a user's collected data. */ -$header = [t('Type'), t('Value')]; -$rows = []; +$header = array(t('Type'), t('Value')); +$rows = array(); foreach ($user_data as $field => $value) { - $rows[] = [ - 'data' => [$field, $value], - ]; + $rows[] = array( + 'data' => array($field, $value), + ); } -print theme('table', [ +print theme('table', array( 'header' => $header, 'rows' => $rows, -]); +)); From c0911737650dc14a234d439da69b5183fdc9f69b Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 20 Apr 2018 09:42:33 +0000 Subject: [PATCH 10/21] By yanniboi: Various updates. --- .../gdpr_export_log/gdpr_export_log.admin.inc | 24 ++- .../gdpr_export_log/gdpr_export_log.module | 4 +- .../gdpr_export_log/gdpr_export_log.views.inc | 1 + ...ws_plugin_display_extender_data_export.inc | 7 + modules/gdpr_fields/gdpr_fields.module | 3 + modules/gdpr_tasks/gdpr_tasks.info | 1 + modules/gdpr_tasks/gdpr_tasks.module | 11 +- modules/gdpr_tasks/gdpr_tasks.views.inc | 8 + .../gdpr_tasks/gdpr_tasks.views_default.inc | 19 ++- ...tasks_create_request_on_behalf_of_user.inc | 147 ++++++++++++++++++ .../gdpr_tasks_handler_operations_field.inc | 30 ++++ 11 files changed, 250 insertions(+), 5 deletions(-) create mode 100644 modules/gdpr_tasks/plugins/content_types/gdpr_tasks_create_request_on_behalf_of_user.inc create mode 100644 modules/gdpr_tasks/views/handlers/gdpr_tasks_handler_operations_field.inc diff --git a/modules/gdpr_export_log/gdpr_export_log.admin.inc b/modules/gdpr_export_log/gdpr_export_log.admin.inc index 907c0c6..fa64b5c 100644 --- a/modules/gdpr_export_log/gdpr_export_log.admin.inc +++ b/modules/gdpr_export_log/gdpr_export_log.admin.inc @@ -19,7 +19,16 @@ function gdpr_export_log_form($form, &$form_state) { field_attach_form('gdpr_export_log', $log, $form, $form_state); - $form['actions'] = array('#type' => 'actions'); + $form['disclaimer'] = array( + '#type' => 'checkbox', + '#title' => t('Please only remove this export log if you have completely destroyed the export on the target computer and any copies of it.'), + '#weight' => 99, + ); + + $form['actions'] = array( + '#type' => 'actions', + '#weight' => 100, + ); $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Save'), @@ -30,6 +39,10 @@ function gdpr_export_log_form($form, &$form_state) { $form['actions']['remove'] = array( '#type' => 'submit', '#value' => t('Remove export'), + '#validate' => array( + 'gdpr_export_log_form_validate', + 'gdpr_export_log_form_validate_remove', + ), '#submit' => array( 'gdpr_export_log_form_submit_remove', 'gdpr_export_log_form_submit_save', @@ -52,6 +65,15 @@ function gdpr_export_log_form_validate($form, &$form_state) { field_attach_validate('gdpr_export_log', $log); } +/** + * Validate handler for logs form. + */ +function gdpr_export_log_form_validate_remove($form, &$form_state) { + $log = $form_state['export_log']; + field_attach_validate('gdpr_export_log', $log); +} + + /** * Submit handler for logs form. */ diff --git a/modules/gdpr_export_log/gdpr_export_log.module b/modules/gdpr_export_log/gdpr_export_log.module index 2a4f829..a1574c1 100644 --- a/modules/gdpr_export_log/gdpr_export_log.module +++ b/modules/gdpr_export_log/gdpr_export_log.module @@ -18,7 +18,7 @@ function gdpr_export_log_entity_info() { 'controller class' => 'GDPRExportLogController', 'module' => 'gdpr_export_log', 'admin ui' => array( - 'path' => 'admin/structure/gdpr-export-log', + 'path' => 'admin/gdpr/gdpr-export-log', 'file' => 'gdpr_export_log.admin.inc', 'menu wildcard' => '%gdpr_export_log', 'controller class' => 'GDPRExportLogUIController', @@ -27,7 +27,7 @@ function gdpr_export_log_entity_info() { 'gdpr_export_log' => array( 'label' => t('Export Log'), 'admin' => array( - 'path' => 'admin/structure/gdpr-export-log', + 'path' => 'admin/gdpr/gdpr-export-log', 'access arguments' => array('administer export log entities'), ), ), diff --git a/modules/gdpr_export_log/gdpr_export_log.views.inc b/modules/gdpr_export_log/gdpr_export_log.views.inc index 17bcecb..b900e2b 100644 --- a/modules/gdpr_export_log/gdpr_export_log.views.inc +++ b/modules/gdpr_export_log/gdpr_export_log.views.inc @@ -17,6 +17,7 @@ function gdpr_export_log_views_post_render(&$view, &$output, &$cache) { $uids = array(); foreach ($view->result as $data) { + // uid will always be set if user table is base table. if (isset($data->uid)) { $uids[$data->uid] = $data->uid; } diff --git a/modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc b/modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc index 54720f9..a2fe417 100644 --- a/modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc +++ b/modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc @@ -22,6 +22,13 @@ class gdpr_export_log_views_plugin_display_extender_data_export extends views_pl return FALSE; } + // Check that user table is a base table. + if ($this->view->display_handler) { + if (!in_array('users', array_keys($this->view->get_base_tables()))) { + return FALSE; + } + } + return TRUE; } diff --git a/modules/gdpr_fields/gdpr_fields.module b/modules/gdpr_fields/gdpr_fields.module index 9b0310e..24a2a0c 100644 --- a/modules/gdpr_fields/gdpr_fields.module +++ b/modules/gdpr_fields/gdpr_fields.module @@ -18,6 +18,9 @@ function gdpr_fields_ctools_plugin_type() { return $plugins; } +/** + * Implements hook_ctools_plugin_directory(). + */ function gdpr_fields_ctools_plugin_directory($owner, $plugin_type) { if ($owner == 'gdpr_fields') { return 'plugins/' . $plugin_type; diff --git a/modules/gdpr_tasks/gdpr_tasks.info b/modules/gdpr_tasks/gdpr_tasks.info index fc160dc..531e5de 100644 --- a/modules/gdpr_tasks/gdpr_tasks.info +++ b/modules/gdpr_tasks/gdpr_tasks.info @@ -13,3 +13,4 @@ files[] = src/Entity/GDPRTaskInterface.php files[] = src/Entity/GDPRTaskController.php files[] = src/Entity/GDPRTaskUIController.php files[] = src/Entity/GDPRTaskType.php +files[] = views/handlers/gdpr_tasks_handler_operations_field.inc diff --git a/modules/gdpr_tasks/gdpr_tasks.module b/modules/gdpr_tasks/gdpr_tasks.module index 3dc29de..2d71b9d 100644 --- a/modules/gdpr_tasks/gdpr_tasks.module +++ b/modules/gdpr_tasks/gdpr_tasks.module @@ -188,7 +188,16 @@ function gdpr_tasks_permission() { * Implements hook_views_api(). */ function gdpr_tasks_views_api($module, $api) { - return array('version' => 2); + return array('version' => 3); +} + +/** + * Implements hook_ctools_plugin_directory(). + */ +function gdpr_tasks_ctools_plugin_directory($owner, $plugin_type) { + if ($owner == 'ctools' && $plugin_type == 'content_types') { + return 'plugins/' . $plugin_type; + } } /** diff --git a/modules/gdpr_tasks/gdpr_tasks.views.inc b/modules/gdpr_tasks/gdpr_tasks.views.inc index cb1e022..a04325a 100644 --- a/modules/gdpr_tasks/gdpr_tasks.views.inc +++ b/modules/gdpr_tasks/gdpr_tasks.views.inc @@ -40,4 +40,12 @@ function gdpr_tasks_views_data_alter(&$data) { 'handler' => 'views_handler_filter_date', ), ); + + $data['gdpr_task']['operations'] = array( + 'field' => array( + 'title' => t('Operations links'), + 'help' => t('Display all operations available for this task.'), + 'handler' => 'gdpr_tasks_handler_operations_field', + ), + ); } diff --git a/modules/gdpr_tasks/gdpr_tasks.views_default.inc b/modules/gdpr_tasks/gdpr_tasks.views_default.inc index d7209a9..7fb72f2 100644 --- a/modules/gdpr_tasks/gdpr_tasks.views_default.inc +++ b/modules/gdpr_tasks/gdpr_tasks.views_default.inc @@ -69,7 +69,6 @@ function gdpr_tasks_views_default_views() { $handler->display->display_options['filters']['status']['id'] = 'status'; $handler->display->display_options['filters']['status']['table'] = 'gdpr_task'; $handler->display->display_options['filters']['status']['field'] = 'status'; - $handler->display->display_options['filters']['status']['operator'] = '='; $handler->display->display_options['filters']['status']['value'] = 'closed'; /* Display: Page */ @@ -80,6 +79,24 @@ function gdpr_tasks_views_default_views() { $handler->display->display_options['menu']['weight'] = '0'; $handler->display->display_options['menu']['context'] = 0; $handler->display->display_options['menu']['context_only_inline'] = 0; + + /* Display: Content pane */ + $handler = $view->new_display('panel_pane', 'Content pane', 'panel_pane_1'); + $handler->display->display_options['defaults']['filter_groups'] = FALSE; + $handler->display->display_options['defaults']['filters'] = FALSE; + $handler->display->display_options['pane_category']['name'] = 'GDPR'; + $handler->display->display_options['pane_category']['weight'] = '0'; + $handler->display->display_options['argument_input'] = array( + 'user_id' => array( + 'type' => 'context', + 'context' => 'entity:user.uid', + 'context_optional' => 0, + 'panel' => '0', + 'fixed' => '', + 'label' => 'Task: User_id', + ), + ); + $views['gdpr_tasks_my_data_requests'] = $view; return $views; diff --git a/modules/gdpr_tasks/plugins/content_types/gdpr_tasks_create_request_on_behalf_of_user.inc b/modules/gdpr_tasks/plugins/content_types/gdpr_tasks_create_request_on_behalf_of_user.inc new file mode 100644 index 0000000..fea2800 --- /dev/null +++ b/modules/gdpr_tasks/plugins/content_types/gdpr_tasks_create_request_on_behalf_of_user.inc @@ -0,0 +1,147 @@ + t('GDPR Task request form'), + 'content_types' => 'gdpr_tasks_create_request_on_behalf_of_user', + // 'single' means not to be subtyped. + 'single' => TRUE, + // Name of a function which will render the block. + 'render callback' => 'gdpr_tasks_create_request_on_behalf_of_user_render', + + // Icon goes in the directory with the content type. + 'description' => t('Show a form to request GDPR tasks.'), + 'required context' => new ctools_context_required(t('User'), 'entity:user'), + 'edit form' => 'gdpr_tasks_create_request_on_behalf_of_user_edit_form', + 'admin title' => 'gdpr_tasks_create_request_on_behalf_of_user_admin_title', + + // presents a block which is used in the preview of the data. + // Pn Panels this is the preview pane shown on the panels building page. + 'category' => array(t('GDPR'), 0), +); + +/** + * Render the User GDPR request form + * + * @param $subtype + * @param $conf + * Configuration as done at admin time + * @param $args + * @param $context + * Context - in this case we don't have any + * + * @return + * An object with at least title and content members + */ +function gdpr_tasks_create_request_on_behalf_of_user_render($subtype, $conf, $args, $context) { + $block = new stdClass(); + $block->title = t('Request task on user behalf'); + $block->content = ''; + + $user = $form_state['#user'] = $context->data; + + if (!empty($user)) { + // Get hold of the form + $form = drupal_get_form('gdpr_tasks_create_request_on_behalf_of_user_form', $user, $conf); + $block->content = drupal_render($form); + } + + return $block; +} + +/** + * Form + */ +function gdpr_tasks_create_request_on_behalf_of_user_form($form, &$form_state, $user, $conf = NULL) { +// form_load_include($form_state, 'inc', 'party', 'party.pages'); + form_load_include($form_state, 'inc', 'gdpr_tasks', 'plugins/content_types/gdpr_tasks_create_request_on_behalf_of_user'); + + $form_state['#user'] = $user; + $form['notes'] = array( + '#type' => 'textarea', + '#title' => t('Notes'), + '#description' => t('Enter the reason for creating this request.'), + ); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['export'] = array( + '#type' => 'submit', + '#submit' => array('gdpr_tasks_create_request_on_behalf_of_user_form_export_submit'), + '#value' => t('Request data export'), + '#weight' => 40, + ); + + $form['actions']['removal'] = array( + '#type' => 'submit', + '#submit' => array('gdpr_tasks_create_request_on_behalf_of_user_form_removal_submit'), + '#value' => t('Request data removal'), + '#weight' => 40, + ); + + return $form; +} + +/** + * Submit Handler + */ +function gdpr_tasks_create_request_on_behalf_of_user_form_removal_submit(&$form, &$form_state) { + // Make sure we stop it redirecting anywhere it shouldn't... + unset($form_state['redirect']); +// $conf = $form_state['build_info']['args'][1]; + $options = array('query' => drupal_get_destination()); + $path = 'user/' . $form_state['#user']->uid . '/gdpr/requests/gdpr_remove/add'; + drupal_goto($path, $options); +} + +/** + * Submit Handler + */ +function gdpr_tasks_create_request_on_behalf_of_user_form_export_submit(&$form, &$form_state) { + // Make sure we stop it redirecting anywhere it shouldn't... + unset($form_state['redirect']); +// $conf = $form_state['build_info']['args'][1]; + $options = array('query' => drupal_get_destination()); + $path = 'user/' . $form_state['#user']->uid . '/gdpr/requests/gdpr_sar/add'; + drupal_goto($path, $options); +} + +/** + * Config Form + */ +function gdpr_tasks_create_request_on_behalf_of_user_edit_form($form, &$form_state) { + return $form; +} + +function gdpr_tasks_create_request_on_behalf_of_user_edit_form_submit(&$form, &$form_state) { + foreach (element_children($form) as $key) { + if (!empty($form_state['values'][$key])) { + $form_state['conf'][$key] = $form_state['values'][$key]; + } + } +} + +/** + * Title Callback + */ +function gdpr_tasks_create_request_on_behalf_of_user_admin_title($subtype, $conf, $context = NULL) { + if ($conf['override_title'] && !empty($conf['override_title_text'])) { + $output = format_string('"@context" !title', array( + '@context' => $context->identifier, + '!title' => filter_xss_admin($conf['override_title_text']), + )); + } + else { + $output = t('"@context" Request task on user behalf', array( + '@context' => $context->identifier, + )); + } + return $output; +} diff --git a/modules/gdpr_tasks/views/handlers/gdpr_tasks_handler_operations_field.inc b/modules/gdpr_tasks/views/handlers/gdpr_tasks_handler_operations_field.inc new file mode 100644 index 0000000..4b6668c --- /dev/null +++ b/modules/gdpr_tasks/views/handlers/gdpr_tasks_handler_operations_field.inc @@ -0,0 +1,30 @@ +additional_fields['id'] = 'id'; + } + + function query() { + $this->ensure_my_table(); + $this->add_additional_fields(); + } + + function render($values) { + + $links = menu_contextual_links('model', 'admin/structure/gdpr-tasks', array($this->get_value($values, 'id'))); + + if (!empty($links)) { + return theme('links', array('links' => $links, 'attributes' => array('class' => array('links', 'inline', 'operations')))); + } + } +} From 209ecd20552144b410fde09ed948e8b548fcb87f Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 20 Apr 2018 11:02:27 +0000 Subject: [PATCH 11/21] By yanniboi: Fix field list with fieldsets. --- .../export_ui/gdpr_fields_ui.class.php | 94 ++++++++++++++----- 1 file changed, 70 insertions(+), 24 deletions(-) diff --git a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php index 76d7f20..e3d0fe3 100644 --- a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php +++ b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php @@ -28,47 +28,70 @@ function hook_menu(&$items) { * @param GDPRFieldData $item */ public function list_build_row($item, &$form_state, $operations) { - parent::list_build_row($item, $form_state, $operations); - + // Set up sorting $name = $item->{$this->plugin['export']['key']}; - $row = $this->rows[$name]; - unset($this->rows[$name]); - - $ops = array_pop($row['data']); + $schema = ctools_export_get_schema($this->plugin['schema']); + + // Note: $item->{$schema['export']['export type string']} should have already been set up by export.inc so + // we can use it safely. + switch ($form_state['values']['order']) { + case 'disabled': + $this->sorts[$name] = empty($item->disabled) . $name; + break; + case 'title': + $this->sorts[$name] = $item->{$this->plugin['export']['admin_title']}; + break; + case 'name': + $this->sorts[$name] = $name; + break; + case 'storage': + $this->sorts[$name] = $item->{$schema['export']['export type string']} . $name; + break; + } - $title = array('data' => check_plain($item->getSetting('label')), 'class' => array('ctools-export-ui-title')); - array_unshift($row['data'], $title); + $row['data'] = array(); + $row['class'] = !empty($item->disabled) ? array('ctools-export-ui-disabled') : array('ctools-export-ui-enabled'); - unset($row['data'][1]); + // If we have an admin title, make it the first row. + $row['data'][] = array('data' => check_plain($item->getSetting('label')), 'class' => array('ctools-export-ui-title')); +// $row['data'][] = array('data' => check_plain($name), 'class' => array('ctools-export-ui-name')); + $row['data'][] = array('data' => check_plain($item->{$schema['export']['export type string']}), 'class' => array('ctools-export-ui-storage')); // $row['data'][] = array('data' => check_plain($item->entity_type), 'class' => array('ctools-export-ui-entity-type')); // $row['data'][] = array('data' => check_plain($item->entity_bundle), 'class' => array('ctools-export-ui-entity-bundle')); // $row['data'][] = array('data' => check_plain($item->field_name), 'class' => array('ctools-export-ui-field-name')); $row['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rta', 'none')), 'class' => array('ctools-export-ui-rta')); $row['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rtf', 'none')), 'class' => array('ctools-export-ui-rtf')); - $row['data'][] = $ops; -// $this->rows[$item->entity_type][$item->entity_bundle][$name] = $row; + $ops = theme('links__ctools_dropbutton', array('links' => $operations, 'attributes' => array('class' => array('links', 'inline')))); + + $row['data'][] = array('data' => $ops, 'class' => array('ctools-export-ui-operations')); + + // Add an automatic mouseover of the description if one exists. + if (!empty($this->plugin['export']['admin_description'])) { + $row['title'] = $item->{$this->plugin['export']['admin_description']}; + } + + $this->rows[$name] = $row; } /** * {@inheritdoc} */ public function list_table_header() { - $header = parent::list_table_header(); - $ops = array_pop($header); + $header = array(); + $header[] = array('data' => t('Label'), 'class' => array('ctools-export-ui-title')); - $title = array('data' => t('Label'), 'class' => array('ctools-export-ui-title')); - array_unshift($header, $title); - - unset($header[1]); +// $header[] = array('data' => t('Name'), 'class' => array('ctools-export-ui-name')); + $header[] = array('data' => t('Storage'), 'class' => array('ctools-export-ui-storage')); // $header[] = array('data' => t('Entity type'), 'class' => array('ctools-export-ui-entity-type')); // $header[] = array('data' => t('Entity bundle'), 'class' => array('ctools-export-ui-entity-bundle')); // $header[] = array('data' => t('Field name'), 'class' => array('ctools-export-ui-field-name')); $header[] = array('data' => t('Right to access'), 'class' => array('ctools-export-ui-rta')); $header[] = array('data' => t('Right to be forgotten'), 'class' => array('ctools-export-ui-rtf')); - $header[] = $ops; + + $header[] = array('data' => t('Operations'), 'class' => array('ctools-export-ui-operations')); return $header; } @@ -77,10 +100,16 @@ public function list_table_header() { * {@inheritdoc} */ public function list_render(&$form_state) { - $tables = ''; + $tables = array(); + $table_data = ''; + drupal_add_library('system', 'drupal.collapse'); + + foreach ($this->rows as $name => $row) { + list($entity_type, $entity_bundle, $field_name) = explode('|', $name); + $tables[$entity_type][$entity_bundle][$name] = $row; + } - foreach ($this->rows as $entity_type => $entities) { - dpm($entity_type); + foreach ($tables as $entity_type => $entities) { foreach ($entities as $bundle => $rows) { $table = array( 'header' => $this->list_table_header(), @@ -88,13 +117,30 @@ public function list_render(&$form_state) { 'attributes' => array('id' => 'ctools-export-ui-list-items'), 'empty' => $this->plugin['strings']['message']['no items'], ); - $tables .= theme('table', $table); + + $fieldset_vars = array( + 'element' => array( + '#title' => t('@entity: @bundle', array( + '@entity' => $entity_type, + '@bundle' => $bundle, + )), + '#value' => theme('table', $table), + '#children' => '
', + '#attributes' => array ( + 'class' => array( + 'collapsible', +// 'collapsed', + ), + ), + ), + ); + + $table_data .= theme('fieldset', $fieldset_vars); } } - return ''; - return $tables; + return $table_data; } } \ No newline at end of file From c4fb1227d9bf7951cf178255fc45292b3c726e6a Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 20 Apr 2018 12:59:59 +0000 Subject: [PATCH 12/21] By zserno: Added consent module. --- modules/gdpr_consent/gdpr_consent.info | 5 + modules/gdpr_consent/gdpr_consent.install | 163 ++++++++ modules/gdpr_consent/gdpr_consent.module | 370 ++++++++++++++++++ .../includes/gdpr_consent.admin.inc | 59 +++ .../includes/gdpr_consent.agreements.inc | 23 ++ 5 files changed, 620 insertions(+) create mode 100644 modules/gdpr_consent/gdpr_consent.info create mode 100644 modules/gdpr_consent/gdpr_consent.install create mode 100644 modules/gdpr_consent/gdpr_consent.module create mode 100644 modules/gdpr_consent/includes/gdpr_consent.admin.inc create mode 100644 modules/gdpr_consent/includes/gdpr_consent.agreements.inc diff --git a/modules/gdpr_consent/gdpr_consent.info b/modules/gdpr_consent/gdpr_consent.info new file mode 100644 index 0000000..6f6edcb --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.info @@ -0,0 +1,5 @@ +name = General Data Protection Regulation (GDPR) - Consent Tracking +description = Allow tracking of GDPR Consent +core = 7.x +dependencies[] = entity +dependencies[] = gdpr diff --git a/modules/gdpr_consent/gdpr_consent.install b/modules/gdpr_consent/gdpr_consent.install new file mode 100644 index 0000000..3b08f96 --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.install @@ -0,0 +1,163 @@ + 'Base table for GDPR Consent Agreement entity.', + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Primary key of the GDPR Consent Agreement entity.', + ), + 'revision_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => 'The ID of consent agreement\'s default revision.', + ), + 'title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Title of the consent agreement.', + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The Unix timestamp of the entity creation time.', + ), + 'changed' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The Unix timestamp the entity was last edited.', + ), + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => "The {users}.uid of the associated user.", + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + 'description' => 'Entity status.', + ), + ), + 'foreign keys' => array( + 'uid' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid') + ), + ), + 'primary key' => array('id'), + ); + + $schema['gdpr_consent_agreement_revision'] = array( + 'description' => 'GDPR Consent Agreement entity revisions.', + 'fields' => array( + 'id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => 'The ID of the attached entity.', + ), + 'revision_id' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Primary Key: Unique revision ID.', + ), + 'description' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'A description of the consent agreement.', + ), + 'long_description' => array( + 'type' => 'text', + 'size' => 'medium', + 'not null' => TRUE, + 'description' => 'A long description of the consent agreement.', + ), + 'agreement_type' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + 'description' => 'Consent agreement\'s type: implicit or explicit.', + ), + ), + 'primary key' => array('revision_id'), + ); + + return $schema; +} + +/** + * Implements hook_field_schema(). + */ +function gdpr_consent_field_schema($field) { + $columns = array( + 'target_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'target_revision_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'agreed' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'date' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'user_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + ), + 'user_id_accepted' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + ), + 'notes' => array( + 'type' => 'text', + 'size' => 'medium', + 'not null' => FALSE, + ), + ); + $indexes = array( + 'target_id' => array('target_id'), + 'target_revision_id' => array('target_revision_id'), + ); + return array( + 'columns' => $columns, + 'indexes' => $indexes, + ); +} diff --git a/modules/gdpr_consent/gdpr_consent.module b/modules/gdpr_consent/gdpr_consent.module new file mode 100644 index 0000000..398915c --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.module @@ -0,0 +1,370 @@ + 'Agreements', + 'description' => 'List Agreement Entities', + 'access callback' => TRUE, + 'page callback' => 'gdpr_consent_collected_agreements', + 'page arguments' => array(1), + 'menu_name' => 'navigation', + 'file' => 'includes/gdpr_consent.agreements.inc', + ); + + return $items; +} + +/** + * Implements hook_permission(). + */ +function gdpr_consent_permission() { + $permissions = array( + 'manage gdpr agreements' => array( + 'title' => t('Manage GDPR Agreements'), + ), + 'grant any consent' => array( + 'title' => t('Grant Any Consent'), + ), + 'grant own consent' => array( + 'title' => t('Grant Own Consent'), + ), + ); + + return $permissions; +} + +/** + * Implements hook_entity_info(). + */ +function gdpr_consent_entity_info() { + $info = array(); + $info['gdpr_consent_agreement'] = array( + 'label' => t('GDPR Consent Agreement'), + 'base table' => 'gdpr_consent_agreement', + 'revision table' => 'gdpr_consent_agreement_revision', + 'entity class' => 'Entity', + 'controller class' => 'ConsentAgreementController', + 'fieldable' => TRUE, + 'bundles' => array( + 'gdpr_consent_agreement' => array( + 'label' => t('GDPR Consent Agreement'), + 'admin' => array( + 'path' => 'admin/gdpr/agreements', + 'access arguments' => array('administer site configuration'), + ), + ), + ), + 'entity keys' => array( + 'id' => 'id', + 'label' => 'title', + 'revision' => 'revision_id', + ), + // Use the default label() and uri() functions. + 'label callback' => 'entity_class_label', + 'uri callback' => 'entity_class_uri', + 'access callback' => 'gdpr_consent_access_callback', + 'admin ui' => array( + 'path' => 'admin/gdpr/agreements', + 'controller class' => 'ConsentAgreementEntityUIController', + 'menu wildcard' => '%gdpr_consent_agreement', + 'file' => 'includes/gdpr_consent.admin.inc', + ), + 'module' => 'gdpr_consent', + ); + + return $info; +} + +/** + * Implements hook_entity_property_info(). + */ +function gdpr_consent_entity_property_info() { + $info = array(); + $properties = &$info['gdpr_consent_agreement']['properties']; + + $properties['id'] = array( + 'label' => t('GDPR Consent Agreement ID'), + 'description' => t('The uniquie ID of the consent agreement entity.'), + 'type' => 'integer', + 'schema field' => 'id', + ); + + $properties['title'] = array( + 'label' => t('Title'), + 'description' => t('Title of the agreement'), + 'type' => 'text', + 'schema field' => 'title', + ); + + $properties['agreement_type'] = array( + 'label' => t('Agreement Type'), + 'description' => t('Whether consent is implicit or explicit. Set to "Explicit" if the user needs to explicitly agree, otherwise "Implicit'), + 'type' => 'boolean', + 'schema field' => 'agreement_type', + ); + + $properties['description'] = array( + 'label' => t('Description'), + 'description' => t('Text displayed to the user on the form'), + 'type' => 'text', + 'schema field' => 'description', + ); + + $properties['long_description'] = array( + 'label' => t('Long Description'), + 'description' => t('Text shown when the user clicks for more details'), + 'type' => 'text', + 'schema field' => 'long_description', + ); + + $properties['created'] = array( + 'label' => t('Created date'), + 'description' => t('Date the consent agreement was created'), + 'type' => 'date', + 'schema field' => 'created', + ); + + $properties['changed'] = array( + 'label' => t('Updated date'), + 'description' => t('Date the consent agreement was last edited'), + 'type' => 'date', + 'schema field' => 'changed', + ); + + $properties['uid'] = array( + 'label' => t('Authored by'), + 'description' => t('The user ID of author of the Consent Agreement entity'), + 'type' => 'user', + 'schema field' => 'uid', + ); + + $properties['status'] = array( + 'label' => t('Publishing status'), + 'description' => t('A boolean indicating whether the Consent Agreement is published.'), + 'type' => 'boolean', + 'schema field' => 'status', + ); + + return $info; +} + +function gdpr_consent_access_callback($op, $entity = NULL, $account = NULL) { + return user_access('manage gdpr agreements'); +} + +function gdpr_consent_agreement_load($id) { + return entity_load_single('gdpr_consent_agreement', $id); +} + +/** + * Implements hook_field_info(). + * + * Provides a user consent field type. + */ +function gdpr_consent_field_info() { + return array( + 'gdpr_user_consent' => array( + 'label' => t('GDPR Consent'), + 'description' => t('Stores user consent for a particular agreement'), + 'default_widget' => 'gdpr_consent_widget', + 'default_formatter' => 'gdpr_consent_formatter', + ), + ); +} + +/** + * Implements hook_field_validate(). + */ +function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { + $valid = TRUE; + + foreach ($items as $delta => $item) { + if (!empty($item['target_id'])) { + if (!$valid) { + $errors[$field['field_name']][$langcode][$delta][] = array( + 'error' => 'gdpr_user_consent_invalid', + 'message' => t('Referenced consent agreement entity is invalid.'), + ); + } + } + } +} + + +/** + * Implements hook_field_is_empty(). + */ +function gdpr_consent_field_is_empty($item, $field) { + return empty($item['target_id']); +} + +/** + * Implements hook_field_formatter_info(). + */ +function gdpr_consent_field_formatter_info() { + return array( + 'gdpr_consent' => array( + 'label' => t('GDPR user consent formatter'), + 'field types' => array('gdpr_user_consent'), + ), + ); +} + +/** + * Implements hook_field_settings_form(). + */ +function gdpr_consent_field_settings_form($field, $instance, $has_data) { + $settings = $field['settings']; + + $form['target_id'] = array( + '#type' => 'textfield', + '#title' => t('User consent agreement entity id'), + '#default_value' => (isset($settings['target_id'])) ? $settings['target_id'] : '', + '#required' => FALSE, + '#element_validate' => array( + 'element_validate_integer_positive', + ), + '#description' => t('The GDPR User Consent Agreement to display'), + ); + + return $form; +} + +/** + * Implements hook_field_formatter_view(). + */ +function gdpr_consent_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { + $element = array(); + + switch ($display['type']) { + case 'gdpr_consent': + foreach ($items as $delta => $item) { + $element[$delta] = array( + '#type' => 'html_tag', + '#tag' => 'p', + '#value' => t('User Consent ID: @entity', array('@entity' => $item['target_id'])), + ); + } + break; + } + + return $element; +} + +/** + * Implements hook_field_widget_info(). + * + * Field widget to show consent information. + */ +function gdpr_consent_field_widget_info() { + return array( + 'gdpr_consent_widget' => array( + 'label' => t('GDPR Consent'), + 'field types' => array('gdpr_user_consent'), + ), + ); +} + +/** + * Implements hook_field_widget_form(). + */ +function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { + global $user; + + $agreed = isset($items[$delta]['agreed']) ? $items[$delta]['agreed'] : ''; + $notes = isset($items[$delta]['notes']) ? $items[$delta]['notes'] : ''; + + $widget = $element; + $widget['#delta'] = $delta; + + // Get current revision of the referenced agreement entity. + $entity_type = 'gdpr_consent_agreement'; + $entity = entity_load_single('gdpr_consent_agreement', $field['settings']['target_id']); + + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + + switch ($instance['widget']['type']) { + + case 'gdpr_consent_widget': + $element['agreed'] = array( + '#type' => 'checkbox', + '#title' => $entity->title, + '#default_value' => $agreed, + '#weight' => 0, + '#description' => $entity->long_description, + ) + $widget; + $element['notes'] = array( + '#type' => 'textfield', + '#title' => 'GDPR Consent Notes', + '#default_value' => $notes, + '#weight' => 10, + '#description' => '', + ) + $widget; + $element['target_id'] = array( + '#type' => 'hidden', + '#value' => $field['settings']['target_id'], + ) + $widget; + $element['target_revision_id'] = array( + '#type' => 'hidden', + '#value' => $vid, + ) + $widget; + $element['date'] = array( + '#type' => 'hidden', + '#value' => REQUEST_TIME, + ) + $widget; + $element['user_id_accepted'] = array( + '#type' => 'hidden', + '#value' => $user->uid, + ) + $widget; + + break; + } + + return $element; +} + +/** + * Custom controller for the gdpr_consent_agreement entity type. + */ +class ConsentAgreementController extends EntityAPIController { + + public function save($entity, DatabaseTransaction $transaction = NULL) { + if (isset($entity->is_new)) { + global $user; + + $entity->created = REQUEST_TIME; + $entity->uid = $user->uid; + } + + $entity->changed = REQUEST_TIME; + + // Always save new revisions. + $entity->is_new_revision = TRUE; + + return parent::save($entity, $transaction); + } +} + +class ConsentAgreementEntityUIController extends EntityDefaultUIController { + +} diff --git a/modules/gdpr_consent/includes/gdpr_consent.admin.inc b/modules/gdpr_consent/includes/gdpr_consent.admin.inc new file mode 100644 index 0000000..cfb539f --- /dev/null +++ b/modules/gdpr_consent/includes/gdpr_consent.admin.inc @@ -0,0 +1,59 @@ + t('Title'), + '#type' => 'textfield', + '#default_value' => isset($entity->title) ? $entity->title : '', + '#description' => t('Agreement Title'), + '#required' => TRUE, + '#weight' => -50, + ); + $form['agreement_type'] = array( + '#title' => t('Agreement Type'), + '#type' => 'select', + '#options' => array( + 0 => t('Implicit'), + 1 => t('Explicit'), + ), + '#default_value' => isset($entity->agreement_type) ? $entity->agreement_type : '', + '#description' => t('Whether consent is implicit or explicit. Set to "Explicit" if the user needs to explicitly agree, otherwise "Implicit'), + ); + $form['description'] = array( + '#title' => t('Description'), + '#type' => 'textfield', + '#default_value' => isset($entity->description) ? $entity->description : '', + '#description' => t('Text displayed to the user on the form'), + ); + $form['long_description'] = array( + '#title' => t('Long Description'), + '#type' => 'textarea', + '#default_value' => isset($entity->long_description) ? $entity->long_description : '', + '#description' => t('Text shown when the user clicks for more details'), + ); + + field_attach_form('gdpr_consent_agreement', $entity, $form, $form_state); + + $form['actions'] = array( + '#type' => 'actions', + 'submit' => array( + '#type' => 'submit', + '#value' => isset($entity->id) ? t('Update consent agreement') : t('Save consent agreement'), + ), + ); + + return $form; +} + +/** + * Submit handler for consent agreement entity form. + */ +function gdpr_consent_agreement_form_submit($form, &$form_state) { + $entity = entity_ui_form_submit_build_entity($form, $form_state); + $entity->save(); + drupal_set_message(t('@title agreement has been saved.', array('@title' => $entity->title))); + $form_state['redirect'] = 'admin/consent'; +} diff --git a/modules/gdpr_consent/includes/gdpr_consent.agreements.inc b/modules/gdpr_consent/includes/gdpr_consent.agreements.inc new file mode 100644 index 0000000..c712fb8 --- /dev/null +++ b/modules/gdpr_consent/includes/gdpr_consent.agreements.inc @@ -0,0 +1,23 @@ +entityCondition('entity_type', 'gdpr_consent_agreement'); + //->propertyCondition('uid', $account->uid); + + $result = $query->execute(); + + if (isset($result['gdpr_consent_agreement'])) { + $consent_ids = array_keys($result['gdpr_consent_agreement']); + $consent_items = entity_load('gdpr_consent_agreement', $consent_ids); + + } + + kpr($consent_items); + + return $account->name; +} From c14b12141ff612142ab3a0ff8347d662d71c8f4c Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 23 Apr 2018 07:25:14 +0000 Subject: [PATCH 13/21] By yanniboi: Updated consent UI controller. --- modules/gdpr_consent/gdpr_consent.module | 39 ++++++++++++++++--- .../includes/gdpr_consent.admin.inc | 2 +- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/modules/gdpr_consent/gdpr_consent.module b/modules/gdpr_consent/gdpr_consent.module index 398915c..466147d 100644 --- a/modules/gdpr_consent/gdpr_consent.module +++ b/modules/gdpr_consent/gdpr_consent.module @@ -80,8 +80,6 @@ function gdpr_consent_entity_info() { 'revision' => 'revision_id', ), // Use the default label() and uri() functions. - 'label callback' => 'entity_class_label', - 'uri callback' => 'entity_class_uri', 'access callback' => 'gdpr_consent_access_callback', 'admin ui' => array( 'path' => 'admin/gdpr/agreements', @@ -210,7 +208,6 @@ function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $ } } - /** * Implements hook_field_is_empty(). */ @@ -236,11 +233,18 @@ function gdpr_consent_field_formatter_info() { function gdpr_consent_field_settings_form($field, $instance, $has_data) { $settings = $field['settings']; + $agreements = array(); + + foreach (entity_load('gdpr_consent_agreement') as $agreement) { + $agreements[$agreement->id] = $agreement->title; + } + $form['target_id'] = array( - '#type' => 'textfield', - '#title' => t('User consent agreement entity id'), + '#type' => 'select', + '#title' => t('User consent agreement'), '#default_value' => (isset($settings['target_id'])) ? $settings['target_id'] : '', '#required' => FALSE, + '#options' => $agreements, '#element_validate' => array( 'element_validate_integer_positive', ), @@ -315,10 +319,11 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, ) + $widget; $element['notes'] = array( '#type' => 'textfield', - '#title' => 'GDPR Consent Notes', + '#title' => 'Consent Notes', '#default_value' => $notes, '#weight' => 10, '#description' => '', + '#access' => user_access('grant any consent', $user), ) + $widget; $element['target_id'] = array( '#type' => 'hidden', @@ -367,4 +372,26 @@ class ConsentAgreementController extends EntityAPIController { class ConsentAgreementEntityUIController extends EntityDefaultUIController { + /** + * {@inheritdoc} + */ + protected function overviewTableHeaders($conditions, $rows, $additional_header = array()) { + $additional_header = array( + t('Type'), + ); + return parent::overviewTableHeaders($conditions, $rows, $additional_header); + } + + /** + * {@inheritdoc} + */ + protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { + $additional_cols = array( + $entity->agreement_type ? 'Explicit' : 'Implicit', + ); + + $row = parent::overviewTableRow($conditions, $id, $entity, $additional_cols); + return $row; + } + } diff --git a/modules/gdpr_consent/includes/gdpr_consent.admin.inc b/modules/gdpr_consent/includes/gdpr_consent.admin.inc index cfb539f..6460a2f 100644 --- a/modules/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/modules/gdpr_consent/includes/gdpr_consent.admin.inc @@ -55,5 +55,5 @@ function gdpr_consent_agreement_form_submit($form, &$form_state) { $entity = entity_ui_form_submit_build_entity($form, $form_state); $entity->save(); drupal_set_message(t('@title agreement has been saved.', array('@title' => $entity->title))); - $form_state['redirect'] = 'admin/consent'; + $form_state['redirect'] = 'admin/gdpr/agreements'; } From 3db79f61d5642161d7a786179f8f5bc7d35facb1 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 23 Apr 2018 11:10:25 +0000 Subject: [PATCH 14/21] By yanniboi: Updated fields list page with better entity filter. --- .../export_ui/gdpr_fields_ui.class.php | 124 ++++++++++++++---- .../plugins/export_ui/gdpr_fields_ui.inc | 55 ++++++-- 2 files changed, 144 insertions(+), 35 deletions(-) diff --git a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php index e3d0fe3..aaca2a0 100644 --- a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php +++ b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php @@ -60,8 +60,22 @@ public function list_build_row($item, &$form_state, $operations) { // $row['data'][] = array('data' => check_plain($item->entity_type), 'class' => array('ctools-export-ui-entity-type')); // $row['data'][] = array('data' => check_plain($item->entity_bundle), 'class' => array('ctools-export-ui-entity-bundle')); // $row['data'][] = array('data' => check_plain($item->field_name), 'class' => array('ctools-export-ui-field-name')); - $row['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rta', 'none')), 'class' => array('ctools-export-ui-rta')); - $row['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rtf', 'none')), 'class' => array('ctools-export-ui-rtf')); + + $rta_labels = array( + '' => 'Not configured', + 'inc' => 'Included', + 'maybe' => 'Maybe included', + 'no' => 'Not included', + ); + $rtf_labels = array( + '' => 'Not configured', + 'anonymise' => 'Anonymise', + 'remove' => 'Remove', + 'maybe' => 'Maybe included', + 'no' => 'Not included', + ); + $row['data'][] = array('data' => $rta_labels[$item->getSetting('gdpr_fields_rta', '')], 'class' => array('ctools-export-ui-rta')); + $row['data'][] = array('data' => $rtf_labels[$item->getSetting('gdpr_fields_rtf', '')], 'class' => array('ctools-export-ui-rtf')); $ops = theme('links__ctools_dropbutton', array('links' => $operations, 'attributes' => array('class' => array('links', 'inline')))); @@ -102,7 +116,6 @@ public function list_table_header() { public function list_render(&$form_state) { $tables = array(); $table_data = ''; - drupal_add_library('system', 'drupal.collapse'); foreach ($this->rows as $name => $row) { list($entity_type, $entity_bundle, $field_name) = explode('|', $name); @@ -110,37 +123,102 @@ public function list_render(&$form_state) { } foreach ($tables as $entity_type => $entities) { - foreach ($entities as $bundle => $rows) { + $fieldset_entity = array( + 'element' => array( + '#title' => t('Entity: @entity', array( + '@entity' => $entity_type, + )), + '#value' => '', + '#children' => '
', + '#attributes' => array ( + 'class' => array( + 'collapsible', + ), + ), + ), + ); + + if (count($entities) === 1) { + $rows = reset($entities); $table = array( 'header' => $this->list_table_header(), 'rows' => $rows, - 'attributes' => array('id' => 'ctools-export-ui-list-items'), 'empty' => $this->plugin['strings']['message']['no items'], ); - - $fieldset_vars = array( - 'element' => array( - '#title' => t('@entity: @bundle', array( - '@entity' => $entity_type, - '@bundle' => $bundle, - )), - '#value' => theme('table', $table), - '#children' => '
', - '#attributes' => array ( - 'class' => array( - 'collapsible', -// 'collapsed', + $fieldset_entity['element']['#value'] = theme('table', $table); + } + else { + foreach ($entities as $bundle => $rows) { + $table = array( + 'header' => $this->list_table_header(), + 'rows' => $rows, + 'empty' => $this->plugin['strings']['message']['no items'], + ); + + $fieldset_bundle = array( + 'element' => array( + '#title' => t('Bundle: @bundle', array( + '@bundle' => $bundle, + )), + '#value' => theme('table', $table), + '#children' => '
', + '#attributes' => array ( + 'class' => array( + 'collapsible', +// 'collapsed', + ), ), ), - ), - ); - - $table_data .= theme('fieldset', $fieldset_vars); + ); + $fieldset_entity['element']['#value'] .= theme('fieldset', $fieldset_bundle); + } } + + $table_data .= theme('fieldset', $fieldset_entity); + } + + $content = array( + '#type' => 'container', + '#attributes' => array ( + 'id' => 'ctools-export-ui-list-items', + ), + 'data' => array('#markup' => !empty($table_data) ? $table_data : $this->plugin['strings']['message']['no items']), + ); + + return drupal_render($content); + } + + /** + * {@inheritdoc} + */ + public function list_form(&$form, &$form_state) { + parent::list_form($form, $form_state); + + $entities = array(); + foreach (entity_get_info() as $key => $entity_info) { + $entities[$key] = $entity_info['label']; } + $form['top row']['gdpr_entity'] = array( + '#type' => 'select', + '#title' => t('Entity'), + '#options' => $entities, + '#multiple' => TRUE, + '#default_value' => array(), + ); + + $form['#attached']['library'][] = array('system', 'drupal.collapse'); + } + + /** + * {@inheritdoc} + */ + public function list_filter($form_state, $item) { + if (!empty($form_state['values']['gdpr_entity']) && !in_array($item->entity_type, $form_state['values']['gdpr_entity'])) { + return TRUE; + } - return $table_data; + parent::list_filter($form_state, $item); } } \ No newline at end of file diff --git a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc index 5187fe6..e9f61ed 100644 --- a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc +++ b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc @@ -18,7 +18,7 @@ $plugin = array( 'title singular' => t('field'), 'title singular proper' => t('Field'), - 'title plural' => t('field'), + 'title plural' => t('fields'), 'title plural proper' => t('Fields'), 'form' => array( @@ -26,19 +26,11 @@ $plugin = array( 'submit' => 'gdpr_fields_field_data_export_ui_form_submit', ), 'handler' => 'gdpr_fields_ui', -// 'handler' => array( -// 'class' => 'gdpr_fields_ui', -// 'parent' => 'ctools_export_ui', -// ), 'strings' => array( 'confirmation' => array( 'revert' => array( - 'information' => t('This action will permanently remove any customizations made to this view.'), - 'success' => t('The view has been reverted.'), - ), - 'delete' => array( - 'information' => t('This action will permanently remove the view from your database.'), - 'success' => t('The view has been deleted.'), + 'information' => t('This action will permanently remove any customizations made to this field.'), + 'success' => t('The field has been reverted.'), ), ), ), @@ -98,6 +90,19 @@ function gdpr_fields_field_data_export_ui_form(&$form, &$form_state) { '#tree' => TRUE, ); + $form['settings']['gdpr_fields_enabled'] = array( + '#type' => 'select', + '#title' => t('GDPR Field'), + '#description' => t('Is this a GDPR field'), + '#options' => array( + '' => 'Not defined', + '1' => 'Yes', + '0' => 'No', + ), + '#default_value' => $field_data->getSetting('gdpr_fields_enabled', ''), + ); + + $form['settings']['gdpr_fields_rta'] = array( '#type' => 'select', '#title' => t('Right to access'), @@ -107,6 +112,11 @@ function gdpr_fields_field_data_export_ui_form(&$form, &$form_state) { 'no' => 'Not', ), '#default_value' => $field_data->getSetting('gdpr_fields_rta', 'no'), + '#states' => array( + 'visible' => array( + ':input[name="settings[gdpr_fields_enabled]"]' => array('value' => 1), + ), + ), ); $form['settings']['gdpr_fields_rtf'] = array( @@ -119,6 +129,11 @@ function gdpr_fields_field_data_export_ui_form(&$form, &$form_state) { 'no' => 'Not', ), '#default_value' => $field_data->getSetting('gdpr_fields_rtf', 'no'), + '#states' => array( + 'visible' => array( + ':input[name="settings[gdpr_fields_enabled]"]' => array('value' => 1), + ), + ), ); // @todo Filter by relevance. @@ -139,10 +154,15 @@ function gdpr_fields_field_data_export_ui_form(&$form, &$form_state) { ), ); - $form['settings']['notes'] = array( + $form['settings']['gdpr_fields_notes'] = array( '#type' => 'textarea', '#title' => t('Notes'), '#default_value' => $field_data->getSetting('gdpr_fields_notes', ''), + '#states' => array( + 'invisible' => array( + ':input[name="settings[gdpr_fields_enabled]"]' => array('value' => ''), + ), + ), ); } @@ -150,6 +170,17 @@ function gdpr_fields_field_data_export_ui_form(&$form, &$form_state) { * Define the submit function for the add/edit form. */ function gdpr_fields_field_data_export_ui_form_submit(&$form, &$form_state) { + if ($form_state['values']['settings']['gdpr_fields_enabled'] === '') { + // Clear all settings. + $form_state['values']['settings'] = array(); + } + elseif ($form_state['values']['settings']['gdpr_fields_enabled'] === '0') { + // Clear field settings, leave notes. + unset($form_state['values']['settings']['gdpr_fields_rta']); + unset($form_state['values']['settings']['gdpr_fields_rtf']); + unset($form_state['values']['settings']['gdpr_fields_sanitizer']); + } + if (isset($form_state['values']['field']['label'])) { $form_state['values']['settings']['label'] = $form_state['values']['field']['label']; } From 86437324e4948cb6cbaba2a1382e5ca09c19c7ba Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 30 Apr 2018 14:41:32 +0100 Subject: [PATCH 15/21] By yanniboi: Make consent agreements exportable. --- modules/gdpr_consent/gdpr_consent.install | 16 ++++++++ modules/gdpr_consent/gdpr_consent.module | 45 ++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/modules/gdpr_consent/gdpr_consent.install b/modules/gdpr_consent/gdpr_consent.install index 3b08f96..fe5b768 100644 --- a/modules/gdpr_consent/gdpr_consent.install +++ b/modules/gdpr_consent/gdpr_consent.install @@ -14,6 +14,12 @@ function gdpr_consent_schema() { 'not null' => TRUE, 'description' => 'Primary key of the GDPR Consent Agreement entity.', ), + 'name' => array( + 'description' => 'The machine-readable name of this consent agreement.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + ), 'revision_id' => array( 'type' => 'int', 'unsigned' => TRUE, @@ -54,6 +60,12 @@ function gdpr_consent_schema() { 'size' => 'tiny', 'description' => 'Entity status.', ), + 'module' => array( + 'description' => 'The name of the providing module if the entity has been defined in code.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), ), 'foreign keys' => array( 'uid' => array( @@ -62,6 +74,10 @@ function gdpr_consent_schema() { ), ), 'primary key' => array('id'), + 'unique key' => array('name'), + 'indexes' => array( + 'name' => array('name'), + ), ); $schema['gdpr_consent_agreement_revision'] = array( diff --git a/modules/gdpr_consent/gdpr_consent.module b/modules/gdpr_consent/gdpr_consent.module index 466147d..cc906f5 100644 --- a/modules/gdpr_consent/gdpr_consent.module +++ b/modules/gdpr_consent/gdpr_consent.module @@ -65,6 +65,7 @@ function gdpr_consent_entity_info() { 'entity class' => 'Entity', 'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, + 'exportable' => TRUE, 'bundles' => array( 'gdpr_consent_agreement' => array( 'label' => t('GDPR Consent Agreement'), @@ -76,6 +77,7 @@ function gdpr_consent_entity_info() { ), 'entity keys' => array( 'id' => 'id', + 'name' => 'name', 'label' => 'title', 'revision' => 'revision_id', ), @@ -170,10 +172,33 @@ function gdpr_consent_access_callback($op, $entity = NULL, $account = NULL) { return user_access('manage gdpr agreements'); } +/** + * Loads agreements. + */ function gdpr_consent_agreement_load($id) { return entity_load_single('gdpr_consent_agreement', $id); } +/** + * Loads multiple agreements. + */ +function gdpr_consent_agreement_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) { + if (empty($ids)) { + $ids = FALSE; + } + + return entity_load('gdpr_consent_agreement', $ids, $conditions, $reset); +} + +/** + * Gets an array of all agreements, keyed by the machine name. + */ +function gdpr_consent_agreement_load_multiple_by_name($name = NULL) { + $signups = entity_load_multiple_by_name('gdpr_consent_agreement', isset($name) ? array($name) : FALSE); + return isset($name) ? reset($signups) : $signups; +} + + /** * Implements hook_field_info(). * @@ -351,8 +376,11 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, /** * Custom controller for the gdpr_consent_agreement entity type. */ -class ConsentAgreementController extends EntityAPIController { +class ConsentAgreementController extends EntityAPIControllerExportable { + /** + * {@inheritdoc} + */ public function save($entity, DatabaseTransaction $transaction = NULL) { if (isset($entity->is_new)) { global $user; @@ -368,6 +396,21 @@ class ConsentAgreementController extends EntityAPIController { return parent::save($entity, $transaction); } + + /** + * {@inheritdoc} + */ + public function export($entity, $prefix = '') { + $vars = get_object_vars($entity); + unset($vars[$this->statusKey], $vars[$this->moduleKey], $vars['is_new']); + if ($this->nameKey != $this->idKey) { + unset($vars[$this->idKey]); + } + if ($this->revisionKey) { + unset($vars[$this->revisionKey]); + } + return entity_var_json_export($vars, $prefix); + } } class ConsentAgreementEntityUIController extends EntityDefaultUIController { From b9d1d7e3bc5cd6a3307bf21694e3a0850cdca037 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 30 Apr 2018 14:43:07 +0100 Subject: [PATCH 16/21] By yanniboi: Updated some sanitizers. --- .../GDPRSanitizerDate.class.php | 3 ++- .../GDPRSanitizerName.class.php | 2 +- .../GDPRSanitizerPartyArchived.class.php | 27 +++++++++++++++++++ .../GDPRSanitizerUsername.class.php | 2 +- .../gdpr_sanitizer_party_archived.inc | 12 +++++++++ 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php create mode 100644 modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_party_archived.inc diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php index 35d8e66..16e60a1 100644 --- a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php @@ -19,7 +19,8 @@ class GDPRSanitizerDate extends GDPRSanitizerDefault { * {@inheritdoc} */ public function sanitize($input, $field = NULL) { - return '1000-01-01'; + $date = new DateTime('1000-01-01'); + return $date->format('U'); } } diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php index fd6c664..baf87e5 100644 --- a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php @@ -35,7 +35,7 @@ public function sanitize($input, $field = NULL) { $random = new GDPRUtilRandom(); return array( - 'given' => $random->word(rand(self::MIN_LENGTH, self::MAX_LENGTH)), + 'given' => 'anon_' . $random->word(rand(self::MIN_LENGTH, self::MAX_LENGTH)), 'family' => $random->word(rand(self::MIN_LENGTH, self::MAX_LENGTH)), ); } diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php new file mode 100644 index 0000000..23d3a68 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php @@ -0,0 +1,27 @@ +name(self::NAME_LENGTH); + return 'anon_' . $random->name(self::NAME_LENGTH); } } diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_party_archived.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_party_archived.inc new file mode 100644 index 0000000..782941f --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_party_archived.inc @@ -0,0 +1,12 @@ + array( + 'class' => 'GDPRSanitizerPartyArchived', + ), +); From bab3c68094e07d14bd720ae85f6d7531287ea462 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 30 Apr 2018 14:46:17 +0100 Subject: [PATCH 17/21] By yanniboi: Remove non newwine specific modules for now. --- .../gdpr_export_log/gdpr_export_log.admin.inc | 169 ----- modules/gdpr_export_log/gdpr_export_log.info | 12 - .../gdpr_export_log/gdpr_export_log.install | 231 ------- .../gdpr_export_log/gdpr_export_log.module | 168 ----- .../gdpr_export_log/gdpr_export_log.views.inc | 56 -- .../src/Entity/GDPRExportLog.php | 143 ---- .../src/Entity/GDPRExportLogController.php | 53 -- .../src/Entity/GDPRExportLogInterface.php | 8 - .../src/Entity/GDPRExportLogUIController.php | 118 ---- ...ws_plugin_display_extender_data_export.inc | 98 --- .../default_fields/user.user.mail.field.php | 16 - .../default_fields/user.user.name.field.php | 16 - .../default_fields/user.user.roles.field.php | 16 - .../default_fields/user.user.status.field.php | 15 - modules/gdpr_fields/gdpr_fields.admin.inc | 7 - modules/gdpr_fields/gdpr_fields.info | 9 - modules/gdpr_fields/gdpr_fields.install | 64 -- modules/gdpr_fields/gdpr_fields.module | 280 -------- .../export_ui/gdpr_fields_ui.class.php | 224 ------- .../plugins/export_ui/gdpr_fields_ui.inc | 190 ------ .../gdpr_data/gdpr_entity_field.class.php | 18 - .../plugins/gdpr_data/gdpr_entity_field.inc | 41 -- .../gdpr_data/gdpr_entity_property.class.php | 18 - .../gdpr_data/gdpr_entity_property.inc | 73 --- .../relationships/entities_from_field.inc | 254 -------- .../relationships/entities_from_schema.inc | 158 ----- .../gdpr_fields/src/Plugins/GDPRFieldData.php | 143 ---- modules/gdpr_tasks/gdpr_tasks.admin.inc | 401 ------------ modules/gdpr_tasks/gdpr_tasks.info | 16 - modules/gdpr_tasks/gdpr_tasks.install | 311 --------- modules/gdpr_tasks/gdpr_tasks.module | 615 ------------------ modules/gdpr_tasks/gdpr_tasks.pages.inc | 50 -- modules/gdpr_tasks/gdpr_tasks.views.inc | 51 -- .../gdpr_tasks/gdpr_tasks.views_default.inc | 103 --- ...tasks_create_request_on_behalf_of_user.inc | 147 ----- modules/gdpr_tasks/src/Anonymizer.php | 313 --------- modules/gdpr_tasks/src/Entity/GDPRTask.php | 108 --- .../src/Entity/GDPRTaskController.php | 25 - .../src/Entity/GDPRTaskInterface.php | 18 - .../gdpr_tasks/src/Entity/GDPRTaskType.php | 30 - .../src/Entity/GDPRTaskUIController.php | 125 ---- .../gdpr_tasks/templates/gdpr_task.tpl.php | 48 -- .../gdpr_tasks_handler_operations_field.inc | 30 - 43 files changed, 4989 deletions(-) delete mode 100644 modules/gdpr_export_log/gdpr_export_log.admin.inc delete mode 100644 modules/gdpr_export_log/gdpr_export_log.info delete mode 100644 modules/gdpr_export_log/gdpr_export_log.install delete mode 100644 modules/gdpr_export_log/gdpr_export_log.module delete mode 100644 modules/gdpr_export_log/gdpr_export_log.views.inc delete mode 100644 modules/gdpr_export_log/src/Entity/GDPRExportLog.php delete mode 100644 modules/gdpr_export_log/src/Entity/GDPRExportLogController.php delete mode 100644 modules/gdpr_export_log/src/Entity/GDPRExportLogInterface.php delete mode 100644 modules/gdpr_export_log/src/Entity/GDPRExportLogUIController.php delete mode 100644 modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc delete mode 100644 modules/gdpr_fields/default_fields/user.user.mail.field.php delete mode 100644 modules/gdpr_fields/default_fields/user.user.name.field.php delete mode 100644 modules/gdpr_fields/default_fields/user.user.roles.field.php delete mode 100644 modules/gdpr_fields/default_fields/user.user.status.field.php delete mode 100644 modules/gdpr_fields/gdpr_fields.admin.inc delete mode 100644 modules/gdpr_fields/gdpr_fields.info delete mode 100644 modules/gdpr_fields/gdpr_fields.install delete mode 100644 modules/gdpr_fields/gdpr_fields.module delete mode 100644 modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php delete mode 100644 modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc delete mode 100644 modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.class.php delete mode 100644 modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.inc delete mode 100644 modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.class.php delete mode 100644 modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc delete mode 100644 modules/gdpr_fields/plugins/relationships/entities_from_field.inc delete mode 100644 modules/gdpr_fields/plugins/relationships/entities_from_schema.inc delete mode 100644 modules/gdpr_fields/src/Plugins/GDPRFieldData.php delete mode 100644 modules/gdpr_tasks/gdpr_tasks.admin.inc delete mode 100644 modules/gdpr_tasks/gdpr_tasks.info delete mode 100644 modules/gdpr_tasks/gdpr_tasks.install delete mode 100644 modules/gdpr_tasks/gdpr_tasks.module delete mode 100644 modules/gdpr_tasks/gdpr_tasks.pages.inc delete mode 100644 modules/gdpr_tasks/gdpr_tasks.views.inc delete mode 100644 modules/gdpr_tasks/gdpr_tasks.views_default.inc delete mode 100644 modules/gdpr_tasks/plugins/content_types/gdpr_tasks_create_request_on_behalf_of_user.inc delete mode 100644 modules/gdpr_tasks/src/Anonymizer.php delete mode 100644 modules/gdpr_tasks/src/Entity/GDPRTask.php delete mode 100644 modules/gdpr_tasks/src/Entity/GDPRTaskController.php delete mode 100644 modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php delete mode 100644 modules/gdpr_tasks/src/Entity/GDPRTaskType.php delete mode 100644 modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php delete mode 100644 modules/gdpr_tasks/templates/gdpr_task.tpl.php delete mode 100644 modules/gdpr_tasks/views/handlers/gdpr_tasks_handler_operations_field.inc diff --git a/modules/gdpr_export_log/gdpr_export_log.admin.inc b/modules/gdpr_export_log/gdpr_export_log.admin.inc deleted file mode 100644 index fa64b5c..0000000 --- a/modules/gdpr_export_log/gdpr_export_log.admin.inc +++ /dev/null @@ -1,169 +0,0 @@ -is_new)) { - $form['info'] = array('#markup' => t('Manually creating new log entries is not supported.')); - return $form; - } - - field_attach_form('gdpr_export_log', $log, $form, $form_state); - - $form['disclaimer'] = array( - '#type' => 'checkbox', - '#title' => t('Please only remove this export log if you have completely destroyed the export on the target computer and any copies of it.'), - '#weight' => 99, - ); - - $form['actions'] = array( - '#type' => 'actions', - '#weight' => 100, - ); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - '#submit' => array('gdpr_export_log_form_submit_save'), - '#weight' => 10, - ); - - $form['actions']['remove'] = array( - '#type' => 'submit', - '#value' => t('Remove export'), - '#validate' => array( - 'gdpr_export_log_form_validate', - 'gdpr_export_log_form_validate_remove', - ), - '#submit' => array( - 'gdpr_export_log_form_submit_remove', - 'gdpr_export_log_form_submit_save', - ), - '#weight' => 40, - ); - - if ($log->status == 'removed') { - $form['actions']['#access'] = FALSE; - } - - return $form; -} - -/** - * Validate handler for logs form. - */ -function gdpr_export_log_form_validate($form, &$form_state) { - $log = $form_state['export_log']; - field_attach_validate('gdpr_export_log', $log); -} - -/** - * Validate handler for logs form. - */ -function gdpr_export_log_form_validate_remove($form, &$form_state) { - $log = $form_state['export_log']; - field_attach_validate('gdpr_export_log', $log); -} - - -/** - * Submit handler for logs form. - */ -function gdpr_export_log_form_submit_save($form, &$form_state) { - /* @var GDPRExportLog $log */ - $log = $form_state['export_log']; - - // General form submission. - field_attach_submit('gdpr_export_log', $log, $form, $form_state); - - // Process and close the task. - $log->save(); - drupal_set_message(t('Log has been updated.')); -} - -/** - * Submit handler for logs form when removing . - */ -function gdpr_export_log_form_submit_remove($form, &$form_state) { -// global $user; - - /* @var GDPRExportLog $log */ - $log = $form_state['export_log']; - - // General form submission. - field_attach_submit('gdpr_export_log', $log, $form, $form_state); - - // Process and close the task. - drupal_set_message(t('Export has been marked as removed.')); - $log->status = 'removed'; - $log->removed = REQUEST_TIME; -} - - -function gdpr_export_log_export_approval($name, $display_id) { - $args = func_get_args(); - // Remove $name and $display_id from the arguments. - array_shift($args); - array_shift($args); - - // Load the view and render it. - if ($view = views_get_view($name)) { - $view->set_display($display_id); - return drupal_get_form('gdpr_export_log_export_approval_form', $view); - - } - - // Fallback; if we get here no view was found or handler was not valid. - return MENU_NOT_FOUND; -} - -function gdpr_export_log_export_approval_form($form, &$form_state, $view) { - global $user; - $values = array( - 'export' => $view->name, - 'exported_by' => $user->uid, - ); - $log = $form_state['export_log'] = entity_create('gdpr_export_log', $values); - - field_attach_form('gdpr_export_log', $log, $form, $form_state); - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Export'), - '#weight' => 10, - ); - - return $form; -} - -/** - * Submit handler for logs form. - */ -function gdpr_export_log_export_approval_form_submit($form, &$form_state) { - /* @var GDPRExportLog $log */ - $log = $form_state['export_log']; - - // General form submission. - field_attach_submit('gdpr_export_log', $log, $form, $form_state); - - // Process and close the task. - $log->save(); - - $options = array(); - if (isset($_GET['destination'])) { - $options['query'] = drupal_get_destination(); - unset($_GET['destination']); - } - $options['query']['gdpr_export_log'] = $log->identifier(); - - drupal_goto(current_path() . '/gdpr_approved', $options); -} \ No newline at end of file diff --git a/modules/gdpr_export_log/gdpr_export_log.info b/modules/gdpr_export_log/gdpr_export_log.info deleted file mode 100644 index 02c682a..0000000 --- a/modules/gdpr_export_log/gdpr_export_log.info +++ /dev/null @@ -1,12 +0,0 @@ -name = General Data Protection Regulation (GDPR) - Exports Log -description = Logs view exports and audit trails. -core = 7.x - -dependencies[] = gdpr -dependencies[] = entity - -files[] = src/Entity/GDPRExportLog.php -files[] = src/Entity/GDPRExportLogInterface.php -files[] = src/Entity/GDPRExportLogController.php -files[] = src/Entity/GDPRExportLogUIController.php -files[] = src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc \ No newline at end of file diff --git a/modules/gdpr_export_log/gdpr_export_log.install b/modules/gdpr_export_log/gdpr_export_log.install deleted file mode 100644 index 2702a8d..0000000 --- a/modules/gdpr_export_log/gdpr_export_log.install +++ /dev/null @@ -1,231 +0,0 @@ - 'The base table for export logs.', - 'fields' => array( - 'id' => array( - 'description' => 'The primary identifier for this export log.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE - ), - 'language' => array( - 'description' => 'The {languages}.language of this export log.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'export' => array( - 'description' => 'The {views_view}.name of this export log.', - 'type' => 'varchar', - 'length' => 128, - 'default' => '' - ), - 'exported_by' => array( - 'description' => 'The {users}.uid that exported the export.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'removed_by' => array( - 'description' => 'The {users}.uid that removed the export.', - 'type' => 'int', - ), - 'status' => array( - 'description' => 'The text status of this export log.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '' - ), - 'created' => array( - 'description' => 'The Unix timestamp when this export log was created.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'changed' => array( - 'description' => 'The Unix timestamp when this export log was most recently saved.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'removed' => array( - 'description' => 'The Unix timestamp when this export log was removed.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('id'), - 'foreign keys' => array( - 'log_exporter' => array( - 'table' => 'users', - 'columns' => array('exported_by' => 'uid'), - ), - 'log_remover' => array( - 'table' => 'users', - 'columns' => array('removed_by' => 'uid'), - ), - ), - ); - - $schema['gdpr_export_log_uid'] = array( - 'description' => 'Stores the users included in an export.', - 'fields' => array( - 'export_id' => array( - 'description' => 'The {gdpr_export_log}.id of the related export log.', - 'type' => 'int', - 'not null' => TRUE, - ), - 'uid' => array( - 'description' => 'The {users}.uid of the user included in the log.', - 'type' => 'int', - 'not null' => TRUE, - ), - 'forgotten_by' => array( - 'description' => 'The {users}.uid of the user who removed this user from log.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'forgotten' => array( - 'description' => 'Whether the user has been removed from the log.', - 'type' => 'int', - 'size' => 'tiny', - ), - ), - 'foreign keys' => array( - 'export' => array( - 'table' => 'gdpr_export_log', - 'columns' => array('export_id' => 'id'), - ), - 'user' => array( - 'table' => 'users', - 'columns' => array('uid' => 'uid'), - ), - 'user_remover' => array( - 'table' => 'users', - 'columns' => array('forgotten_by' => 'uid'), - ), - ), - ); - - return $schema; -} - -/** - * Implements hook_schema(). - */ -function gdpr_export_log_install() { - $t = get_t(); - - // Add an export location field. - if (!field_info_field('gdpr_export_log_destination')) { - $field = array( - 'field_name' => 'gdpr_export_log_destination', - 'type' => 'text', - 'locked' => TRUE, - 'cardinality' => 1, - ); - field_create_field($field); - } - if (!field_info_instance('gdpr_export_log', 'gdpr_export_log_destination', 'gdpr_export_log')) { - $instance = array( - 'label' => $t('Export storage destination'), - 'description' => $t('Fill in the location where the export will be saved.'), - 'field_name' => 'gdpr_export_log_destination', - 'entity_type' => 'gdpr_export_log', - 'bundle' => 'gdpr_export_log', - 'required' => TRUE, - 'widget' => array( - 'type' => 'text_textfield', - ), - 'display' => array( - 'default' => array( - 'type' => 'text_default', - ), - ), - ); - field_create_instance($instance); - } - - // Add an export reason field. - if (!field_info_field('gdpr_export_log_reason')) { - $field = array( - 'field_name' => 'gdpr_export_log_reason', - 'type' => 'text_long', - 'locked' => TRUE, - 'cardinality' => 1, - ); - field_create_field($field); - } - if (!field_info_instance('gdpr_export_log', 'gdpr_export_log_reason', 'gdpr_export_log')) { - $instance = array( - 'label' => $t('Reason for export'), - 'description' => $t('Fill in the reason why the export is dealt with.'), - 'field_name' => 'gdpr_export_log_reason', - 'entity_type' => 'gdpr_export_log', - 'bundle' => 'gdpr_export_log', - 'required' => TRUE, - 'widget' => array( - 'type' => 'text_textarea', - ), - 'display' => array( - 'default' => array( - 'type' => 'text_default', - ), - ), - ); - field_create_instance($instance); - } - - // Add an export lifetime field. - if (!field_info_field('gdpr_export_log_lifetime')) { - $field = array( - 'field_name' => 'gdpr_export_log_lifetime', - 'type' => 'number_integer', - 'locked' => TRUE, - 'cardinality' => 1, - ); - field_create_field($field); - } - if (!field_info_instance('gdpr_export_log', 'gdpr_export_log_lifetime', 'gdpr_export_log')) { - $instance = array( - 'label' => $t('Export lifetime'), - 'description' => $t('Fill in the length (in days) that the export should live for.'), - 'field_name' => 'gdpr_export_log_lifetime', - 'entity_type' => 'gdpr_export_log', - 'bundle' => 'gdpr_export_log', - 'required' => TRUE, - 'default_value' => array( - array( - 'value' => 90, - ), - ), - 'widget' => array( - 'type' => 'number', - ), - 'display' => array( - 'default' => array( - 'type' => 'number_integer', - ), - ), - 'settings' => array( - 'suffix' => 'days', - ), - ); - field_create_instance($instance); - } - -} diff --git a/modules/gdpr_export_log/gdpr_export_log.module b/modules/gdpr_export_log/gdpr_export_log.module deleted file mode 100644 index a1574c1..0000000 --- a/modules/gdpr_export_log/gdpr_export_log.module +++ /dev/null @@ -1,168 +0,0 @@ - t('Export Log'), - 'base table' => 'gdpr_export_log', - 'entity class' => 'GDPRExportLog', - 'controller class' => 'GDPRExportLogController', - 'module' => 'gdpr_export_log', - 'admin ui' => array( - 'path' => 'admin/gdpr/gdpr-export-log', - 'file' => 'gdpr_export_log.admin.inc', - 'menu wildcard' => '%gdpr_export_log', - 'controller class' => 'GDPRExportLogUIController', - ), - 'bundles' => array( - 'gdpr_export_log' => array( - 'label' => t('Export Log'), - 'admin' => array( - 'path' => 'admin/gdpr/gdpr-export-log', - 'access arguments' => array('administer export log entities'), - ), - ), - ), - 'access callback' => 'gdpr_export_log_access', - 'entity keys' => array( - 'id' => 'id', - 'label' => 'id', - 'language' => 'language', - ), - 'fieldable' => TRUE, - ); - - return $entities; -} - -/** - * Implements hook_permission(). - */ -function gdpr_export_log_permission() { - // @todo IMPORTANT!! Permission review. - return array( - 'administer export log entities' => array( - 'title' => t('Administer Export Log entities'), - 'restrict access' => TRUE, - ), - 'view gdpr export logs' => array( - 'title' => t('View GDPR export logs'), - ), - 'edit gdpr export logs' => array( - 'title' => t('Edit GDPR export logs'), - ), - ); -} - -/** - * Implements hook_views_api(). - */ -function gdpr_export_log_views_api($module, $api) { - return array( - 'api' => 3, - 'path' => drupal_get_path('module', 'gdpr_export_log'), - ); -} - -/** - * Implement hook_menu_alter(). - */ -function gdpr_export_log_menu_alter(&$items) { - $views = views_get_applicable_views('uses views_data_export'); - foreach ($views as $data) { - list($view, $display_id) = $data; - $view->set_display($display_id); - $include = FALSE; - - if (isset($view->display_handler)) { - $include = $view->display_handler->get_option('gdpr_export_log_audit'); - } - - if ($include) { - $result = $view->execute_hook_menu($display_id, $items); - if (is_array($result)) { - foreach (array_keys($result) as $path) { - if (isset($items[$path])) { - $items[$path . '/gdpr_approved'] = $items[$path]; - $items[$path]['page callback'] = 'gdpr_export_log_export_approval'; - $items[$path]['module'] = 'gdpr_export_log'; - $items[$path]['file'] = 'gdpr_export_log.admin.inc'; - } - } - } - } - } - - // Save memory: Destroy those views. - foreach ($views as $data) { - list($view, $display_id) = $data; - $view->destroy(); - } -} - -/** - * Implements hook_module_implements_alter(). - */ -function gdpr_export_log_module_implements_alter(&$implementations, $hook) { - if ($hook == 'menu_alter') { - $group = $implementations['gdpr_export_log']; - unset($implementations['gdpr_export_log']); - $implementations['gdpr_export_log'] = $group; - } -} - -/** - * Load a GDPR Export Log entity. - * - * @param $id - * The id of the log. - * - * @return GDPRExportLog|null - * The fully loaded log entity if available. - */ -function gdpr_export_log_load($id) { - return entity_load_single('gdpr_export_log', $id); -} - -/** - * Load logs from the database. - * - * @param $ids - * An array of log IDs. - * @param $conditions - * (deprecated) An associative array of conditions on the {gdpr_export_log} - * table, where the keys are the database fields and the values are the - * values those fields must have. Instead, it is preferable to use - * EntityFieldQuery to retrieve a list of entity IDs loadable by - * this function. - * @param $reset - * Whether to reset the internal static entity cache. - * - * @return GDPRExportLog[] - * An array of log objects, indexed by log ID. - * - * @see entity_load() - * @see EntityFieldQuery - */ -function gdpr_export_log_load_multiple($ids = FALSE, $conditions = array(), $reset = FALSE) { - return entity_load('gdpr_export_log', $ids, $conditions, $reset); -} - -/** - * Access callback for export log entities. - */ -function gdpr_export_log_access($op, $export_log = NULL, $account = NULL) { - // @todo Support other operations. - if (user_access('administer export log entities', $account)) { - return TRUE; - } -} diff --git a/modules/gdpr_export_log/gdpr_export_log.views.inc b/modules/gdpr_export_log/gdpr_export_log.views.inc deleted file mode 100644 index b900e2b..0000000 --- a/modules/gdpr_export_log/gdpr_export_log.views.inc +++ /dev/null @@ -1,56 +0,0 @@ -result as $data) { - // uid will always be set if user table is base table. - if (isset($data->uid)) { - $uids[$data->uid] = $data->uid; - } - } - - if (!empty($uids)) { - // Set log uids. - $log->setUsers($uids); - $log->save(); - } -} - -/** - * Implements hook_views_plugins(). - */ -function gdpr_export_log_views_plugins() { - return array( - 'display_extender' => array( - 'gdpr_views_data_export' => array( - 'title' => t(''), - 'help' => t(''), - 'path' => drupal_get_path('module', 'gdpr_export_log') . '/src/Plugin/views/display_extender', - 'handler' => 'gdpr_export_log_views_plugin_display_extender_data_export', - ), - ), - ); -} - -/** - * Implements hook_views_plugins_alter(). - */ -function gdpr_export_log_views_plugins_alter(&$plugins) { - if (isset($plugins['display']['views_data_export'])) { - $plugins['display']['views_data_export']['uses views_data_export'] = TRUE; - } -} \ No newline at end of file diff --git a/modules/gdpr_export_log/src/Entity/GDPRExportLog.php b/modules/gdpr_export_log/src/Entity/GDPRExportLog.php deleted file mode 100644 index 3bdc9ed..0000000 --- a/modules/gdpr_export_log/src/Entity/GDPRExportLog.php +++ /dev/null @@ -1,143 +0,0 @@ -uids)) { - $this->init(); - } - } - - public function init() { - $uids = db_select('gdpr_export_log_uid', 'u') - ->fields('u') - ->condition('export_id', $this->identifier()) - ->execute() - ->fetchAllAssoc('uid', PDO::FETCH_ASSOC); - - $this->setUsers($uids); - } - - /** - * {@inheritdoc} - */ - protected function defaultLabel() { - $view = views_get_view($this->export); - return "Log {$this->id} - {$view->get_human_name()}"; - } - - /** - * @return array - */ - protected function defaultUserData() { - return array( - 'export_id' => $this->identifier(), - ); - } - - /** - * @return array - */ - public function getUsers() { - return $this->uids; - } - - /** - * @param array $uids - * @return $this - */ - public function setUsers(array $uids) { - $user_data = array(); - - foreach ($uids as $uid => $data) { - if (is_array($data)) { - if (!isset($data['uid'])) { - $data['uid'] = $uid; - } - } - else { - $data = array('uid' => $uid); - } - $data += $this->defaultUserData(); - $user_data[$data['uid']] = $data; - } - - $this->uids = $user_data; - return $this; - } - -} diff --git a/modules/gdpr_export_log/src/Entity/GDPRExportLogController.php b/modules/gdpr_export_log/src/Entity/GDPRExportLogController.php deleted file mode 100644 index f0e5988..0000000 --- a/modules/gdpr_export_log/src/Entity/GDPRExportLogController.php +++ /dev/null @@ -1,53 +0,0 @@ - 'exported'); - $values += array('created' => REQUEST_TIME); - - $task = parent::create($values); - return $task; - } - -// public function load($ids = array(), $conditions = array()) { -// /* @var GDPRExportLog[] $entities */ -// $entities = parent::load($ids, $conditions); -// -// foreach ($entities as $entity) { -// $entity->init(); -// } -// -// return $entities; -// } - - - /** - * {@inheritdoc} - */ - public function save($entity, DatabaseTransaction $transaction = NULL) { - /* @var GDPRExportLog $entity */ - $entity->changed = REQUEST_TIME; - $return = parent::save($entity, $transaction); - - foreach ($entity->getUsers() as $uid_data) { - db_merge('gdpr_export_log_uid') - ->key(array( - 'export_id' => $entity->identifier(), - 'uid' => $uid_data['uid'], - )) - ->fields($uid_data) - ->execute(); - } - - return $return; - } - - -} \ No newline at end of file diff --git a/modules/gdpr_export_log/src/Entity/GDPRExportLogInterface.php b/modules/gdpr_export_log/src/Entity/GDPRExportLogInterface.php deleted file mode 100644 index 721c2a4..0000000 --- a/modules/gdpr_export_log/src/Entity/GDPRExportLogInterface.php +++ /dev/null @@ -1,8 +0,0 @@ -path . '/add']); - - return $items; - } - - /** - * Generates the render array for a overview tables for different statuses. - * - * @param $conditions - * An array of conditions as needed by entity_load(). - - * @return array - * A renderable array. - */ -// public function overviewTable($conditions = array()) { -// $query = new EntityFieldQuery(); -// $query->entityCondition('entity_type', $this->entityType); -// $query->propertyOrderBy('created'); -// -// // Add all conditions to query. -// foreach ($conditions as $key => $value) { -// $query->propertyCondition($key, $value); -// } -// -// if ($this->overviewPagerLimit) { -// $query->pager($this->overviewPagerLimit); -// } -// -// $results = $query->execute(); -// -// $ids = isset($results[$this->entityType]) ? array_keys($results[$this->entityType]) : array(); -// $entities = $ids ? entity_load($this->entityType, $ids) : array(); -// ksort($entities); -// -// // Always show at least requested and complete tables. -// $rows = array( -// 'requested' => array(), -// 'complete' => array(), -// ); -// foreach ($entities as $entity) { -// $rows[$entity->status][] = $this->overviewTableRow($conditions, entity_id($this->entityType, $entity), $entity); -// } -// -// $render = array(); -// foreach ($rows as $status => $status_rows) { -// $render[$status] = array( -// '#theme' => 'table', -// '#header' => $this->overviewTableHeaders($conditions, $status_rows), -// '#rows' => $status_rows, -// '#caption' => t('Tasks with status - @status', array('@status' => ucfirst($status))), -// '#empty' => t('No tasks.'), -// '#weight' => 3, -// ); -// -// // @todo Find a better way to order statuses. -// if ($status == 'requested') { -// $render[$status]['#weight'] = 0; -// } -// } -// -// return $render; -// } - - /** - * {@inheritdoc} - */ - protected function overviewTableHeaders($conditions, $rows, $additional_header = array()) { - $additional_header = array( - t('User count'), - t('Status'), - t('Exported by'), - t('Exported'), - t('Lifetime'), - ); - return parent::overviewTableHeaders($conditions, $rows, $additional_header); - } - - /** - * {@inheritdoc} - */ - protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { - /* @var GDPRExportLog $entity */ - - // $time_left = $entity->created + ($entity->getLifetime('U')) - $time_left = $entity->created + ($entity->wrapper()->gdpr_export_log_lifetime->value() * 60 * 60 * 24) - REQUEST_TIME; - $left_to_go = t('%time to go', array('%time' => format_interval($time_left, 2))); - - - $additional_cols = array( - count($entity->getUsers()), - $entity->status, - theme('username', array('account' => user_load($entity->exported_by))), - format_date($entity->created, 'short'), - $left_to_go, - ); - $row = parent::overviewTableRow($conditions, $id, $entity, $additional_cols); - // @todo Fix hardcoded links. -// $row[0] = l($entity->label(), $this->path . '/' . $id . '/view', array('query' => drupal_get_destination())); - $row[0] = $entity->label(); - - return $row; - } - -} diff --git a/modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc b/modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc deleted file mode 100644 index a2fe417..0000000 --- a/modules/gdpr_export_log/src/Plugin/views/display_extender/gdpr_export_log_views_plugin_display_extender_data_export.inc +++ /dev/null @@ -1,98 +0,0 @@ -display instanceof views_data_export_plugin_display_export) { - return FALSE; - } - - // Check that user table is a base table. - if ($this->view->display_handler) { - if (!in_array('users', array_keys($this->view->get_base_tables()))) { - return FALSE; - } - } - - return TRUE; - } - - /** - * {@inheritdoc} - */ - function option_definition() { - $options = array(); - - if ($this->applies()) { - $options['gdpr_export_log_audit'] = array('default' => FALSE); - } - - return $options; - } - - /** - * {@inheritdoc} - */ - function options_definition_alter(&$options) { - if ($this->applies()) { - $options['gdpr_export_log_audit'] = array('default' => FALSE); - } - } - - /** - * {@inheritdoc} - */ - function options_form(&$form, &$form_state) { - if ($this->applies()) { - if (substr($form['#section'], -21) == 'gdpr_export_log_audit') { - $form['gdpr_export_log_audit'] = array( - '#type' => 'checkbox', - '#title' => t('Audit exports for GDPR'), - '#description' => t('Include this export in a GDPR audit log.'), - '#default_value' => $this->display->get_option('gdpr_export_log_audit'), - ); - } - } - } - - /** - * {@inheritdoc} - */ - function options_submit(&$form, &$form_state) { - if ($this->applies()) { - if (isset($form['options']['gdpr_export_log_audit'])) { - $this->display->set_option('gdpr_export_log_audit', $form_state['values']['gdpr_export_log_audit']); - } - } - } - - /** - * {@inheritdoc} - */ - function options_summary(&$categories, &$options) { - if ($this->applies()) { - $value = $this->display->get_option('gdpr_export_log_audit'); - $options['gdpr_export_log_audit'] = array( - 'category' => 'page', - 'title' => 'GDPR Audit', - 'value' => $value ? t('Include') : t("Don't include"), - ); - } - } - -} diff --git a/modules/gdpr_fields/default_fields/user.user.mail.field.php b/modules/gdpr_fields/default_fields/user.user.mail.field.php deleted file mode 100644 index ec10aed..0000000 --- a/modules/gdpr_fields/default_fields/user.user.mail.field.php +++ /dev/null @@ -1,16 +0,0 @@ -disabled = FALSE; /* Edit this to true to make a default field disabled initially */ -$field->name = 'user|user|mail'; -$field->plugin_type = 'gdpr_entity_property'; -$field->entity_type = 'user'; -$field->entity_bundle = 'user'; -$field->field_name = 'mail'; -$field->settings = array( - 'gdpr_fields_rta' => 'inc', - 'gdpr_fields_rtf' => 'anonymise', - 'gdpr_fields_sanitizer' => 'gdpr_sanitizer_email', - 'notes' => '', - 'label' => 'Email', - 'description' => NULL, -); diff --git a/modules/gdpr_fields/default_fields/user.user.name.field.php b/modules/gdpr_fields/default_fields/user.user.name.field.php deleted file mode 100644 index 87dcb5b..0000000 --- a/modules/gdpr_fields/default_fields/user.user.name.field.php +++ /dev/null @@ -1,16 +0,0 @@ -disabled = FALSE; /* Edit this to true to make a default field disabled initially */ -$field->name = 'user|user|name'; -$field->plugin_type = 'gdpr_entity_property'; -$field->entity_type = 'user'; -$field->entity_bundle = 'user'; -$field->field_name = 'name'; -$field->settings = array( - 'gdpr_fields_rta' => 'maybe', - 'gdpr_fields_rtf' => 'anonymise', - 'gdpr_fields_sanitizer' => 'gdpr_sanitizer_username', - 'notes' => '', - 'label' => 'Name', - 'description' => NULL, -); diff --git a/modules/gdpr_fields/default_fields/user.user.roles.field.php b/modules/gdpr_fields/default_fields/user.user.roles.field.php deleted file mode 100644 index aebdb9b..0000000 --- a/modules/gdpr_fields/default_fields/user.user.roles.field.php +++ /dev/null @@ -1,16 +0,0 @@ -disabled = FALSE; /* Edit this to true to make a default field disabled initially */ -$field->name = 'user|user|roles'; -$field->plugin_type = 'gdpr_entity_property'; -$field->entity_type = 'user'; -$field->entity_bundle = 'user'; -$field->field_name = 'roles'; -$field->settings = array( - 'gdpr_fields_rta' => 'maybe', - 'gdpr_fields_rtf' => 'remove', - 'gdpr_fields_sanitizer' => 'gdpr_sanitizer_text', - 'notes' => '', - 'label' => 'User roles', - 'description' => NULL, -); diff --git a/modules/gdpr_fields/default_fields/user.user.status.field.php b/modules/gdpr_fields/default_fields/user.user.status.field.php deleted file mode 100644 index f476db7..0000000 --- a/modules/gdpr_fields/default_fields/user.user.status.field.php +++ /dev/null @@ -1,15 +0,0 @@ -disabled = FALSE; /* Edit this to true to make a default field disabled initially */ -$field->name = 'user|user|status'; -$field->plugin_type = 'gdpr_entity_property'; -$field->entity_type = 'user'; -$field->entity_bundle = 'user'; -$field->field_name = 'status'; -$field->settings = array( - 'gdpr_fields_rta' => 'no', - 'gdpr_fields_rtf' => 'remove', - 'gdpr_fields_sanitizer' => 'gdpr_sanitizer_text', - 'notes' => '', - 'label' => 'Status', -); diff --git a/modules/gdpr_fields/gdpr_fields.admin.inc b/modules/gdpr_fields/gdpr_fields.admin.inc deleted file mode 100644 index f8fad22..0000000 --- a/modules/gdpr_fields/gdpr_fields.admin.inc +++ /dev/null @@ -1,7 +0,0 @@ - 'Stores GDPR field data.', - 'export' => array( - 'key' => 'name', - 'key name' => 'Name', -// 'admin_title' => 'label', -// 'admin_description' => 'description', - 'primary key' => 'name', - 'identifier' => 'field', - 'default hook' => 'gdpr_fields_default_field_data', - 'object' => 'GDPRFieldData', - ), - 'fields' => array( - 'name' => array( - 'type' => 'varchar', - 'length' => 255, - 'description' => 'Machine name for field.', - ), - 'plugin_type' => array( - 'type' => 'varchar', - 'length' => 32, - 'description' => 'Plugin type of the field.', - ), - 'entity_type' => array( - 'type' => 'varchar', - 'length' => 32, - 'description' => 'Entity type of field.', - ), - 'entity_bundle' => array( - 'type' => 'varchar', - 'length' => 32, - 'description' => 'Entity bundle of field.', - ), - 'field_name' => array( - 'type' => 'varchar', - 'length' => 32, - 'description' => 'Field name of field.', - ), - 'settings' => array( - 'type' => 'text', - 'size' => 'big', - 'description' => 'Additional settings.', - 'serialize' => TRUE, - ), - ), - 'primary key' => array('name'), - 'keys' => array( - 'enabled' => array('enabled'), - ) - ); - - return $schema; -} \ No newline at end of file diff --git a/modules/gdpr_fields/gdpr_fields.module b/modules/gdpr_fields/gdpr_fields.module deleted file mode 100644 index 24a2a0c..0000000 --- a/modules/gdpr_fields/gdpr_fields.module +++ /dev/null @@ -1,280 +0,0 @@ - array('handler'), - 'child plugins' => TRUE, - 'use hooks' => TRUE, - ); - - return $plugins; -} - -/** - * Implements hook_ctools_plugin_directory(). - */ -function gdpr_fields_ctools_plugin_directory($owner, $plugin_type) { - if ($owner == 'gdpr_fields') { - return 'plugins/' . $plugin_type; - } - if ($owner == 'ctools' && $plugin_type == 'export_ui') { - return 'plugins/' . $plugin_type; - } - if ($owner == 'ctools' && $plugin_type == 'relationships') { - return 'plugins/' . $plugin_type; - } -} - -/** - * Fetch metadata for all context plugins. - * - * @return array - * An array of arrays with information about all available panel contexts. - */ -function gdpr_fields_get_contexts() { - ctools_include('plugins'); - ctools_include('export-ui'); - - -// dpm(ctools_get_plugins('ctools', 'export_ui')); -// dpm(gdpr_fields_get_gdpr_data()); -} - -/** - * Fetch metadata for all context plugins. - * - * @return array - * An array of arrays with information about all available panel contexts. - */ -function gdpr_fields_get_gdpr_data() { - ctools_include('plugins'); - - return ctools_get_plugins('gdpr_fields', 'gdpr_data'); -} - -/** - * Implements hook_menu(). - */ -function gdpr_fields_menu() { - -} - -/** - * Implements hook_gdpr_fields_default_field_data(). - * - * Default hook for building field data plugins. - */ -function gdpr_fields_gdpr_fields_default_field_data() { - $export = array(); - - $plugins = gdpr_fields_get_gdpr_data(); - foreach ($plugins as $name => $plugin) { - $export[$name] = GDPRFieldData::createFromPlugin($plugin); - } - - // Scan fields directory for default files. - $files = file_scan_directory(dirname(__FILE__) . '/default_fields', '/\.field.php/', array('key' => 'name')); - foreach ($files as $file) { - $field = new GDPRFieldData(); - if ((include $file->uri) == 1) { - $name = $field->plugin_type . ':' . $field->name; - $export[$name] = $field; - } - } - - return $export; -} - - -function gdpr_fields_exluded_entities($relationship) { - list($field, $from, $to) = explode('-', $relationship['name']); - - $exluded_source = array( - 'gdpr_task', - ); - $exluded_destination = array( - 'gdpr_task', - ); - - if (in_array($from, $exluded_source)) { - return TRUE; - } - - if (in_array($to, $exluded_destination)) { - return TRUE; - } - - return FALSE; -} - -function gdpr_fields_collect_gdpr_entities(&$entity_list, $entity_type, $entity) { - // Check for recursion. - list($entity_id, , $bundle) = entity_extract_ids($entity_type, $entity); - if (isset($entity_list[$entity_type][$bundle][$entity_id])) { - return; - } - - // Set entity. - $entity_list[$entity_type][$bundle][$entity_id] = $entity; - - ctools_include('context'); - ctools_include('plugins'); - - $context = ctools_context_create("entity:{$entity_type}", $entity); - - // Get available relationships from context. - $available_relationships = array(); - $relationships = ctools_get_relationships(); - - // Go through each relationship. - foreach ($relationships as $rid => $relationship) { - // For each relationship, see if there is a context that satisfies it. - if (!$context) { - var_dump($context); - var_dump($entity_type); - } - if (ctools_context_filter(array($context), $relationship['required context'])) { - $available_relationships[$rid] = $relationship; - } - } - - // @todo Turn these into hooks. - $allowed_relationships = array( - 'entities_from_schema', - 'entities_from_field', - 'party_from_user', - 'attached_entity_from_party', - ); - - $forbidden_relationships = array( - 'entities_from_field' => 'gdpr_fields_exluded_entities', - 'entities_from_schema' => 'gdpr_fields_exluded_entities', - ); - - foreach ($available_relationships as $relationship_name => $relationship) { - if (is_numeric(strpos($relationship_name, ':'))) { - list($rel_plugin, $rel_plugin_name) = explode(':', $relationship_name); - } - else { - $rel_plugin = $relationship_name; - } - - if (!in_array($rel_plugin, $allowed_relationships)) { - continue; - } - - if (in_array($rel_plugin, array_keys($forbidden_relationships))) { - if ($forbidden_relationships[$rel_plugin]($relationship)) { - continue; - } - } - - // @todo can identifier be loaded elsewhere? - $relationship['identifier'] = $relationship['title']; - - $entity_contexts = gdpr_fields_get_contexts_from_relationship($relationship, $context); - - if (!is_array($entity_contexts)) { - $entity_contexts = array($entity_contexts); - } - - foreach ($entity_contexts as $entity_context) { - if (!empty($entity_context->data)) { - list($plugin_type, $entity_type) = explode(':', $entity_context->plugin); - if (!$entity_type) { - var_dump($entity_context->plugin); - } - gdpr_fields_collect_gdpr_entities($entity_list, $entity_type, $entity_context->data); - } - } - } -} - -/** - * Implements hook_permission(). - */ -function gdpr_fields_permission() { - $perms = array( - 'administer gdpr fields' => array( - 'title' => t('Administer GDPR field settings'), - 'restrict access' => TRUE, - ), - ); - - return $perms; -} - -/** - * Return a context from a relationship. - * - * @param array $relationship - * The configuration of a relationship. It must contain the following data: - * - name: The name of the relationship plugin being used. - * - relationship_settings: The configuration based upon the plugin forms. - * - identifier: The human readable identifier for this relationship, usually - * defined by the UI. - * - keyword: The keyword used for this relationship for substitutions. - * - * @param ctools_context $source_context - * The context this relationship is based upon. - * @param bool $placeholders - * If TRUE, placeholders are acceptable. - * - * @return ctools_context|null - * A context object if one can be loaded, otherwise NULL. - * - * @see ctools_context_get_relevant_relationships() - * @see ctools_context_get_context_from_relationships() - */ -function gdpr_fields_get_contexts_from_relationship($relationship, $source_context, $placeholders = FALSE) { - ctools_include('plugins'); - - // Attempt to get custom contexts function. - $function = ctools_plugin_load_function('ctools', 'relationships', $relationship['name'], 'contexts'); - - // Fall back on default context function. - if (!$function) { - $function = ctools_plugin_load_function('ctools', 'relationships', $relationship['name'], 'context'); - } - - if ($function) { - // Backward compatibility: Merge old style settings into new style: - if (!empty($relationship['relationship_settings'])) { - $relationship += $relationship['relationship_settings']; - unset($relationship['relationship_settings']); - } - - $contexts = $function($source_context, $relationship, $placeholders); - - - - if ($contexts && !is_array($contexts)) { - $contexts = array($contexts); - } - - if (!empty($contexts)) { - - foreach ($contexts as &$context) { - $context->identifier = $relationship['identifier']; - $context->page_title = isset($relationship['title']) ? $relationship['title'] : ''; - $context->keyword = $relationship['keyword']; - if (!empty($context->empty)) { - $context->placeholder = array( - 'type' => 'relationship', - 'conf' => $relationship, - ); - } - } - return $contexts; - } - } - return NULL; -} diff --git a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php deleted file mode 100644 index aaca2a0..0000000 --- a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.class.php +++ /dev/null @@ -1,224 +0,0 @@ -plugin['menu']['items']['add']); - // @todo Make sure import always overrides and never adds. - $this->plugin['menu']['items']['import']['title'] = 'Override'; - parent::hook_menu($items); - } - - /** - * {@inheritdoc} - * - * @param GDPRFieldData $item - */ - public function list_build_row($item, &$form_state, $operations) { - // Set up sorting - $name = $item->{$this->plugin['export']['key']}; - $schema = ctools_export_get_schema($this->plugin['schema']); - - // Note: $item->{$schema['export']['export type string']} should have already been set up by export.inc so - // we can use it safely. - switch ($form_state['values']['order']) { - case 'disabled': - $this->sorts[$name] = empty($item->disabled) . $name; - break; - case 'title': - $this->sorts[$name] = $item->{$this->plugin['export']['admin_title']}; - break; - case 'name': - $this->sorts[$name] = $name; - break; - case 'storage': - $this->sorts[$name] = $item->{$schema['export']['export type string']} . $name; - break; - } - - $row['data'] = array(); - $row['class'] = !empty($item->disabled) ? array('ctools-export-ui-disabled') : array('ctools-export-ui-enabled'); - - // If we have an admin title, make it the first row. - $row['data'][] = array('data' => check_plain($item->getSetting('label')), 'class' => array('ctools-export-ui-title')); -// $row['data'][] = array('data' => check_plain($name), 'class' => array('ctools-export-ui-name')); - $row['data'][] = array('data' => check_plain($item->{$schema['export']['export type string']}), 'class' => array('ctools-export-ui-storage')); - -// $row['data'][] = array('data' => check_plain($item->entity_type), 'class' => array('ctools-export-ui-entity-type')); -// $row['data'][] = array('data' => check_plain($item->entity_bundle), 'class' => array('ctools-export-ui-entity-bundle')); -// $row['data'][] = array('data' => check_plain($item->field_name), 'class' => array('ctools-export-ui-field-name')); - - $rta_labels = array( - '' => 'Not configured', - 'inc' => 'Included', - 'maybe' => 'Maybe included', - 'no' => 'Not included', - ); - $rtf_labels = array( - '' => 'Not configured', - 'anonymise' => 'Anonymise', - 'remove' => 'Remove', - 'maybe' => 'Maybe included', - 'no' => 'Not included', - ); - $row['data'][] = array('data' => $rta_labels[$item->getSetting('gdpr_fields_rta', '')], 'class' => array('ctools-export-ui-rta')); - $row['data'][] = array('data' => $rtf_labels[$item->getSetting('gdpr_fields_rtf', '')], 'class' => array('ctools-export-ui-rtf')); - - $ops = theme('links__ctools_dropbutton', array('links' => $operations, 'attributes' => array('class' => array('links', 'inline')))); - - $row['data'][] = array('data' => $ops, 'class' => array('ctools-export-ui-operations')); - - // Add an automatic mouseover of the description if one exists. - if (!empty($this->plugin['export']['admin_description'])) { - $row['title'] = $item->{$this->plugin['export']['admin_description']}; - } - - $this->rows[$name] = $row; - } - - /** - * {@inheritdoc} - */ - public function list_table_header() { - $header = array(); - $header[] = array('data' => t('Label'), 'class' => array('ctools-export-ui-title')); - -// $header[] = array('data' => t('Name'), 'class' => array('ctools-export-ui-name')); - $header[] = array('data' => t('Storage'), 'class' => array('ctools-export-ui-storage')); - -// $header[] = array('data' => t('Entity type'), 'class' => array('ctools-export-ui-entity-type')); -// $header[] = array('data' => t('Entity bundle'), 'class' => array('ctools-export-ui-entity-bundle')); -// $header[] = array('data' => t('Field name'), 'class' => array('ctools-export-ui-field-name')); - $header[] = array('data' => t('Right to access'), 'class' => array('ctools-export-ui-rta')); - $header[] = array('data' => t('Right to be forgotten'), 'class' => array('ctools-export-ui-rtf')); - - $header[] = array('data' => t('Operations'), 'class' => array('ctools-export-ui-operations')); - return $header; - - } - - /** - * {@inheritdoc} - */ - public function list_render(&$form_state) { - $tables = array(); - $table_data = ''; - - foreach ($this->rows as $name => $row) { - list($entity_type, $entity_bundle, $field_name) = explode('|', $name); - $tables[$entity_type][$entity_bundle][$name] = $row; - } - - foreach ($tables as $entity_type => $entities) { - $fieldset_entity = array( - 'element' => array( - '#title' => t('Entity: @entity', array( - '@entity' => $entity_type, - )), - '#value' => '', - '#children' => '
', - '#attributes' => array ( - 'class' => array( - 'collapsible', - ), - ), - ), - ); - - if (count($entities) === 1) { - $rows = reset($entities); - $table = array( - 'header' => $this->list_table_header(), - 'rows' => $rows, - 'empty' => $this->plugin['strings']['message']['no items'], - ); - $fieldset_entity['element']['#value'] = theme('table', $table); - } - else { - foreach ($entities as $bundle => $rows) { - $table = array( - 'header' => $this->list_table_header(), - 'rows' => $rows, - 'empty' => $this->plugin['strings']['message']['no items'], - ); - - $fieldset_bundle = array( - 'element' => array( - '#title' => t('Bundle: @bundle', array( - '@bundle' => $bundle, - )), - '#value' => theme('table', $table), - '#children' => '
', - '#attributes' => array ( - 'class' => array( - 'collapsible', -// 'collapsed', - ), - ), - ), - ); - $fieldset_entity['element']['#value'] .= theme('fieldset', $fieldset_bundle); - } - } - - $table_data .= theme('fieldset', $fieldset_entity); - } - - $content = array( - '#type' => 'container', - '#attributes' => array ( - 'id' => 'ctools-export-ui-list-items', - ), - 'data' => array('#markup' => !empty($table_data) ? $table_data : $this->plugin['strings']['message']['no items']), - ); - - return drupal_render($content); - } - - /** - * {@inheritdoc} - */ - public function list_form(&$form, &$form_state) { - parent::list_form($form, $form_state); - - $entities = array(); - foreach (entity_get_info() as $key => $entity_info) { - $entities[$key] = $entity_info['label']; - } - - $form['top row']['gdpr_entity'] = array( - '#type' => 'select', - '#title' => t('Entity'), - '#options' => $entities, - '#multiple' => TRUE, - '#default_value' => array(), - ); - - $form['#attached']['library'][] = array('system', 'drupal.collapse'); - } - - /** - * {@inheritdoc} - */ - public function list_filter($form_state, $item) { - if (!empty($form_state['values']['gdpr_entity']) && !in_array($item->entity_type, $form_state['values']['gdpr_entity'])) { - return TRUE; - } - - parent::list_filter($form_state, $item); - } - -} \ No newline at end of file diff --git a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc b/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc deleted file mode 100644 index e9f61ed..0000000 --- a/modules/gdpr_fields/plugins/export_ui/gdpr_fields_ui.inc +++ /dev/null @@ -1,190 +0,0 @@ - 'gdpr_fields_field_data', - 'access' => 'administer gdpr fields', - - 'menu' => array( - 'menu prefix' => 'admin/gdpr', - 'menu item' => 'field-list', - 'menu title' => 'GDPR Field config', - 'menu description' => 'Find and configure GDPR field data.', - ), - - 'title singular' => t('field'), - 'title singular proper' => t('Field'), - 'title plural' => t('fields'), - 'title plural proper' => t('Fields'), - - 'form' => array( - 'settings' => 'gdpr_fields_field_data_export_ui_form', - 'submit' => 'gdpr_fields_field_data_export_ui_form_submit', - ), - 'handler' => 'gdpr_fields_ui', - 'strings' => array( - 'confirmation' => array( - 'revert' => array( - 'information' => t('This action will permanently remove any customizations made to this field.'), - 'success' => t('The field has been reverted.'), - ), - ), - ), -); - -/** - * Define the preset add/edit form. - */ -function gdpr_fields_field_data_export_ui_form(&$form, &$form_state) { - /* @var GDPRFieldData $field_data */ - $field_data = $form_state['item']; - - $form['field'] = array( - '#type' => 'fieldset', - '#title' => t('Field info'), - '#tree' => TRUE, - ); - - $form['field']['entity_type'] = array( - '#type' => 'textfield', - '#title' => t('Entity type'), - '#default_value' => $field_data->entity_type, - '#disabled' => TRUE, - ); - - $form['field']['entity_bundle'] = array( - '#type' => 'textfield', - '#title' => t('Bundle'), - '#default_value' => $field_data->entity_bundle, - '#disabled' => TRUE, - ); - - $form['field']['field_name'] = array( - '#type' => 'textfield', - '#title' => t('Field name'), - '#default_value' => $field_data->field_name, - '#disabled' => TRUE, - ); - - $form['field']['label'] = array( - '#type' => 'textfield', - '#title' => t('Label'), - '#default_value' => $field_data->getSetting('label', ''), - '#disabled' => TRUE, - ); - - $form['field']['descriptions'] = array( - '#type' => 'textfield', - '#title' => t('Description'), - '#default_value' => $field_data->getSetting('description', ''), - '#disabled' => TRUE, - ); - - $form['settings'] = array( - '#type' => 'fieldset', - '#title' => t('Settings'), - '#tree' => TRUE, - ); - - $form['settings']['gdpr_fields_enabled'] = array( - '#type' => 'select', - '#title' => t('GDPR Field'), - '#description' => t('Is this a GDPR field'), - '#options' => array( - '' => 'Not defined', - '1' => 'Yes', - '0' => 'No', - ), - '#default_value' => $field_data->getSetting('gdpr_fields_enabled', ''), - ); - - - $form['settings']['gdpr_fields_rta'] = array( - '#type' => 'select', - '#title' => t('Right to access'), - '#options' => array( - 'inc' => 'Included', - 'maybe' => 'Maybe', - 'no' => 'Not', - ), - '#default_value' => $field_data->getSetting('gdpr_fields_rta', 'no'), - '#states' => array( - 'visible' => array( - ':input[name="settings[gdpr_fields_enabled]"]' => array('value' => 1), - ), - ), - ); - - $form['settings']['gdpr_fields_rtf'] = array( - '#type' => 'select', - '#title' => t('Right to be forgotten'), - '#options' => array( - 'anonymise' => 'Anonymise', - 'remove' => 'Remove', - 'maybe' => 'Maybe', - 'no' => 'Not', - ), - '#default_value' => $field_data->getSetting('gdpr_fields_rtf', 'no'), - '#states' => array( - 'visible' => array( - ':input[name="settings[gdpr_fields_enabled]"]' => array('value' => 1), - ), - ), - ); - - // @todo Filter by relevance. - $sanitizer_options = array(); - foreach (ctools_export_load_object('gdpr_dump_sanitizers') as $sanitizer) { - $sanitizer_options[$sanitizer->name] = $sanitizer->label; - } - - $form['settings']['gdpr_fields_sanitizer'] = array( - '#type' => 'select', - '#title' => t('Sanitizer to use'), - '#options' => $sanitizer_options, - '#default_value' => $field_data->getSetting('gdpr_fields_sanitizer', ''), - '#states' => array( - 'visible' => array( - ':input[name="settings[gdpr_fields_rtf]"]' => array('value' => 'anonymise'), - ), - ), - ); - - $form['settings']['gdpr_fields_notes'] = array( - '#type' => 'textarea', - '#title' => t('Notes'), - '#default_value' => $field_data->getSetting('gdpr_fields_notes', ''), - '#states' => array( - 'invisible' => array( - ':input[name="settings[gdpr_fields_enabled]"]' => array('value' => ''), - ), - ), - ); -} - -/** - * Define the submit function for the add/edit form. - */ -function gdpr_fields_field_data_export_ui_form_submit(&$form, &$form_state) { - if ($form_state['values']['settings']['gdpr_fields_enabled'] === '') { - // Clear all settings. - $form_state['values']['settings'] = array(); - } - elseif ($form_state['values']['settings']['gdpr_fields_enabled'] === '0') { - // Clear field settings, leave notes. - unset($form_state['values']['settings']['gdpr_fields_rta']); - unset($form_state['values']['settings']['gdpr_fields_rtf']); - unset($form_state['values']['settings']['gdpr_fields_sanitizer']); - } - - if (isset($form_state['values']['field']['label'])) { - $form_state['values']['settings']['label'] = $form_state['values']['field']['label']; - } - if (isset($form_state['values']['field']['description'])) { - $form_state['values']['settings']['description'] = $form_state['values']['field']['description']; - } -} diff --git a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.class.php b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.class.php deleted file mode 100644 index bf1e3d9..0000000 --- a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.class.php +++ /dev/null @@ -1,18 +0,0 @@ -plugin = $plugin; - } - -} diff --git a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.inc b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.inc deleted file mode 100644 index fe69c2a..0000000 --- a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_field.inc +++ /dev/null @@ -1,41 +0,0 @@ - array( - 'class' => 'gdpr_entity_field', - ), - 'get child' => 'gdpr_fields_gdpr_entity_field_get_child', - 'get children' => 'gdpr_fields_gdpr_entity_field_get_children', -); - - -function gdpr_fields_gdpr_entity_field_get_child($plugin, $parent, $child) { - $plugins = gdpr_fields_gdpr_entity_field_get_children($plugin, $parent); - return $plugins[$parent . ':' . $child]; -} - -function gdpr_fields_gdpr_entity_field_get_children($plugin, $parent) { - $instances = field_info_instances(); - $plugins = array(); - - foreach ($instances as $entity_type => $type_bundles) { - foreach ($type_bundles as $bundle => $bundle_instances) { - foreach ($bundle_instances as $field_name => $instance) { -// $field = field_info_field($field_name); - $name = "{$parent}:{$entity_type}|{$bundle}|{$field_name}"; - $child_plugin = $plugin; - $child_plugin['name'] = $name; - $child_plugin['label'] = $instance['label']; - $child_plugin['description'] = $instance['description']; - $plugins[$name] = $child_plugin; - } - } - } - - return $plugins; -} \ No newline at end of file diff --git a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.class.php b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.class.php deleted file mode 100644 index d392064..0000000 --- a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.class.php +++ /dev/null @@ -1,18 +0,0 @@ -plugin = $plugin; - } - -} diff --git a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc b/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc deleted file mode 100644 index e47d347..0000000 --- a/modules/gdpr_fields/plugins/gdpr_data/gdpr_entity_property.inc +++ /dev/null @@ -1,73 +0,0 @@ - FALSE, - 'handler' => array( - 'class' => 'gdpr_entity_property', - ), - 'get child' => 'gdpr_fields_gdpr_entity_property_get_child', - 'get children' => 'gdpr_fields_gdpr_entity_property_get_children', -); - -function gdpr_fields_gdpr_entity_property_get_child($plugin, $parent, $child) { - $plugins = gdpr_fields_gdpr_entity_property_get_children($plugin, $parent); - return $plugins[$parent . ':' . $child]; -} - -function gdpr_fields_gdpr_entity_property_get_children($plugin, $parent) { - $entities = entity_get_info(); - - $plugins = array(); - foreach (array_keys($entities) as $entity_type) { - - $info = entity_get_info($entity_type); - $property_info = entity_get_property_info($entity_type); - - if (empty($property_info)) { - continue; - } - - foreach (array_keys($info['bundles']) as $bundle) { - $properties = array(); - - // Default properties - foreach ($property_info['properties'] as $property_name => $property) { - $name = "{$parent}:{$entity_type}|{$bundle}|{$property_name}"; - $properties[$name] = $property; - } - - // Bundle properties - if (isset($property_info['bundles'][$bundle])) { - foreach ($property_info['bundles'][$bundle]['properties'] as $property_name => $property) { - $name = "{$parent}:{$entity_type}|{$bundle}|{$property_name}"; - $properties[$name] = $property; - } - } - - // Add plugins for properties. - foreach ($properties as $name => $property) { - if (!empty($property['field'])) { - continue; - } - $child_plugin = $plugin; - $child_plugin['name'] = $name; - $child_plugin['label'] = $property['label']; - - $child_plugin['description'] = isset($property['description']) ? $property['description'] : ''; - - // @todo Should computed properties be removed instead or disabled? - if (!empty($property['computed'])) { - $child_plugin['computed'] = TRUE; - } - $plugins[$name] = $child_plugin; - } - } - } - - return $plugins; -} \ No newline at end of file diff --git a/modules/gdpr_fields/plugins/relationships/entities_from_field.inc b/modules/gdpr_fields/plugins/relationships/entities_from_field.inc deleted file mode 100644 index a095a6c..0000000 --- a/modules/gdpr_fields/plugins/relationships/entities_from_field.inc +++ /dev/null @@ -1,254 +0,0 @@ - t('Entity'), - 'description' => t('Creates an entity context from a foreign key on a field.'), - 'context' => 'gdpr_fields_entities_from_field_context', - 'edit form' => 'gdpr_fields_entities_from_field_edit_form', - 'get child' => 'gdpr_fields_entities_from_field_get_child', - 'get children' => 'gdpr_fields_entities_from_field_get_children', - 'defaults' => array('delta' => 0), - // 'no ui' => TRUE, -); - -function gdpr_fields_entities_from_field_get_child($plugin, $parent, $child) { - $plugins = gdpr_fields_entities_from_field_get_children($plugin, $parent); - return $plugins[$parent . ':' . $child]; -} - -function gdpr_fields_entities_from_field_get_children($parent_plugin, $parent) { - $cid = $parent_plugin['name'] . ':' . $parent; - $cache = &drupal_static(__FUNCTION__); - if (!empty($cache[$cid])) { - return $cache[$cid]; - } - - ctools_include('fields'); - $entities = entity_get_info(); - $plugins = array(); - $context_types = array(); - - // Get the schema information for every field. - $fields_info = field_info_fields(); - foreach ($fields_info as $field_name => $field) { - foreach ($field['bundles'] as $from_entity => $bundles) { - foreach ($bundles as $bundle) { - // There might be fields attached to bundles that are disabled (e.g. a - // module that declared a node's content type, is now disabled), but the - // field instance is still shown. - if (!empty($entities[$from_entity]['bundles'][$bundle])) { - $foreign_keys = ctools_field_foreign_keys($field_name); - - foreach ($foreign_keys as $key => $info) { - if (isset($info['table'])) { - foreach ($entities as $to_entity => $to_entity_info) { - $from_entity_info = $entities[$from_entity]; - // If somehow the bundle doesn't exist on the to-entity, - // skip. - if (!isset($from_entity_info['bundles'][$bundle])) { - continue; - } - - if (isset($to_entity_info['base table']) && $to_entity_info['base table'] == $info['table'] && array_keys($info['columns'], $to_entity_info['entity keys']['id'])) { - $name = $field_name . '-' . $from_entity . '-' . $to_entity; - $plugin_id = $parent . ':' . $name; - - // Record the bundle for later. - $context_types[$plugin_id]['types'][$bundle] = $from_entity_info['bundles'][$bundle]['label']; - - // We check for every bundle; this plugin may already have - // been created, so don't recreate it. - if (!isset($plugins[$plugin_id])) { - $plugin = $parent_plugin; - $replacements = array( - '@to_entity' => $to_entity_info['label'], - '@from_entity' => $from_entity_info['label'], - '@field_name' => $field_name, - '@field_label' => ctools_field_label($field_name), - ); - $plugin['title'] = t('@to_entity from @from_entity (on @from_entity: @field_label [@field_name])', $replacements); - $plugin['keyword'] = $to_entity; - $plugin['context name'] = $name; - $plugin['name'] = $plugin_id; - $plugin['description'] = t('Creates a @to_entity context from @from_entity using the @field_name field on @from_entity.', $replacements); - $plugin['from entity'] = $from_entity; - $plugin['to entity'] = $to_entity; - $plugin['field name'] = $field_name; - $plugin['join key'] = $key; - $plugin['source key'] = current(array_keys($info['columns'])); - $plugin['reverse'] = FALSE; - $plugin['parent'] = $parent; - - $plugins[$plugin_id] = $plugin; - - // Build the reverse - $plugin = $parent_plugin; - $name = $field_name . '-' . $to_entity . '-' . $from_entity; - $plugin_id = $parent . ':' . $name; - - $plugin['title'] = t('@from_entity from @to_entity (on @from_entity: @field_label [@field_name])', $replacements); - $plugin['keyword'] = $to_entity; - $plugin['context name'] = $name; - $plugin['name'] = $plugin_id; - $plugin['description'] = t('Creates a @from_entity context from @to_entity using the @field_name field on @from_entity.', $replacements); - - $plugin['from entity'] = $from_entity; - $plugin['to entity'] = $to_entity; - $plugin['field name'] = $field_name; - $plugin['reverse'] = TRUE; - $plugin['parent'] = $parent; - - // Since we can't apply restrictions on the reverse relationship - // we just add the required context here. - $plugin['required context'] = new ctools_context_required($to_entity_info['label'], $to_entity); - -// $plugin_entities = array( -// 'to' => array($from_entity => $from_entity_info), -// 'from' => array($to_entity => $to_entity_info) -// ); -// drupal_alter('ctools_entity_context', $plugin, $plugin_entities, $plugin_id); - - $plugins[$plugin_id] = $plugin; - } - } - } - } - } - } - } - } - } - - foreach ($context_types as $key => $context) { - list($parent, $plugin_name) = explode(':', $key); - list($field_name, $from_entity, $to_entity) = explode('-', $plugin_name); - - $from_entity_info = $entities[$from_entity]; -// $to_entity_info = $entities[$to_entity]; - - $plugins[$key]['required context'] = new ctools_context_required($from_entity_info['label'], $from_entity, array('type' => array_keys($context['types']))); - -// $plugin_entities = array( -// 'to' => array($to_entity => $to_entity_info), -// 'from' => array($from_entity => $from_entity_info), -// ); -// drupal_alter('ctools_entity_context', $plugins[$key], $plugin_entities, $key); - } -// drupal_alter('ctools_entity_contexts', $plugins); - - $cache[$cid] = $plugins; - return $plugins; -} - -/** - * Return a new context based on an existing context. - */ -function gdpr_fields_entities_from_field_context($context, $conf) { - // Perform access check on current logged in user. - global $user; - // Clone user object so account can be passed by value to access callback. - $account = clone $user; - - $delta = !empty($conf['delta']) ? intval($conf['delta']) : 0; - $plugin = $conf['name']; - list($plugin, $plugin_name) = explode(':', $plugin); - list($field_name, $from_entity, $to_entity) = explode('-', $plugin_name); - // If unset it wants a generic, unfilled context, which is just NULL. - $entity_info = entity_get_info($from_entity); - if (empty($context->data) || !isset($context->data->{$entity_info['entity keys']['id']})) { - return ctools_context_create_empty('entity:' . $to_entity, NULL); - } - - if (isset($context->data->{$entity_info['entity keys']['id']})) { - // Load the entity. - $id = $context->data->{$entity_info['entity keys']['id']}; - - if (!$conf['reverse']) { - $entity = entity_load($from_entity, array($id)); - $entity = $entity[$id]; - if ($items = field_get_items($from_entity, $entity, $field_name)) { - if (isset($items[$delta])) { - ctools_include('fields'); - $to_entity_info = entity_get_info($to_entity); - - $plugin_info = ctools_get_relationship($conf['name']); - - if (!isset($plugin_info['source key'])) { - dpm($conf['name']); - dpm($plugin_info); - } - - $to_entity_id = $items[$delta][$plugin_info['source key']]; - $loaded_to_entity = entity_load($to_entity, array($to_entity_id)); - $loaded_to_entity = array_shift($loaded_to_entity); - - // Pass current user account and entity type to access callback. - if (isset($to_entity_info['access callback']) && function_exists($to_entity_info['access callback']) && !call_user_func($to_entity_info['access callback'], 'view', $loaded_to_entity, $account, $to_entity)) { - return ctools_context_create_empty('entity:' . $to_entity, NULL); - } - else { - // Send it to ctools. - return ctools_context_create('entity:' . $to_entity, $to_entity_id); - } - } - else { - // In case that delta was empty. - return ctools_context_create_empty('entity:' . $to_entity, NULL); - } - } - } - else { - // @todo Do reverse context. -// $result = db_select($conf['base_table'], 'base') -// ->fields('base', array($conf['base_table_id'])) -// ->condition($conf['base_table_key'], $id, '=') -// // @todo figure out performance improvement for more contexts. -// ->range(0,1) -// ->execute(); -// -// $contexts = array(); -// while($record = $result->fetchAssoc()) { -// $contexts[] = ctools_context_create('entity:' . $to_entity, $record[$conf['base_table_id']]); -// } -// -// return $contexts; - } - - } -} - -function gdpr_fields_entities_from_field_edit_form($form, &$form_state) { - $field = field_info_field($form_state['plugin']['field name']); - $conf = $form_state['conf']; - - if ($field && $field['cardinality'] != 1) { - if ($field['cardinality'] == -1) { - $form['delta'] = array( - '#type' => 'textfield', - '#title' => t('Delta'), - '#description' => t('The relationship can only create one context, but multiple items can be related. Please select which one. Since this can have unlimited items, type in the number you want. The first one will be 0.'), - '#default_value' => !empty($conf['delta']) ? $conf['delta'] : 0, - ); - } - else { - $form['delta'] = array( - '#type' => 'select', - '#title' => t('Delta'), - '#description' => t('The relationship can only create one context, but multiple items can be related. Please select which one.'), - '#options' => range(1, $field['cardinality']), - '#default_value' => !empty($conf['delta']) ? $conf['delta'] : 0, - ); - } - } - - return $form; -} diff --git a/modules/gdpr_fields/plugins/relationships/entities_from_schema.inc b/modules/gdpr_fields/plugins/relationships/entities_from_schema.inc deleted file mode 100644 index 8659a00..0000000 --- a/modules/gdpr_fields/plugins/relationships/entities_from_schema.inc +++ /dev/null @@ -1,158 +0,0 @@ - t('GDPR Entities'), - 'description' => t('Creates an entity context from a foreign key on a field.'), - 'context' => 'gdpr_fields_entities_from_schema_context', - 'get child' => 'gdpr_fields_entities_from_schema_get_child', - 'get children' => 'gdpr_fields_entities_from_schema_get_children', -// 'no ui' => TRUE, -); - -function gdpr_fields_entities_from_schema_get_child($plugin, $parent, $child) { - $plugins = gdpr_fields_entities_from_schema_get_children($plugin, $parent); - return $plugins[$parent . ':' . $child]; -} - -function gdpr_fields_entities_from_schema_get_children($parent_plugin, $parent) { - $entities = entity_get_info(); - $plugins = array(); - - foreach (module_implements('entity_info') as $module) { - module_load_install($module); - $schemas = drupal_get_schema(); - - foreach ($entities as $from_entity => $from_entity_info) { - if (empty($from_entity_info['base table'])) { - continue; - } - - $table = $from_entity_info['base table']; - if (isset($schemas[$table]['foreign keys'])) { - foreach ($schemas[$table]['foreign keys'] as $relationship => $info) { - foreach ($entities as $to_entity => $to_entity_info) { - if (empty($info['table'])) { - continue; - } - - if (isset($to_entity_info['base table']) && $info['table'] == $to_entity_info['base table'] && in_array($to_entity_info['entity keys']['id'], $info['columns'])) { - $this_col = gdpr_fields_entities_from_schema_columns_filter($info['columns'], $to_entity_info['entity keys']['id']); - - // Set up our t() replacements as we reuse these. - $replacements = array( - '@relationship' => $relationship, - '@base_table' => $table, - '@to_entity' => $to_entity_info['label'], - '@from_entity' => $from_entity_info['label'], - ); - - $name = $this_col . '-' . $from_entity . '-' . $to_entity; - $plugin_id = $parent . ':' . $name; - $plugin = $parent_plugin; - - $plugin['title'] = t('@to_entity from @from_entity (on @base_table.@relationship)', $replacements); - $plugin['keyword'] = $to_entity; - $plugin['context name'] = $name; - $plugin['name'] = $plugin_id; - $plugin['description'] = t('Builds a relationship from a @from_entity to a @to_entity using the @base_table.@relationship field.', $replacements); - $plugin['reverse'] = FALSE; - - $plugin['required context'] = new ctools_context_required($from_entity_info['label'], $from_entity); - -// $plugin_entities = array('to' => array($to_entity => $to_entity_info), 'from' => array($from_entity => $from_entity_info)); -// drupal_alter('ctools_entity_context', $plugin, $plugin_entities, $plugin_id); - $plugins[$plugin_id] = $plugin; - - // Add the relation in the reverse direction. - $name = $this_col . '-' . $to_entity . '-' . $from_entity; - $plugin_id = $parent . ':' . $name; - $plugin = $parent_plugin; - - $plugin['title'] = t('@from_entity from @to_entity (on @base_table.@relationship)', $replacements); - $plugin['keyword'] = $to_entity; - $plugin['context name'] = $name; - $plugin['name'] = $plugin_id; - $plugin['description'] = t('Builds a relationship from a @to_entity to a @from_entity using the @base_table.@relationship field.', $replacements); - $plugin['reverse'] = TRUE; - $plugin['base_table'] = $table; - $plugin['base_table_key'] = key($info['columns']); - $plugin['base_table_id'] = $from_entity_info['entity keys']['id']; - - $plugin['required context'] = new ctools_context_required($to_entity_info['label'], $to_entity); - -// $plugin_entities = array('to' => array($from_entity => $from_entity_info), 'from' => array($to_entity => $to_entity_info)); -// drupal_alter('ctools_entity_context', $plugin, $plugin_entities, $plugin_id); - $plugins[$plugin_id] = $plugin; - - } - } - } - } - } - } -// drupal_alter('ctools_entity_contexts', $plugins); - return $plugins; -} - -function gdpr_fields_entities_from_schema_columns_filter($columns, $value) { - foreach ($columns as $this_col => $that_col) { - if ($value == $that_col) { - return $this_col; - } - } -} - -/** - * Return a new context based on an existing context. - */ -function gdpr_fields_entities_from_schema_context($context, $conf) { - - $plugin = $conf['name']; - list($plugin, $plugin_name) = explode(':', $plugin); - list($this_col, $from_entity, $to_entity) = explode('-', $plugin_name); - // If unset it wants a generic, unfilled context, which is just NULL. - $entity_info = entity_get_info($from_entity); - if (empty($context->data) || !isset($context->data->{$entity_info['entity keys']['id']})) { - return ctools_context_create_empty('entity:' . $to_entity); - } - - if (isset($context->data->{$entity_info['entity keys']['id']})) { - // Load the entity. - $id = $context->data->{$entity_info['entity keys']['id']}; - - if (!$conf['reverse']) { - $entity = entity_load($from_entity, array($id)); - $entity = $entity[$id]; - if (isset($entity->$this_col)) { - $to_entity_id = $entity->$this_col; - // Send it to ctools. - return ctools_context_create('entity:' . $to_entity, $to_entity_id); - } - } - else { - - $result = db_select($conf['base_table'], 'base') - ->fields('base', array($conf['base_table_id'])) - ->condition($conf['base_table_key'], $id, '=') - // @todo figure out performance improvement for more contexts. - ->range(0,1) - ->execute(); - - $contexts = array(); - while($record = $result->fetchAssoc()) { - $contexts[] = ctools_context_create('entity:' . $to_entity, $record[$conf['base_table_id']]); - } - - return $contexts; - } - } -} diff --git a/modules/gdpr_fields/src/Plugins/GDPRFieldData.php b/modules/gdpr_fields/src/Plugins/GDPRFieldData.php deleted file mode 100644 index 100e92d..0000000 --- a/modules/gdpr_fields/src/Plugins/GDPRFieldData.php +++ /dev/null @@ -1,143 +0,0 @@ -entity_type = $entity_type; - $field->entity_bundle = $entity_bundle; - $field->field_name = $field_name; - $field->name = $name; - $field->plugin_type = $plugin_type; - - // @todo Should computed properties be removed instead or disabled? - if (!empty($plugin['computed'])) { - $field->disabled = TRUE; - } - -// $field->name = $plugin['name']; - if (isset($plugin['label'])) { - $field->label = $plugin['label']; - $field->setSetting('label', $plugin['label']); - } - if (isset($plugin['description'])) { - $field->description = $plugin['label']; - $field->setSetting('description', $plugin['description']); - } - - return $field; - } - - /** - * Get a stored setting. - * - * @param string $setting - * The key of the setting to be fetched. - * @param mixed|null $default - * The default to be returned if not stored. - * - * @return mixed|null - * The field data setting. - */ - public function getSetting($setting, $default = NULL) { - if (isset($this->settings[$setting])) { - return $this->settings[$setting]; - } - - return $default; - } - - /** - * Get a stored setting. - * - * @param string $setting - * The key of the setting to be stored. - * @param mixed $value - * The value to be stored. - * - * @return $this - */ - public function setSetting($setting, $value) { - $this->settings[$setting] = $value; - return $this; - } - - - public function getValue($user) { - return $user->name; - } - -} diff --git a/modules/gdpr_tasks/gdpr_tasks.admin.inc b/modules/gdpr_tasks/gdpr_tasks.admin.inc deleted file mode 100644 index f4aed4e..0000000 --- a/modules/gdpr_tasks/gdpr_tasks.admin.inc +++ /dev/null @@ -1,401 +0,0 @@ - 'markup', - '#markup' => 'Editing of GDPR Task types is not currently supported.' - ); - - return $form; -} - -/** - * Form callback for all task bundles. - */ -function gdpr_task_form($form, &$form_state) { - $task = $form_state['task']; - field_attach_form('gdpr_task', $task, $form, $form_state); - - if ($task->user_id == $task->requested_by) { - $form['gdpr_tasks_notes']['#access'] = FALSE; - } - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - '#weight' => 40, - ); - - return $form; -} - -/** - * Validate handler for all task bundles. - */ -function gdpr_task_form_validate($form, &$form_state) { - $task = $form_state['task']; - field_attach_validate('gdpr_task', $task); -} - -/** - * Submit handler for all task bundles. - */ -function gdpr_task_form_submit($form, &$form_state) { - global $user; - - /* @var GDPRTask $task */ - $task = $form_state['task']; - - // General form submission. - field_attach_submit('gdpr_task', $task, $form, $form_state); - - // Process and close the task. - $task->processed_by = $user->uid; - drupal_set_message(t('Task has been processed.')); - $task->status = 'closed'; - $task->save(); - - // Send confirmation email. - gdpr_tasks_send_mail('task_processed', $task); -} - -/** - * Form callback for removal tasks. - */ -function gdpr_task_edit_gdpr_remove_form($form, &$form_state) { - $task = $form_state['task'] = $form_state['build_info']['args'][0]; - $form = gdpr_task_form($form, $form_state); - - $header_table = array( - 'Name', - 'Data', - 'Notes', - 'Right to access', - ); - $rows = gdpr_tasks_collect_rtf_data(user_load($task->user_id)); - - $data_table = array( - '#theme' => 'table', - '#header' => $header_table, - '#rows' => $rows, - '#caption' => 'Export data', - ); - - $form['data'] = array( - '#markup' => drupal_render($data_table), - ); - - $form['actions']['submit']['#value'] = t('Remove and Anonymise Data'); - $form['actions']['submit']['#name'] = 'remove'; - - if ($task->status == 'closed') { -// $form['actions']['#access'] = FALSE; - } - - return $form; -} - -/** - * Form callback for export tasks. - */ -function gdpr_task_edit_gdpr_sar_form($form, &$form_state) { - $task = $form_state['task'] = $form_state['build_info']['args'][0]; - $form = gdpr_task_form($form, $form_state); - - // Disable export field form element. - $form['gdpr_tasks_sar_export']['#disabled'] = TRUE; - - - $header_table = array( - 'Name', - 'Data', - 'Notes', - 'Right to access', - ); - $rows = gdpr_tasks_collect_rta_data(user_load($task->user_id)); - - $data_table = array( - '#theme' => 'table', - '#header' => $header_table, - '#rows' => $rows, - '#caption' => 'Export data', - ); - - $form['data'] = array( - '#markup' => drupal_render($data_table), - ); - - $form['actions']['submit']['#value'] = t('Process'); - $form['actions']['submit']['#name'] = 'export'; - - - if ($task->status == 'closed') { - $form['gdpr_tasks_manual_data']['#disabled'] = TRUE; - $form['actions']['#access'] = FALSE; - } - - return $form; -} - -/** - * Validate handler for removal tasks. - */ -function gdpr_task_edit_gdpr_remove_form_validate($form, &$form_state) { - gdpr_task_form_validate($form, $form_state); -} - -/** - * Validate handler for export tasks. - */ -function gdpr_task_edit_gdpr_sar_form_validate($form, &$form_state) { - gdpr_task_form_validate($form, $form_state); -} - -/** - * Submit handler for removal tasks. - */ -function gdpr_task_edit_gdpr_remove_form_submit($form, &$form_state) { - $anonymizer = new Anonymizer(); - $task = $form_state['task']; - $errors = $anonymizer->run($task); - - // Copy log to form_state. - $form_state['values']['gdpr_tasks_removal_log'] = $task->gdpr_tasks_removal_log; - - if (empty($errors)) { - gdpr_task_form_submit($form, $form_state); - } - else { - dpm($errors); - $form_state['rebuild'] = TRUE; - } - -} - -/** - * Submit handler for export tasks. - */ -function gdpr_task_edit_gdpr_sar_form_submit($form, &$form_state) { - gdpr_task_form_submit($form, $form_state); - - // Process the export. - /* @var GDPRTask $task */ - $task = $form_state['task']; - $manual = $form_state['values']['gdpr_tasks_manual_data'][LANGUAGE_NONE][0]['value']; - - // @todo add getOwner method to Task. - $data = gdpr_tasks_collect_rta_data(user_load($task->user_id)); - - $inc = array(); - foreach ($data as $key => $values) { - $rta = $values['rta']; - unset($values['rat']); - if ($rta == 'inc') { - $inc[$key] = $values; - } - } - - $file = $task->wrapper()->gdpr_tasks_sar_export->file->value(); - $file_name = $file->filename; - $file_uri = $file->uri; - $dirname = str_replace($file_name, '', $file_uri); - - $destination = gdpr_tasks_update_task_csv($inc, $dirname); - $export = file_get_contents($destination); - - $export .= $manual; - - // @todo Add headers to csv export. - file_save_data($export, $file_uri, FILE_EXISTS_REPLACE); -} - -/** - * Config form for automated emails for task requests. - */ -function gdpr_tasks_email_settings($form, &$form_state) { - $form['gdpr_tasks_emails'] = array('#tree' => TRUE); - $form['gdpr_tasks_emails']['emails'] = array( - '#type' => 'vertical_tabs', - ); - - $emails = variable_get('gdpr_tasks_emails', array()); - $tokens = array('site', 'gdpr_task'); - - $title = t('Request requested (by user)'); - $description = t('This email is sent when a task is requested by a user.'); - $form['gdpr_tasks_emails'] += gdpr_tasks_email_settings_subform('task_requested_self', $title, $description, $emails, $tokens); - - $title = t('Request requested (by staff)'); - $description = t('This email is sent when a task is requested by a staff member or administrator.'); - $form['gdpr_tasks_emails'] += gdpr_tasks_email_settings_subform('task_requested_other', $title, $description, $emails, $tokens); - - $title = t('Task processed'); - $description = t('This email is sent when a task has been prcessed by a staff member or administrator.'); - $form['gdpr_tasks_emails'] += gdpr_tasks_email_settings_subform('task_processed', $title, $description, $emails, $tokens); - - - // Make sure anything not exposed is preserved. - foreach ($emails as $key => $value) { - if (!isset($form['gdpr_tasks_emails'][$key])) { - $form['gdpr_tasks_emails'][$key] = array( - '#type' => 'value', - '#value' => $value, - ); - } - } - - $form['gdpr_tasks_emails_from'] = array( - '#type' => 'textfield', - '#title' => t('Email from address'), - '#description' => t('Leave blank to use the site wide email address.'), - '#default_value' => variable_get('gdpr_tasks_emails_from', NULL), - ); - - $form['#validate'][] = 'gdpr_tasks_email_settings_validate'; - $form['#submit'][] = 'gdpr_tasks_email_settings_submit'; - return system_settings_form($form); -} - -/** - * Validation handler for gdpr_tasks_email_settings(). - */ -function gdpr_tasks_email_settings_validate(&$form, &$form_state) { - foreach (element_children($form['gdpr_tasks_emails']) as $key) { - // Skip our vertical tabs. - if ($key == 'emails') { - continue; - } - - $element = $form['gdpr_tasks_emails'][$key]; - - // If enabled, check we have our required values. - $enabled = drupal_array_get_nested_value($form_state['values'], $element['enabled']['#parents']); - if (!empty($enabled) && !empty($element['enabled']['#commerce_booking_team_email_dependents'])) { - foreach ($element['enabled']['#commerce_booking_team_email_dependents'] as $array_parents) { - // Get hold of the sub element we are requiring. - $sub_element = drupal_array_get_nested_value($element, $array_parents); - if (!$sub_element) { - continue; - } - - // Get hold of it's value and check it. Show an error if it's empty. - $value = drupal_array_get_nested_value($form_state['values'], $sub_element['#parents']); - if (empty($value)) { - $error = t('%title is required if %set is enabled.', array( - '%title' => $sub_element['#title'], - '%set' => $element['#title'], - )); - form_error($sub_element, $error); - } - } - } - } -} - -/** - * Submission handler for gdpr_tasks_email_settings(). - */ -function gdpr_tasks_email_settings_submit(&$form, &$form_state) { - // Remove the vertical tabs hidden element. - unset($form_state['values']['gdpr_tasks_emails']['emails']); -} - -/** - * Build the form elements for a particular - * - * @param $key string - * The form key for the element. - * @param $title string - * The translated title for this email. - * @param $description string - * The translated description for this email. - * @param $settings array - * An array of settings for this email. - * @param array $tokens - * An optional array of tokens which are supported for this email. - * - * @return array - * A fieldset form element array. - */ -function gdpr_tasks_email_settings_subform($key, $title, $description, $settings = array(), $tokens = array()) { - // Pull the relevant key out of the settings. - $settings = isset($settings[$key]) ? $settings[$key] : array(); - - // Build our fieldset. - $element = array( - '#type' => 'fieldset', - '#collapsible' => TRUE, - '#group' => 'gdpr_tasks_emails][emails', - '#title' => $title, - '#description' => $description, - '#commerce_booking_teams_email' => TRUE, - ); - - // Allow this email to be enabled/disabled. - $element['enabled'] = array( - '#type' => 'checkbox', - '#title' => t('Enable %title', array('%title' => $title)), - '#default_value' => !empty($settings['enabled']), - '#commerce_booking_team_email_dependents' => array(array('email', 'subject'), array('email', 'body', 'value')), - ); - - $element['email'] = array( - '#type' => 'container', - '#states' => array( - 'visible' => array( - ":input[name=\"gdpr_tasks_emails[{$key}][enabled]\"]" => array('checked' => TRUE), - ), - ), - '#parents' => array('gdpr_tasks_emails', $key), - ); - - // If we have tokens, output some help information. - if (!empty($tokens)) { - $element['email']['tokens'] = array( - '#theme' => 'token_tree_link', - '#token_types' => $tokens, - ); - - } - - // Subject line. - $element['email']['subject'] = array( - '#type' => 'textfield', - '#title' => t('Subject'), - '#default_value' => isset($settings['subject']) ? $settings['subject'] : NULL, - '#maxlength' => 180, - '#states' => array( - 'required' => array( - ":input[name=\"gdpr_tasks_emails[{$key}][enabled]\"]" => array('checked' => TRUE), - ), - ), - ); - - // Body with format. - $element['email']['body'] = array( - '#type' => 'text_format', - '#title' => t('Body'), - '#rows' => 15, - '#format' => isset($settings['body']['format']) ? $settings['body']['format'] : NULL, - '#default_value' => isset($settings['body']['value']) ? $settings['body']['value'] : NULL, - '#states' => array( - 'required' => array( - ":input[name=\"gdpr_tasks_emails[{$key}][enabled]\"]" => array('checked' => TRUE), - ), - ), - ); - - // Return with our key. - return array($key => $element); -} diff --git a/modules/gdpr_tasks/gdpr_tasks.info b/modules/gdpr_tasks/gdpr_tasks.info deleted file mode 100644 index 531e5de..0000000 --- a/modules/gdpr_tasks/gdpr_tasks.info +++ /dev/null @@ -1,16 +0,0 @@ -name = General Data Protection Regulation (GDPR) - Tasks -description = Allow tracking of SARs and Removal requests. -core = 7.x - -dependencies[] = gdpr -dependencies[] = gdpr_fields -dependencies[] = entity -dependencies[] = views - -files[] = src/Anonymizer.php -files[] = src/Entity/GDPRTask.php -files[] = src/Entity/GDPRTaskInterface.php -files[] = src/Entity/GDPRTaskController.php -files[] = src/Entity/GDPRTaskUIController.php -files[] = src/Entity/GDPRTaskType.php -files[] = views/handlers/gdpr_tasks_handler_operations_field.inc diff --git a/modules/gdpr_tasks/gdpr_tasks.install b/modules/gdpr_tasks/gdpr_tasks.install deleted file mode 100644 index 609e5a9..0000000 --- a/modules/gdpr_tasks/gdpr_tasks.install +++ /dev/null @@ -1,311 +0,0 @@ - 'The base table for tasks.', - 'fields' => array( - 'id' => array( - 'description' => 'The primary identifier for a task.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE - ), - 'type' => array( - 'description' => 'The {gdpr_task_type} of this task.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '' - ), - 'language' => array( - 'description' => 'The {languages}.language of this task.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'user_id' => array( - 'description' => 'The {users}.uid that this task is for.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'status' => array( - 'description' => 'The text status of this task.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '' - ), - 'created' => array( - 'description' => 'The Unix timestamp when the task was created.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'changed' => array( - 'description' => 'The Unix timestamp when the task was most recently saved.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'complete' => array( - 'description' => 'The Unix timestamp when the task was completed.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'requested_by' => array( - 'description' => 'The {users}.uid that requested this task.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'processed_by' => array( - 'description' => 'The {users}.uid that processed this task.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('id'), - 'foreign keys' => array( - 'task_author' => array( - 'table' => 'users', - 'columns' => array('user_id' => 'uid'), - ), - 'task_requester' => array( - 'table' => 'users', - 'columns' => array('requested_by' => 'uid'), - ), - 'task_processor' => array( - 'table' => 'users', - 'columns' => array('processed_by' => 'uid'), - ), - ), - ); - - $schema['gdpr_task_type'] = array( - 'description' => 'Stores information about all defined profile types.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique profile type ID.', - ), - 'type' => array( - 'description' => 'The machine-readable name of this profile type.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - ), - 'label' => array( - 'description' => 'The human-readable name of this profile type.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'weight' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'The weight of this profile type in relation to others.', - ), - 'data' => array( - 'type' => 'text', - 'not null' => FALSE, - 'size' => 'big', - 'serialize' => TRUE, - 'description' => 'A serialized array of additional data related to this profile type.', - ), - 'status' => array( - 'type' => 'int', - 'not null' => TRUE, - // Set the default to ENTITY_CUSTOM without using the constant as it is - // not safe to use it at this point. - 'default' => 0x01, - 'size' => 'tiny', - 'description' => 'The exportable status of the entity.', - ), - 'module' => array( - 'description' => 'The name of the providing module if the entity has been defined in code.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - ), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'type' => array('type'), - ), - ); - - return $schema; -} - -/** - * Implements hook_schema(). - */ -function gdpr_tasks_install() { - $t = get_t(); - - // Add an export field to sar task. - if (!field_info_field('gdpr_tasks_sar_export')) { - $field = array( - 'field_name' => 'gdpr_tasks_sar_export', - 'type' => 'file', - 'locked' => TRUE, - 'settings' => array( - 'display_field' => FALSE, - 'uri_scheme' => 'private', - ), - 'cardinality' => 1, - ); - field_create_field($field); - } - - if (!field_info_instance('gdpr_task', 'gdpr_tasks_sar_export', 'gdpr_sar')) { - $instance = array( - 'label' => $t('Data Export'), - 'field_name' => 'gdpr_tasks_sar_export', - 'entity_type' => 'gdpr_task', - 'bundle' => 'gdpr_sar', - 'required' => TRUE, - 'widget' => array( - 'type' => 'file_generic', - ), - 'settings' => array( - 'file_display' => 'gdpr-exports', - 'file_extensions' => 'csv', - ), - 'display' => array( - 'default' => array( - 'type' => 'hidden', - ), - ), - ); - field_create_instance($instance); - } - - // Add an manual data override field to sar task. - if (!field_info_field('gdpr_tasks_manual_data')) { - $field = array( - 'field_name' => 'gdpr_tasks_manual_data', - 'type' => 'text_long', - 'locked' => TRUE, - 'cardinality' => 1, - ); - field_create_field($field); - } - if (!field_info_instance('gdpr_task', 'gdpr_tasks_manual_data', 'gdpr_sar')) { - $instance = array( - 'label' => $t('Data Override'), - 'field_name' => 'gdpr_tasks_manual_data', - 'entity_type' => 'gdpr_task', - 'bundle' => 'gdpr_sar', - 'required' => FALSE, - 'widget' => array( - 'type' => 'text_textarea', - ), - 'display' => array( - 'default' => array( - 'type' => 'hidden', - ), - ), - ); - field_create_instance($instance); - } - - // Add an manual data override field to sar task. - if (!field_info_field('gdpr_tasks_removal_log')) { - $field = array( - 'field_name' => 'gdpr_tasks_removal_log', - 'type' => 'text_long', - 'locked' => TRUE, - 'cardinality' => 1, - ); - field_create_field($field); - } - if (!field_info_instance('gdpr_task', 'gdpr_tasks_removal_log', 'gdpr_remove')) { - $instance = array( - 'label' => $t('Removal Log'), - 'field_name' => 'gdpr_tasks_removal_log', - 'entity_type' => 'gdpr_task', - 'bundle' => 'gdpr_remove', - 'required' => FALSE, - 'widget' => array( - 'type' => 'text_textarea', - ), - 'display' => array( - 'default' => array( - 'type' => 'hidden', - ), - ), - ); - field_create_instance($instance); - } - - // Add a notes field to all tasks. - if (!field_info_field('gdpr_tasks_notes')) { - $field = array( - 'field_name' => 'gdpr_tasks_notes', - 'type' => 'text_long', - 'locked' => TRUE, - 'cardinality' => 1, - ); - field_create_field($field); - } - foreach (array('gdpr_remove', 'gdpr_sar') as $bundle) { - if (!field_info_instance('gdpr_task', 'gdpr_tasks_notes', $bundle)) { - $instance = array( - 'label' => $t('Notes'), - 'field_name' => 'gdpr_tasks_notes', - 'entity_type' => 'gdpr_task', - 'bundle' => $bundle, - 'required' => FALSE, - 'widget' => array( - 'type' => 'text_textarea', - ), - 'display' => array( - 'default' => array( - 'type' => 'hidden', - ), - ), - ); - field_create_instance($instance); - } - } - - // Set default email values. - // @todo Make email content more useful. - $default_emails = array( - 'task_requested_self' => array( - 'enabled' => TRUE, - 'subject' => $t('Request submitted'), - 'body' => array( - 'value' => $t('A task has been requested.'), - ), - ), - 'task_requested_other' => array( - 'enabled' => FALSE, - ), - 'task_processed' => array( - 'enabled' => TRUE, - 'subject' => $t('Request complete'), - 'body' => array( - 'value' => $t('Your requested task has been completed.'), - ), - ), - ); - variable_set('gdpr_tasks_emails', $default_emails); -} diff --git a/modules/gdpr_tasks/gdpr_tasks.module b/modules/gdpr_tasks/gdpr_tasks.module deleted file mode 100644 index 2d71b9d..0000000 --- a/modules/gdpr_tasks/gdpr_tasks.module +++ /dev/null @@ -1,615 +0,0 @@ - t('Task'), - 'base table' => 'gdpr_task', - 'entity class' => 'GDPRTask', - 'controller class' => 'GDPRTaskController', - 'module' => 'gdpr_tasks', - 'admin ui' => array( - 'path' => 'admin/structure/gdpr-tasks', - 'file' => 'gdpr_tasks.admin.inc', - 'menu wildcard' => '%gdpr_task', - 'controller class' => 'GDPRTaskUIController', - ), - 'access callback' => 'gdpr_task_access', - 'entity keys' => array( - 'id' => 'id', - 'bundle' => 'type', - 'label' => 'id', - 'language' => 'language', - ), - 'bundle keys' => array( - 'bundle' => 'type', - ), - 'bundles' => array( - 'gdpr_remove' => array( - 'label' => 'Removal Request', - 'admin' => array( - 'path' => 'admin/structure/gdpr-tasks/manage/%', - 'real path' => 'admin/structure/gdpr-tasks/manage/gdpr_remove', - 'bundle argument' => 4, - 'access arguments' => array('administer task entities'), - ), - ), - 'gdpr_sar' => array( - 'label' => 'SARs Request', - 'admin' => array( - 'path' => 'admin/structure/gdpr-tasks/manage/%', - 'real path' => 'admin/structure/gdpr-tasks/manage/gdpr_sar', - 'bundle argument' => 4, - 'access arguments' => array('administer task entities'), - ), - ), - ), - 'fieldable' => TRUE, - ); - - $entities['gdpr_task_type'] = array( - 'label' => t('Task type'), - 'plural label' => t('Task types'), - 'description' => t('Task types for GDPR Tasks.'), - 'entity class' => 'GDPRTaskType', - 'controller class' => 'EntityAPIControllerExportable', - 'base table' => 'gdpr_task_type', - 'fieldable' => FALSE, - 'bundle of' => 'gdpr_task', - 'exportable' => TRUE, - 'entity keys' => array( - 'id' => 'id', - 'name' => 'type', - 'label' => 'label', - ), - 'access callback' => 'gdpr_task_type_access', - 'module' => 'gdpr_tasks', - 'admin ui' => array( - 'path' => 'admin/structure/gdpr-tasks', - 'file' => 'gdpr_tasks.admin.inc', - 'controller class' => 'EntityDefaultUIController', - ), - ); - - return $entities; -} - -/** - * Implements hook_theme(). - */ -function gdpr_tasks_theme() { - return array( - 'gdpr_task' => array( - 'render element' => 'elements', - 'template' => 'templates/gdpr_task', - ), - ); -} - -/** - * Implements hook_menu(). - */ -function gdpr_tasks_menu() { - $items['user/%user/gdpr/list'] = array( - 'title' => 'Summary', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items['user/%user/gdpr/requests'] = array( - 'title' => 'My data requests', - 'access arguments' => array('view own gdpr tasks'), - 'page callback' => 'gdpr_task_user_request', - 'page arguments' => array(1), - 'type' => MENU_LOCAL_TASK, - 'file' => 'gdpr_tasks.pages.inc', - ); - $items['user/%user/gdpr/requests/gdpr_remove/add'] = array( - 'title' => 'Request data removal', - 'page callback' => 'gdpr_tasks_request', - 'page arguments' => array(1, 4), - 'access arguments' => array('request gdpr tasks'), - 'type' => MENU_LOCAL_ACTION, - 'file' => 'gdpr_tasks.pages.inc', - ); - $items['user/%user/gdpr/requests/gdpr_sar/add'] = array( - 'title' => 'Request data export', - 'page callback' => 'gdpr_tasks_request', - 'page arguments' => array(1, 4), - 'access arguments' => array('request gdpr tasks'), - 'type' => MENU_LOCAL_ACTION, - 'file' => 'gdpr_tasks.pages.inc', - ); - - $items['admin/gdpr/task-email'] = array( - 'title' => 'Task Emails', - 'description' => 'Configure email templates to be sent for task requests.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('gdpr_tasks_email_settings'), - 'access arguments' => array('administer gdpr settings'), - 'file' => 'gdpr_tasks.admin.inc', - 'weight' => 99, - ); - return $items; -} - -/** - * Implements hook_permission(). - */ -function gdpr_tasks_permission() { - // @todo IMPORTANT!! Permission review. - $perms = array( - 'administer task entities' => array( - 'title' => t('Administer task entities'), - 'restrict access' => TRUE, - ), - 'administer task type entities' => array( - 'title' => t('Administer task entities'), - 'restrict access' => TRUE, - ), - 'process gdpr tasks' => array( - 'title' => t('Process GDPR tasks'), - 'restrict access' => TRUE, - ), - 'view gdpr tasks' => array( - 'title' => t('View GDPR tasks'), - 'description' => t(''), - ), - 'view own gdpr tasks' => array( - 'title' => t('View you own GDPR tasks'), - 'description' => t(''), - ), - 'edit gdpr tasks' => array( - 'title' => t('Edit GDPR tasks'), - 'description' => t(''), - ), - 'request gdpr tasks' => array( - 'title' => t('Request new GDPR tasks for themselves'), - 'description' => t(''), - ), - 'request any gdpr tasks' => array( - 'title' => t('Request new GDPR tasks for anyone'), - 'description' => t(''), - ), - ); - - return $perms; -} - -/** - * Implements hook_views_api(). - */ -function gdpr_tasks_views_api($module, $api) { - return array('version' => 3); -} - -/** - * Implements hook_ctools_plugin_directory(). - */ -function gdpr_tasks_ctools_plugin_directory($owner, $plugin_type) { - if ($owner == 'ctools' && $plugin_type == 'content_types') { - return 'plugins/' . $plugin_type; - } -} - -/** - * Implements hook_default_gdpr_task_type(). - */ -function gdpr_tasks_default_gdpr_task_type() { - $types['gdpr_sar'] = new GDPRTaskType(array( - 'type' => 'gdpr_sar', - 'label' => t('SARs Request'), - 'weight' => 0, - 'locked' => TRUE, - )); - $types['gdpr_remove'] = new GDPRTaskType(array( - 'type' => 'gdpr_remove', - 'label' => t('Removal Request'), - 'weight' => 0, - 'locked' => TRUE, - )); - return $types; -} - -/** - * Implements hook_mail(). - */ -function gdpr_tasks_mail($key, &$message, $params) { - $context = $params['context']; - $options = array('clear' => TRUE); - - $message['to'] = token_replace($message['to'], $context, $options); - $message['subject'] = token_replace($context['subject'], $context, $options); - $message['body'][] = token_replace($context['body'], $context, $options); - $message['params']['plaintext'] = token_replace($params['plaintext'], $context, $options); -} - -/** - * Access callback for task entities. - */ -function gdpr_task_access($op, $task = NULL, $account = NULL) { - // @todo Support other operations. - if (user_access('administer task entities', $account)) { - return TRUE; - } - - if (user_access('process gdpr tasks', $account)) { - return TRUE; - } -} - -/** - * Access callback for task type entities. - */ -function gdpr_task_type_access($op, $task_type = NULL, $account = NULL) { - if (user_access('administer task type entities', $account)) { - return TRUE; - } -} - -/** - * Load a GDPR Task entity. - * - * @param $id - * The id of the task. - * - * @return GDPRTask|null - * The fully loaded task entity if available. - */ -function gdpr_task_load($id) { - return entity_load_single('gdpr_task', $id); -} - -/** - * Load tasks from the database. - * - * @param $ids - * An array of task IDs. - * @param $conditions - * (deprecated) An associative array of conditions on the {gdpr_task} - * table, where the keys are the database fields and the values are the - * values those fields must have. Instead, it is preferable to use - * EntityFieldQuery to retrieve a list of entity IDs loadable by - * this function. - * @param $reset - * Whether to reset the internal static entity cache. - * - * @return - * An array of task objects, indexed by task ID. - * - * @see entity_load() - * @see EntityFieldQuery - */ -function gdpr_task_load_multiple($ids = FALSE, $conditions = array(), $reset = FALSE) { - return entity_load('gdpr_task', $ids, $conditions, $reset); -} - - -function gdpr_tasks_get_user_tasks($user, $gdpr_task_type = NULL) { - $query = db_select('gdpr_task', 't') - ->fields('t', array('id')) - ->condition('user_id', $user->uid); - - if ($gdpr_task_type) { - $query->condition('type', $gdpr_task_type); - } - - $result = $query->execute() - ->fetchAssoc(); - - if (!empty($result)) { - return gdpr_task_load_multiple($result); - } - - return $result; -} - -/** - * Implements hook_entity_load(). - * - * TEMP function for api testing. - */ -function gdpr_tasks_entity_load($entities, $type) { - if ($type != 'gdpr_task') { - return; - } - return; - - foreach ($entities as $entity) { - if ($entity->type != 'gdpr_sar') { - continue; - } - - - ctools_include('context'); - ctools_include('plugins'); - - - $context = ctools_context_create('entity:user', $entity->user_id); - $available_relationships = ctools_context_get_relevant_relationships(array($context)); - - // @todo work out how to configure any required entity_properties. - if (isset($available_relationships['entity_property'])) { - unset($available_relationships['entity_property']); - } - - if (isset($available_relationships['user_category_edit_form_from_user'])) { - unset($available_relationships['user_category_edit_form_from_user']); - } - - foreach (array_keys($available_relationships) as $relationship) { - $relationship = ctools_get_relationship($relationship); - // @todo can identifier be loaded elsewhere? - $relationship['identifier'] = $relationship['title']; - - $entity_contexts = gdpr_fields_get_contexts_from_relationship($relationship, $context); - - if (!is_array($entity_contexts)) { - $entity_contexts = array($entity_contexts); - } - - foreach ($entity_contexts as $entity_context) { - if (!empty($entity_context->data)) { -// dpm($entity_context->data); - } - } - } - } -} - - -/** - * Implements hook_entity_load(). - */ -function gdpr_tasks_entity_presave($entity, $type) { - if ($type != 'gdpr_task') { - return; - } - - if ($entity->type != 'gdpr_sar') { - return; - } - - - if (empty($entity->gdpr_tasks_sar_export)) { - $wrapper = entity_metadata_wrapper('gdpr_task', $entity); - - $info = field_info_field('gdpr_tasks_sar_export'); - $instance = field_info_instance('gdpr_task', 'gdpr_tasks_sar_export', 'gdpr_sar'); - - $dirname = $info['settings']['uri_scheme'] . '://' . $instance['settings']['file_display']; - - $data = gdpr_tasks_collect_rta_data(user_load($entity->user_id)); - - $inc = array(); - $maybe = array(); - foreach ($data as $key => $values) { - $rta = $values['rta']; - unset($values['rta']); - $inc[$key] = $values; - - if ($rta == 'maybe') { - $maybe[$key] = $values; - } - } - - - $destination = gdpr_tasks_update_task_csv($inc, $dirname); - $export = file_get_contents($destination); - - // Generate a file entity. - // @todo Add headers to csv export. - $file = file_save_data($export, $destination, FILE_EXISTS_REPLACE); - - $wrapper->gdpr_tasks_sar_export->file = $file; - - $temp = gdpr_tasks_update_task_csv($maybe, $dirname); - $entity->gdpr_tasks_manual_data[LANGUAGE_NONE][0]['value'] = file_get_contents($temp); - } - -} - -function gdpr_tasks_update_task_csv($data, $dirname = 'private://') { - // Prepare destination. - file_prepare_directory($dirname, FILE_CREATE_DIRECTORY); - - // Generate a file entity. - $random = new GDPRUtilRandom(); - $destination = $dirname . '/' . $random->name(10) . '.csv'; - - // Update csv with actual data. - $fp = fopen($destination, 'w'); - foreach($data as $line){ - fputcsv($fp, $line); - } - fclose($fp); - - return $destination; -} - -/** - * @param $user - */ -function gdpr_tasks_collect_rta_data($user, $include_plugins = FALSE) { - ctools_include('plugins'); - ctools_include('export'); - - $plugins = ctools_export_load_object('gdpr_fields_field_data'); - $fields = array(); - - $gdpr_entities = array(); - gdpr_fields_collect_gdpr_entities($gdpr_entities, 'user', $user); - - dpm($gdpr_entities); - - foreach ($gdpr_entities as $entity_type => $bundles) { - foreach ($bundles as $entity_bundle => $entities) { - foreach ($entities as $entity) { - foreach ($entity as $key => $value) { - $plugin_name = "{$entity_type}|{$entity_bundle}|{$key}"; - $wrapper = entity_metadata_wrapper($entity_type, $entity); - - if (isset($plugins[$plugin_name])) { - $field = $plugins[$plugin_name]; - - /* @var GDPRFieldData $field */ - if ($field->disabled) { - continue; - } - - if ($field->getSetting('gdpr_fields_rta')) { - // @todo Better data rendering. - if (is_array($value)) { - $value = ''; - if ($wrapper->{$key} instanceof EntityValueWrapper) { - $value .= ' ' . $wrapper->{$key}->value(); - } - elseif ($wrapper->{$key} instanceof EntityStructureWrapper) { - $key_data = $wrapper->{$key}->value(); - if (!empty($key_data)) { - foreach (array_keys($key_data) as $sub_key) { - $list = $wrapper->{$key}->getPropertyInfo(); - if (!empty($list) && in_array($sub_key, array_keys($list)) && $wrapper->{$key}->{$sub_key} instanceof EntityValueWrapper) { - $value .= ' ' . $wrapper->{$key}->{$sub_key}->value(); - } - } - } - } - } - $data = array( - 'label' => $field->getSetting('label'), - 'value' => (string) $value, - 'notes' => $field->getSetting('notes'), - 'rta' => $field->getSetting('gdpr_fields_rta'), - ); - - if ($include_plugins) { - $data['plugin'] = $field; - } - $fields[$plugin_name] = $data; - } - } - } - } - } - } - - return $fields; -} - - -/** - * @param $user - */ -function gdpr_tasks_collect_rtf_data($user, $include_plugins = FALSE) { - ctools_include('plugins'); - ctools_include('export'); - - $plugins = ctools_export_load_object('gdpr_fields_field_data'); - $fields = array(); - - $gdpr_entities = array(); - gdpr_fields_collect_gdpr_entities($gdpr_entities, 'user', $user); - - foreach ($gdpr_entities as $entity_type => $bundles) { - foreach ($bundles as $entity_bundle => $entities) { - foreach ($entities as $entity) { - foreach ($entity as $key => $value) { - $plugin_name = "{$entity_type}|{$entity_bundle}|{$key}"; - $wrapper = entity_metadata_wrapper($entity_type, $entity); - - if (isset($plugins[$plugin_name])) { - $field = $plugins[$plugin_name]; - - /* @var GDPRFieldData $field */ - if ($field->disabled) { - continue; - } - - if ($field->getSetting('gdpr_fields_rtf')) { - // @todo Better data rendering. - if (is_array($value)) { - $value = ''; - if ($wrapper->{$key} instanceof EntityValueWrapper) { - $value .= ' ' . $wrapper->{$key}->value(); - } - - elseif ($wrapper->{$key} instanceof EntityStructureWrapper) { - $key_data = $wrapper->{$key}->value(); - if (!empty($key_data)) { - foreach (array_keys($key_data) as $sub_key) { - $list = $wrapper->{$key}->getPropertyInfo(); - if (!empty($list) && in_array($sub_key, array_keys($list)) && $wrapper->{$key}->{$sub_key} instanceof EntityValueWrapper) { - $value .= ' ' . $wrapper->{$key}->{$sub_key}->value(); - } - } - } - } - } - $data = array( - 'label' => $field->getSetting('label'), - 'value' => (string) $value, - 'notes' => $field->getSetting('notes'), - 'rtf' => $field->getSetting('gdpr_fields_rtf'), - ); - - if ($include_plugins) { - $data['plugin'] = $field; - } - $fields[$plugin_name] = $data; - } - } - } - } - } - } - - return $fields; -} - -/** - * Helper function to send emails notifications to users. - * - * @param string $key - * The email key to send. - * @param GDPRTask $task - * The wrapped ticket entity. - * @param array $tokens - * Optional array of tokens suitable for token_replace(). - * gdpr_task is be added automatically. - */ -function gdpr_tasks_send_mail($key, GDPRTask $task, array $tokens = array()) { - $emails = variable_get('gdpr_tasks_emails', array()); - - // Allow other modules to alter the task emails. - drupal_alter('gdpr_tasks_send_mail', $emails, $key, $ticket, $tokens); - - if (empty($emails[$key]['enabled'])) { - return; - } - $email = $emails[$key]; - - // Gather our params. - global $language; - $tokens['gdpr_task'] = $task; - $params = array( - 'context' => array( - 'subject' => $email['subject'], - 'body' => check_markup($email['body']['value'], $email['body']['format'], $language->language, TRUE), - ) + $tokens, - 'attachments' => NULL, - 'plaintext' => NULL, - ); - - $from = variable_get('gdpr_tasks_emails_from', NULL); - $to = $task->getOwner()->mail; - drupal_mail('gdpr_tasks', 'gdpr_tasks_' . $key, $to, $language, $params, $from); -} diff --git a/modules/gdpr_tasks/gdpr_tasks.pages.inc b/modules/gdpr_tasks/gdpr_tasks.pages.inc deleted file mode 100644 index d71f4a0..0000000 --- a/modules/gdpr_tasks/gdpr_tasks.pages.inc +++ /dev/null @@ -1,50 +0,0 @@ - array( - '#markup' => 'Make data access requests.' - ), - ); -} - -/** - * Request page for user. - */ -function gdpr_tasks_request($account, $gdpr_task_type) { - $tasks = gdpr_tasks_get_user_tasks($account, $gdpr_task_type); - - if (!empty($tasks)) { - drupal_set_message('You already have a pending task.', 'warning'); - } - else { - global $user; - - $values = array( - 'type' => $gdpr_task_type, - 'user_id' => $account->uid, - 'requested_by' => $user->uid, - ); - $task = entity_create('gdpr_task', $values); - $task->save(); - - // Send confirmation email to user. - if ($task->requested_by == $task->user_id) { - gdpr_tasks_send_mail('task_requested_self', $task); - } - else { - gdpr_tasks_send_mail('task_requested_other', $task); - } - drupal_set_message('Your request has been logged.'); - } - - drupal_goto("user/{$account->uid}/gdpr/requests"); -} \ No newline at end of file diff --git a/modules/gdpr_tasks/gdpr_tasks.views.inc b/modules/gdpr_tasks/gdpr_tasks.views.inc deleted file mode 100644 index a04325a..0000000 --- a/modules/gdpr_tasks/gdpr_tasks.views.inc +++ /dev/null @@ -1,51 +0,0 @@ - t('Created date'), - 'help' => t('The date the task was requested.'), - 'field' => array( - 'handler' => 'views_handler_field_date', - 'click sortable' => TRUE, - ), - 'sort' => array( - 'handler' => 'views_handler_sort_date', - ), - 'filter' => array( - 'handler' => 'views_handler_filter_date', - ), - ); - - $data['gdpr_task']['changed'] = array( - 'title' => t('Changed date'), - 'help' => t('The date the task was last updated.'), - 'field' => array( - 'handler' => 'views_handler_field_date', - 'click sortable' => TRUE, - ), - 'sort' => array( - 'handler' => 'views_handler_sort_date', - ), - 'filter' => array( - 'handler' => 'views_handler_filter_date', - ), - ); - - $data['gdpr_task']['operations'] = array( - 'field' => array( - 'title' => t('Operations links'), - 'help' => t('Display all operations available for this task.'), - 'handler' => 'gdpr_tasks_handler_operations_field', - ), - ); -} diff --git a/modules/gdpr_tasks/gdpr_tasks.views_default.inc b/modules/gdpr_tasks/gdpr_tasks.views_default.inc deleted file mode 100644 index 7fb72f2..0000000 --- a/modules/gdpr_tasks/gdpr_tasks.views_default.inc +++ /dev/null @@ -1,103 +0,0 @@ -name = 'gdpr_tasks_my_data_requests'; - $view->description = ''; - $view->tag = 'default'; - $view->base_table = 'gdpr_task'; - $view->human_name = 'GDRP User Data Requests'; - $view->core = 7; - $view->api_version = '3.0'; - $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ - - /* Display: Master */ - $handler = $view->new_display('default', 'Master', 'default'); - $handler->display->display_options['title'] = 'GDRP User Data Requests'; - $handler->display->display_options['use_more_always'] = FALSE; - $handler->display->display_options['access']['type'] = 'perm'; - $handler->display->display_options['access']['perm'] = 'view gdpr tasks'; - $handler->display->display_options['cache']['type'] = 'none'; - $handler->display->display_options['query']['type'] = 'views_query'; - $handler->display->display_options['exposed_form']['type'] = 'basic'; - $handler->display->display_options['pager']['type'] = 'none'; - $handler->display->display_options['style_plugin'] = 'table'; - /* Field: Task: Created date */ - $handler->display->display_options['fields']['created']['id'] = 'created'; - $handler->display->display_options['fields']['created']['table'] = 'gdpr_task'; - $handler->display->display_options['fields']['created']['field'] = 'created'; - $handler->display->display_options['fields']['created']['label'] = 'Requested date'; - $handler->display->display_options['fields']['created']['date_format'] = 'short'; - $handler->display->display_options['fields']['created']['second_date_format'] = 'panopoly_time'; - /* Field: Task: Data Export */ - $handler->display->display_options['fields']['gdpr_tasks_sar_export']['id'] = 'gdpr_tasks_sar_export'; - $handler->display->display_options['fields']['gdpr_tasks_sar_export']['table'] = 'field_data_gdpr_tasks_sar_export'; - $handler->display->display_options['fields']['gdpr_tasks_sar_export']['field'] = 'gdpr_tasks_sar_export'; - $handler->display->display_options['fields']['gdpr_tasks_sar_export']['click_sort_column'] = 'fid'; - $handler->display->display_options['fields']['gdpr_tasks_sar_export']['type'] = 'file_download_link'; - $handler->display->display_options['fields']['gdpr_tasks_sar_export']['settings'] = array( - 'text' => 'Download', - ); - /* Contextual filter: Task: User_id */ - $handler->display->display_options['arguments']['user_id']['id'] = 'user_id'; - $handler->display->display_options['arguments']['user_id']['table'] = 'gdpr_task'; - $handler->display->display_options['arguments']['user_id']['field'] = 'user_id'; - $handler->display->display_options['arguments']['user_id']['default_action'] = 'default'; - $handler->display->display_options['arguments']['user_id']['default_argument_type'] = 'user'; - $handler->display->display_options['arguments']['user_id']['default_argument_options']['user'] = FALSE; - $handler->display->display_options['arguments']['user_id']['summary']['number_of_records'] = '0'; - $handler->display->display_options['arguments']['user_id']['summary']['format'] = 'default_summary'; - $handler->display->display_options['arguments']['user_id']['summary_options']['items_per_page'] = '25'; - /* Filter criterion: Task: Type */ - $handler->display->display_options['filters']['type']['id'] = 'type'; - $handler->display->display_options['filters']['type']['table'] = 'gdpr_task'; - $handler->display->display_options['filters']['type']['field'] = 'type'; - $handler->display->display_options['filters']['type']['value'] = array( - 'gdpr_sar' => 'gdpr_sar', - ); - /* Filter criterion: Task: Status */ - $handler->display->display_options['filters']['status']['id'] = 'status'; - $handler->display->display_options['filters']['status']['table'] = 'gdpr_task'; - $handler->display->display_options['filters']['status']['field'] = 'status'; - $handler->display->display_options['filters']['status']['value'] = 'closed'; - - /* Display: Page */ - $handler = $view->new_display('page', 'Page', 'page'); - $handler->display->display_options['path'] = 'user/%/gdpr/requests'; - $handler->display->display_options['menu']['type'] = 'tab'; - $handler->display->display_options['menu']['title'] = 'My data requests'; - $handler->display->display_options['menu']['weight'] = '0'; - $handler->display->display_options['menu']['context'] = 0; - $handler->display->display_options['menu']['context_only_inline'] = 0; - - /* Display: Content pane */ - $handler = $view->new_display('panel_pane', 'Content pane', 'panel_pane_1'); - $handler->display->display_options['defaults']['filter_groups'] = FALSE; - $handler->display->display_options['defaults']['filters'] = FALSE; - $handler->display->display_options['pane_category']['name'] = 'GDPR'; - $handler->display->display_options['pane_category']['weight'] = '0'; - $handler->display->display_options['argument_input'] = array( - 'user_id' => array( - 'type' => 'context', - 'context' => 'entity:user.uid', - 'context_optional' => 0, - 'panel' => '0', - 'fixed' => '', - 'label' => 'Task: User_id', - ), - ); - - $views['gdpr_tasks_my_data_requests'] = $view; - - return $views; -} diff --git a/modules/gdpr_tasks/plugins/content_types/gdpr_tasks_create_request_on_behalf_of_user.inc b/modules/gdpr_tasks/plugins/content_types/gdpr_tasks_create_request_on_behalf_of_user.inc deleted file mode 100644 index fea2800..0000000 --- a/modules/gdpr_tasks/plugins/content_types/gdpr_tasks_create_request_on_behalf_of_user.inc +++ /dev/null @@ -1,147 +0,0 @@ - t('GDPR Task request form'), - 'content_types' => 'gdpr_tasks_create_request_on_behalf_of_user', - // 'single' means not to be subtyped. - 'single' => TRUE, - // Name of a function which will render the block. - 'render callback' => 'gdpr_tasks_create_request_on_behalf_of_user_render', - - // Icon goes in the directory with the content type. - 'description' => t('Show a form to request GDPR tasks.'), - 'required context' => new ctools_context_required(t('User'), 'entity:user'), - 'edit form' => 'gdpr_tasks_create_request_on_behalf_of_user_edit_form', - 'admin title' => 'gdpr_tasks_create_request_on_behalf_of_user_admin_title', - - // presents a block which is used in the preview of the data. - // Pn Panels this is the preview pane shown on the panels building page. - 'category' => array(t('GDPR'), 0), -); - -/** - * Render the User GDPR request form - * - * @param $subtype - * @param $conf - * Configuration as done at admin time - * @param $args - * @param $context - * Context - in this case we don't have any - * - * @return - * An object with at least title and content members - */ -function gdpr_tasks_create_request_on_behalf_of_user_render($subtype, $conf, $args, $context) { - $block = new stdClass(); - $block->title = t('Request task on user behalf'); - $block->content = ''; - - $user = $form_state['#user'] = $context->data; - - if (!empty($user)) { - // Get hold of the form - $form = drupal_get_form('gdpr_tasks_create_request_on_behalf_of_user_form', $user, $conf); - $block->content = drupal_render($form); - } - - return $block; -} - -/** - * Form - */ -function gdpr_tasks_create_request_on_behalf_of_user_form($form, &$form_state, $user, $conf = NULL) { -// form_load_include($form_state, 'inc', 'party', 'party.pages'); - form_load_include($form_state, 'inc', 'gdpr_tasks', 'plugins/content_types/gdpr_tasks_create_request_on_behalf_of_user'); - - $form_state['#user'] = $user; - $form['notes'] = array( - '#type' => 'textarea', - '#title' => t('Notes'), - '#description' => t('Enter the reason for creating this request.'), - ); - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['export'] = array( - '#type' => 'submit', - '#submit' => array('gdpr_tasks_create_request_on_behalf_of_user_form_export_submit'), - '#value' => t('Request data export'), - '#weight' => 40, - ); - - $form['actions']['removal'] = array( - '#type' => 'submit', - '#submit' => array('gdpr_tasks_create_request_on_behalf_of_user_form_removal_submit'), - '#value' => t('Request data removal'), - '#weight' => 40, - ); - - return $form; -} - -/** - * Submit Handler - */ -function gdpr_tasks_create_request_on_behalf_of_user_form_removal_submit(&$form, &$form_state) { - // Make sure we stop it redirecting anywhere it shouldn't... - unset($form_state['redirect']); -// $conf = $form_state['build_info']['args'][1]; - $options = array('query' => drupal_get_destination()); - $path = 'user/' . $form_state['#user']->uid . '/gdpr/requests/gdpr_remove/add'; - drupal_goto($path, $options); -} - -/** - * Submit Handler - */ -function gdpr_tasks_create_request_on_behalf_of_user_form_export_submit(&$form, &$form_state) { - // Make sure we stop it redirecting anywhere it shouldn't... - unset($form_state['redirect']); -// $conf = $form_state['build_info']['args'][1]; - $options = array('query' => drupal_get_destination()); - $path = 'user/' . $form_state['#user']->uid . '/gdpr/requests/gdpr_sar/add'; - drupal_goto($path, $options); -} - -/** - * Config Form - */ -function gdpr_tasks_create_request_on_behalf_of_user_edit_form($form, &$form_state) { - return $form; -} - -function gdpr_tasks_create_request_on_behalf_of_user_edit_form_submit(&$form, &$form_state) { - foreach (element_children($form) as $key) { - if (!empty($form_state['values'][$key])) { - $form_state['conf'][$key] = $form_state['values'][$key]; - } - } -} - -/** - * Title Callback - */ -function gdpr_tasks_create_request_on_behalf_of_user_admin_title($subtype, $conf, $context = NULL) { - if ($conf['override_title'] && !empty($conf['override_title_text'])) { - $output = format_string('"@context" !title', array( - '@context' => $context->identifier, - '!title' => filter_xss_admin($conf['override_title_text']), - )); - } - else { - $output = t('"@context" Request task on user behalf', array( - '@context' => $context->identifier, - )); - } - return $output; -} diff --git a/modules/gdpr_tasks/src/Anonymizer.php b/modules/gdpr_tasks/src/Anonymizer.php deleted file mode 100644 index 778b921..0000000 --- a/modules/gdpr_tasks/src/Anonymizer.php +++ /dev/null @@ -1,313 +0,0 @@ -getOwner(); - - $errors = []; - $entities = []; - $successes = []; - $failures = []; - $log = []; - - if (!$this->checkExportDirectoryExists()) { - $errors[] = 'An export directory has not been set. Please set this under Configuration -> GDPR -> Right to be Forgotten'; - } - - foreach (gdpr_tasks_collect_rtf_data($user, TRUE) as $plugin_id => $data) { - $plugin = $data['plugin']; - unset($data['plugin']); - - $this->plugins[$plugin->entity_type][$plugin_id] = $plugin; - $this->plugin_data[$plugin->entity_type][$plugin_id] = $data; - } - gdpr_fields_collect_gdpr_entities($entities, 'user', $user); - - foreach ($entities as $entity_type => $bundles) { - foreach ($bundles as $entity_bundle => $entities) { - foreach ($entities as $bundle_entity_id => $bundle_entity) { - - // Re-load a fresh copy of the bundle entity from storage so we don't - // end up modifying any other references to the entity in memory. - $bundle_entity = entity_load_unchanged($entity_type, $bundle_entity_id); - $entity_success = TRUE; - - foreach ($this->getFieldsToProcess($entity_type, $bundle_entity) as $field_info) { - $mode = $field_info['mode']; - - $success = TRUE; - $msg = NULL; - $sanitizer = ''; - - if ($mode == 'anonymise') { - list($success, $msg, $sanitizer) = $this->anonymize($field_info, $bundle_entity); - } - elseif ($mode == 'remove') { - list($success, $msg) = $this->remove($field_info, $bundle_entity); - } - - if ($success === TRUE) { - $log[] = 'success'; - $log[] = [ - 'entity_id' => $bundle_entity_id, - 'entity_type' => $entity_type . '.' . $entity_bundle, - 'field_name' => $field_info['field'], - 'action' => $mode, - 'sanitizer' => $sanitizer, - ]; - } - else { - // Could not anonymize/remove field. Record to errors list. - // Prevent entity from being saved. - $entity_success = FALSE; - $errors[] = $msg; - $log[] = 'error'; - $log[] = [ - 'error' => $msg, - 'entity_id' => $bundle_entity_id, - 'entity_type' => $entity_type . '.' . $entity_bundle, - 'field_name' => $field_info['field'], - 'action' => $mode, - 'sanitizer' => $sanitizer, - ]; - } - } - - if ($entity_success) { - $successes[$entity_type][$bundle_entity_id] = $bundle_entity; - } - else { - $failures[] = $bundle_entity; - } - } - } - } - - // @todo Better log field. - $task->wrapper()->gdpr_tasks_removal_log = json_encode($log); - - if (count($failures) === 0) { - $tx = db_transaction(); - - try { - /* @var EntityInterface $entity */ - foreach ($successes as $entity_type => $entities) { - foreach ($entities as $entity) { - entity_save($entity_type, $entity); - } - } - // Re-fetch the user so we see any changes that were made. - $user = entity_load_unchanged('user', $task->user_id); - user_save($user, array('status' => 0)); - - // @todo Write a log to file system. -// $this->writeLogToFile($task, $log); - } - catch (\Exception $e) { - $tx->rollback(); - $errors[] = $e->getMessage(); - } - } - - return $errors; - } - - /** - * Removes the field value. - * - * @param string $field_info - * The current field to process. - * @param EntityInterface|stdClass $entity - * The current field to process. - * - * @return array - * First element is success boolean, second element is the error message. - */ - private function remove($field_info, $entity) { - try { - /* @var EntityDrupalWrapper $wrapper */ - $wrapper = entity_metadata_wrapper($field_info['entity_type'], $entity); - - /* @var EntityMetadataWrapper $field */ - $field = $wrapper->{$field_info['field']}; - $this->clearField($field); - return [TRUE, NULL]; - } - catch (Exception $e) { - return [FALSE, $e->getMessage()]; - } - } - - /** - * Removes the field value. - * - * @param EntityMetadataWrapper $field - * The current field to process. - * - * @return array - * First element is success boolean, second element is the error message. - */ - protected function clearField($field) { - if ($field instanceof EntityValueWrapper) { - // @todo Add any other types that come up. - switch ($field->info()['type']) { - case 'text': - $field->set(''); - break; - - default: - $field->set(NULL); - } - } - elseif ($field instanceof EntityListWrapper) { - $field->set(array()); - } - elseif ($field instanceof EntityStructureWrapper) { - $list = $field->getPropertyInfo(); - if (!empty($list) && !empty($field->value())) { - foreach (array_keys($field->value()) as $key) { - if (!empty($list) && in_array($key, array_keys($list))) { - $sub_field = $field->{$key}; - $this->clearField($sub_field); - } - } - } - } - } - - /** - * Runs anonymize functionality against a field. - * - * @param string $field_info - * The field to anonymise. - * @param $entity - * The parent entity. - * - * @return array - * First element is success boolean, second element is the error message. - */ - private function anonymize($field_info, $entity) { - $sanitizer_id = $this->getSanitizerId($field_info, $entity); - - if (!$sanitizer_id) { - return [ - FALSE, - "Could not anonymize field {$field_info['field']}. Please consider changing this field from 'anonymize' to 'remove', or register a custom sanitizer.", - NULL, - ]; - } - - try { - $plugin = gdpr_dump_get_gdpr_sanitizer($sanitizer_id); - $class = ctools_plugin_get_class($plugin, 'handler'); - /* @var GDPRSanitizerDefault $sanitizer */ - $sanitizer = $class::create($plugin); - - $wrapper = entity_metadata_wrapper($field_info['entity_type'], $entity); - - $wrapper->{$field_info['field']} = $sanitizer->sanitize($field_info['value'], $wrapper->{$field_info['field']}); - return [TRUE, NULL, $sanitizer_id]; - } - catch (\Exception $e) { - return [FALSE, $e->getMessage(), NULL]; - } - } - - - /** - * Checks that the export directory has been set. - * - * @return bool - * Indicates whether the export directory has been configured and exists. - */ - private function checkExportDirectoryExists() { - // @todo Configure export directory. - $directory = 'private://gdpr-export'; - - return !empty($directory) && file_prepare_directory($directory, FILE_CREATE_DIRECTORY); - } - - /** - * Gets fields to anonymize/remove. - * - * @param $entity - * The entity to anonymise. - * - * @return array - * Array containing metadata about the entity. - * Elements are entity_type, bundle, field, value, mode and plugin. - */ - private function getFieldsToProcess($entity_type, $entity) { - if (!isset($this->plugins[$entity_type])) { - return array(); - } - - $plugins = $this->plugins[$entity_type]; - list(, , $bundle) = entity_extract_ids($entity_type, $entity); - - // Get fields for entity. - $fields = []; - foreach ($entity as $field_id => $field) { - $plugin_name = "{$entity_type}|{$bundle}|{$field_id}"; - - if (isset($plugins[$plugin_name])) { - $plugin = $plugins[$plugin_name]; - $data = $this->plugin_data[$entity_type][$plugin_name]; - - if (!empty($data['rtf']) && $data['rtf'] !== 'no') { - $fields[] = array( - 'entity_type' => $entity_type, - 'bundle' => $bundle, - 'field' => $field_id, - 'value' => $data['value'], - 'mode' => $data['rtf'], - 'plugin' => $plugin, - ); - } - } - } - - return $fields; - } - - /** - * Gets the ID of the sanitizer plugin to use on this field. - * - * @param string $field_info - * The field to anonymise. - * @param $entity - * The parent entity. - * - * @return string - * The sanitizer ID or null. - */ - private function getSanitizerId($field_info, $entity) { - // First check if this field has a sanitizer defined. - $sanitizer = $field_info['plugin']->settings['gdpr_fields_sanitizer']; - - // @todo Allow sanitizers to fall back to type selection relevant for the field type. - if (!$sanitizer) { - $sanitizer = 'gdpr_sanitizer_text'; - } - return $sanitizer; - } - -} diff --git a/modules/gdpr_tasks/src/Entity/GDPRTask.php b/modules/gdpr_tasks/src/Entity/GDPRTask.php deleted file mode 100644 index 397076f..0000000 --- a/modules/gdpr_tasks/src/Entity/GDPRTask.php +++ /dev/null @@ -1,108 +0,0 @@ -user_id); - } - - /** - * {@inheritdoc} - */ - protected function defaultLabel() { - return "Task {$this->id}"; - } - - /** - * {@inheritdoc} - */ - public function bundleLabel() { - // Add in the translated specified label property. - return $this->entityInfo['bundles'][$this->bundle()]['label']; - } -} \ No newline at end of file diff --git a/modules/gdpr_tasks/src/Entity/GDPRTaskController.php b/modules/gdpr_tasks/src/Entity/GDPRTaskController.php deleted file mode 100644 index 2611cbf..0000000 --- a/modules/gdpr_tasks/src/Entity/GDPRTaskController.php +++ /dev/null @@ -1,25 +0,0 @@ - 'requested'); - $values += array('created' => REQUEST_TIME); - - $task = parent::create($values); - return $task; - } - - public function save($entity, DatabaseTransaction $transaction = NULL) { - $entity->changed = REQUEST_TIME; - return parent::save($entity, $transaction); - } - - -} \ No newline at end of file diff --git a/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php b/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php deleted file mode 100644 index 301f6cc..0000000 --- a/modules/gdpr_tasks/src/Entity/GDPRTaskInterface.php +++ /dev/null @@ -1,18 +0,0 @@ -label; - } - -} \ No newline at end of file diff --git a/modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php b/modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php deleted file mode 100644 index 882f5e1..0000000 --- a/modules/gdpr_tasks/src/Entity/GDPRTaskUIController.php +++ /dev/null @@ -1,125 +0,0 @@ -entityInfo['plural label']) ? $this->entityInfo['plural label'] : $this->entityInfo['label'] . 's'; - - $items['admin/gdpr/task-list'] = array( - 'title' => $plural_label, - 'page callback' => 'drupal_get_form', - 'page arguments' => array($this->entityType . '_overview_form', $this->entityType), - 'description' => 'Manage ' . $plural_label . '.', - 'access callback' => 'entity_access', - 'access arguments' => array('view', $this->entityType), - 'file' => 'includes/entity.ui.inc', - 'weight' => 10, - ); - - return $items; - } - - /** - * Generates the render array for a overview tables for different statuses. - * - * @param $conditions - * An array of conditions as needed by entity_load(). - - * @return array - * A renderable array. - */ - public function overviewTable($conditions = array()) { - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', $this->entityType); - $query->propertyOrderBy('created'); - - // Add all conditions to query. - foreach ($conditions as $key => $value) { - $query->propertyCondition($key, $value); - } - - if ($this->overviewPagerLimit) { - $query->pager($this->overviewPagerLimit); - } - - $results = $query->execute(); - - $ids = isset($results[$this->entityType]) ? array_keys($results[$this->entityType]) : array(); - $entities = $ids ? entity_load($this->entityType, $ids) : array(); - ksort($entities); - - // Always show at least requested and complete tables. - $rows = array( - 'requested' => array(), - 'complete' => array(), - ); - foreach ($entities as $entity) { - $rows[$entity->status][] = $this->overviewTableRow($conditions, entity_id($this->entityType, $entity), $entity); - } - - $render = array(); - foreach ($rows as $status => $status_rows) { - $render[$status] = array( - '#theme' => 'table', - '#header' => $this->overviewTableHeaders($conditions, $status_rows), - '#rows' => $status_rows, - '#caption' => t('Tasks with status - @status', array('@status' => ucfirst($status))), - '#empty' => t('No tasks.'), - '#weight' => 3, - ); - - // @todo Find a better way to order statuses. - if ($status == 'requested') { - $render[$status]['#weight'] = 0; - } - } - - return $render; - } - - /** - * {@inheritdoc} - */ - protected function overviewTableHeaders($conditions, $rows, $additional_header = array()) { - $additional_header = array( - t('Type'), - t('Status'), - t('User'), - t('Requested'), - ); - return parent::overviewTableHeaders($conditions, $rows, $additional_header); - } - - /** - * {@inheritdoc} - */ - protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { - /* @var GDPRTask $entity */ - - $time_diff = REQUEST_TIME - $entity->created; - $created_ago = t('%time ago', array('%time' => format_interval($time_diff, 1))); - - $additional_cols = array( - $entity->bundleLabel(), - $entity->status, - theme('username', array('account' => user_load($entity->user_id))), - format_date($entity->created, 'short') . ' - ' . $created_ago, - ); - $row = parent::overviewTableRow($conditions, $id, $entity, $additional_cols); - // @todo Fix hardcoded links. - $row[0] = l($entity->label(), $this->path . '/' . $id . '/view', array('query' => drupal_get_destination())); - $row[5] = l(t('edit'), $this->path . '/' . $id . '/edit', array('query' => drupal_get_destination())); - $row[6] = l(t('delete'), $this->path . '/' . $id . '/delete', array('query' => drupal_get_destination())); - - return $row; - } - -} diff --git a/modules/gdpr_tasks/templates/gdpr_task.tpl.php b/modules/gdpr_tasks/templates/gdpr_task.tpl.php deleted file mode 100644 index 3c9524f..0000000 --- a/modules/gdpr_tasks/templates/gdpr_task.tpl.php +++ /dev/null @@ -1,48 +0,0 @@ - -
> - - - > - - - - - - - - -
> - -
-
diff --git a/modules/gdpr_tasks/views/handlers/gdpr_tasks_handler_operations_field.inc b/modules/gdpr_tasks/views/handlers/gdpr_tasks_handler_operations_field.inc deleted file mode 100644 index 4b6668c..0000000 --- a/modules/gdpr_tasks/views/handlers/gdpr_tasks_handler_operations_field.inc +++ /dev/null @@ -1,30 +0,0 @@ -additional_fields['id'] = 'id'; - } - - function query() { - $this->ensure_my_table(); - $this->add_additional_fields(); - } - - function render($values) { - - $links = menu_contextual_links('model', 'admin/structure/gdpr-tasks', array($this->get_value($values, 'id'))); - - if (!empty($links)) { - return theme('links', array('links' => $links, 'attributes' => array('class' => array('links', 'inline', 'operations')))); - } - } -} From 9152485224d50bb108f778e98412684f366fab37 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 30 Apr 2018 15:10:14 +0100 Subject: [PATCH 18/21] By yanniboi: Remove duplicate plugin data. --- modules/gdpr_dump/gdpr_dump.module | 82 ------------------- .../plugins/sanitizer/EmailSanitizer.inc | 22 ----- .../plugins/sanitizer/LongTextSanitizer.inc | 61 -------------- .../plugins/sanitizer/PasswordSanitizer.inc | 19 ----- .../plugins/sanitizer/TextSanitizer.inc | 60 -------------- .../plugins/sanitizer/UsernameSanitizer.inc | 59 ------------- 6 files changed, 303 deletions(-) delete mode 100644 modules/gdpr_dump/plugins/sanitizer/EmailSanitizer.inc delete mode 100644 modules/gdpr_dump/plugins/sanitizer/LongTextSanitizer.inc delete mode 100644 modules/gdpr_dump/plugins/sanitizer/PasswordSanitizer.inc delete mode 100644 modules/gdpr_dump/plugins/sanitizer/TextSanitizer.inc delete mode 100644 modules/gdpr_dump/plugins/sanitizer/UsernameSanitizer.inc diff --git a/modules/gdpr_dump/gdpr_dump.module b/modules/gdpr_dump/gdpr_dump.module index 7ec11ee..8bd6886 100644 --- a/modules/gdpr_dump/gdpr_dump.module +++ b/modules/gdpr_dump/gdpr_dump.module @@ -170,88 +170,6 @@ function gdpr_get_complete_schema() { return $schema; } -/** - * Random word generator function. - */ -function gdpr_random_word($length = 8) { - mt_srand((double) microtime() * 1000000); - $vowels = [ - "a", - "e", - "i", - "o", - "u", - ]; - $cons = [ - "b", - "c", - "d", - "g", - "h", - "j", - "k", - "l", - "m", - "n", - "p", - "r", - "s", - "t", - "u", - "v", - "w", - "tr", - "cr", - "br", - "fr", - "th", - "dr", - "ch", - "ph", - "wr", - "st", - "sp", - "sw", - "pr", - "sl", - "cl", - "sh", - ]; - $num_vowels = count($vowels); - $num_cons = count($cons); - $word = ''; - while (strlen($word) < $length) { - $word .= $cons[mt_rand(0, $num_cons - 1)] . $vowels[mt_rand(0, $num_vowels - 1)]; - } - return substr($word, 0, $length); -} - -/** - * Random sentence generator. - */ -function gdpr_random_sentence($word_count) { - $output_array = []; - - for ($i = 0; $i < $word_count; $i++) { - $output_array[] = gdpr_random_word(rand(1, 12)); - } - - $output = implode(' ', $output_array); - - return ucfirst($output) . '.'; -} - -/** - * Random paragraph generator. - */ -function gdpr_random_paragraphs($paragraph_count) { - $output = ''; - for ($i = 1; $i <= $paragraph_count; $i++) { - $output .= gdpr_random_sentence(mt_rand(20, 60)) . "\n\n"; - } - return $output; -} - /** * Helper function to load gdpr dump service. */ diff --git a/modules/gdpr_dump/plugins/sanitizer/EmailSanitizer.inc b/modules/gdpr_dump/plugins/sanitizer/EmailSanitizer.inc deleted file mode 100644 index ec97cf4..0000000 --- a/modules/gdpr_dump/plugins/sanitizer/EmailSanitizer.inc +++ /dev/null @@ -1,22 +0,0 @@ - t('Email Sanitizer'), - 'sanitize callback' => 'gdpr_email_sanitize', -]; - -/** - * Email sanitize callback. - */ -function gdpr_email_sanitize($input) { - if (empty($input)) { - return $input; - } - - return gdpr_random_word(12) . '@example.com'; -} diff --git a/modules/gdpr_dump/plugins/sanitizer/LongTextSanitizer.inc b/modules/gdpr_dump/plugins/sanitizer/LongTextSanitizer.inc deleted file mode 100644 index 0e9cdb3..0000000 --- a/modules/gdpr_dump/plugins/sanitizer/LongTextSanitizer.inc +++ /dev/null @@ -1,61 +0,0 @@ - t('Long Text Sanitizer'), - 'sanitize callback' => 'gdpr_long_text_sanitize', -]; - -/** - * Long text sanitize callback. - */ -function gdpr_long_text_sanitize($input) { - if (empty($input)) { - return $input; - } - - $output = gdpr_remote_generator($input); - - if ($output === $input) { - $output = gdpr_local_generator($input); - } - - return $output; -} - -/** - * Remote generator for long text sanitizer. - */ -function gdpr_long_text_remote_generator($input) { - $result = NULL; - try { - $result = drupal_http_request('https://loripsum.net/api/3/medium/plaintext'); - } - catch (\Exception $e) { - // @todo: Log? - return $input; - } - - if (NULL !== $result && 200 == $result->code) { - $data = $result->data; - if (NULL === $data) { - return $input; - } - - return substr(trim($data), 0, strlen($input)); - } - - return $input; -} - -/** - * Local generator for long text sanitizer. - */ -function gdpr_long_text_local_generator($input) { - $paragraphCount = str_word_count($input) % 5; - return gdpr_random_paragraphs($paragraphCount); -} diff --git a/modules/gdpr_dump/plugins/sanitizer/PasswordSanitizer.inc b/modules/gdpr_dump/plugins/sanitizer/PasswordSanitizer.inc deleted file mode 100644 index 1b4db3d..0000000 --- a/modules/gdpr_dump/plugins/sanitizer/PasswordSanitizer.inc +++ /dev/null @@ -1,19 +0,0 @@ - t('Password Sanitizer'), - 'sanitize callback' => 'gdpr_password_sanitize', -]; - -/** - * Password sanitize callback. - */ -function gdpr_password_sanitize($input) { - require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); - return user_hash_password(user_password(8)); -} diff --git a/modules/gdpr_dump/plugins/sanitizer/TextSanitizer.inc b/modules/gdpr_dump/plugins/sanitizer/TextSanitizer.inc deleted file mode 100644 index 681425e..0000000 --- a/modules/gdpr_dump/plugins/sanitizer/TextSanitizer.inc +++ /dev/null @@ -1,60 +0,0 @@ - t('Text Sanitizer'), - 'sanitize callback' => 'gdpr_text_sanitize', -]; - -/** - * Text sanitize callback. - */ -function gdpr_text_sanitize($input) { - if (empty($input)) { - return $input; - } - - $output = gdpr_text_remote_generator($input); - - if ($output === $input) { - $output = gdpr_text_local_generator($input); - } - - return $output; -} - -/** - * Remote generator for text sanitize. - */ -function gdpr_text_remote_generator($input) { - $result = NULL; - try { - $result = drupal_http_request('https://loripsum.net/api/1/short/plaintext'); - } - catch (\Exception $e) { - // @todo: Log? - return $input; - } - - if (NULL !== $result && 200 == $result->code) { - $data = $result->data; - if (NULL === $data) { - return $input; - } - - return substr(trim($data), 0, strlen($input)); - } - - return $input; -} - -/** - * Local generator for text sanitize. - */ -function gdpr_text_local_generator($input) { - return gdpr_random_sentence(str_word_count($input)); -} diff --git a/modules/gdpr_dump/plugins/sanitizer/UsernameSanitizer.inc b/modules/gdpr_dump/plugins/sanitizer/UsernameSanitizer.inc deleted file mode 100644 index 124ce65..0000000 --- a/modules/gdpr_dump/plugins/sanitizer/UsernameSanitizer.inc +++ /dev/null @@ -1,59 +0,0 @@ - t('Username Sanitizer'), - 'sanitize callback' => 'gdpr_username_sanitize', -]; - -/** - * Username sanitize callback. - */ -function gdpr_username_sanitize($input) { - if (empty($input)) { - return $input; - } - - $output = gdpr_username_remote_generator($input); - - if ($output === $input) { - $output = gdpr_username_local_generator($input); - } - - return $output; -} - -/** - * Remote generator for username sanitizer. - */ -function gdpr_username_remote_generator($input) { - $result = NULL; - try { - $result = drupal_http_request('https://randomuser.me/api/?format=pretty&results=1&inc=name&noinfo&nat=us,gb'); - } - catch (\Exception $e) { - // @todo: Log? - return $input; - } - - if (NULL !== $result && 200 == $result->code) { - $data = $result->data; - $data = json_decode($data, TRUE); - - $name = reset($data['results'])['name']; - return $name['first'] . '.' . $name['last']; - } - - return $input; -} - -/** - * Local generator for username sanitizer. - */ -function gdpr_username_local_generator($input) { - return gdpr_random_word(strlen($input)); -} From 7831a2a2fcf9b2e9671dbe45ddc98f7c8a8df378 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 30 Apr 2018 15:20:34 +0100 Subject: [PATCH 19/21] By yanniboi: Fix faulty rebase. --- gdpr.module | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/gdpr.module b/gdpr.module index 4885424..ef39f5c 100644 --- a/gdpr.module +++ b/gdpr.module @@ -184,7 +184,7 @@ function gdpr_checklistapi_checklist_info() { 'content_related_suggestions' => array( '#title' => t('Content related suggestions'), '#description' => t('

Automated search performed on uploaded content of site.

') - . $content_discovery_description, + . $content_discovery_description, 'privacy_policy_page' => array( '#title' => t('I have checked through the uploaded content and verified that a page containing Privacy Policy exists and has been published to this site.'), '#description' => t('No nodes with the following terms have been found: "Privacy Policy", "Terms of Use", "About us" or "Impressum". Please verify manually that such content of similar titles exists and has been published.'), @@ -204,17 +204,17 @@ function gdpr_checklistapi_checklist_info() { 'external_traffic_measurement' => array( '#title' => t("Yes, I'm aware there are external traffic measurement and user tracking services integrated on this site"), '#description' => t('As the owner and/or maintainer of the site I have, to the best of my knowledge, taken the necessary measures to set up these 3rd-party data collecting tools to ensure maximum compliance with GDPR regulations.') - . _get_module_list('tracking'), + . _get_module_list('tracking'), ), 'social_media_connections' => array( '#title' => t("Yes, I'm aware there are connections established between this site and some social media platforms"), '#description' => t('As many social media platforms collects identifiable personal data, I acknowledge that the owner of this site is responsible for setting up these connections to ensure maximum compliance with GDPR regulations.') - . _get_module_list('social'), + . _get_module_list('social'), ), 'module_data_collection' => array( '#title' => t('I confirm that all Drupal core, community, and custom modules have been revised as to not gather any unnecessary personal data of site visitors'), '#description' => t('Owner and/or maintainer of the site has to ensure that the modules listed below have been revised as to gather only necessary (i.e. not needed for provision of service) personal data of site visitors.') - . _get_module_list('collect'), + . _get_module_list('collect'), ), 'user_role_permissions' => array( '#title' => t('I have examined all aspects of displayed personal data, associated with this site, to ensure that all users can access only permitted types of information according to their role'), @@ -464,16 +464,15 @@ function gdpr_search_content() { ->propertyCondition('title', $pattern, 'RLIKE') ->execute(); - if (!empty($results['node'])) { - $nids = array_keys($results['node']); - $nodes = node_load_multiple($nids); - return $nodes; - } - else { - return array(); + if (!empty($results['node'])) { + $nids = array_keys($results['node']); + $nodes = node_load_multiple($nids); + return $nodes; + } + else { + return array(); + } } - - return $nodes; } /** From 5f19c9d4d430198bebf24231c207344b57dc6335 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 30 Apr 2018 15:42:54 +0100 Subject: [PATCH 20/21] By yanniboi: PHPCS review. --- gdpr.module | 9 ++- modules/gdpr_consent/gdpr_consent.install | 8 ++- modules/gdpr_consent/gdpr_consent.module | 8 ++- .../includes/gdpr_consent.admin.inc | 5 ++ .../includes/gdpr_consent.agreements.inc | 12 ++-- modules/gdpr_dump/gdpr_dump.admin.inc | 1 - modules/gdpr_dump/gdpr_dump.install | 4 +- modules/gdpr_dump/gdpr_dump.module | 9 ++- .../export_ui/gdpr_sanitizer_ui.class.php | 62 +------------------ .../plugins/export_ui/gdpr_sanitizer_ui.inc | 8 +-- .../GDPRSanitizerDate.class.php | 4 +- .../GDPRSanitizerEmail.class.php | 5 +- .../GDPRSanitizerName.class.php | 5 +- .../GDPRSanitizerPartyArchived.class.php | 4 +- .../GDPRSanitizerText.class.php | 8 +-- .../GDPRSanitizerUsername.class.php | 5 +- .../gdpr_sanitizer/gdpr_sanitizer_date.inc | 5 ++ .../gdpr_sanitizer/gdpr_sanitizer_email.inc | 5 ++ .../gdpr_sanitizer_long_text.inc | 5 ++ .../gdpr_sanitizer/gdpr_sanitizer_name.inc | 5 ++ .../gdpr_sanitizer_party_archived.inc | 5 ++ .../gdpr_sanitizer_password.inc | 5 ++ .../gdpr_sanitizer/gdpr_sanitizer_text.inc | 5 ++ .../gdpr_sanitizer_username.inc | 5 ++ modules/gdpr_dump/src/GDPRUtilRandom.php | 5 +- .../src/Plugins/GDPRSanitizerDefault.php | 10 ++- 26 files changed, 109 insertions(+), 103 deletions(-) diff --git a/gdpr.module b/gdpr.module index ef39f5c..d6c6577 100644 --- a/gdpr.module +++ b/gdpr.module @@ -95,7 +95,6 @@ function gdpr_checklistapi_checklist_info() { '#suffix' => '', '#markup' => t('published'), ); - $is_published = TRUE; } else { $status = array( @@ -184,7 +183,7 @@ function gdpr_checklistapi_checklist_info() { 'content_related_suggestions' => array( '#title' => t('Content related suggestions'), '#description' => t('

Automated search performed on uploaded content of site.

') - . $content_discovery_description, + . $content_discovery_description, 'privacy_policy_page' => array( '#title' => t('I have checked through the uploaded content and verified that a page containing Privacy Policy exists and has been published to this site.'), '#description' => t('No nodes with the following terms have been found: "Privacy Policy", "Terms of Use", "About us" or "Impressum". Please verify manually that such content of similar titles exists and has been published.'), @@ -204,17 +203,17 @@ function gdpr_checklistapi_checklist_info() { 'external_traffic_measurement' => array( '#title' => t("Yes, I'm aware there are external traffic measurement and user tracking services integrated on this site"), '#description' => t('As the owner and/or maintainer of the site I have, to the best of my knowledge, taken the necessary measures to set up these 3rd-party data collecting tools to ensure maximum compliance with GDPR regulations.') - . _get_module_list('tracking'), + . _get_module_list('tracking'), ), 'social_media_connections' => array( '#title' => t("Yes, I'm aware there are connections established between this site and some social media platforms"), '#description' => t('As many social media platforms collects identifiable personal data, I acknowledge that the owner of this site is responsible for setting up these connections to ensure maximum compliance with GDPR regulations.') - . _get_module_list('social'), + . _get_module_list('social'), ), 'module_data_collection' => array( '#title' => t('I confirm that all Drupal core, community, and custom modules have been revised as to not gather any unnecessary personal data of site visitors'), '#description' => t('Owner and/or maintainer of the site has to ensure that the modules listed below have been revised as to gather only necessary (i.e. not needed for provision of service) personal data of site visitors.') - . _get_module_list('collect'), + . _get_module_list('collect'), ), 'user_role_permissions' => array( '#title' => t('I have examined all aspects of displayed personal data, associated with this site, to ensure that all users can access only permitted types of information according to their role'), diff --git a/modules/gdpr_consent/gdpr_consent.install b/modules/gdpr_consent/gdpr_consent.install index fe5b768..f5c3b4f 100644 --- a/modules/gdpr_consent/gdpr_consent.install +++ b/modules/gdpr_consent/gdpr_consent.install @@ -1,4 +1,10 @@ array( 'uid' => array( 'table' => 'users', - 'columns' => array('uid' => 'uid') + 'columns' => array('uid' => 'uid'), ), ), 'primary key' => array('id'), diff --git a/modules/gdpr_consent/gdpr_consent.module b/modules/gdpr_consent/gdpr_consent.module index cc906f5..61f0281 100644 --- a/modules/gdpr_consent/gdpr_consent.module +++ b/modules/gdpr_consent/gdpr_consent.module @@ -168,6 +168,9 @@ function gdpr_consent_entity_property_info() { return $info; } +/** + * Access callback for agreements. + */ function gdpr_consent_access_callback($op, $entity = NULL, $account = NULL) { return user_access('manage gdpr agreements'); } @@ -198,7 +201,6 @@ function gdpr_consent_agreement_load_multiple_by_name($name = NULL) { return isset($name) ? reset($signups) : $signups; } - /** * Implements hook_field_info(). * @@ -411,8 +413,12 @@ class ConsentAgreementController extends EntityAPIControllerExportable { } return entity_var_json_export($vars, $prefix); } + } +/** + * Custom UI controller for the gdpr_consent_agreement entity type. + */ class ConsentAgreementEntityUIController extends EntityDefaultUIController { /** diff --git a/modules/gdpr_consent/includes/gdpr_consent.admin.inc b/modules/gdpr_consent/includes/gdpr_consent.admin.inc index 6460a2f..d7a30ed 100644 --- a/modules/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/modules/gdpr_consent/includes/gdpr_consent.admin.inc @@ -1,5 +1,10 @@ entityCondition('entity_type', 'gdpr_consent_agreement'); - //->propertyCondition('uid', $account->uid); $result = $query->execute(); if (isset($result['gdpr_consent_agreement'])) { $consent_ids = array_keys($result['gdpr_consent_agreement']); $consent_items = entity_load('gdpr_consent_agreement', $consent_ids); - } - kpr($consent_items); - - return $account->name; + return MENU_ACCESS_DENIED; } diff --git a/modules/gdpr_dump/gdpr_dump.admin.inc b/modules/gdpr_dump/gdpr_dump.admin.inc index 2e9dc1e..76c3304 100644 --- a/modules/gdpr_dump/gdpr_dump.admin.inc +++ b/modules/gdpr_dump/gdpr_dump.admin.inc @@ -4,4 +4,3 @@ * @file * Administrative page callbacks for the GDPR Dump module. */ - diff --git a/modules/gdpr_dump/gdpr_dump.install b/modules/gdpr_dump/gdpr_dump.install index 75fbc7a..498b2d1 100644 --- a/modules/gdpr_dump/gdpr_dump.install +++ b/modules/gdpr_dump/gdpr_dump.install @@ -47,8 +47,8 @@ function gdpr_dump_schema() { 'primary key' => array('name'), 'keys' => array( 'enabled' => array('enabled'), - ) + ), ); return $schema; -} \ No newline at end of file +} diff --git a/modules/gdpr_dump/gdpr_dump.module b/modules/gdpr_dump/gdpr_dump.module index 8bd6886..269b956 100644 --- a/modules/gdpr_dump/gdpr_dump.module +++ b/modules/gdpr_dump/gdpr_dump.module @@ -197,6 +197,9 @@ function gdpr_dump_ctools_plugin_type() { return $plugins; } +/** + * Implements hook_ctools_plugin_directory(). + */ function gdpr_dump_ctools_plugin_directory($owner, $plugin_type) { if ($owner == 'gdpr_dump') { return 'plugins/' . $plugin_type; @@ -218,6 +221,9 @@ function gdpr_dump_get_gdpr_sanitizers() { return ctools_get_plugins('gdpr_dump', 'gdpr_sanitizer'); } +/** + * Fetch metadata for context plugins by id. + */ function gdpr_dump_get_gdpr_sanitizer($plugin_id) { ctools_include('plugins'); @@ -230,7 +236,7 @@ function gdpr_dump_get_gdpr_sanitizer($plugin_id) { * Default hook for building field data plugins. */ function gdpr_dump_gdpr_dump_default_sanitizer() { - $export = array(); + $export = array(); $plugins = gdpr_dump_get_gdpr_sanitizers(); @@ -255,4 +261,3 @@ function gdpr_dump_permission() { return $perms; } - diff --git a/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php index 1157a85..74d64d0 100644 --- a/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php +++ b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php @@ -8,64 +8,4 @@ /** * CTools Export UI class handler for GDPR Fields UI. */ -class gdpr_sanitizer_ui extends ctools_export_ui { - - protected $rows = array(); - - /** - * {@inheritdoc} - */ - function hook_menu(&$items) { -// unset($this->plugin['menu']['items']['add']); - // @todo Make sure import always overrides and never adds. -// $this->plugin['menu']['items']['import']['title'] = 'Override'; - parent::hook_menu($items); - } - - /** - * {@inheritdoc} - * - * @param GDPRFieldData $item - */ - public function list_build_row($item, &$form_state, $operations) { - parent::list_build_row($item, $form_state, $operations); - return; - - $name = $item->{$this->plugin['export']['key']}; - $ops = array_pop($this->rows[$name]['data']); - - $title = array('data' => check_plain($item->getSetting('label')), 'class' => array('ctools-export-ui-title')); - array_unshift($this->rows[$name]['data'], $title); - - $this->rows[$name]['data'][] = array('data' => check_plain($item->entity_type), 'class' => array('ctools-export-ui-entity-type')); - $this->rows[$name]['data'][] = array('data' => check_plain($item->entity_bundle), 'class' => array('ctools-export-ui-entity-bundle')); - $this->rows[$name]['data'][] = array('data' => check_plain($item->field_name), 'class' => array('ctools-export-ui-field-name')); - $this->rows[$name]['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rta', 'none')), 'class' => array('ctools-export-ui-rta')); - $this->rows[$name]['data'][] = array('data' => check_plain($item->getSetting('gdpr_fields_rtf', 'none')), 'class' => array('ctools-export-ui-rtf')); - $this->rows[$name]['data'][] = $ops; - } - - /** - * {@inheritdoc} - */ - public function list_table_header() { - $header = parent::list_table_header(); - return $header; - - - $ops = array_pop($header); - - $title = array('data' => t('Label'), 'class' => array('ctools-export-ui-title')); - array_unshift($header, $title); - - $header[] = array('data' => t('Entity type'), 'class' => array('ctools-export-ui-entity-type')); - $header[] = array('data' => t('Entity bundle'), 'class' => array('ctools-export-ui-entity-bundle')); - $header[] = array('data' => t('Field name'), 'class' => array('ctools-export-ui-field-name')); - $header[] = array('data' => t('Right to access'), 'class' => array('ctools-export-ui-rta')); - $header[] = array('data' => t('Right to be forgotten'), 'class' => array('ctools-export-ui-rtf')); - $header[] = $ops; - return $header; - - } - -} \ No newline at end of file +class gdpr_sanitizer_ui extends ctools_export_ui {} diff --git a/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc index d7e476f..3184cc9 100644 --- a/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc +++ b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.inc @@ -23,7 +23,6 @@ $plugin = array( 'form' => array( 'settings' => 'gdpr_dump_sanitizer_export_ui_form', -// 'submit' => 'gdpr_dump_sanitizer_export_ui_form_submit', ), 'handler' => 'gdpr_sanitizer_ui', 'strings' => array( @@ -47,7 +46,7 @@ function gdpr_dump_sanitizer_export_ui_form(&$form, &$form_state) { /* @var GDPRSanitizerDefault $sanitizer */ $sanitizer = $form_state['item']; - $form['settings'] = array( + $form['settings'] = array( '#type' => 'fieldset', '#title' => t('Settings'), '#tree' => TRUE, @@ -59,8 +58,3 @@ function gdpr_dump_sanitizer_export_ui_form(&$form, &$form_state) { '#default_value' => $sanitizer->getSetting('notes', ''), ); } - -/** - * Define the submit function for the add/edit form. - */ -//function gdpr_dump_sanitizer_export_ui_form_submit(&$form, &$form_state) {} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php index 16e60a1..e88214f 100644 --- a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php @@ -8,12 +8,12 @@ class GDPRSanitizerDate extends GDPRSanitizerDefault { /** * {@inheritdoc} */ - var $name = 'gdpr_sanitizer_date'; + public $name = 'gdpr_sanitizer_date'; /** * {@inheritdoc} */ - var $label = 'Date sanitizer'; + public $label = 'Date sanitizer'; /** * {@inheritdoc} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerEmail.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerEmail.class.php index 74821ea..9dce20a 100644 --- a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerEmail.class.php +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerEmail.class.php @@ -8,12 +8,12 @@ class GDPRSanitizerEmail extends GDPRSanitizerDefault { /** * {@inheritdoc} */ - var $name = 'gdpr_sanitizer_email'; + public $name = 'gdpr_sanitizer_email'; /** * {@inheritdoc} */ - var $label = 'Email sanitizer'; + public $label = 'Email sanitizer'; /** * Constant for email length. @@ -33,4 +33,5 @@ public function sanitize($input, $field = NULL) { $random = new GDPRUtilRandom(); return $random->word(self::EMAIL_LENGTH) . '@example.com'; } + } diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php index baf87e5..176722f 100644 --- a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php @@ -8,12 +8,12 @@ class GDPRSanitizerName extends GDPRSanitizerDefault { /** * {@inheritdoc} */ - var $name = 'gdpr_sanitizer_name'; + public $name = 'gdpr_sanitizer_name'; /** * {@inheritdoc} */ - var $label = 'Name field sanitizer'; + public $label = 'Name field sanitizer'; /** * Constant for name length. @@ -39,4 +39,5 @@ public function sanitize($input, $field = NULL) { 'family' => $random->word(rand(self::MIN_LENGTH, self::MAX_LENGTH)), ); } + } diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php index 23d3a68..3714cdb 100644 --- a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php @@ -10,12 +10,12 @@ class GDPRSanitizerPartyArchived extends GDPRSanitizerDefault { /** * {@inheritdoc} */ - var $name = 'gdpr_sanitizer_party_archived'; + public $name = 'gdpr_sanitizer_party_archived'; /** * {@inheritdoc} */ - var $label = 'Archive party sanitizer'; + public $label = 'Archive party sanitizer'; /** * {@inheritdoc} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerText.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerText.class.php index 86489c0..b9bc8e1 100644 --- a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerText.class.php +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerText.class.php @@ -8,12 +8,12 @@ class GDPRSanitizerText extends GDPRSanitizerDefault { /** * {@inheritdoc} */ - var $name = 'gdpr_sanitizer_text'; + public $name = 'gdpr_sanitizer_text'; /** * {@inheritdoc} */ - var $label = 'Text sanitizer'; + public $label = 'Text sanitizer'; /** * {@inheritdoc} @@ -23,8 +23,7 @@ class GDPRSanitizerText extends GDPRSanitizerDefault { public function sanitize($input, $field = NULL) { if ($field != NULL) { /* @var EntityValueWrapper $field */ -// dpm($field->info()); -// $max_length = $entity['settings']['max_length']; + // @todo Get max length from field settings. } $value = ''; @@ -40,4 +39,5 @@ public function sanitize($input, $field = NULL) { } return $value; } + } diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php index e211b64..55c5466 100644 --- a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php @@ -8,12 +8,12 @@ class GDPRSanitizerUsername extends GDPRSanitizerDefault { /** * {@inheritdoc} */ - var $name = 'gdpr_sanitizer_username'; + public $name = 'gdpr_sanitizer_username'; /** * {@inheritdoc} */ - var $label = 'Username sanitizer'; + public $label = 'Username sanitizer'; /** * Constant for username length. @@ -31,4 +31,5 @@ public function sanitize($input, $field = NULL) { $random = new GDPRUtilRandom(); return 'anon_' . $random->name(self::NAME_LENGTH); } + } diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc index 8e78f0c..05135ae 100644 --- a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc @@ -1,5 +1,10 @@ Date: Tue, 1 May 2018 08:04:11 +0100 Subject: [PATCH 21/21] By zserno: Revisioning updates. --- modules/gdpr_consent/css/gdpr_consent.css | 3 + modules/gdpr_consent/gdpr_consent.install | 32 ++++ modules/gdpr_consent/gdpr_consent.module | 177 +++++++++++++++++- .../includes/gdpr_consent.admin.inc | 81 ++++++++ .../includes/gdpr_consent.agreements.inc | 68 +++++++ modules/gdpr_consent/js/gdpr_consent.js | 36 ++++ 6 files changed, 391 insertions(+), 6 deletions(-) create mode 100644 modules/gdpr_consent/css/gdpr_consent.css create mode 100644 modules/gdpr_consent/js/gdpr_consent.js diff --git a/modules/gdpr_consent/css/gdpr_consent.css b/modules/gdpr_consent/css/gdpr_consent.css new file mode 100644 index 0000000..0744974 --- /dev/null +++ b/modules/gdpr_consent/css/gdpr_consent.css @@ -0,0 +1,3 @@ +.gdpr_agreed_toggle { + padding-left: 5px; +} diff --git a/modules/gdpr_consent/gdpr_consent.install b/modules/gdpr_consent/gdpr_consent.install index f5c3b4f..dff7a84 100644 --- a/modules/gdpr_consent/gdpr_consent.install +++ b/modules/gdpr_consent/gdpr_consent.install @@ -101,6 +101,26 @@ function gdpr_consent_schema() { 'not null' => TRUE, 'description' => 'Primary Key: Unique revision ID.', ), + 'title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Title of the consent agreement.', + ), + 'timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'A Unix timestamp indicating when this version was created.', + ), + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => "The {users}.uid of the associated user.", + ), 'description' => array( 'type' => 'varchar', 'length' => 255, @@ -121,6 +141,18 @@ function gdpr_consent_schema() { 'size' => 'tiny', 'description' => 'Consent agreement\'s type: implicit or explicit.', ), + 'notes' => array( + 'type' => 'text', + 'size' => 'medium', + 'not null' => FALSE, + 'description' => 'Notes for staff to put their rationale for why they have done this for auditors.', + ), + 'log' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + 'description' => 'The log entry explaining the changes in this version.', + ), ), 'primary key' => array('revision_id'), ); diff --git a/modules/gdpr_consent/gdpr_consent.module b/modules/gdpr_consent/gdpr_consent.module index 61f0281..40f6e1a 100644 --- a/modules/gdpr_consent/gdpr_consent.module +++ b/modules/gdpr_consent/gdpr_consent.module @@ -24,12 +24,65 @@ function gdpr_consent_menu() { $items['user/%user/gdpr/agreements'] = array( 'title' => 'Agreements', 'description' => 'List Agreement Entities', - 'access callback' => TRUE, 'page callback' => 'gdpr_consent_collected_agreements', 'page arguments' => array(1), + 'access arguments' => array('manage gdpr agreements'), 'menu_name' => 'navigation', 'file' => 'includes/gdpr_consent.agreements.inc', ); + $items['admin/gdpr/agreements/%gdpr_consent_agreement'] = array( + 'title' => 'Consent Agreement', + 'page callback' => 'gdpr_consent_agreement_view_entity', + //'page callback' => 'entity_ui_entity_page_view', + 'page arguments' => array(3), + 'access callback' => TRUE, + 'file' => 'includes/gdpr_consent.agreements.inc', + ); + // 'View' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/view'] = array( + 'title' => 'View', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + // 'Edit' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/edit'] = array( + 'title' => 'Edit', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('gdpr_consent_agreement_form', 3), + 'access arguments' => array('manage gdpr agreements'), + 'type' => MENU_LOCAL_TASK, + 'weight' => -5, + 'file' => 'includes/gdpr_consent.admin.inc', + ); + // 'Revisions' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/revisions'] = array( + 'title' => 'Revisions', + 'page callback' => 'gdpr_consent_agreement_revision_overview', + 'page arguments' => array(3), + 'type' => MENU_LOCAL_TASK, + 'access arguments' => array('manage gdpr agreements'), + 'file' => 'includes/gdpr_consent.admin.inc', + 'weight' => -3, + ); + // An individual revision view page. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/revisions/%/view'] = array( + 'title' => 'Revision', + 'page callback' => 'gdpr_consent_agreement_view_revision', + 'page arguments' => array(5), + 'access arguments' => array('manage gdpr agreements'), + 'file' => 'includes/gdpr_consent.agreements.inc', + 'weight' => -3, + ); + // 'Delete' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/delete'] = array( + 'title' => 'Delete', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('gdpr_consent_agreement_form', 3), + 'access arguments' => array('manage gdpr agreements'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'includes/gdpr_consent.admin.inc', + 'weight' => 0, + ); return $items; } @@ -62,7 +115,7 @@ function gdpr_consent_entity_info() { 'label' => t('GDPR Consent Agreement'), 'base table' => 'gdpr_consent_agreement', 'revision table' => 'gdpr_consent_agreement_revision', - 'entity class' => 'Entity', + 'entity class' => 'ConsentAgreement', 'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, 'exportable' => TRUE, @@ -81,7 +134,7 @@ function gdpr_consent_entity_info() { 'label' => 'title', 'revision' => 'revision_id', ), - // Use the default label() and uri() functions. + 'uri callback' => 'entity_class_uri', 'access callback' => 'gdpr_consent_access_callback', 'admin ui' => array( 'path' => 'admin/gdpr/agreements', @@ -137,6 +190,13 @@ function gdpr_consent_entity_property_info() { 'schema field' => 'long_description', ); + $properties['notes'] = array( + 'label' => t('Notes'), + 'description' => t('This should contain the rationale behind the agreement.'), + 'type' => 'text', + 'schema field' => 'notes', + ); + $properties['created'] = array( 'label' => t('Created date'), 'description' => t('Date the consent agreement was created'), @@ -176,10 +236,17 @@ function gdpr_consent_access_callback($op, $entity = NULL, $account = NULL) { } /** - * Loads agreements. + * Menu autoloader for gdpr agreements. */ -function gdpr_consent_agreement_load($id) { - return entity_load_single('gdpr_consent_agreement', $id); +function gdpr_consent_agreement_load($id, $revision_id = NULL) { + if (is_numeric($revision_id)) { + $entity = entity_revision_load('gdpr_consent_agreement', $revision_id); + return $entity; + } + else { + $entity = entity_load_single('gdpr_consent_agreement', $id); + } + return $entity; } /** @@ -235,6 +302,21 @@ function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $ } } +/** + * Implements hook_field_insert(). + */ +function gdpr_consent_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { + $message = message_create('consent_agreement_accepted', array('uid' => $entity->uid)); + $wrapper = entity_metadata_wrapper('message', $message); + + $wrapper->user->set($field['user_id_accepted']); + $wrapper->agreement->set(array($field['target_id'], $field['target_revision_id'])); + $wrapper->notes->set($field['notes']); + $wrapper->agreed->set($field['agreed']); + + $wrapper->save(); +} + /** * Implements hook_field_is_empty(). */ @@ -342,6 +424,13 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, '#title' => $entity->title, '#default_value' => $agreed, '#weight' => 0, + '#attributes' => array( + 'class' => array('gdpr_consent_agreement'), + ), + '#attached' => array( + 'css' => array(drupal_get_path('module', 'gdpr_consent') . '/css/gdpr_consent.css'), + 'js' => array(drupal_get_path('module', 'gdpr_consent') . '/js/gdpr_consent.js'), + ), '#description' => $entity->long_description, ) + $widget; $element['notes'] = array( @@ -375,6 +464,52 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, return $element; } +/** + * Implements hook_default_message_type(). + */ +function gdpr_consent_default_message_type() { + $items = array(); + + $items['consent_agreement_accepted'] = entity_import('message_type', '{ + "name" : "consent_agreement_accepted", + "description" : "GDPR Consent Agreement", + "argument_keys" : [], + "argument" : [], + "category" : "message_type", + "data" : { + "token options" : { "clear" : 0 }, + "purge" : { "override" : 1, "enabled" : 1, "quota" : "1000", "days" : "30" } + }, + "language" : "", + "arguments" : null, + "message_text" : { "und" : [ + { + "value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022[message:agreement:entity:url]\/revisions\/[message:agreement:target_revision_id]\/view\u0022\u003E[message:agreement:entity:title]\u003C\/a\u003E\u003Cbr \/\u003E\r\nAgreed: [message:agreed]\u003Cbr \/\u003E\r\nNotes: [message:notes]\u003C\/p\u003E", + "format" : "filtered_html", + "safe_value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022url]\/revisions\/[message:agreement:target_revision_id]\/view\u0022\u003E[message:agreement:entity:title]\u003C\/a\u003E\u003Cbr \/\u003E\nAgreed: [message:agreed]\u003Cbr \/\u003E\nNotes: [message:notes]\u003C\/p\u003E\n" + } + ] + }, + "rdf_mapping" : [] + }'); + + return $items; +} + +/** + * Our custom entity class. + */ +class ConsentAgreement extends Entity { + + /** + * {@inheritdoc} + */ + protected function defaultUri() { + return array('path' => 'admin/gdpr/agreements/' . $this->identifier()); + } + +} + /** * Custom controller for the gdpr_consent_agreement entity type. */ @@ -395,6 +530,7 @@ class ConsentAgreementController extends EntityAPIControllerExportable { // Always save new revisions. $entity->is_new_revision = TRUE; + $entity->timestamp = REQUEST_TIME; return parent::save($entity, $transaction); } @@ -421,6 +557,35 @@ class ConsentAgreementController extends EntityAPIControllerExportable { */ class ConsentAgreementEntityUIController extends EntityDefaultUIController { + /** + * {@inheritdoc} + */ + public function buildContent($entity, $view_mode = 'default', $langcode = NULL, $content = array()) { + $build = parent::buildContent($entity, $view_mode, $langcode, $content); + + $build['description'] = array( + '#type' => 'markup', + '#markup' => $entity->description, + ); + + $build['long_description'] = array( + '#type' => 'markup', + '#markup' => $entity->long_description, + ); + + $build['notes'] = array( + '#type' => 'markup', + '#markup' => $entity->notes, + ); + + $build['agreement_type'] = array( + '#type' => 'markup', + '#markup' => $entity->agreement_type, + ); + + return $build; + } + /** * {@inheritdoc} */ diff --git a/modules/gdpr_consent/includes/gdpr_consent.admin.inc b/modules/gdpr_consent/includes/gdpr_consent.admin.inc index d7a30ed..d5195f9 100644 --- a/modules/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/modules/gdpr_consent/includes/gdpr_consent.admin.inc @@ -39,6 +39,24 @@ function gdpr_consent_agreement_form($form, &$form_state, $entity = NULL) { '#default_value' => isset($entity->long_description) ? $entity->long_description : '', '#description' => t('Text shown when the user clicks for more details'), ); + $form['notes'] = array( + '#title' => t('Notes'), + '#type' => 'textarea', + '#default_value' => isset($entity->notes) ? $entity->notes : '', + '#description' => t('This should contain the rationale behind the agreement.'), + ); + $form['revision'] = array( + '#type' => 'checkbox', + '#title' => t('Create new revision'), + '#default_value' => 1, + ); + $form['log'] = array( + '#type' => 'textarea', + '#title' => t('Revision log message'), + '#rows' => 4, + '#default_value' => !empty($entity->log) ? $entity->log : '', + '#description' => t('Briefly describe the changes you have made.'), + ); field_attach_form('gdpr_consent_agreement', $entity, $form, $form_state); @@ -62,3 +80,66 @@ function gdpr_consent_agreement_form_submit($form, &$form_state) { drupal_set_message(t('@title agreement has been saved.', array('@title' => $entity->title))); $form_state['redirect'] = 'admin/gdpr/agreements'; } + +/** + * Generates an overview table of older revisions of a Consent Agreement. + * + * @param $agreement + * A consent agreement entity. + * + * @return array + * An array as expected by drupal_render(). + */ +function gdpr_consent_agreement_revision_overview($agreement) { + drupal_set_title(t('Revisions for @title', array('@title' => $agreement->title))); + + $header = array(t('Revision'), array('data' => t('Operations'), 'colspan' => 2)); + + $revisions = gdpr_consent_revision_list($agreement); + + $rows = array(); + + foreach ($revisions as $revision) { + $row = array(); + $operations = array(); + + if ($revision->current_revision > 0) { + $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "admin/gdpr/agreements/$agreement->id"), '!username' => theme('username', array('account' => $revision)))), + 'class' => array('revision-current')); + $operations[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current'), 'colspan' => 2); + } + else { + $row[] = t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/view"), '!username' => theme('username', array('account' => $revision)))); + $operations[] = l(t('revert'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/revert"); + $operations[] = l(t('delete'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/delete"); + } + $rows[] = array_merge($row, $operations); + } + + $build['gdpr_consent_revisions_table'] = array( + '#theme' => 'table', + '#rows' => $rows, + '#header' => $header, + ); + + return $build; +} + +/** + * Returns a list of all the existing revision numbers. + * + * @param $agreement + * The Consent Agreement entity. + * + * @return + * An associative array keyed by revision number. + */ +function gdpr_consent_revision_list($agreement) { + $revisions = array(); + $result = db_query('SELECT r.revision_id, a.title, r.uid, a.revision_id AS current_revision, r.timestamp, u.name FROM {gdpr_consent_agreement_revision} r LEFT JOIN {gdpr_consent_agreement} a ON a.revision_id = r.revision_id INNER JOIN {users} u ON u.uid = r.uid WHERE r.id = :id ORDER BY r.revision_id DESC', array(':id' => $agreement->id)); + foreach ($result as $revision) { + $revisions[$revision->revision_id] = $revision; + } + + return $revisions; +} diff --git a/modules/gdpr_consent/includes/gdpr_consent.agreements.inc b/modules/gdpr_consent/includes/gdpr_consent.agreements.inc index 8e3eced..ec6cd2e 100644 --- a/modules/gdpr_consent/includes/gdpr_consent.agreements.inc +++ b/modules/gdpr_consent/includes/gdpr_consent.agreements.inc @@ -23,3 +23,71 @@ function gdpr_consent_collected_agreements($account) { return MENU_ACCESS_DENIED; } + +/** + * Callback for /admin/gdpr/agreements/{gdpr_consent_agreement ID} page. + * + * As we load the entity for display, we're responsible for invoking a number + * of hooks in their proper order. + * + * @see hook_entity_prepare_view() + * @see hook_entity_view() + * @see hook_entity_view_alter() + */ +function gdpr_consent_agreement_view_entity($entity, $view_mode = 'default') { + // Our entity type, for convenience. + $entity_type = 'gdpr_consent_agreement'; + // Start setting up the content. + $entity->content = array( + '#view_mode' => $view_mode, + ); + // Build fields content - this is where the Field API really comes in to play. + // The task has very little code here because it all gets taken care of by + // field module. + // field_attach_prepare_view() lets the fields load any data they need + // before viewing. + field_attach_prepare_view($entity_type, array($entity->id => $entity), + $view_mode); + // We call entity_prepare_view() so it can invoke hook_entity_prepare_view() + // for us. + entity_prepare_view($entity_type, array($entity->id => $entity)); + // Now field_attach_view() generates the content for the fields. + $entity->content += field_attach_view($entity_type, $entity, $view_mode); + + // OK, Field API done, now we can set up some of our own data. + $entity->content['created'] = array( + '#type' => 'item', + '#title' => t('Created date'), + '#markup' => format_date($entity->created), + ); + $entity->content['description'] = array( + '#type' => 'item', + '#title' => t('Description'), + '#markup' => $entity->description, + ); + + // Now to invoke some hooks. We need the language code for + // hook_entity_view(), so let's get that. + global $language; + $langcode = $language->language; + // And now invoke hook_entity_view(). + module_invoke_all('entity_view', $entity, $entity_type, $view_mode, + $langcode); + // Now invoke hook_entity_view_alter(). + drupal_alter(array('gdpr_consent_agreement_view_entity', 'entity_view'), + $entity->content, $entity_type); + + // And finally return the content. + return $entity->content; +} + +/** + * Callback for /admin/gdpr/agreements/{gdpr_consent_agreement ID}/{revision ID} page. + */ +function gdpr_consent_agreement_view_revision($revision_id) { + $agreement = entity_revision_load('gdpr_consent_agreement', $revision_id); + $output = gdpr_consent_agreement_view_entity($agreement); + drupal_set_title(t('@title revision @id', array('@title' => $agreement->title, '@id' => $agreement->revision_id))); + + return $output; +} diff --git a/modules/gdpr_consent/js/gdpr_consent.js b/modules/gdpr_consent/js/gdpr_consent.js new file mode 100644 index 0000000..339d617 --- /dev/null +++ b/modules/gdpr_consent/js/gdpr_consent.js @@ -0,0 +1,36 @@ +(function ($) { + $(function () { + // Hide the description for any GDPR checkboxes. + var container = $('.gdpr_consent_agreement').parent(); + var desc = container.find('.description'); + + if (!desc.length) { + container = container.parent(); + desc = container.find('.description'); + } + + desc.hide(); + + $('?') + .insertAfter(container.find('label')) + .click(function () { + var desc = $(this).find('.description'); + if (!desc.length) { + desc = $(this).parent().find('.description'); + } + + desc.slideToggle() + }); + + // Do the same for implicit + container = $('.gdpr_consent_implicit').parent(); + desc = container.next('.description'); + desc.hide(); + + $('?') + .appendTo(container) + .click(function () { + $(this).next('.description').slideToggle() + }); + }); +})(jQuery);