Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions gdpr.links.task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,10 @@ gdpr.collected_user_data:
title: 'All your data'
route_name: gdpr.collected_user_data
base_route: entity.user.canonical


gdpr.collected_user_data_default:
title: 'Summary'
parent_id: gdpr.collected_user_data
route_name: gdpr.collected_user_data
base_route: entity.user.canonical
2 changes: 1 addition & 1 deletion gdpr.routing.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
gdpr.collected_user_data:
path: '/user/{user}/collected-data'
path: '/user/{user}/gdpr'
defaults:
_title: 'Data stored about you'
_controller: '\Drupal\gdpr\Controller\UserController::collectedData'
Expand Down
7 changes: 3 additions & 4 deletions modules/gdpr_fields/gdpr_fields.module
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,16 @@ function gdpr_fields_form_field_config_edit_form_alter(&$form, FormStateInterfac
$field = $form_state->getFormObject()->getEntity();
// @todo Check that target entity is a content entity.

$enabled = $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_enabled', FALSE);
$form['field']['gdpr_fields'] = [
'#type' => 'details',
'#title' => t('GDPR field settings'),
'#open' => $enabled,
'#open' => TRUE,
];

$form['field']['gdpr_fields']['gdpr_fields_enabled'] = [
'#type' => 'checkbox',
'#title' => t('This is a GDPR field'),
'#default_value' => $enabled,
'#default_value' => $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_enabled', FALSE),
];

$form['field']['gdpr_fields']['gdpr_fields_rta'] = [
Expand All @@ -53,7 +52,7 @@ function gdpr_fields_form_field_config_edit_form_alter(&$form, FormStateInterfac
'#type' => 'select',
'#title' => t('Right to be forgotten'),
'#options' => [
'obfuscate' => 'Obfuscate',
'anonymise' => 'Anonymise',
'remove' => 'Remove',
'maybe' => 'Maybe',
'no' => 'Not',
Expand Down
51 changes: 51 additions & 0 deletions modules/gdpr_fields/src/Controller/GDPRController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\gdpr_fields\GDPRCollector;
use Drupal\user\UserInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
Expand Down Expand Up @@ -107,4 +108,54 @@ protected function buildFieldTable($entity_type, $bundle_id) {
];
}

/**
* Builds data for Right to Access data requests.
*
* @param \Drupal\user\UserInterface $user
* The user to fetch data for.
*
* @return array
* Structured array of user related data.
*/
public function rtaData(UserInterface $user) {
$rows = [];
$entities = [];
$this->collector->getValueEntities($entities, 'user', $user);

foreach ($entities as $entity_type => $bundles) {
foreach ($bundles as $bundle_entity) {
$rows += $this->collector->fieldValues($entity_type, $bundle_entity, ['rta' => 'rta']);
}
}

// Sort rows by field name.
ksort($rows);
return $rows;
}

/**
* Builds data for Right to be Forgotten data requests.
*
* @param \Drupal\user\UserInterface $user
* The user to fetch data for.
*
* @return array
* Structured array of user related data.
*/
public function rtfData(UserInterface $user) {
$rows = [];
$entities = [];
$this->collector->getValueEntities($entities, 'user', $user);

foreach ($entities as $entity_type => $bundles) {
foreach ($bundles as $bundle_entity) {
$rows += $this->collector->fieldValues($entity_type, $bundle_entity, ['rtf' => 'rtf']);
}
}

// Sort rows by field name.
ksort($rows);
return $rows;
}

}
158 changes: 150 additions & 8 deletions modules/gdpr_fields/src/GDPRCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Drupal\gdpr_fields;

use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Link;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Entity\EntityTypeManager;
Expand All @@ -29,13 +30,6 @@ class GDPRCollector {
*/
protected $relationshipManager;

/**
* A prepared list of all fields, keyed by base_table and handler type.
*
* @var array
*/
protected $fields;

/**
* Constructs a GDPRCollector object.
*
Expand Down Expand Up @@ -66,6 +60,8 @@ public function getEntities(&$entity_list, $entity_type = 'user', $bundle_id = N
return;
}

// @todo Add way of excluding irrelevant entity types.

if (!$bundle_id) {
if ($definition->getBundleEntityType()) {
$bundle_storage = $this->entityTypeManager->getStorage($definition->getBundleEntityType());
Expand Down Expand Up @@ -93,7 +89,7 @@ public function getEntities(&$entity_list, $entity_type = 'user', $bundle_id = N
$definitions = $this->relationshipManager->getDefinitionsForContexts([$context]);

foreach ($definitions as $definition_id => $definition) {
list($type, , , $field) = explode(':', $definition_id);
list($type, , ,) = explode(':', $definition_id);

if ($type == 'typed_data_entity_relationship') {
if (isset($definition['target_entity_type'])) {
Expand All @@ -111,6 +107,74 @@ public function getEntities(&$entity_list, $entity_type = 'user', $bundle_id = N
}
}

/**
* Get entity value tree for GDPR entities.
*
* @param array $entity_list
* List of all gotten entities keyed by entity type and bundle id.
* @param string $entity_type
* The entity type id.
* @param \Drupal\Core\Entity\EntityInterface $entity
* The fully loaded entity for which values are gotten.
*/
public function getValueEntities(array &$entity_list, $entity_type, EntityInterface $entity) {
$definition = $this->entityTypeManager->getDefinition($entity_type);

if ($definition instanceof ConfigEntityTypeInterface) {
return;
}

// @todo Add way of excluding irrelevant entity types.

// Check for recursion.
if (isset($entity_list[$entity_type][$entity->id()])) {
return;
}

// Set entity.
$entity_list[$entity_type][$entity->id()] = $entity;

// Find relationships.
$context = new Context(new ContextDefinition("entity:{$entity_type}"));
$definitions = $this->relationshipManager->getDefinitionsForContexts([$context]);


foreach ($definitions as $definition_id => $definition) {
list($type, $definition_entity, ,) = explode(':', $definition_id);

// Ignore entity revisions for now.
if ($definition_entity == 'entity_revision') {
continue;
}

if ($type == 'typed_data_entity_relationship') {
/* @var \Drupal\ctools\Plugin\Relationship\TypedDataEntityRelationship $plugin */
$plugin = $this->relationshipManager->createInstance($definition_id);
$plugin->setContextValue('base', $entity);

$relationship = $plugin->getRelationship();
if ($relationship->hasContextValue()) {
$relationship_entity = $relationship->getContextValue();
$this->getValueEntities($entity_list, $relationship_entity->getEntityTypeId(), $relationship_entity);
}
}
elseif ($type == 'typed_data_entity_relationship_reverse') {
/* @var \Drupal\gdpr_fields\Plugin\Relationship\TypedDataEntityRelationshipReverse $plugin */
$plugin = $this->relationshipManager->createInstance($definition_id);
$plugin->setContextValue('base', $entity);

$relationship = $plugin->getRelationship();
if ($relationship->hasContextValue()) {
$relationship_entity = $relationship->getContextValue();
$this->getValueEntities($entity_list, $relationship_entity->getEntityTypeId(), $relationship_entity);
}
}
else {
continue;
}
}
}

/**
* List fields on entity including their GDPR values.
*
Expand Down Expand Up @@ -176,4 +240,82 @@ public function listFields($entity_type = 'user', $bundle_id) {
return $fields;
}

/**
* List field values on an entity including their GDPR values.
*
* @param string $entity_type
* The entity type id.
* @param \Drupal\Core\Entity\EntityInterface $entity
* The fully loaded entity for which values are listed.
* @param array $extra_fields
* Add extra fields if required
*
* @return array
* GDPR entity field value list.
*/
public function fieldValues($entity_type = 'user', EntityInterface $entity, $extra_fields = []) {
$entity_definition = $this->entityTypeManager->getDefinition($entity_type);
$bundle_type = $entity_definition->getBundleEntityType();
$bundle_id = $entity->bundle();
if ($bundle_type) {
$bundle_storage = $this->entityTypeManager->getStorage($bundle_type);
$bundle_label = $bundle_storage->load($bundle_id)->label();
}
else {
$bundle_label = $entity->getEntityType()->getLabel();
}


// Get fields for entity.
$fields = [];
foreach ($entity as $field_id => $field) {
/** @var \Drupal\Core\Field\FieldItemListInterface $field */
$field_definition = $field->getFieldDefinition();

$config = $field_definition->getConfig($bundle_id);
if (!$config->getThirdPartySetting('gdpr_fields', 'gdpr_fields_enabled', FALSE)) {
continue;
}


$key = "$entity_type.{$entity->id()}.$field_id";

$fieldValue = $field->getString();
$fields[$key] = [
'title' => $field_definition->getLabel(),
'value' => $fieldValue,
'entity' => $entity->getEntityType()->getLabel(),
'bundle' => $bundle_label,
];

if (empty($extra_fields)) {
continue;
}

// Fetch and validate based on field settings.
if (isset($extra_fields['rta'])) {
$rta_value = $config->getThirdPartySetting('gdpr_fields', 'gdpr_fields_rta', FALSE);

if ($rta_value && $rta_value !== 'no') {
$fields[$key]['gdpr_rta'] = $rta_value;
}
else {
unset($fields[$key]);
}
}
if (isset($extra_fields['rtf'])) {
$rtf_value = $config->getThirdPartySetting('gdpr_fields', 'gdpr_fields_rtf', FALSE);

if ($rtf_value && $rtf_value !== 'no') {
$fields[$key]['gdpr_rtf'] = $rtf_value;
}
else {
unset($fields[$key]);
}
}
}

return $fields;
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<?php

namespace Drupal\gdpr_fields\Plugin\Relationship;

use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\Query\QueryException;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\ctools\Plugin\Relationship\TypedDataEntityRelationship;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextInterface;

/**
* @Relationship(
Expand Down Expand Up @@ -50,4 +55,61 @@ public static function create(ContainerInterface $container, array $configuratio
);
}

/**
* {@inheritdoc}
*/
public function getRelationship() {
$plugin_definition = $this->getPluginDefinition();

if (!isset($plugin_definition['source_entity_type'])) {
return parent::getRelationship();
}
$source_entity_type = $plugin_definition['source_entity_type'];
$context_definition = new ContextDefinition("entity:{$source_entity_type}", $plugin_definition['label']);
$context_value = NULL;

// If the 'base' context has a value, then get the property value to put on
// the context (otherwise, mapping hasn't occurred yet and we just want to
// return the context with the right definition and no value).
if ($this->getContext('base')->hasContextValue()) {
$data = $this->getData($this->getContext('base'), $source_entity_type);
if ($data) {
$context_value = $data;
}
}

$context_definition->setDefaultValue($context_value);
return new Context($context_definition, $context_value);
}

/**
* {@inheritdoc}
*/
protected function getData(ContextInterface $context, $source_entity_type = NULL) {
if (!$source_entity_type) {
return FALSE;
}

/** @var \Drupal\Core\Entity\EntityInterface $base */
$base = $context->getContextValue();
$name = $this->getPluginDefinition()['property_name'];

try {
$query = \Drupal::entityQuery($source_entity_type)
->condition($name, $base->id(), '=')
->range(0, 1)
->execute();
}
catch (QueryException $e) {
return FALSE;
}

$data = reset($query);
if ($data) {
$data = \Drupal::entityTypeManager()->getStorage($source_entity_type)->load($data);
}

return $data;
}

}
Loading