From 08032479a60216e09feca3ae938f907e391c2574 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Thu, 15 Mar 2018 09:13:28 +0000 Subject: [PATCH 01/21] Include uuid in task export --- modules/gdpr_tasks/src/Anonymizer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/gdpr_tasks/src/Anonymizer.php b/modules/gdpr_tasks/src/Anonymizer.php index e5d4497..bdd3cfa 100644 --- a/modules/gdpr_tasks/src/Anonymizer.php +++ b/modules/gdpr_tasks/src/Anonymizer.php @@ -352,7 +352,7 @@ private function checkExportDirectoryExists() { * Log of processed fields. */ private function writeLogToFile(TaskInterface $task, array $log) { - $filename = 'GDPR_RTF_' . date('Y-m-d H-i-s') . '_' . $task->id() . '.json'; + $filename = 'GDPR_RTF_' . date('Y-m-d H-i-s') . '_' . $task->uuid() . '.json'; $dir = $this->configFactory->get(RemovalSettingsForm::CONFIG_KEY) ->get(RemovalSettingsForm::EXPORT_DIRECTORY); @@ -361,6 +361,7 @@ private function writeLogToFile(TaskInterface $task, array $log) { // Don't serialize the whole entity as we don't need all fields. $output = [ 'task_id' => $task->id(), + 'task_uuid' => $task->uuid(), 'owner_id' => $task->getOwnerId(), 'created' => $task->getCreatedTime(), 'processed_by' => $this->currentUser->id(), From 59eee662bf5d45d8222d230142a0b6ddbaa3f15d Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Wed, 14 Mar 2018 17:13:58 +0000 Subject: [PATCH 02/21] Initial work on GDPR Consent --- modules/gdpr_consent/gdpr_consent.info.yml | 8 + .../gdpr_consent.links.action.yml | 5 + .../gdpr_consent/gdpr_consent.links.task.yml | 22 ++ modules/gdpr_consent/gdpr_consent.routing.yml | 0 .../src/ConsentAgreementHtmlRouteProvider.php | 196 +++++++++++ .../src/ConsentAgreementStorage.php | 58 ++++ .../src/ConsentAgreementStorageInterface.php | 61 ++++ .../Controller/ConsentAgreementController.php | 177 ++++++++++ .../src/Entity/ConsentAgreement.php | 321 ++++++++++++++++++ .../src/Entity/ConsentAgreementInterface.php | 114 +++++++ .../src/Form/ConsentAgreementForm.php | 106 ++++++ .../ConsentAgreementRevisionRevertForm.php | 149 ++++++++ .../Field/FieldFormatter/ConsentFormatter.php | 32 ++ .../Field/FieldType/UserConsentItem.php | 159 +++++++++ .../Field/FieldWidget/ConsentWidget.php | 76 +++++ modules/gdpr_fields/gdpr_fields.module | 5 + .../Plugin/Field/FieldType/TaskLogItem.php | 1 + 17 files changed, 1490 insertions(+) create mode 100644 modules/gdpr_consent/gdpr_consent.info.yml create mode 100644 modules/gdpr_consent/gdpr_consent.links.action.yml create mode 100644 modules/gdpr_consent/gdpr_consent.links.task.yml create mode 100644 modules/gdpr_consent/gdpr_consent.routing.yml create mode 100644 modules/gdpr_consent/src/ConsentAgreementHtmlRouteProvider.php create mode 100644 modules/gdpr_consent/src/ConsentAgreementStorage.php create mode 100644 modules/gdpr_consent/src/ConsentAgreementStorageInterface.php create mode 100644 modules/gdpr_consent/src/Controller/ConsentAgreementController.php create mode 100644 modules/gdpr_consent/src/Entity/ConsentAgreement.php create mode 100644 modules/gdpr_consent/src/Entity/ConsentAgreementInterface.php create mode 100644 modules/gdpr_consent/src/Form/ConsentAgreementForm.php create mode 100644 modules/gdpr_consent/src/Form/ConsentAgreementRevisionRevertForm.php create mode 100644 modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php create mode 100644 modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php create mode 100644 modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php diff --git a/modules/gdpr_consent/gdpr_consent.info.yml b/modules/gdpr_consent/gdpr_consent.info.yml new file mode 100644 index 0000000..1094730 --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.info.yml @@ -0,0 +1,8 @@ +name: General Data Protection Regulation (GDPR) - Consent Tracking +description: Allow tracking of GDPR Consent +core: 8.x +type: module +package: General Data Protection Regulation + +dependencies: + - gdpr:gdpr diff --git a/modules/gdpr_consent/gdpr_consent.links.action.yml b/modules/gdpr_consent/gdpr_consent.links.action.yml new file mode 100644 index 0000000..007f071 --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.links.action.yml @@ -0,0 +1,5 @@ +entity.gdpr_consent_agreement.add_form: + route_name: entity.gdpr_consent_agreement.add_form + title: 'Add Consent Agreement' + appears_on: + - entity.gdpr_consent_agreement.collection diff --git a/modules/gdpr_consent/gdpr_consent.links.task.yml b/modules/gdpr_consent/gdpr_consent.links.task.yml new file mode 100644 index 0000000..cf1bef2 --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.links.task.yml @@ -0,0 +1,22 @@ +# Consent Agreement routing definition +gdpr_consent_agreement.settings_tab: + route_name: gdpr_consent_agreement.settings + title: 'Settings' + base_route: gdpr_consent_agreement.settings + +entity.gdpr_consent_agreement.canonical: + route_name: entity.gdpr_consent_agreement.canonical + base_route: entity.gdpr_consent_agreement.canonical + title: 'View' + +entity.gdpr_consent_agreement.edit_form: + route_name: entity.gdpr_consent_agreement.edit_form + base_route: entity.gdpr_consent_agreement.canonical + title: 'Edit' + +entity.gdpr_consent_agreement.delete_form: + route_name: entity.gdpr_consent_agreement.delete_form + base_route: entity.gdpr_consent_agreement.canonical + title: Delete + weight: 10 + diff --git a/modules/gdpr_consent/gdpr_consent.routing.yml b/modules/gdpr_consent/gdpr_consent.routing.yml new file mode 100644 index 0000000..e69de29 diff --git a/modules/gdpr_consent/src/ConsentAgreementHtmlRouteProvider.php b/modules/gdpr_consent/src/ConsentAgreementHtmlRouteProvider.php new file mode 100644 index 0000000..5c111ef --- /dev/null +++ b/modules/gdpr_consent/src/ConsentAgreementHtmlRouteProvider.php @@ -0,0 +1,196 @@ +id(); + + if ($history_route = $this->getHistoryRoute($entity_type)) { + $collection->add("entity.{$entity_type_id}.version_history", $history_route); + } + + if ($revision_route = $this->getRevisionRoute($entity_type)) { + $collection->add("entity.{$entity_type_id}.revision", $revision_route); + } + + if ($revert_route = $this->getRevisionRevertRoute($entity_type)) { + $collection->add("entity.{$entity_type_id}.revision_revert", $revert_route); + } + + if ($delete_route = $this->getRevisionDeleteRoute($entity_type)) { + $collection->add("entity.{$entity_type_id}.revision_delete", $delete_route); + } + + if ($translation_route = $this->getRevisionTranslationRevertRoute($entity_type)) { + $collection->add("{$entity_type_id}.revision_revert_translation_confirm", $translation_route); + } + + if ($settings_form_route = $this->getSettingsFormRoute($entity_type)) { + $collection->add("$entity_type_id.settings", $settings_form_route); + } + + return $collection; + } + + /** + * Gets the version history route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getHistoryRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('version-history')) { + $route = new Route($entity_type->getLinkTemplate('version-history')); + $route + ->setDefaults([ + '_title' => "{$entity_type->getLabel()} revisions", + '_controller' => '\Drupal\gdpr_consent\Controller\ConsentAgreementController::revisionOverview', + ]) + ->setRequirement('_permission', 'access consent agreement revisions') + ->setOption('_admin_route', TRUE); + + return $route; + } + } + + /** + * Gets the revision route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getRevisionRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('revision')) { + $route = new Route($entity_type->getLinkTemplate('revision')); + $route + ->setDefaults([ + '_controller' => '\Drupal\gdpr_consent\Controller\ConsentAgreementController::revisionShow', + '_title_callback' => '\Drupal\gdpr_consent\Controller\ConsentAgreementController::revisionPageTitle', + ]) + ->setRequirement('_permission', 'access consent agreement revisions') + ->setOption('_admin_route', TRUE); + + return $route; + } + } + + /** + * Gets the revision revert route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getRevisionRevertRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('revision_revert')) { + $route = new Route($entity_type->getLinkTemplate('revision_revert')); + $route + ->setDefaults([ + '_form' => '\Drupal\gdpr_consent\Form\ConsentAgreementRevisionRevertForm', + '_title' => 'Revert to earlier revision', + ]) + ->setRequirement('_permission', 'revert all consent agreement revisions') + ->setOption('_admin_route', TRUE); + + return $route; + } + } + + /** + * Gets the revision delete route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getRevisionDeleteRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('revision_delete')) { + $route = new Route($entity_type->getLinkTemplate('revision_delete')); + $route + ->setDefaults([ + '_form' => '\Drupal\gdpr_consent\Form\ConsentAgreementRevisionDeleteForm', + '_title' => 'Delete earlier revision', + ]) + ->setRequirement('_permission', 'delete all consent agreement revisions') + ->setOption('_admin_route', TRUE); + + return $route; + } + } + + /** + * Gets the revision translation revert route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getRevisionTranslationRevertRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('translation_revert')) { + $route = new Route($entity_type->getLinkTemplate('translation_revert')); + $route + ->setDefaults([ + '_form' => '\Drupal\gdpr_consent\Form\ConsentAgreementRevisionRevertTranslationForm', + '_title' => 'Revert to earlier revision of a translation', + ]) + ->setRequirement('_permission', 'revert all consent agreement revisions') + ->setOption('_admin_route', TRUE); + + return $route; + } + } + + /** + * Gets the settings form route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getSettingsFormRoute(EntityTypeInterface $entity_type) { + if (!$entity_type->getBundleEntityType()) { + $route = new Route("/admin/structure/{$entity_type->id()}/settings"); + $route + ->setDefaults([ + '_form' => 'Drupal\gdpr_consent\Form\ConsentAgreementSettingsForm', + '_title' => "{$entity_type->getLabel()} settings", + ]) + ->setRequirement('_permission', $entity_type->getAdminPermission()) + ->setOption('_admin_route', TRUE); + + return $route; + } + } + +} diff --git a/modules/gdpr_consent/src/ConsentAgreementStorage.php b/modules/gdpr_consent/src/ConsentAgreementStorage.php new file mode 100644 index 0000000..a536fc9 --- /dev/null +++ b/modules/gdpr_consent/src/ConsentAgreementStorage.php @@ -0,0 +1,58 @@ +database->query( + 'SELECT revision_id FROM {gdpr_consent_agreement_revision} WHERE id=:id ORDER BY revision_id', + [':id' => $entity->id()] + )->fetchCol(); + } + + /** + * {@inheritdoc} + */ + public function userRevisionIds(AccountInterface $account) { + return $this->database->query( + 'SELECT revision_id FROM {gdpr_consent_agreement_field_revision} WHERE uid = :uid ORDER BY revision_id', + [':uid' => $account->id()] + )->fetchCol(); + } + + /** + * {@inheritdoc} + */ + public function countDefaultLanguageRevisions(ConsentAgreementInterface $entity) { + return $this->database->query('SELECT COUNT(*) FROM {gdpr_consent_agreement_field_revision} WHERE id = :id AND default_langcode = 1', [':id' => $entity->id()]) + ->fetchField(); + } + + /** + * {@inheritdoc} + */ + public function clearRevisionsLanguage(LanguageInterface $language) { + return $this->database->update('gdpr_consent_agreement_revision') + ->fields(['langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED]) + ->condition('langcode', $language->getId()) + ->execute(); + } + +} diff --git a/modules/gdpr_consent/src/ConsentAgreementStorageInterface.php b/modules/gdpr_consent/src/ConsentAgreementStorageInterface.php new file mode 100644 index 0000000..fb38ec9 --- /dev/null +++ b/modules/gdpr_consent/src/ConsentAgreementStorageInterface.php @@ -0,0 +1,61 @@ +entityManager() + ->getStorage('gdpr_consent_agreement') + ->loadRevision($gdpr_consent_agreement_revision); + $view_builder = $this->entityManager() + ->getViewBuilder('gdpr_consent_agreement'); + + return $view_builder->view($gdpr_consent_agreement); + } + + /** + * Page title callback for a Consent Agreement revision. + * + * @param int $gdpr_consent_agreement_revision + * The Consent Agreement revision ID. + * + * @return string + * The page title. + */ + public function revisionPageTitle($gdpr_consent_agreement_revision) { + $gdpr_consent_agreement = $this->entityManager() + ->getStorage('gdpr_consent_agreement') + ->loadRevision($gdpr_consent_agreement_revision); + return $this->t('Revision of %title from %date', [ + '%title' => $gdpr_consent_agreement->label(), + '%date' => format_date($gdpr_consent_agreement->getRevisionCreationTime()), + ]); + } + + /** + * Generates an overview table of older revisions of a Consent Agreement . + * + * @param \Drupal\gdpr_consent\Entity\ConsentAgreement $agreement + * A Consent Agreement object. + * + * @return array + * An array as expected by drupal_render(). + */ + public function revisionOverview($gdpr_consent_agreement) { + $agreement = ConsentAgreement::load($gdpr_consent_agreement); + $account = $this->currentUser(); + $storage = $this->entityManager()->getStorage('gdpr_consent_agreement'); + + $build['#title'] = $this->t('Revisions for %title', ['%title' => $agreement->label()]); + $header = [$this->t('Revision'), $this->t('Operations')]; + + $revert_permission = (($account->hasPermission("revert all consent agreement revisions") || $account->hasPermission('administer consent agreement entities'))); + $delete_permission = (($account->hasPermission("delete all consent agreement revisions") || $account->hasPermission('administer consent agreement entities'))); + + $rows = []; + + $vids = $storage->revisionIds($agreement); + + $latest_revision = TRUE; + + foreach (array_reverse($vids) as $vid) { + /** @var \Drupal\gdpr_consent\Entity\ConsentAgreement $revision */ + $revision = $storage->loadRevision($vid); + + $username = [ + '#theme' => 'username', + '#account' => $revision->getRevisionUser(), + ]; + + // Use revision link to link to revisions that are not active. + $date = \Drupal::service('date.formatter') + ->format($revision->getRevisionCreationTime(), 'short'); + if ($vid != $agreement->getRevisionId()) { + $link = $this->l($date, new Url('entity.gdpr_consent_agreement.revision', [ + 'gdpr_consent_agreement' => $agreement->id(), + 'gdpr_consent_agreement_revision' => $vid, + ])); + } + else { + $link = $agreement->link($date); + } + + $row = []; + $column = [ + 'data' => [ + '#type' => 'inline_template', + '#template' => '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}

{{ message }}

{% endif %}', + '#context' => [ + 'date' => $link, + 'username' => \Drupal::service('renderer')->renderPlain($username), + 'message' => [ + '#markup' => $revision->getRevisionLogMessage(), + '#allowed_tags' => Xss::getHtmlTagList(), + ], + ], + ], + ]; + $row[] = $column; + + if ($latest_revision) { + $row[] = [ + 'data' => [ + '#prefix' => '', + '#markup' => $this->t('Current revision'), + '#suffix' => '', + ], + ]; + foreach ($row as &$current) { + $current['class'] = ['revision-current']; + } + $latest_revision = FALSE; + } + else { + $links = []; + if ($revert_permission) { + $links['revert'] = [ + 'title' => $this->t('Revert'), + 'url' => Url::fromRoute('entity.gdpr_consent_agreement.revision_revert', [ + 'gdpr_consent_agreement' => $agreement->id(), + 'gdpr_consent_agreement_revision' => $vid, + ]), + ]; + } + + if ($delete_permission) { + $links['delete'] = [ + 'title' => $this->t('Delete'), + 'url' => Url::fromRoute('entity.gdpr_consent_agreement.revision_delete', [ + 'gdpr_consent_agreement' => $agreement->id(), + 'gdpr_consent_agreement_revision' => $vid, + ]), + ]; + } + + $row[] = [ + 'data' => [ + '#type' => 'operations', + '#links' => $links, + ], + ]; + } + + $rows[] = $row; + } + + $build['gdpr_consent_agreement_revisions_table'] = [ + '#theme' => 'table', + '#rows' => $rows, + '#header' => $header, + ]; + + return $build; + } + +} diff --git a/modules/gdpr_consent/src/Entity/ConsentAgreement.php b/modules/gdpr_consent/src/Entity/ConsentAgreement.php new file mode 100644 index 0000000..e4d80c9 --- /dev/null +++ b/modules/gdpr_consent/src/Entity/ConsentAgreement.php @@ -0,0 +1,321 @@ + \Drupal::currentUser()->id(), + ]; + } + + /** + * {@inheritdoc} + */ + protected function urlRouteParameters($rel) { + $uri_route_parameters = parent::urlRouteParameters($rel); + + if ($rel === 'revision_revert' && $this instanceof RevisionableInterface) { + $uri_route_parameters[$this->getEntityTypeId() . '_revision'] = $this->getRevisionId(); + } + elseif ($rel === 'revision_delete' && $this instanceof RevisionableInterface) { + $uri_route_parameters[$this->getEntityTypeId() . '_revision'] = $this->getRevisionId(); + } + + return $uri_route_parameters; + } + + /** + * {@inheritdoc} + */ + public function preSave(EntityStorageInterface $storage) { + parent::preSave($storage); + + foreach (array_keys($this->getTranslationLanguages()) as $langcode) { + $translation = $this->getTranslation($langcode); + + // If no owner has been set explicitly, make the anonymous user the owner. + if (!$translation->getOwner()) { + $translation->setOwnerId(0); + } + } + + // If no revision author has been set explicitly, + // make the ConsentAgreement owner the + // revision author. + if (!$this->getRevisionUser()) { + $this->setRevisionUserId($this->getOwnerId()); + } + } + + public function requiresExplicitAcceptance() { + return $this->get('mode')->value == 'explicit'; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return $this->get('name')->value; + } + + /** + * {@inheritdoc} + */ + public function setName($name) { + $this->set('name', $name); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getCreatedTime() { + return $this->get('created')->value; + } + + /** + * {@inheritdoc} + */ + public function setCreatedTime($timestamp) { + $this->set('created', $timestamp); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getOwner() { + return $this->get('user_id')->entity; + } + + /** + * {@inheritdoc} + */ + public function getOwnerId() { + return $this->get('user_id')->target_id; + } + + /** + * {@inheritdoc} + */ + public function setOwnerId($uid) { + $this->set('user_id', $uid); + return $this; + } + + /** + * {@inheritdoc} + */ + public function setOwner(UserInterface $account) { + $this->set('user_id', $account->id()); + return $this; + } + + /** + * {@inheritdoc} + */ + public function isPublished() { + return (bool) $this->getEntityKey('status'); + } + + /** + * {@inheritdoc} + */ + public function setPublished($published) { + $this->set('status', $published ? TRUE : FALSE); + return $this; + } + + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = parent::baseFieldDefinitions($entity_type); + + $fields['title'] = BaseFieldDefinition::create('string') + ->setLabel(t('Title')) + ->setRevisionable(TRUE) + ->setDescription(t('Agreement Title')) + ->setRequired(TRUE) + ->setDisplayOptions('view', [ + 'type' => 'textfield', + ]) + ->setDisplayOptions('form', [ + 'type' => 'textfield', + 'weight' => 0, + ]) + ->setDisplayConfigurable('form', TRUE) + ->setDisplayConfigurable('view', TRUE); + + $fields['mode'] = BaseFieldDefinition::create('list_string') + ->setLabel(t('Agreement Type')) + ->setRevisionable(TRUE) + ->setDescription(t('Whether consent is implicit or explicit. Set to "Explicit" if the user needs to explicitly agree, otherwise "Implicit".')) + ->setDefaultValue('explicit') + ->setSetting('allowed_values_function', ['\Drupal\gdpr_consent\Entity\ConsentAgreement', 'getModes']) + ->setRequired(TRUE) + ->setDisplayOptions('view', [ + 'type' => 'select', + ]) + ->setDisplayOptions('form', [ + 'type' => 'select', + 'weight' => 1, + ]) + ->setDisplayConfigurable('form', TRUE) + ->setDisplayConfigurable('view', TRUE); + + $fields['description'] = BaseFieldDefinition::create('string') + ->setLabel(t('Description')) + ->setRevisionable(TRUE) + ->setDescription(t('Text displayed to the user on the form')) + ->setRequired(TRUE) + ->setDisplayOptions('view', [ + 'type' => 'textfield', + ]) + ->setDisplayOptions('form', [ + 'type' => 'textfield', + 'weight' => 2, + ]) + ->setDisplayConfigurable('form', TRUE) + ->setDisplayConfigurable('view', TRUE); + + $fields['long_description'] = BaseFieldDefinition::create('string_long') + ->setLabel(t('Long Description')) + ->setRevisionable(TRUE) + ->setDescription(t('Text shown when the user clicks for more details')) + ->setDisplayOptions('view', [ + 'type' => 'textarea', + ]) + ->setDisplayOptions('form', [ + 'type' => 'textarea', + 'weight' => 3, + ]) + ->setDisplayConfigurable('form', TRUE) + ->setDisplayConfigurable('view', TRUE); + + $fields['status'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Publishing status')) + ->setDescription(t('A boolean indicating whether the Consent Agreement is published.')) + ->setRevisionable(TRUE) + ->setDefaultValue(TRUE); + + $fields['created'] = BaseFieldDefinition::create('created') + ->setLabel(t('Created')) + ->setDescription(t('The time that the entity was created.')); + + $fields['changed'] = BaseFieldDefinition::create('changed') + ->setLabel(t('Changed')) + ->setDescription(t('The time that the entity was last edited.')); + + $fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Revision translation affected')) + ->setDescription(t('Indicates if the last edit of a translation belongs to current revision.')) + ->setReadOnly(TRUE) + ->setRevisionable(TRUE) + ->setTranslatable(TRUE); + + $fields['user_id'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Authored by')) + ->setDescription(t('The user ID of author of the Consent Agreement entity.')) + ->setRevisionable(TRUE) + ->setSetting('target_type', 'user') + ->setSetting('handler', 'default') + ->setTranslatable(TRUE) + ->setDisplayOptions('view', [ + // 'label' => 'hidden', + 'type' => 'author', + ]) + ->setDisplayOptions('form', [ + 'type' => 'hidden', + 'settings' => [ + 'match_operator' => 'CONTAINS', + 'size' => '60', + 'autocomplete_type' => 'tags', + 'placeholder' => '', + ], + ]) + ->setDisplayConfigurable('form', TRUE) + ->setDisplayConfigurable('view', TRUE); + + return $fields; + } + + public static function getModes() { + return [ + 'implicit' => 'Implicit', + 'explicit' => 'Explicit', + ]; + } + + /** + * {@inheritdoc} + */ + public function __toString() { + return $this->get('title')->getString(); + } + + +} \ No newline at end of file diff --git a/modules/gdpr_consent/src/Entity/ConsentAgreementInterface.php b/modules/gdpr_consent/src/Entity/ConsentAgreementInterface.php new file mode 100644 index 0000000..1058a58 --- /dev/null +++ b/modules/gdpr_consent/src/Entity/ConsentAgreementInterface.php @@ -0,0 +1,114 @@ +entity; + +// $form['title'] = [ +// '#type' => 'textfield', +// '#required' => TRUE, +// '#title' => 'Title', +// '#default_value' => $entity->title->value, +// ]; + +// $form['mode'] = [ +// '#type' => 'select', +// '#title' => 'Agreement Type', +// '#required' => TRUE, +// '#options' => ConsentAgreement::getModes(), +// '#default_value' => $entity->mode->value, +// '#description' => 'Set to "Explicit" if the user needs to explicitly agree, otherwise "Implicit"', +// ]; + +// $form['description'] = [ +// '#type' => 'textfield', +// '#title' => 'Description', +// '#required' => TRUE, +// '#description' => 'Text displayed to the user on the form', +// '#default_value' => $entity->description->value, +// ]; +// +// $form['long_description'] = [ +// '#type' => 'textarea', +// '#title' => 'Long Description', +// '#description' => 'Text shown when the user clicks for more details', +// '#default_value' => $entity->long_description->value, +// ]; + + if (!$this->entity->isNew()) { + $form['new_revision'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Create new revision'), + '#default_value' => TRUE, + '#weight' => 10, + ]; + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + $entity = $this->entity; + + // Save as a new revision if requested to do so. + if (!$form_state->isValueEmpty('new_revision') && $form_state->getValue('new_revision') != FALSE) { + $entity->setNewRevision(); + + // If a new revision is created, save the current user as revision author. + $entity->setRevisionCreationTime(REQUEST_TIME); + $entity->setRevisionUserId(\Drupal::currentUser()->id()); + } + else { + $entity->setNewRevision(FALSE); + } + + $status = parent::save($form, $form_state); + + switch ($status) { + case SAVED_NEW: + drupal_set_message($this->t('Created the %label Consent Agreement.', [ + '%label' => $entity->label(), + ])); + break; + + default: + drupal_set_message($this->t('Saved the %label Consent Agreement.', [ + '%label' => $entity->label(), + ])); + } + $form_state->setRedirect('entity.gdpr_consent_agreement.canonical', ['gdpr_consent_agreement' => $entity->id()]); + } + + protected function showRevisionUi() { + return FALSE; + } +} diff --git a/modules/gdpr_consent/src/Form/ConsentAgreementRevisionRevertForm.php b/modules/gdpr_consent/src/Form/ConsentAgreementRevisionRevertForm.php new file mode 100644 index 0000000..c7356d8 --- /dev/null +++ b/modules/gdpr_consent/src/Form/ConsentAgreementRevisionRevertForm.php @@ -0,0 +1,149 @@ +ConsentAgreementStorage = $entity_storage; + $this->dateFormatter = $date_formatter; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity.manager')->getStorage('gdpr_consent_agreement'), + $container->get('date.formatter') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'gdpr_consent_agreement_revision_revert_confirm'; + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return t('Are you sure you want to revert to the revision from %revision-date?', ['%revision-date' => $this->dateFormatter->format($this->revision->getRevisionCreationTime())]); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('entity.gdpr_consent_agreement.version_history', ['gdpr_consent_agreement' => $this->revision->id()]); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return t('Revert'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return ''; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, $gdpr_consent_agreement_revision = NULL) { + $this->revision = $this->ConsentAgreementStorage->loadRevision($gdpr_consent_agreement_revision); + $form = parent::buildForm($form, $form_state); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + // The revision timestamp will be updated when the revision is saved. Keep + // the original one for the confirmation message. + $original_revision_timestamp = $this->revision->getRevisionCreationTime(); + + $this->revision = $this->prepareRevertedRevision($this->revision, $form_state); + $this->revision->revision_log = t('Copy of the revision from %date.', ['%date' => $this->dateFormatter->format($original_revision_timestamp)]); + $this->revision->save(); + + $this->logger('content')->notice('Consent Agreement: reverted %title revision %revision.', ['%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId()]); + \Drupal::messenger()->addMessage(t('Consent Agreement %title has been reverted to the revision from %revision-date.', ['%title' => $this->revision->label(), '%revision-date' => $this->dateFormatter->format($original_revision_timestamp)])); + $form_state->setRedirect( + 'entity.gdpr_consent_agreement.version_history', + ['gdpr_consent_agreement' => $this->revision->id()] + ); + } + + /** + * Prepares a revision to be reverted. + * + * @param \Drupal\gdpr_consent\Entity\ConsentAgreementInterface $revision + * The revision to be reverted. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return \Drupal\gdpr_consent\Entity\ConsentAgreementInterface + * The prepared revision ready to be stored. + */ + protected function prepareRevertedRevision(ConsentAgreementInterface $revision, FormStateInterface $form_state) { + $revision->setNewRevision(); + $revision->isDefaultRevision(TRUE); + $revision->setRevisionCreationTime(REQUEST_TIME); + + return $revision; + } + +} diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php b/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php new file mode 100644 index 0000000..654dcac --- /dev/null +++ b/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php @@ -0,0 +1,32 @@ + '' ] + + parent::defaultFieldSettings(); + } + + + /** + * {@inheritdoc} + */ +// public static function defaultStorageSettings() { +// return ['target_type' => 'gdpr_consent_agreement']; +// } + + /** + * {@inheritdoc} + */ + public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { + // Include target property form base. + //$properties = parent::propertyDefinitions($field_definition); + + $properties['target_id'] = DataReferenceTargetDefinition::create('integer') + ->setLabel('Target agreement ID') + ->setSetting('unsigned', TRUE) + ->setRequired(TRUE); + + $properties['target_revision_id'] = DataDefinition::create('integer') + ->setLabel('Revision ID'); + + $properties['agreed'] = DataDefinition::create('boolean') + ->setLabel('Agreed'); + + $properties['date'] = DataDefinition::create('datetime_iso8601') + ->setLabel('Date stored'); + + $properties['user_id'] = DataReferenceTargetDefinition::create('integer') + ->setLabel('User ID'); + +// $properties['user'] = DataReferenceDefinition::create('entity') +// ->setLabel('User') +// ->setDescription('The referenced User') +// ->setComputed(TRUE) +// ->setReadOnly(FALSE) +// ->setTargetDefinition(EntityDataDefinition::create('user')); + + + return $properties; + } + +// public function onChange($property_name, $notify = TRUE) { +// +// if ($property_name == 'user') { +// $property = $this->get('user'); +// $user_id = $property->isTargetNew() ? NULL : $property->getTargetIdentifier(); +// $this->writePropertyValue('user_id', $user_id); +// } +// elseif ($property_name == 'user_id') { +// $this->writePropertyValue('user', $this->user_id); +// } +// parent::onChange($property_name, $notify); +// } + public function fieldSettingsForm(array $form, FormStateInterface $form_state) { +// $form = parent::fieldSettingsForm($form, $form_state); // TODO: Change the autogenerated stub + // Hide the "Reference Type" section. +// $form['handler']['#type'] = 'hidden'; + + $agreement_ids = \Drupal::entityQuery('gdpr_consent_agreement') + ->condition('status', 1) + ->sort('title') + ->execute(); + + $agreements = ConsentAgreement::loadMultiple($agreement_ids); + + $element = []; + + $element['agreement'] = [ + '#type' => 'details', + '#title' => 'Agreement', + '#open' => TRUE, + '#tree' => TRUE, + 'agreement' => [ + '#type' => 'select', + '#required' => TRUE, + '#options' => $agreements, + '#default_value' => $this->getSetting('target_id') + ], + ]; + + return $element; + } + + + public static function schema(FieldStorageDefinitionInterface $field_definition) { + $schema = [ + 'indexes' => [ + 'target_id' => ['target_id'], + ], + ]; + + $schema['columns']['target_id'] = [ + 'description' => 'The ID of the target entity.', + 'type' => 'int', + 'unsigned' => TRUE, + ]; + + $schema['columns']['target_revision_id'] = [ + 'description' => 'The Revision ID of the target entity.', + 'type' => 'int', + ]; + + $schema['columns']['agreed'] = [ + 'description' => 'Whether the user has agreed.', + 'type' => 'int', + 'size' => 'tiny', + 'default' => 0, + ]; + + $schema['columns']['user_id'] = [ + 'description' => 'The user ID', + 'type' => 'int', + ]; + + $schema['columns']['date'] = [ + 'description' => 'Time that the user agreed.', + 'type' => 'varchar', + 'length' => 20, + ]; + + return $schema; + } + +} diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php new file mode 100644 index 0000000..f63f414 --- /dev/null +++ b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php @@ -0,0 +1,76 @@ +getFieldDefinition()->getSetting('target_id'); + $agreement = ConsentAgreement::load($agreement_id); + $item = $items[$delta]; + + $element['consent_text'] = [ + '#markup' => 'By submitting this form you agree to the following privacy policy: ' . $agreement->get('description')->value, + ]; + + $element['target_id'] = [ + '#type' => 'hidden', + '#default_value' => $agreement_id, + ]; + + $element['target_revision_id'] = [ + '#type' => 'hidden', + '#default_value' => isset($item->target_revision_id) ? $item->target_revision_id : $agreement->getRevisionId(), + ]; + + if ($agreement->requiresExplicitAcceptance()) { + $element['agreed'] = [ + '#type' => 'checkbox', + '#title' => 'I agree', + '#required' => TRUE, + '#default_value' => isset($item->agreed) && $item->agreed == TRUE, + ]; + } + + return $element; + } + + /** + * {@inheritdoc} + */ + public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { + for ($i = 0; $i < count($values); ++$i) { + if (!isset($values[$i]['user_id'])) { + $values[$i]['user_id'] = \Drupal::currentUser()->id(); + } + if (!isset($values[$i]['date'])) { + $values[$i]['date'] = date('Y-m-d H:i:s'); + } + } + return $values; + } + +} \ No newline at end of file diff --git a/modules/gdpr_fields/gdpr_fields.module b/modules/gdpr_fields/gdpr_fields.module index 392ab51..cc6f03e 100644 --- a/modules/gdpr_fields/gdpr_fields.module +++ b/modules/gdpr_fields/gdpr_fields.module @@ -22,6 +22,11 @@ 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. + // Do not add GDPR settings to the GDPR Consent Agreement form. + if ($field->getType() == 'gdpr_user_consent') { + return; + } + $form['field']['gdpr_fields'] = [ '#type' => 'details', '#title' => t('GDPR field settings'), diff --git a/modules/gdpr_tasks/src/Plugin/Field/FieldType/TaskLogItem.php b/modules/gdpr_tasks/src/Plugin/Field/FieldType/TaskLogItem.php index 8f11393..e6c4dad 100644 --- a/modules/gdpr_tasks/src/Plugin/Field/FieldType/TaskLogItem.php +++ b/modules/gdpr_tasks/src/Plugin/Field/FieldType/TaskLogItem.php @@ -13,6 +13,7 @@ * id = "gdpr_task_item", * label = @Translation("GDPR Removal Task Item"), * description = @Translation("GDPR Removal Task Item"), + * category = @Translation("GDPR"), * default_widget = "gdpr_task_item", * default_formatter = "gdpr_task_item" * ) From d6b4ddcd74093ad11cb903ecfdb0eceffa9c84c5 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Mon, 19 Mar 2018 10:19:59 +0000 Subject: [PATCH 03/21] List builder for consent entities --- .../src/Entity/ConsentAgreement.php | 2 +- .../Entity/ConsentAgreementListBuilder.php | 40 +++++++++++++ .../Field/FieldFormatter/ConsentFormatter.php | 16 ++++- .../Field/FieldType/UserConsentItem.php | 59 +++++-------------- .../Field/FieldWidget/ConsentWidget.php | 2 +- 5 files changed, 70 insertions(+), 49 deletions(-) create mode 100644 modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php diff --git a/modules/gdpr_consent/src/Entity/ConsentAgreement.php b/modules/gdpr_consent/src/Entity/ConsentAgreement.php index e4d80c9..4356895 100644 --- a/modules/gdpr_consent/src/Entity/ConsentAgreement.php +++ b/modules/gdpr_consent/src/Entity/ConsentAgreement.php @@ -25,7 +25,7 @@ * revision_table = "gdpr_consent_agreement_revision", * handlers = { * "storage" = "Drupal\gdpr_consent\ConsentAgreementStorage", - * "list_builder" = "Drupal\Core\Entity\EntityListBuilder", + * "list_builder" = "Drupal\gdpr_consent\Entity\ConsentAgreementListBuilder", * "route_provider" = { * "html" = "Drupal\gdpr_consent\ConsentAgreementHtmlRouteProvider" * }, diff --git a/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php b/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php new file mode 100644 index 0000000..ff524e1 --- /dev/null +++ b/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php @@ -0,0 +1,40 @@ + 'Title', + 'mode' => 'Implicit/Explicit', + ]; + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + $row = [ + 'title' => $entity->get('title')->value, + 'mode' => $entity->get('mode')->value, + ]; + return $row + parent::buildRow($entity); + } + +} diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php b/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php index 654dcac..972d9ae 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php @@ -22,11 +22,23 @@ class ConsentFormatter extends FormatterBase { * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { - $elements = []; + $output = []; + $storage = \Drupal::entityTypeManager() + ->getStorage('gdpr_consent_agreement'); + foreach ($items as $delta => $item) { + $agreement = $storage->loadRevision($item->target_revision_id); - return $elements; + $output[$delta] = [ + 'name' => [ + '#markup' => $agreement->toLink($agreement->title->value)->toString() . ' on ' . $item->date, + ], + ]; + + } + + return $output; } } diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php b/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php index b89bb3a..b2618b0 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php @@ -2,13 +2,10 @@ namespace Drupal\gdpr_consent\Plugin\Field\FieldType; -use Drupal\Core\Entity\TypedData\EntityDataDefinition; use Drupal\Core\Field\FieldItemBase; use Drupal\Core\Field\FieldStorageDefinitionInterface; -use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\TypedData\DataDefinition; -use Drupal\Core\TypedData\DataReferenceDefinition; use Drupal\Core\TypedData\DataReferenceTargetDefinition; use Drupal\gdpr_consent\Entity\ConsentAgreement; @@ -18,34 +15,25 @@ * @FieldType( * id = "gdpr_user_consent", * label = @Translation("GDPR Consent"), - * description = @Translation("Stores user consent for a particular agreement"), - * category = @Translation("GDPR"), - * default_widget = "gdpr_consent_widget", - * default_formatter = "gdpr_consent_formatter", + * description = @Translation("Stores user consent for a particular + * agreement"), category = @Translation("GDPR"), default_widget = + * "gdpr_consent_widget", default_formatter = "gdpr_consent_formatter", * ) */ class UserConsentItem extends FieldItemBase { - public static function defaultFieldSettings() { - return [ 'target_id' => '' ] - + parent::defaultFieldSettings(); - } - - /** * {@inheritdoc} */ -// public static function defaultStorageSettings() { -// return ['target_type' => 'gdpr_consent_agreement']; -// } + public static function defaultFieldSettings() { + return ['target_id' => ''] + + parent::defaultFieldSettings(); + } /** * {@inheritdoc} */ public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { - // Include target property form base. - //$properties = parent::propertyDefinitions($field_definition); - $properties['target_id'] = DataReferenceTargetDefinition::create('integer') ->setLabel('Target agreement ID') ->setSetting('unsigned', TRUE) @@ -63,34 +51,13 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel $properties['user_id'] = DataReferenceTargetDefinition::create('integer') ->setLabel('User ID'); -// $properties['user'] = DataReferenceDefinition::create('entity') -// ->setLabel('User') -// ->setDescription('The referenced User') -// ->setComputed(TRUE) -// ->setReadOnly(FALSE) -// ->setTargetDefinition(EntityDataDefinition::create('user')); - - return $properties; } -// public function onChange($property_name, $notify = TRUE) { -// -// if ($property_name == 'user') { -// $property = $this->get('user'); -// $user_id = $property->isTargetNew() ? NULL : $property->getTargetIdentifier(); -// $this->writePropertyValue('user_id', $user_id); -// } -// elseif ($property_name == 'user_id') { -// $this->writePropertyValue('user', $this->user_id); -// } -// parent::onChange($property_name, $notify); -// } + /** + * {@inheritdoc} + */ public function fieldSettingsForm(array $form, FormStateInterface $form_state) { -// $form = parent::fieldSettingsForm($form, $form_state); // TODO: Change the autogenerated stub - // Hide the "Reference Type" section. -// $form['handler']['#type'] = 'hidden'; - $agreement_ids = \Drupal::entityQuery('gdpr_consent_agreement') ->condition('status', 1) ->sort('title') @@ -109,14 +76,16 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) { '#type' => 'select', '#required' => TRUE, '#options' => $agreements, - '#default_value' => $this->getSetting('target_id') + '#default_value' => $this->getSetting('target_id'), ], ]; return $element; } - + /** + * {@inheritdoc} + */ public static function schema(FieldStorageDefinitionInterface $field_definition) { $schema = [ 'indexes' => [ diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php index f63f414..8707365 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php @@ -33,7 +33,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $item = $items[$delta]; $element['consent_text'] = [ - '#markup' => 'By submitting this form you agree to the following privacy policy: ' . $agreement->get('description')->value, + '#markup' => $agreement->get('description')->value . '
' . $agreement->toLink('View Details')->toString(), ]; $element['target_id'] = [ From 71b77035ba090da5875db3b10e6f0d8a1003a8bc Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Mon, 19 Mar 2018 11:49:59 +0000 Subject: [PATCH 04/21] Ensure consent agreement formatter links to correct revision --- .../Entity/ConsentAgreementListBuilder.php | 19 +++++++++++++++---- .../Field/FieldFormatter/ConsentFormatter.php | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php b/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php index ff524e1..7a336d5 100644 --- a/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php +++ b/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php @@ -1,15 +1,12 @@ $this->t('View'), + 'weight' => 0, + 'url' => $this->ensureDestination($entity->toUrl('canonical')), + ], + ]; + return $operations + parent::getDefaultOperations($entity); + } + } diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php b/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php index 972d9ae..09fc853 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldFormatter/ConsentFormatter.php @@ -32,7 +32,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { $output[$delta] = [ 'name' => [ - '#markup' => $agreement->toLink($agreement->title->value)->toString() . ' on ' . $item->date, + '#markup' => $agreement->toLink($agreement->title->value, 'revision')->toString() . ' on ' . $item->date, ], ]; From 20a7d231b52872158aafa1f8cba1d69643e52fc7 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Mon, 19 Mar 2018 13:36:16 +0000 Subject: [PATCH 05/21] Add gdpr_consent library and show description --- modules/gdpr_consent/css/gdpr_consent.css | 6 ++++ .../gdpr_consent/gdpr_consent.libraries.yml | 9 ++++++ modules/gdpr_consent/js/gdpr_consent.js | 14 ++++++++ .../Field/FieldWidget/ConsentWidget.php | 32 +++++++++++++------ 4 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 modules/gdpr_consent/css/gdpr_consent.css create mode 100644 modules/gdpr_consent/gdpr_consent.libraries.yml 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..eae1981 --- /dev/null +++ b/modules/gdpr_consent/css/gdpr_consent.css @@ -0,0 +1,6 @@ +.gdpr_agreed_toggle { + padding-left: 5px; +} +.gdpr_consent_implicit { + display: none; +} \ No newline at end of file diff --git a/modules/gdpr_consent/gdpr_consent.libraries.yml b/modules/gdpr_consent/gdpr_consent.libraries.yml new file mode 100644 index 0000000..9655418 --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.libraries.yml @@ -0,0 +1,9 @@ +gdpr_consent_display: + css: + theme: + css/gdpr_consent.css: { } + js: + js/gdpr_consent.js: { } + dependencies: + - core/jquery + - core/drupal diff --git a/modules/gdpr_consent/js/gdpr_consent.js b/modules/gdpr_consent/js/gdpr_consent.js new file mode 100644 index 0000000..653c645 --- /dev/null +++ b/modules/gdpr_consent/js/gdpr_consent.js @@ -0,0 +1,14 @@ +(function($) { + $(function() { + // Hide the description for any GDPR checkboxes. + var container = $('.gdpr_consent_agreement').parent().parent(); + var desc = container.next('.description'); + desc.hide(); + + $('?') + .appendTo(container) + .click(function() { + $(this).parent().next('.description').slideToggle() + }); + }); +})(jQuery); diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php index 8707365..8748f10 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php @@ -32,9 +32,9 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $agreement = ConsentAgreement::load($agreement_id); $item = $items[$delta]; - $element['consent_text'] = [ - '#markup' => $agreement->get('description')->value . '
' . $agreement->toLink('View Details')->toString(), - ]; + // $element['consent_text'] = [ + // '#markup' => $agreement->get('description')->value . '
' . $agreement->toLink('View Details')->toString(), + // ]; $element['target_id'] = [ '#type' => 'hidden', @@ -46,13 +46,25 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen '#default_value' => isset($item->target_revision_id) ? $item->target_revision_id : $agreement->getRevisionId(), ]; - if ($agreement->requiresExplicitAcceptance()) { - $element['agreed'] = [ - '#type' => 'checkbox', - '#title' => 'I agree', - '#required' => TRUE, - '#default_value' => isset($item->agreed) && $item->agreed == TRUE, - ]; + $element['agreed'] = [ + '#type' => 'checkbox', + '#title' => $agreement->get('description')->value, + //put a question mark icon at the end of the label so that the description is hidden. + '#description' => $agreement->get('long_description')->value, + '#required' => TRUE, + '#default_value' => isset($item->agreed) && $item->agreed == TRUE, + '#attributes' => ['class' => ['gdpr_consent_agreement']], + '#attached' => [ + 'library' => [ + 'gdpr_consent/gdpr_consent_display', + ], + ], + ]; + + // If we only require implicit agreement, hide the checkbox and set it to true. + if (!$agreement->requiresExplicitAcceptance()) { + $element['#type'] = 'item'; + $element['agreed']['#attributes']['class'][] = 'gdpr_consent_implicit'; } return $element; From 6d42235e669501b3f421ce5b9a47e111eecf23a3 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Mon, 19 Mar 2018 16:03:23 +0000 Subject: [PATCH 06/21] Add new tab for listing agreements --- modules/gdpr_consent/gdpr_consent.links.task.yml | 4 ++++ modules/gdpr_consent/gdpr_consent.routing.yml | 7 +++++++ .../src/Controller/ConsentAgreementController.php | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/modules/gdpr_consent/gdpr_consent.links.task.yml b/modules/gdpr_consent/gdpr_consent.links.task.yml index cf1bef2..7117f0d 100644 --- a/modules/gdpr_consent/gdpr_consent.links.task.yml +++ b/modules/gdpr_consent/gdpr_consent.links.task.yml @@ -20,3 +20,7 @@ entity.gdpr_consent_agreement.delete_form: title: Delete weight: 10 +gdpr_consent.agreements_tab: + title: GDPR Consent + route_name: gdpr_consent.agreements + base_route: entity.user.canonical \ No newline at end of file diff --git a/modules/gdpr_consent/gdpr_consent.routing.yml b/modules/gdpr_consent/gdpr_consent.routing.yml index e69de29..4715d5a 100644 --- a/modules/gdpr_consent/gdpr_consent.routing.yml +++ b/modules/gdpr_consent/gdpr_consent.routing.yml @@ -0,0 +1,7 @@ +gdpr_consent.agreements: + path: '/user/{user}/gdpr/agreements' + defaults: + _controller: \Drupal\gdpr_consent\Controller\ConsentAgreementController::myAgreements + requirements: + _permission: 'administer users' + user: \d+ \ No newline at end of file diff --git a/modules/gdpr_consent/src/Controller/ConsentAgreementController.php b/modules/gdpr_consent/src/Controller/ConsentAgreementController.php index d321f97..334f209 100644 --- a/modules/gdpr_consent/src/Controller/ConsentAgreementController.php +++ b/modules/gdpr_consent/src/Controller/ConsentAgreementController.php @@ -174,4 +174,11 @@ public function revisionOverview($gdpr_consent_agreement) { return $build; } + function myAgreements($user) { + $build = [ + '#markup' => t('Hello World!'), + ]; + return $build; + } + } From 0dac6def6af41b63323f0390417ffc1cd43203be Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Tue, 20 Mar 2018 14:37:45 +0000 Subject: [PATCH 07/21] Allow submodules to modify menu structure --- gdpr.module | 53 ++++++++++++++++++++++ modules/gdpr_tasks/gdpr_tasks.module | 67 +++++++--------------------- 2 files changed, 69 insertions(+), 51 deletions(-) diff --git a/gdpr.module b/gdpr.module index dd17037..604fa69 100644 --- a/gdpr.module +++ b/gdpr.module @@ -469,3 +469,56 @@ function _get_modules() { return $modules_list; } } + +/** + * Implements hook_toolbar(). + */ +function gdpr_toolbar() { + $user = \Drupal::currentUser(); + + $items = []; + + $items['gdpr'] = [ + '#cache' => [ + 'contexts' => [ + 'user', + ], + ], + ]; + + // Just add the root menu item. + // GDPR Tasks and GDPR Consent modules + // will modify this in hook_toolbar_alter. + if ($user->hasPermission('view gdpr tasks') || $user->hasPermission('manage gdpr agreements')) { + $items['gdpr'] += [ + '#type' => 'toolbar_item', + 'tab' => [ + '#type' => 'link', + '#title' => t('GDPR'), + // Task module adds in the URL. + '#url' => '', + '#attributes' => [ + 'title' => t('GDPR'), + 'class' => ['toolbar-icon', 'toolbar-icon-shortcut'], + ], + ], + 'tray' => [ + 'links' => [ + '#theme' => 'links', + '#links' => [], + '#attributes' => [ + 'class' => ['toolbar-menu'], + ], + ], + ], + '#weight' => -10, + '#attached' => [ + 'library' => [ + 'shortcut/drupal.shortcut', + ], + ], + ]; + + } + return $items; +} \ No newline at end of file diff --git a/modules/gdpr_tasks/gdpr_tasks.module b/modules/gdpr_tasks/gdpr_tasks.module index 4bd176f..d81d694 100644 --- a/modules/gdpr_tasks/gdpr_tasks.module +++ b/modules/gdpr_tasks/gdpr_tasks.module @@ -15,65 +15,30 @@ use Drupal\gdpr_tasks\TaskManager; /** * Implements hook_toolbar(). */ -function gdpr_tasks_toolbar() { +function gdpr_tasks_toolbar_alter(&$items) { $user = \Drupal::currentUser(); - $items = []; - $items['gdpr'] = [ - '#cache' => [ - 'contexts' => [ - 'user', - ], - ], - ]; - if ($user->hasPermission('view gdpr tasks')) { - $links = [ - 'summary' => [ - 'title' => t('Summary'), - 'url' => Url::fromRoute('gdpr_tasks.summary'), - 'attributes' => [ - 'title' => t('GDPR Summary'), - ], - ], - 'tasks' => [ - 'title' => t('Tasks'), - 'url' => Url::fromRoute('entity.gdpr_task.collection'), - 'attributes' => [ - 'title' => t('GDPR Task list'), - ], + // Root menu url. + $items['gdpr']['tab']['#url'] = Url::fromRoute('gdpr_tasks.summary'); + + $items['gdpr']['tray']['links']['#links']['summary'] = [ + 'title' => t('Summary'), + 'url' => Url::fromRoute('gdpr_tasks.summary'), + 'attributes' => [ + 'title' => t('GDPR Summary'), ], + 'weight' => 0, ]; - $items['gdpr'] += [ - '#type' => 'toolbar_item', - 'tab' => [ - '#type' => 'link', - '#title' => t('GDPR'), - '#url' => Url::fromRoute('gdpr_tasks.summary'), - '#attributes' => [ - 'title' => t('GDPR'), - 'class' => ['toolbar-icon', 'toolbar-icon-shortcut'], - ], - ], - 'tray' => [ - 'links' => [ - '#theme' => 'links', - '#links' => $links, - '#attributes' => [ - 'class' => ['toolbar-menu'], - ], - ], - ], - '#weight' => -10, - '#attached' => [ - 'library' => [ - 'shortcut/drupal.shortcut', - ], + + $items['gdpr']['tray']['links']['#links']['tasks'] = [ + 'title' => t('Tasks'), + 'url' => Url::fromRoute('entity.gdpr_task.collection'), + 'attributes' => [ + 'title' => t('GDPR Task list'), ], ]; } - - return $items; } /** From 7ccce92f6a43b45adec5e89fe6b3d0c9ea206e5c Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Tue, 20 Mar 2018 14:37:50 +0000 Subject: [PATCH 08/21] Add discovery for populating user ID in consent --- .../gdpr_consent/gdpr_consent.links.menu.yml | 7 ++ .../gdpr_consent/gdpr_consent.links.task.yml | 12 +- modules/gdpr_consent/gdpr_consent.module | 26 +++++ .../gdpr_consent/gdpr_consent.permissions.yml | 8 ++ modules/gdpr_consent/gdpr_consent.routing.yml | 2 +- .../gdpr_consent/gdpr_consent.services.yml | 4 + .../ConsentUserResolverPluginManager.php | 103 ++++++++++++++++++ .../GdprConsentUserResolver.php | 54 +++++++++ .../GdprConsentUserResolverInterface.php | 20 ++++ .../Controller/ConsentAgreementController.php | 62 ++++++++++- .../src/Entity/ConsentAgreement.php | 2 +- .../Field/FieldType/UserConsentItem.php | 57 +++++++--- .../Field/FieldWidget/ConsentWidget.php | 18 +-- .../ConsentUserResolver/ProfileResolver.php | 29 +++++ .../Gdpr/ConsentUserResolver/UserResolver.php | 29 +++++ 15 files changed, 405 insertions(+), 28 deletions(-) create mode 100644 modules/gdpr_consent/gdpr_consent.links.menu.yml create mode 100644 modules/gdpr_consent/gdpr_consent.module create mode 100644 modules/gdpr_consent/gdpr_consent.permissions.yml create mode 100644 modules/gdpr_consent/gdpr_consent.services.yml create mode 100644 modules/gdpr_consent/src/ConsentUserResolver/ConsentUserResolverPluginManager.php create mode 100644 modules/gdpr_consent/src/ConsentUserResolver/GdprConsentUserResolver.php create mode 100644 modules/gdpr_consent/src/ConsentUserResolver/GdprConsentUserResolverInterface.php create mode 100644 modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/ProfileResolver.php create mode 100644 modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/UserResolver.php diff --git a/modules/gdpr_consent/gdpr_consent.links.menu.yml b/modules/gdpr_consent/gdpr_consent.links.menu.yml new file mode 100644 index 0000000..36eb37e --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.links.menu.yml @@ -0,0 +1,7 @@ +# Task menu items definition +entity.gdpr_consent_agreement.collection: + title: 'Agreements' + route_name: entity.gdpr_consent_agreement.collection + description: 'List Agreement Entities' + parent: system.admin_structure + weight: 100 diff --git a/modules/gdpr_consent/gdpr_consent.links.task.yml b/modules/gdpr_consent/gdpr_consent.links.task.yml index 7117f0d..69d17a6 100644 --- a/modules/gdpr_consent/gdpr_consent.links.task.yml +++ b/modules/gdpr_consent/gdpr_consent.links.task.yml @@ -21,6 +21,14 @@ entity.gdpr_consent_agreement.delete_form: weight: 10 gdpr_consent.agreements_tab: - title: GDPR Consent + title: Consent route_name: gdpr_consent.agreements - base_route: entity.user.canonical \ No newline at end of file + parent_id: gdpr.collected_user_data + base_route: entity.user.canonical + weight: 100 + +entity.gdpr_consent_agreement.collection: + route_name: entity.gdpr_consent_agreement.collection + title: 'Agreements' + base_route: gdpr_tasks.summary + weight: 100 \ No newline at end of file diff --git a/modules/gdpr_consent/gdpr_consent.module b/modules/gdpr_consent/gdpr_consent.module new file mode 100644 index 0000000..d578549 --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.module @@ -0,0 +1,26 @@ +hasPermission('manage gdpr agreements')) { + $items['gdpr']['tray']['links']['#links']['agreements'] = [ + 'title' => t('Agreements'), + 'url' => Url::fromRoute('entity.gdpr_consent_agreement.collection'), + 'attributes' => [ + 'title' => t('Consent Agreements'), + ], + 'weight' => 100, + ]; + } +} diff --git a/modules/gdpr_consent/gdpr_consent.permissions.yml b/modules/gdpr_consent/gdpr_consent.permissions.yml new file mode 100644 index 0000000..f93d291 --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.permissions.yml @@ -0,0 +1,8 @@ +manage gdpr agreements: + title: 'Manage GDPR Agreements' + +grant gdpr any consent: + title: 'Grant Any Consent' + +grant gdpr own consent: + title: 'Grant Own Consent' \ No newline at end of file diff --git a/modules/gdpr_consent/gdpr_consent.routing.yml b/modules/gdpr_consent/gdpr_consent.routing.yml index 4715d5a..37709a3 100644 --- a/modules/gdpr_consent/gdpr_consent.routing.yml +++ b/modules/gdpr_consent/gdpr_consent.routing.yml @@ -4,4 +4,4 @@ gdpr_consent.agreements: _controller: \Drupal\gdpr_consent\Controller\ConsentAgreementController::myAgreements requirements: _permission: 'administer users' - user: \d+ \ No newline at end of file + user_id: \d+ \ No newline at end of file diff --git a/modules/gdpr_consent/gdpr_consent.services.yml b/modules/gdpr_consent/gdpr_consent.services.yml new file mode 100644 index 0000000..600eef4 --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.services.yml @@ -0,0 +1,4 @@ +services: + plugin.manager.gdpr_consent_resolver: + class: Drupal\gdpr_consent\ConsentUserResolver\ConsentUserResolverPluginManager + parent: default_plugin_manager \ No newline at end of file diff --git a/modules/gdpr_consent/src/ConsentUserResolver/ConsentUserResolverPluginManager.php b/modules/gdpr_consent/src/ConsentUserResolver/ConsentUserResolverPluginManager.php new file mode 100644 index 0000000..d7936ef --- /dev/null +++ b/modules/gdpr_consent/src/ConsentUserResolver/ConsentUserResolverPluginManager.php @@ -0,0 +1,103 @@ +setCacheBackend($cacheBackend, 'gdpr_consent_resolver_plugins'); + $this->alterInfo('gdpr_consent_resolver_info'); + } + + + /** + * Finds a resolver for the specified entity type/bundle. + * + * @param string $entity_type + * The entity type. + * @param string $bundle + * The bundle. + * + * @return \Drupal\gdpr_consent\ConsentUserResolver\GdprConsentUserResolverInterface + * The resolver that will be used to find the User for a specific entity. + * + * @throws \Exception + */ + public function getForEntityType($entity_type, $bundle) { + $definitions = $this->getDefinitions(); + + // Get all plugins that act on the entity type. + $definitions_for_entity = array_filter($definitions, function ($d) use ($entity_type) { + return $d['entityType'] == $entity_type; + }); + + $definitions_for_bundle = array_filter($definitions_for_entity, function ($d) use ($bundle) { + return array_key_exists('bundle', $d) && $d['bundle'] == $bundle; + }); + + + if (count($definitions_for_bundle) > 0) { + // Get first item from the array. + $definition = reset($definitions_for_bundle); + } + elseif (count($definitions_for_entity) > 0) { + // None matched for bundle. + // Find any with no bundle. + $definitions_for_bundle = array_filter($definitions_for_entity, function ($d) { + return !array_key_exists('bundle', $d) || $d['bundle'] == ''; + }); + + if (count($definitions_for_bundle) > 0) { + // Get first item from array. + $definition = reset($definitions_for_bundle); + } + } + + if (!isset($definition)) { + throw new \Exception("Could not determine user ID for entity type $entity_type. Please ensure there is a resolver registered."); + } + + return $this->createInstance($definition['id']); + } + +} diff --git a/modules/gdpr_consent/src/ConsentUserResolver/GdprConsentUserResolver.php b/modules/gdpr_consent/src/ConsentUserResolver/GdprConsentUserResolver.php new file mode 100644 index 0000000..a2ead4c --- /dev/null +++ b/modules/gdpr_consent/src/ConsentUserResolver/GdprConsentUserResolver.php @@ -0,0 +1,54 @@ +entityFieldManager = $entity_field_manager; + } + + public static function create(ContainerInterface $container) { + return new static($container->get('entity_field.manager')); + } + /** * Displays a Consent Agreement revision. * @@ -71,8 +90,8 @@ public function revisionOverview($gdpr_consent_agreement) { $build['#title'] = $this->t('Revisions for %title', ['%title' => $agreement->label()]); $header = [$this->t('Revision'), $this->t('Operations')]; - $revert_permission = (($account->hasPermission("revert all consent agreement revisions") || $account->hasPermission('administer consent agreement entities'))); - $delete_permission = (($account->hasPermission("delete all consent agreement revisions") || $account->hasPermission('administer consent agreement entities'))); + $revert_permission = $account->hasPermission('create gdpr agreements'); + $delete_permission = $account->hasPermission('create gdpr agreements'); $rows = []; @@ -175,8 +194,45 @@ public function revisionOverview($gdpr_consent_agreement) { } function myAgreements($user) { + $map = $this->entityFieldManager->getFieldMapByFieldType('gdpr_user_consent'); + + $entity_type_manager = \Drupal::entityTypeManager(); + + foreach ($map as $entity_type => $fields) { + $field_names = array_keys($fields); + + foreach($field_names as $field_name) { + + $ids = \Drupal::entityQuery($entity_type) + ->condition( $field_name . '.user_id', $user) + ->execute(); + + $entities = $entity_type_manager->getStorage($entity_type)->loadMultiple($ids); + + foreach($entities as $entity) { +// $agreement = $entity->{$field_name}->target_id + } + +// \Drupal::entityTypeManager()->in + + } + } + + //TODO: The actual query + + + $header = ['Agreement', 'Date Agreed', 'Operations']; + + $rows=[]; + $build = [ - '#markup' => t('Hello World!'), + //'#markup' => t('Hello World!'), + '#title' => 'Consent Agreements', + 'table' => [ + '#theme' => 'table', + '#rows' => $rows, + '#header' => $header, + ] ]; return $build; } diff --git a/modules/gdpr_consent/src/Entity/ConsentAgreement.php b/modules/gdpr_consent/src/Entity/ConsentAgreement.php index 4356895..e200520 100644 --- a/modules/gdpr_consent/src/Entity/ConsentAgreement.php +++ b/modules/gdpr_consent/src/Entity/ConsentAgreement.php @@ -43,7 +43,7 @@ * "status" = "status", * "revision" = "revision_id", * }, - * admin_permission = "administer content", + * admin_permission = "create gdpr agreements", * links = { * "canonical" = "/admin/gdpr/agreements/{gdpr_consent_agreement}", * "add-form" = "/admin/gdpr/agreements/add", diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php b/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php index b2618b0..d425a56 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php @@ -15,9 +15,10 @@ * @FieldType( * id = "gdpr_user_consent", * label = @Translation("GDPR Consent"), - * description = @Translation("Stores user consent for a particular - * agreement"), category = @Translation("GDPR"), default_widget = - * "gdpr_consent_widget", default_formatter = "gdpr_consent_formatter", + * description = @Translation("Stores user consent for a particular agreement"), + * category = @Translation("GDPR"), + * default_widget ="gdpr_consent_widget", + * default_formatter = "gdpr_consent_formatter" * ) */ class UserConsentItem extends FieldItemBase { @@ -51,13 +52,36 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel $properties['user_id'] = DataReferenceTargetDefinition::create('integer') ->setLabel('User ID'); + $properties['user_id_accepted'] = DataReferenceTargetDefinition::create('integer') + ->setLabel('User ID Accepted'); + + $properties['notes'] = DataReferenceTargetDefinition::create('string') + ->setLabel('Notes'); + return $properties; } + /** + * {@inheritdoc} + */ + public function preSave() { + $definition = $this->getFieldDefinition(); + + /* @var \Drupal\gdpr_consent\ConsentUserResolver\ConsentUserResolverPluginManager $plugin_manager */ + $plugin_manager = \Drupal::service('plugin.manager.gdpr_consent_resolver'); + $resolver = $plugin_manager->getForEntityType($definition->getTargetEntityTypeId(), $definition->getTargetBundle()); + $user = $resolver->resolve($this->getEntity()); + + if ($user != NULL) { + $this->set('user_id', $user->id()); + } + } + /** * {@inheritdoc} */ public function fieldSettingsForm(array $form, FormStateInterface $form_state) { + $agreement_ids = \Drupal::entityQuery('gdpr_consent_agreement') ->condition('status', 1) ->sort('title') @@ -67,17 +91,12 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) { $element = []; - $element['agreement'] = [ - '#type' => 'details', + $element['target_id'] = [ + '#type' => 'select', '#title' => 'Agreement', - '#open' => TRUE, - '#tree' => TRUE, - 'agreement' => [ - '#type' => 'select', - '#required' => TRUE, - '#options' => $agreements, - '#default_value' => $this->getSetting('target_id'), - ], + '#required' => TRUE, + '#options' => ['' => 'Please select'] + $agreements, + '#default_value' => $this->getSetting('target_id'), ]; return $element; @@ -112,7 +131,7 @@ public static function schema(FieldStorageDefinitionInterface $field_definition) ]; $schema['columns']['user_id'] = [ - 'description' => 'The user ID', + 'description' => 'ID of the user who has accepted.', 'type' => 'int', ]; @@ -122,6 +141,16 @@ public static function schema(FieldStorageDefinitionInterface $field_definition) 'length' => 20, ]; + $schema['columns']['user_id_accepted'] = [ + 'description' => 'ID of the user who recorded the acceptance', + 'type' => 'int', + ]; + + $schema['columns']['notes'] = [ + 'description' => 'Additional notes on the acceptance', + 'type' => 'varchar', + 'length' => '255', + ]; return $schema; } diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php index 8748f10..5d340f9 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php @@ -28,14 +28,19 @@ class ConsentWidget extends WidgetBase { * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { + if (array_key_exists(0, $form['#parents']) && $form['#parents'][0] == 'default_value_input') { + // Don't show as part of the 'Default Value' form. + return []; + } + $agreement_id = $items->getFieldDefinition()->getSetting('target_id'); + if ($agreement_id == '') { + // Don't display if an agreement hasn't been configured for this field yet. + return []; + } $agreement = ConsentAgreement::load($agreement_id); $item = $items[$delta]; - // $element['consent_text'] = [ - // '#markup' => $agreement->get('description')->value . '
' . $agreement->toLink('View Details')->toString(), - // ]; - $element['target_id'] = [ '#type' => 'hidden', '#default_value' => $agreement_id, @@ -75,8 +80,8 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen */ public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { for ($i = 0; $i < count($values); ++$i) { - if (!isset($values[$i]['user_id'])) { - $values[$i]['user_id'] = \Drupal::currentUser()->id(); + if (!isset($values[$i]['user_id_accepted'])) { + $values[$i]['user_id_accepted'] = \Drupal::currentUser()->id(); } if (!isset($values[$i]['date'])) { $values[$i]['date'] = date('Y-m-d H:i:s'); @@ -84,5 +89,4 @@ public function massageFormValues(array $values, array $form, FormStateInterface } return $values; } - } \ No newline at end of file diff --git a/modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/ProfileResolver.php b/modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/ProfileResolver.php new file mode 100644 index 0000000..f89065e --- /dev/null +++ b/modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/ProfileResolver.php @@ -0,0 +1,29 @@ +uid->entity; + } + +} diff --git a/modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/UserResolver.php b/modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/UserResolver.php new file mode 100644 index 0000000..808572e --- /dev/null +++ b/modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/UserResolver.php @@ -0,0 +1,29 @@ + Date: Tue, 20 Mar 2018 15:15:22 +0000 Subject: [PATCH 09/21] Ensure edit functionality is disabled when trying to edit another user's consent without appropriate permissions --- .../Field/FieldWidget/ConsentWidget.php | 64 +++++++++++++++++-- .../Gdpr/ConsentUserResolver/UserResolver.php | 4 +- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php index 5d340f9..b1ae3d4 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php @@ -7,7 +7,6 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\gdpr_consent\Entity\ConsentAgreement; - /** * Plugin implementation of the 'gdpr_consent_widget' widget. * @@ -29,15 +28,37 @@ class ConsentWidget extends WidgetBase { */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { if (array_key_exists(0, $form['#parents']) && $form['#parents'][0] == 'default_value_input') { - // Don't show as part of the 'Default Value' form. + // Don't show as part of the 'Default Value' form when configuring field. return []; } + $can_edit_anyones_consent = \Drupal::currentUser()->hasPermission('grant gdpr any consent'); + $can_edit_own_consent = \Drupal::currentUser()->hasPermission('grant gdpr own consent'); + // Consenting user and current user may not be the same. + // For example, a staff member editing consent on behalf of a user who + // calls the office. + $current_user = \Drupal::currentUser(); + $consenting_user = $this->getConsentingUser($items); + $agreement_id = $items->getFieldDefinition()->getSetting('target_id'); + if ($agreement_id == '') { - // Don't display if an agreement hasn't been configured for this field yet. + // Don't display if an agreement hasn't + // been configured for this field yet. + return []; + } + + if (!$can_edit_anyones_consent && $consenting_user->id() != $current_user->id()) { + // Abort if the current user does not have permission + // to edit other user's consent and we're editing another user. + return []; + } + + if (!$can_edit_own_consent && $consenting_user->id() == $current_user->id()) { + // Abort if the current user cannot edit their own consent. return []; } + $agreement = ConsentAgreement::load($agreement_id); $item = $items[$delta]; @@ -54,7 +75,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $element['agreed'] = [ '#type' => 'checkbox', '#title' => $agreement->get('description')->value, - //put a question mark icon at the end of the label so that the description is hidden. '#description' => $agreement->get('long_description')->value, '#required' => TRUE, '#default_value' => isset($item->agreed) && $item->agreed == TRUE, @@ -66,12 +86,23 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen ], ]; - // If we only require implicit agreement, hide the checkbox and set it to true. + // If we only require implicit agreement, + // hide the checkbox and set it to true. if (!$agreement->requiresExplicitAcceptance()) { $element['#type'] = 'item'; $element['agreed']['#attributes']['class'][] = 'gdpr_consent_implicit'; } + // Only show the notes field if the user has permission. + if ($can_edit_anyones_consent) { + $element['notes'] = [ + '#type' => 'textarea', + '#title' => 'Notes', + '#required' => TRUE, + '#default_value' => isset($item->notes) ? $item->notes : '', + ]; + } + return $element; } @@ -89,4 +120,25 @@ public function massageFormValues(array $values, array $form, FormStateInterface } return $values; } -} \ No newline at end of file + + /** + * Gets the user who the consent will be stored against. + * + * @param \Drupal\Core\Field\FieldItemListInterface $items + * The field. + * + * @return \Drupal\user\Entity\User + * The user + * + * @throws \Exception + */ + private function getConsentingUser(FieldItemListInterface $items) { + $definition = $items->getFieldDefinition(); + /* @var \Drupal\gdpr_consent\ConsentUserResolver\ConsentUserResolverPluginManager $plugin_manager */ + $plugin_manager = \Drupal::service('plugin.manager.gdpr_consent_resolver'); + $resolver = $plugin_manager->getForEntityType($definition->getTargetEntityTypeId(), $definition->getTargetBundle()); + $user = $resolver->resolve($items->getEntity()); + return $user; + } + +} diff --git a/modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/UserResolver.php b/modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/UserResolver.php index 808572e..3ac52fd 100644 --- a/modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/UserResolver.php +++ b/modules/gdpr_consent/src/Plugin/Gdpr/ConsentUserResolver/UserResolver.php @@ -11,8 +11,8 @@ * Resolves user reference for a Profile entity. * * @GdprConsentUserResolver( - * id = "gdpr_consent_profile_resolver", - * label = "GDPR Consent Profile Resolver", + * id = "gdpr_consent_user_resolver", + * label = "GDPR Consent User Resolver", * entityType = "user" * ) * @package Drupal\gdpr_consent\Plugin\Gdpr\ConsentUserResolver From 729095c29ce75ea400ca31c90bc48867d305d976 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Tue, 20 Mar 2018 15:37:15 +0000 Subject: [PATCH 10/21] Implement implicit agreement UI --- modules/gdpr_consent/css/gdpr_consent.css | 3 --- modules/gdpr_consent/js/gdpr_consent.js | 25 +++++++++++++------ .../Field/FieldWidget/ConsentWidget.php | 9 +++++-- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/modules/gdpr_consent/css/gdpr_consent.css b/modules/gdpr_consent/css/gdpr_consent.css index eae1981..caf99c8 100644 --- a/modules/gdpr_consent/css/gdpr_consent.css +++ b/modules/gdpr_consent/css/gdpr_consent.css @@ -1,6 +1,3 @@ .gdpr_agreed_toggle { padding-left: 5px; -} -.gdpr_consent_implicit { - display: none; } \ No newline at end of file diff --git a/modules/gdpr_consent/js/gdpr_consent.js b/modules/gdpr_consent/js/gdpr_consent.js index 653c645..7156b6c 100644 --- a/modules/gdpr_consent/js/gdpr_consent.js +++ b/modules/gdpr_consent/js/gdpr_consent.js @@ -1,14 +1,25 @@ -(function($) { - $(function() { +(function ($) { + $(function () { // Hide the description for any GDPR checkboxes. var container = $('.gdpr_consent_agreement').parent().parent(); var desc = container.next('.description'); desc.hide(); - $('?') - .appendTo(container) - .click(function() { - $(this).parent().next('.description').slideToggle() - }); + $('?') + .appendTo(container) + .click(function () { + $(this).parent().next('.description').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); diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php index b1ae3d4..a520396 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php @@ -89,8 +89,13 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen // If we only require implicit agreement, // hide the checkbox and set it to true. if (!$agreement->requiresExplicitAcceptance()) { - $element['#type'] = 'item'; - $element['agreed']['#attributes']['class'][] = 'gdpr_consent_implicit'; + $element['agreed']['#title'] = ''; + $element['agreed']['#type'] = 'item'; + // Just render an empty span that the javascript can hook onto. + $element['agreed']['#markup'] = + '' . $agreement->get('description')->value . '' + . + $element['agreed']['#default_value'] = TRUE; } // Only show the notes field if the user has permission. From cd492d6193e5a81f8a4a444a43da3d3602c9e927 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Tue, 20 Mar 2018 16:21:13 +0000 Subject: [PATCH 11/21] Missing semicolon --- .../src/Plugin/Field/FieldWidget/ConsentWidget.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php index a520396..e849bd1 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php @@ -93,8 +93,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $element['agreed']['#type'] = 'item'; // Just render an empty span that the javascript can hook onto. $element['agreed']['#markup'] = - '' . $agreement->get('description')->value . '' - . + '' . $agreement->get('description')->value . ''; $element['agreed']['#default_value'] = TRUE; } From 2e455e929a432a06bcd05ac9ed1e47e7176aa31e Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Tue, 20 Mar 2018 16:43:38 +0000 Subject: [PATCH 12/21] Null check --- modules/gdpr_fields/src/GDPRCollector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/gdpr_fields/src/GDPRCollector.php b/modules/gdpr_fields/src/GDPRCollector.php index 2d8dbd8..256ec95 100644 --- a/modules/gdpr_fields/src/GDPRCollector.php +++ b/modules/gdpr_fields/src/GDPRCollector.php @@ -265,7 +265,8 @@ public function fieldValues($entity_type = 'user', EntityInterface $entity, $ext $bundle_id = $entity->bundle(); if ($bundle_type) { $bundle_storage = $this->entityTypeManager->getStorage($bundle_type); - $bundle_label = $bundle_storage->load($bundle_id)->label(); + $bundle_entity = $bundle_storage->load($bundle_id); + $bundle_label = $bundle_entity == NULL ? '' : $bundle_entity->label(); } else { $bundle_label = $entity->getEntityType()->getLabel(); From ebfb00887b2d5ecdcd2f40e6f3feea042e287f6e Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Wed, 21 Mar 2018 15:44:44 +0000 Subject: [PATCH 13/21] Ensure collapsing works with other themes --- modules/gdpr_consent/js/gdpr_consent.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/gdpr_consent/js/gdpr_consent.js b/modules/gdpr_consent/js/gdpr_consent.js index 7156b6c..48fc446 100644 --- a/modules/gdpr_consent/js/gdpr_consent.js +++ b/modules/gdpr_consent/js/gdpr_consent.js @@ -1,14 +1,25 @@ (function ($) { $(function () { // Hide the description for any GDPR checkboxes. - var container = $('.gdpr_consent_agreement').parent().parent(); + var container = $('.gdpr_consent_agreement').parent(); var desc = container.next('.description'); + + if(!desc.length) { + container = container.parent(); + desc = container.next('.description'); + } + desc.hide(); $('?') .appendTo(container) .click(function () { - $(this).parent().next('.description').slideToggle() + var desc = $(this).next('.description'); + if(!desc.length) { + desc = $(this).parent().next('.description'); + } + + desc.slideToggle() }); // Do the same for implicit From e6a7a247f45beaa3e42491a76f00be9bc50634e8 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Wed, 21 Mar 2018 15:51:04 +0000 Subject: [PATCH 14/21] Fix indentation --- .../src/Controller/GDPRController.php | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/modules/gdpr_tasks/src/Controller/GDPRController.php b/modules/gdpr_tasks/src/Controller/GDPRController.php index 012d046..18aac7a 100644 --- a/modules/gdpr_tasks/src/Controller/GDPRController.php +++ b/modules/gdpr_tasks/src/Controller/GDPRController.php @@ -79,21 +79,24 @@ public function summaryPage() { * Return user to GDPR requests. */ public function requestPage(AccountInterface $user, $gdpr_task_type) { - $tasks = $this->taskManager->getUserTasks($user, $gdpr_task_type); + $tasks = $this->taskManager->getUserTasks($user, $gdpr_task_type); - if (!empty($tasks)) { - $this->messenger->addWarning('You already have a pending task.'); - } - else { - $values = [ - 'type' => $gdpr_task_type, - 'user_id' => $user->id(), - ]; - $this->entityTypeManager->getStorage('gdpr_task')->create($values)->save(); - $this->messenger->addStatus('Your request has been logged'); - } + if (!empty($tasks)) { + $this->messenger->addWarning('You already have a pending task.'); + } + else { + $values = [ + 'type' => $gdpr_task_type, + 'user_id' => $user->id(), + ]; + $this->entityTypeManager->getStorage('gdpr_task') + ->create($values) + ->save(); + $this->messenger->addStatus('Your request has been logged'); + } - $response = new RedirectResponse(Url::fromRoute('view.gdpr_tasks_my_data_requests.page_1', ['user' => $user->id()])->toString()); + $response = new RedirectResponse(Url::fromRoute('view.gdpr_tasks_my_data_requests.page_1', ['user' => $user->id()]) + ->toString()); return $response; } From db6b4ab3d5743ccd02cf37ac3be135e84c92a0c3 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Wed, 21 Mar 2018 15:58:48 +0000 Subject: [PATCH 15/21] My agreements --- .../Controller/ConsentAgreementController.php | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/modules/gdpr_consent/src/Controller/ConsentAgreementController.php b/modules/gdpr_consent/src/Controller/ConsentAgreementController.php index e0ad095..a948b1f 100644 --- a/modules/gdpr_consent/src/Controller/ConsentAgreementController.php +++ b/modules/gdpr_consent/src/Controller/ConsentAgreementController.php @@ -7,6 +7,7 @@ use Drupal\Core\Controller\ControllerBase; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityFieldManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Url; use Drupal\gdpr_consent\Entity\ConsentAgreement; use Drupal\gdpr_consent\Entity\ConsentAgreementInterface; @@ -27,12 +28,21 @@ class ConsentAgreementController extends ControllerBase implements ContainerInje */ private $entityFieldManager; + /** + * The entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + //private $entityTypeManager; + public function __construct(EntityFieldManagerInterface $entity_field_manager) { $this->entityFieldManager = $entity_field_manager; } public static function create(ContainerInterface $container) { - return new static($container->get('entity_field.manager')); + return new static( + $container->get('entity_field.manager') + ); } /** @@ -157,9 +167,9 @@ public function revisionOverview($gdpr_consent_agreement) { $links['revert'] = [ 'title' => $this->t('Revert'), 'url' => Url::fromRoute('entity.gdpr_consent_agreement.revision_revert', [ - 'gdpr_consent_agreement' => $agreement->id(), - 'gdpr_consent_agreement_revision' => $vid, - ]), + 'gdpr_consent_agreement' => $agreement->id(), + 'gdpr_consent_agreement_revision' => $vid, + ]), ]; } @@ -195,44 +205,59 @@ public function revisionOverview($gdpr_consent_agreement) { function myAgreements($user) { $map = $this->entityFieldManager->getFieldMapByFieldType('gdpr_user_consent'); - - $entity_type_manager = \Drupal::entityTypeManager(); + $agreement_storage = $this->entityTypeManager()->getStorage('gdpr_consent_agreement'); + $rows = []; foreach ($map as $entity_type => $fields) { $field_names = array_keys($fields); - foreach($field_names as $field_name) { + foreach ($field_names as $field_name) { $ids = \Drupal::entityQuery($entity_type) - ->condition( $field_name . '.user_id', $user) + ->condition($field_name . '.user_id', $user) ->execute(); - $entities = $entity_type_manager->getStorage($entity_type)->loadMultiple($ids); + $entities = $this->entityTypeManager()->getStorage($entity_type) + ->loadMultiple($ids); - foreach($entities as $entity) { -// $agreement = $entity->{$field_name}->target_id - } + foreach ($entities as $entity) { + $agreement = $agreement_storage->loadRevision($entity->{$field_name}->target_revision_id); -// \Drupal::entityTypeManager()->in + $row = []; + $row[] = [ + 'data' => [ + '#markup' => $agreement->toLink($agreement->title->value, 'revision')->toString() + ], + ]; + + $row[] = [ + 'data' => [ + '#markup' => $entity->{$field_name}->date + ], + ]; + + $row[] = [ + 'date' => [ + '#markup' => 'Ops' + ] + ]; + + $rows[] = $row; + } } } - //TODO: The actual query - $header = ['Agreement', 'Date Agreed', 'Operations']; - $rows=[]; - $build = [ - //'#markup' => t('Hello World!'), '#title' => 'Consent Agreements', 'table' => [ '#theme' => 'table', '#rows' => $rows, '#header' => $header, - ] + ], ]; return $build; } From 7c8b12370ef760e1eddb70324a14d2d3d05d3d04 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Wed, 21 Mar 2018 16:25:13 +0000 Subject: [PATCH 16/21] Required --- .../gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php index e849bd1..fd8fa86 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php @@ -76,7 +76,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen '#type' => 'checkbox', '#title' => $agreement->get('description')->value, '#description' => $agreement->get('long_description')->value, - '#required' => TRUE, + '#required' => $items->getFieldDefinition()->isRequired(), '#default_value' => isset($item->agreed) && $item->agreed == TRUE, '#attributes' => ['class' => ['gdpr_consent_agreement']], '#attached' => [ From 418e8e81be919bb7d8f23dfe216411e1bce784dd Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Wed, 21 Mar 2018 16:26:49 +0000 Subject: [PATCH 17/21] Cleanup menu links for GDPR fields --- .../gdpr_fields/gdpr_fields.links.task.yml | 4 +++- modules/gdpr_fields/gdpr_fields.module | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/modules/gdpr_fields/gdpr_fields.links.task.yml b/modules/gdpr_fields/gdpr_fields.links.task.yml index 3787fc9..ebccaf3 100644 --- a/modules/gdpr_fields/gdpr_fields.links.task.yml +++ b/modules/gdpr_fields/gdpr_fields.links.task.yml @@ -1,4 +1,6 @@ gdpr_fields.fields_list: route_name: gdpr_fields.fields_list - title: 'Used in GDPR' + title: 'GDPR field list' base_route: entity.field_storage_config.collection + base_route: gdpr_tasks.summary + weight: 99 \ No newline at end of file diff --git a/modules/gdpr_fields/gdpr_fields.module b/modules/gdpr_fields/gdpr_fields.module index cc6f03e..72f0af5 100644 --- a/modules/gdpr_fields/gdpr_fields.module +++ b/modules/gdpr_fields/gdpr_fields.module @@ -6,6 +6,25 @@ */ use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; + +/** + * Implements hook_toolbar_alter(). + */ +function gdpr_fields_toolbar_alter(&$items) { + $user = \Drupal::currentUser(); + + if ($user->hasPermission('view gdpr fields')) { + $items['gdpr']['tray']['links']['#links']['fields'] = [ + 'title' => t('Field list'), + 'url' => Url::fromRoute('gdpr_fields.fields_list'), + 'attributes' => [ + 'title' => t('Field list'), + ], + 'weight' => 100, + ]; + } +} /** * Implements hook_form_FORM_ID_alter(). From 8b644a9401b36d1adbd659244490969761e10e2d Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Wed, 21 Mar 2018 16:30:14 +0000 Subject: [PATCH 18/21] Don't make gdpr notes required --- .../src/Plugin/Field/FieldWidget/ConsentWidget.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php index fd8fa86..f37fbd5 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldWidget/ConsentWidget.php @@ -101,8 +101,8 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen if ($can_edit_anyones_consent) { $element['notes'] = [ '#type' => 'textarea', - '#title' => 'Notes', - '#required' => TRUE, + '#title' => 'GDPR Consent Notes', + '#required' => FALSE, '#default_value' => isset($item->notes) ? $item->notes : '', ]; } From e40b3754794413a287bcd05f3aead866b4d81acb Mon Sep 17 00:00:00 2001 From: yanniboi Date: Thu, 22 Mar 2018 14:01:51 +0000 Subject: [PATCH 19/21] By yanniboi: GDPR tasks module requires GDPR dump module for sanitation. --- modules/gdpr_tasks/gdpr_tasks.info.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/gdpr_tasks/gdpr_tasks.info.yml b/modules/gdpr_tasks/gdpr_tasks.info.yml index 68919c1..bc48549 100644 --- a/modules/gdpr_tasks/gdpr_tasks.info.yml +++ b/modules/gdpr_tasks/gdpr_tasks.info.yml @@ -6,3 +6,4 @@ package: General Data Protection Regulation dependencies: - gdpr:gdpr + - gdpr:dump From 53d01576f94813924868b81e1e3f2f3ecedda6ca Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Wed, 21 Mar 2018 15:46:55 +0000 Subject: [PATCH 20/21] Begin adding consent log --- gdpr.info.yml | 1 + ...sage.consent_agreement_accepted.agreed.yml | 23 + ...e.consent_agreement_accepted.agreement.yml | 25 + ...reement_accepted.agreement_revision_id.yml | 18 + ...ssage.consent_agreement_accepted.notes.yml | 19 + ...essage.consent_agreement_accepted.user.yml | 21 + ...nsent_agreement_accepted.user_accepted.yml | 28 ++ .../install/field.storage.message.agreed.yml | 18 + .../field.storage.message.agreement.yml | 20 + ....storage.message.agreement_revision_id.yml | 18 + .../install/field.storage.message.notes.yml | 19 + .../install/field.storage.message.user.yml | 20 + .../field.storage.message.user_accepted.yml | 20 + ...ge.template.consent_agreement_accepted.yml | 17 + .../install/views.view.gdpr_log_messages.yml | 463 ++++++++++++++++++ modules/gdpr_consent/gdpr_consent.info.yml | 2 + .../Controller/ConsentAgreementController.php | 8 +- .../Plugin/Block/GdprMyAgreementsBlock.php | 29 ++ .../Deriver/GdprMyAgreementsDeriver.php | 27 + .../Field/FieldType/UserConsentItem.php | 38 +- 20 files changed, 823 insertions(+), 11 deletions(-) create mode 100644 modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreed.yml create mode 100644 modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement.yml create mode 100644 modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement_revision_id.yml create mode 100644 modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.notes.yml create mode 100644 modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.user.yml create mode 100644 modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.user_accepted.yml create mode 100644 modules/gdpr_consent/config/install/field.storage.message.agreed.yml create mode 100644 modules/gdpr_consent/config/install/field.storage.message.agreement.yml create mode 100644 modules/gdpr_consent/config/install/field.storage.message.agreement_revision_id.yml create mode 100644 modules/gdpr_consent/config/install/field.storage.message.notes.yml create mode 100644 modules/gdpr_consent/config/install/field.storage.message.user.yml create mode 100644 modules/gdpr_consent/config/install/field.storage.message.user_accepted.yml create mode 100644 modules/gdpr_consent/config/install/message.template.consent_agreement_accepted.yml create mode 100644 modules/gdpr_consent/config/install/views.view.gdpr_log_messages.yml create mode 100644 modules/gdpr_consent/src/Plugin/Block/GdprMyAgreementsBlock.php create mode 100644 modules/gdpr_consent/src/Plugin/Deriver/GdprMyAgreementsDeriver.php diff --git a/gdpr.info.yml b/gdpr.info.yml index ca13e3c..d0f4226 100644 --- a/gdpr.info.yml +++ b/gdpr.info.yml @@ -7,3 +7,4 @@ package: General Data Protection Regulation dependencies: - drupal:checklistapi + - drupal:message \ No newline at end of file diff --git a/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreed.yml b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreed.yml new file mode 100644 index 0000000..09ba1f7 --- /dev/null +++ b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreed.yml @@ -0,0 +1,23 @@ +uuid: 3b601cf3-2f83-4b18-a271-3025a16db2ad +langcode: en +status: true +dependencies: + config: + - field.storage.message.agreed + - message.template.consent_agreement_accepted +id: message.consent_agreement_accepted.agreed +field_name: agreed +entity_type: message +bundle: consent_agreement_accepted +label: Agreed +description: '' +required: false +translatable: false +default_value: + - + value: 0 +default_value_callback: '' +settings: + on_label: 'Yes' + off_label: 'No' +field_type: boolean diff --git a/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement.yml b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement.yml new file mode 100644 index 0000000..80b62b0 --- /dev/null +++ b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement.yml @@ -0,0 +1,25 @@ +uuid: 7c7f0457-3470-4c14-925a-0b71506ec410 +langcode: en +status: true +dependencies: + config: + - field.storage.message.agreement + - message.template.consent_agreement_accepted +id: message.consent_agreement_accepted.agreement +field_name: agreement +entity_type: message +bundle: consent_agreement_accepted +label: Agreement +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + handler: 'default:gdpr_consent_agreement' + handler_settings: + target_bundles: null + sort: + field: _none + auto_create: false +field_type: entity_reference diff --git a/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement_revision_id.yml b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement_revision_id.yml new file mode 100644 index 0000000..5e462ff --- /dev/null +++ b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement_revision_id.yml @@ -0,0 +1,18 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.message.agreement_revision_id + - message.template.consent_agreement_accepted +id: message.consent_agreement_accepted.agreement_revision_id +field_name: agreement_revision_id +entity_type: message +bundle: consent_agreement_accepted +label: Agreement Revision ID +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: { } +field_type: integer diff --git a/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.notes.yml b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.notes.yml new file mode 100644 index 0000000..6f2cdd6 --- /dev/null +++ b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.notes.yml @@ -0,0 +1,19 @@ +uuid: a6562ec7-6913-4944-9fec-ab9ef0ab2bb1 +langcode: en +status: true +dependencies: + config: + - field.storage.message.notes + - message.template.consent_agreement_accepted +id: message.consent_agreement_accepted.notes +field_name: notes +entity_type: message +bundle: consent_agreement_accepted +label: Notes +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: { } +field_type: string_long diff --git a/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.user.yml b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.user.yml new file mode 100644 index 0000000..7a8c943 --- /dev/null +++ b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.user.yml @@ -0,0 +1,21 @@ +uuid: 2a6fc21e-93cb-4db8-96a0-7d1f77efc3d1 +langcode: en +status: true +dependencies: + config: + - field.storage.message.user + - message.template.consent_agreement_accepted +id: message.consent_agreement_accepted.user +field_name: user +entity_type: message +bundle: consent_agreement_accepted +label: User +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + handler: 'default:user' + handler_settings: { } +field_type: entity_reference diff --git a/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.user_accepted.yml b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.user_accepted.yml new file mode 100644 index 0000000..4e9c954 --- /dev/null +++ b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.user_accepted.yml @@ -0,0 +1,28 @@ +uuid: 9168354c-5505-495d-bfce-382aa4f12be7 +langcode: en +status: true +dependencies: + config: + - field.storage.message.user_accepted + - message.template.consent_agreement_accepted +id: message.consent_agreement_accepted.user_accepted +field_name: user_accepted +entity_type: message +bundle: consent_agreement_accepted +label: 'User Accepted' +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + handler: 'default:user' + handler_settings: + include_anonymous: true + filter: + type: _none + target_bundles: null + sort: + field: _none + auto_create: false +field_type: entity_reference diff --git a/modules/gdpr_consent/config/install/field.storage.message.agreed.yml b/modules/gdpr_consent/config/install/field.storage.message.agreed.yml new file mode 100644 index 0000000..16e564c --- /dev/null +++ b/modules/gdpr_consent/config/install/field.storage.message.agreed.yml @@ -0,0 +1,18 @@ +langcode: en +status: true +dependencies: + module: + - message +id: message.agreed +field_name: agreed +entity_type: message +type: boolean +settings: + case_sensitive: false +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/modules/gdpr_consent/config/install/field.storage.message.agreement.yml b/modules/gdpr_consent/config/install/field.storage.message.agreement.yml new file mode 100644 index 0000000..7a70390 --- /dev/null +++ b/modules/gdpr_consent/config/install/field.storage.message.agreement.yml @@ -0,0 +1,20 @@ +uuid: 97862999-d01d-4842-a9bc-405696ac3620 +langcode: en +status: true +dependencies: + module: + - gdpr_consent + - message +id: message.agreement +field_name: agreement +entity_type: message +type: entity_reference +settings: + target_type: gdpr_consent_agreement +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/modules/gdpr_consent/config/install/field.storage.message.agreement_revision_id.yml b/modules/gdpr_consent/config/install/field.storage.message.agreement_revision_id.yml new file mode 100644 index 0000000..d292d38 --- /dev/null +++ b/modules/gdpr_consent/config/install/field.storage.message.agreement_revision_id.yml @@ -0,0 +1,18 @@ +langcode: en +status: true +dependencies: + module: + - message +id: message.agreement_revision_id +field_name: agreement_revision_id +entity_type: message +type: integer +settings: + case_sensitive: false +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/modules/gdpr_consent/config/install/field.storage.message.notes.yml b/modules/gdpr_consent/config/install/field.storage.message.notes.yml new file mode 100644 index 0000000..819abaf --- /dev/null +++ b/modules/gdpr_consent/config/install/field.storage.message.notes.yml @@ -0,0 +1,19 @@ +uuid: 24feee44-56c7-4e92-b4d2-fe51c5bf710d +langcode: en +status: true +dependencies: + module: + - message +id: message.notes +field_name: notes +entity_type: message +type: string_long +settings: + case_sensitive: false +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/modules/gdpr_consent/config/install/field.storage.message.user.yml b/modules/gdpr_consent/config/install/field.storage.message.user.yml new file mode 100644 index 0000000..0a8d3e8 --- /dev/null +++ b/modules/gdpr_consent/config/install/field.storage.message.user.yml @@ -0,0 +1,20 @@ +uuid: 4bcd4001-6d9c-4928-845a-ec518697c55b +langcode: en +status: true +dependencies: + module: + - message + - user +id: message.user +field_name: user +entity_type: message +type: entity_reference +settings: + target_type: user +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/modules/gdpr_consent/config/install/field.storage.message.user_accepted.yml b/modules/gdpr_consent/config/install/field.storage.message.user_accepted.yml new file mode 100644 index 0000000..339ef60 --- /dev/null +++ b/modules/gdpr_consent/config/install/field.storage.message.user_accepted.yml @@ -0,0 +1,20 @@ +uuid: b457e6e8-cf95-493e-b25a-8248bc60449f +langcode: en +status: true +dependencies: + module: + - message + - user +id: message.user_accepted +field_name: user_accepted +entity_type: message +type: entity_reference +settings: + target_type: user +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/modules/gdpr_consent/config/install/message.template.consent_agreement_accepted.yml b/modules/gdpr_consent/config/install/message.template.consent_agreement_accepted.yml new file mode 100644 index 0000000..6b02cae --- /dev/null +++ b/modules/gdpr_consent/config/install/message.template.consent_agreement_accepted.yml @@ -0,0 +1,17 @@ +uuid: a020749c-7309-4278-9fbf-4d1edfdafd51 +langcode: en +status: true +dependencies: { } +template: consent_agreement_accepted +label: 'GDPR Consent Agreement' +description: '' +text: + - + value: "

Agreement: [message:agreement:entity:title] 
\r\nAgreed: [message:agreed]
\r\nNotes: [message:notes]

\r\n" + format: basic_html +settings: + 'token options': + clear: false + 'token replace': true + purge_override: true + purge_methods: { } diff --git a/modules/gdpr_consent/config/install/views.view.gdpr_log_messages.yml b/modules/gdpr_consent/config/install/views.view.gdpr_log_messages.yml new file mode 100644 index 0000000..5b0cd40 --- /dev/null +++ b/modules/gdpr_consent/config/install/views.view.gdpr_log_messages.yml @@ -0,0 +1,463 @@ +uuid: 52f8494f-9b35-4894-8947-920798e20bde +langcode: en +status: true +dependencies: + config: + - message.template.consent_agreement_accepted + module: + - message + - user +_core: + default_config_hash: 2Up5NUvwF4aBVoO6eFTPeBXtnpUw_yiexv9I5u6ZD-g +id: gdpr_log_messages +label: 'GDPR Log Messages' +module: views +description: '' +tag: '' +base_table: message_field_data +base_field: mid +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: perm + options: + perm: 'grant gdpr own consent' + cache: + type: none + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: full + options: + items_per_page: 25 + offset: 0 + id: 0 + total_pages: null + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + tags: + previous: '‹ previous' + next: 'next ›' + first: '« first' + last: 'last »' + quantity: 9 + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + override: true + sticky: false + caption: '' + summary: '' + description: '' + columns: + message_bulk_form_1: message_bulk_form_1 + mid: mid + template: template + get_text: get_text + uid: uid + created: created + info: + message_bulk_form_1: + align: '' + separator: '' + empty_column: false + responsive: '' + mid: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: '' + template: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-medium + get_text: + sortable: false + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: '' + uid: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-medium + created: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: priority-low + default: mid + empty_table: false + row: + type: fields + fields: + get_text: + id: get_text + table: message + field: get_text + relationship: none + group_type: group + admin_label: '' + label: 'Message text' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 500 + word_boundary: true + ellipsis: false + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: true + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + entity_type: message + plugin_id: get_text + created: + id: created + table: message_field_data + field: created + relationship: none + group_type: group + admin_label: '' + label: Date + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: timestamp + settings: + date_format: medium + custom_date_format: '' + timezone: '' + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: 0 + date_format: fallback + custom_date_format: '' + timezone: '' + entity_type: message + entity_field: created + plugin_id: date + uid: + id: uid + table: message_field_data + field: uid + relationship: none + group_type: group + admin_label: '' + label: 'Created by' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: target_id + type: entity_reference_label + settings: + link: true + group_column: target_id + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: message + entity_field: uid + plugin_id: field + filters: + template: + id: template + table: message_field_data + field: template + relationship: none + group_type: group + admin_label: '' + operator: in + value: + consent_agreement_accepted: consent_agreement_accepted + group: 1 + exposed: false + expose: + operator_id: template_op + label: Template + description: null + use_operator: false + operator: template_op + identifier: template + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + entity_type: message + entity_field: template + plugin_id: bundle + sorts: + created: + id: created + table: message_field_data + field: created + relationship: none + group_type: group + admin_label: '' + order: DESC + exposed: false + expose: + label: '' + granularity: second + entity_type: message + entity_field: created + plugin_id: date + title: Message + header: { } + footer: { } + empty: + area: + id: area + table: views + field: area + relationship: none + group_type: group + admin_label: '' + empty: true + tokenize: false + content: + value: 'No messages created yet' + format: basic_html + plugin_id: text + relationships: { } + arguments: + user_target_id: + id: user_target_id + table: message__user + field: user_target_id + relationship: none + group_type: group + admin_label: '' + default_action: default + exception: + value: all + title_enable: false + title: All + title_enable: false + title: '' + default_argument_type: user + default_argument_options: + user: false + default_argument_skip_url: false + summary_options: + base_path: '' + count: true + items_per_page: 25 + override: false + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: false + validate: + type: none + fail: 'not found' + validate_options: { } + break_phrase: false + not: false + plugin_id: numeric + display_extenders: { } + filter_groups: + operator: AND + groups: { } + show_admin_links: true + cache_metadata: + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + cacheable: false + max-age: -1 + tags: { } + block_1: + display_plugin: block + id: block_1 + display_title: Block + position: 2 + display_options: + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } diff --git a/modules/gdpr_consent/gdpr_consent.info.yml b/modules/gdpr_consent/gdpr_consent.info.yml index 1094730..31d7b39 100644 --- a/modules/gdpr_consent/gdpr_consent.info.yml +++ b/modules/gdpr_consent/gdpr_consent.info.yml @@ -6,3 +6,5 @@ package: General Data Protection Regulation dependencies: - gdpr:gdpr + - token + - message \ No newline at end of file diff --git a/modules/gdpr_consent/src/Controller/ConsentAgreementController.php b/modules/gdpr_consent/src/Controller/ConsentAgreementController.php index a948b1f..a66a82b 100644 --- a/modules/gdpr_consent/src/Controller/ConsentAgreementController.php +++ b/modules/gdpr_consent/src/Controller/ConsentAgreementController.php @@ -237,19 +237,13 @@ function myAgreements($user) { ], ]; - $row[] = [ - 'date' => [ - '#markup' => 'Ops' - ] - ]; - $rows[] = $row; } } } - $header = ['Agreement', 'Date Agreed', 'Operations']; + $header = ['Agreement', 'Date Agreed']; $build = [ '#title' => 'Consent Agreements', diff --git a/modules/gdpr_consent/src/Plugin/Block/GdprMyAgreementsBlock.php b/modules/gdpr_consent/src/Plugin/Block/GdprMyAgreementsBlock.php new file mode 100644 index 0000000..13c7283 --- /dev/null +++ b/modules/gdpr_consent/src/Plugin/Block/GdprMyAgreementsBlock.php @@ -0,0 +1,29 @@ +getContextValue('user'); + // Just delegate to the controller to do the work. + $ctrl = new ConsentAgreementController(\Drupal::getContainer()->get('entity_field.manager')); + return $ctrl->myAgreements($user->id()); + } +} \ No newline at end of file diff --git a/modules/gdpr_consent/src/Plugin/Deriver/GdprMyAgreementsDeriver.php b/modules/gdpr_consent/src/Plugin/Deriver/GdprMyAgreementsDeriver.php new file mode 100644 index 0000000..9f9971e --- /dev/null +++ b/modules/gdpr_consent/src/Plugin/Deriver/GdprMyAgreementsDeriver.php @@ -0,0 +1,27 @@ +derivatives['contacts_dashboard'] = $base_plugin_definition; + $this->derivatives['contacts_dashboard']['admin_label'] = 'GDPR Agreements Accepted'; + $this->derivatives['contacts_dashboard']['context'] = [ + 'user' => new ContextDefinition('entity:user', 'User', FALSE), + ]; + + return $this->derivatives; + } + +} diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php b/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php index d425a56..7985237 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php @@ -8,6 +8,7 @@ use Drupal\Core\TypedData\DataDefinition; use Drupal\Core\TypedData\DataReferenceTargetDefinition; use Drupal\gdpr_consent\Entity\ConsentAgreement; +use Drupal\message\Entity\Message; /** * Plugin implementation of the 'gdpr_user_consent' field type. @@ -15,10 +16,9 @@ * @FieldType( * id = "gdpr_user_consent", * label = @Translation("GDPR Consent"), - * description = @Translation("Stores user consent for a particular agreement"), - * category = @Translation("GDPR"), - * default_widget ="gdpr_consent_widget", - * default_formatter = "gdpr_consent_formatter" + * description = @Translation("Stores user consent for a particular + * agreement"), category = @Translation("GDPR"), default_widget + * ="gdpr_consent_widget", default_formatter = "gdpr_consent_formatter" * ) */ class UserConsentItem extends FieldItemBase { @@ -77,6 +77,36 @@ public function preSave() { } } + /** + * @inheritDoc + */ + public function postSave($update) { + $should_log = FALSE; + + if (!$update) { + // Always log on a create. + $should_log = TRUE; + } + else { + $field_name = $this->getFieldDefinition()->getName(); + $original_value = $this->getEntity()->original->{$field_name}->agreed; + if ($original_value != $this->agreed) { + $should_log = TRUE; + } + } + + if ($should_log) { + $msg = Message::create(['template' => 'consent_agreement_accepted']); + $msg->set('user', $this->user_id); + $msg->set('user_accepted', $this->user_id_accepted); + $msg->set('agreement', $this->target_id); + $msg->set('notes', $this->notes); + $msg->set('agreement_revision_id', $this->target_revision_id); + $msg->set('agreed', $this->agreed); + $msg->save(); + } + } + /** * {@inheritdoc} */ From a9acb085efc5a129a654c61feca80b25370516c6 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Mon, 26 Mar 2018 11:53:53 +0100 Subject: [PATCH 21/21] Switch to using a revisioned entity reference for the log --- composer.json | 3 ++- ...ge.consent_agreement_accepted.agreement.yml | 6 ++++-- ...greement_accepted.agreement_revision_id.yml | 18 ------------------ .../field.storage.message.agreement.yml | 7 ++++--- ...d.storage.message.agreement_revision_id.yml | 18 ------------------ ...age.template.consent_agreement_accepted.yml | 4 ++-- modules/gdpr_consent/gdpr_consent.info.yml | 3 ++- .../src/Entity/ConsentAgreementListBuilder.php | 14 -------------- .../Plugin/Field/FieldType/UserConsentItem.php | 3 +-- 9 files changed, 15 insertions(+), 61 deletions(-) delete mode 100644 modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement_revision_id.yml delete mode 100644 modules/gdpr_consent/config/install/field.storage.message.agreement_revision_id.yml diff --git a/composer.json b/composer.json index 795f046..ace483f 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ } ], "require": { - "drupal/checklistapi": "^1.0" + "drupal/checklistapi": "^1.0", + "drupal/entity_reference_revisions": "^1.4" } } diff --git a/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement.yml b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement.yml index 80b62b0..88629b5 100644 --- a/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement.yml +++ b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement.yml @@ -1,10 +1,12 @@ -uuid: 7c7f0457-3470-4c14-925a-0b71506ec410 +uuid: 000c8918-277d-4444-8dac-922d86366238 langcode: en status: true dependencies: config: - field.storage.message.agreement - message.template.consent_agreement_accepted + module: + - entity_reference_revisions id: message.consent_agreement_accepted.agreement field_name: agreement entity_type: message @@ -22,4 +24,4 @@ settings: sort: field: _none auto_create: false -field_type: entity_reference +field_type: entity_reference_revisions diff --git a/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement_revision_id.yml b/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement_revision_id.yml deleted file mode 100644 index 5e462ff..0000000 --- a/modules/gdpr_consent/config/install/field.field.message.consent_agreement_accepted.agreement_revision_id.yml +++ /dev/null @@ -1,18 +0,0 @@ -langcode: en -status: true -dependencies: - config: - - field.storage.message.agreement_revision_id - - message.template.consent_agreement_accepted -id: message.consent_agreement_accepted.agreement_revision_id -field_name: agreement_revision_id -entity_type: message -bundle: consent_agreement_accepted -label: Agreement Revision ID -description: '' -required: false -translatable: false -default_value: { } -default_value_callback: '' -settings: { } -field_type: integer diff --git a/modules/gdpr_consent/config/install/field.storage.message.agreement.yml b/modules/gdpr_consent/config/install/field.storage.message.agreement.yml index 7a70390..6f197fa 100644 --- a/modules/gdpr_consent/config/install/field.storage.message.agreement.yml +++ b/modules/gdpr_consent/config/install/field.storage.message.agreement.yml @@ -1,17 +1,18 @@ -uuid: 97862999-d01d-4842-a9bc-405696ac3620 +uuid: 83ca8ca3-88c0-4eb4-bdbf-cadb24186abf langcode: en status: true dependencies: module: + - entity_reference_revisions - gdpr_consent - message id: message.agreement field_name: agreement entity_type: message -type: entity_reference +type: entity_reference_revisions settings: target_type: gdpr_consent_agreement -module: core +module: entity_reference_revisions locked: false cardinality: 1 translatable: true diff --git a/modules/gdpr_consent/config/install/field.storage.message.agreement_revision_id.yml b/modules/gdpr_consent/config/install/field.storage.message.agreement_revision_id.yml deleted file mode 100644 index d292d38..0000000 --- a/modules/gdpr_consent/config/install/field.storage.message.agreement_revision_id.yml +++ /dev/null @@ -1,18 +0,0 @@ -langcode: en -status: true -dependencies: - module: - - message -id: message.agreement_revision_id -field_name: agreement_revision_id -entity_type: message -type: integer -settings: - case_sensitive: false -module: core -locked: false -cardinality: 1 -translatable: true -indexes: { } -persist_with_no_fields: false -custom_storage: false diff --git a/modules/gdpr_consent/config/install/message.template.consent_agreement_accepted.yml b/modules/gdpr_consent/config/install/message.template.consent_agreement_accepted.yml index 6b02cae..e466bf4 100644 --- a/modules/gdpr_consent/config/install/message.template.consent_agreement_accepted.yml +++ b/modules/gdpr_consent/config/install/message.template.consent_agreement_accepted.yml @@ -7,8 +7,8 @@ label: 'GDPR Consent Agreement' description: '' text: - - value: "

Agreement: [message:agreement:entity:title] 
\r\nAgreed: [message:agreed]
\r\nNotes: [message:notes]

\r\n" - format: basic_html + value: "

Agreement: [message:agreement:entity:title]
\r\nAgreed: [message:agreed]
\r\nNotes: [message:notes]

\r\n" + format: full_html settings: 'token options': clear: false diff --git a/modules/gdpr_consent/gdpr_consent.info.yml b/modules/gdpr_consent/gdpr_consent.info.yml index 31d7b39..0c26ccc 100644 --- a/modules/gdpr_consent/gdpr_consent.info.yml +++ b/modules/gdpr_consent/gdpr_consent.info.yml @@ -7,4 +7,5 @@ package: General Data Protection Regulation dependencies: - gdpr:gdpr - token - - message \ No newline at end of file + - message + - entity_reference_revisions \ No newline at end of file diff --git a/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php b/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php index 7a336d5..b7dd8a5 100644 --- a/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php +++ b/modules/gdpr_consent/src/Entity/ConsentAgreementListBuilder.php @@ -34,18 +34,4 @@ public function buildRow(EntityInterface $entity) { return $row + parent::buildRow($entity); } - /** - * {@inheritdoc} - */ - protected function getDefaultOperations(EntityInterface $entity) { - $operations = [ - $operations['view'] = [ - 'title' => $this->t('View'), - 'weight' => 0, - 'url' => $this->ensureDestination($entity->toUrl('canonical')), - ], - ]; - return $operations + parent::getDefaultOperations($entity); - } - } diff --git a/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php b/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php index 7985237..ff730c0 100644 --- a/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php +++ b/modules/gdpr_consent/src/Plugin/Field/FieldType/UserConsentItem.php @@ -99,9 +99,8 @@ public function postSave($update) { $msg = Message::create(['template' => 'consent_agreement_accepted']); $msg->set('user', $this->user_id); $msg->set('user_accepted', $this->user_id_accepted); - $msg->set('agreement', $this->target_id); + $msg->set('agreement', ['target_id' => $this->target_id, 'target_revision_id' => $this->target_revision_id]); $msg->set('notes', $this->notes); - $msg->set('agreement_revision_id', $this->target_revision_id); $msg->set('agreed', $this->agreed); $msg->save(); }