From a62a644ea444cbbd6a0a1999a328a4fd9c9dc22e Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Tue, 10 Apr 2018 16:21:23 +0200 Subject: [PATCH 01/46] Initial work towards a gdpr_consent submodule --- gdpr_consent/gdpr_consent.info | 5 +++ gdpr_consent/gdpr_consent.module | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 gdpr_consent/gdpr_consent.info create mode 100644 gdpr_consent/gdpr_consent.module diff --git a/gdpr_consent/gdpr_consent.info b/gdpr_consent/gdpr_consent.info new file mode 100644 index 0000000..6f6edcb --- /dev/null +++ b/gdpr_consent/gdpr_consent.info @@ -0,0 +1,5 @@ +name = General Data Protection Regulation (GDPR) - Consent Tracking +description = Allow tracking of GDPR Consent +core = 7.x +dependencies[] = entity +dependencies[] = gdpr diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module new file mode 100644 index 0000000..9c9dfd6 --- /dev/null +++ b/gdpr_consent/gdpr_consent.module @@ -0,0 +1,52 @@ + 'Agreements', + 'description' => 'List Agreement Entities', + 'access callback' => TRUE, + 'page callback' => 'gdpr_consent_collected_agreements', + 'menu_name' => 'navigation', + 'file' => 'includes/gdpr_consent.agreements.inc', + ); + + return $items; +} + + +/** + * Implements hook_entity_info(). + */ +function gdpr_consent_entity_info() { + $info = array(); + $info['gdpr_consent_agreement'] = array( + 'label' => t('GDPR Consent Agreement'), + // Table for storing entity data, defined in hook_schema(). + 'base table' => 'gdpr_consent_agreement', + 'entity keys' => array( + 'id' => 'id', + ), + ); + + return $info; +} From 8c45d6cd702b572478dadedfab5f263fa3fd0112 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Tue, 10 Apr 2018 23:46:46 +0200 Subject: [PATCH 02/46] Add hook_schema with base table for consent entity --- gdpr_consent/gdpr_consent.install | 62 +++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 gdpr_consent/gdpr_consent.install diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install new file mode 100644 index 0000000..968c427 --- /dev/null +++ b/gdpr_consent/gdpr_consent.install @@ -0,0 +1,62 @@ + 'Base table for GDPR Consent Agreement entity.', + 'fields' => array( + 'id' => array( + 'description' => 'Primary key of the GDPR Consent Agreement entity.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'title' => array( + 'description' => 'Title of the consent agreement.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'description' => array( + 'description' => 'A description of the consent agreement.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'created' => array( + 'description' => 'The Unix timestamp of the entity creation time.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => "The {users}.uid of the associated user.", + ), + 'status' => array( + 'description' => 'Entity status.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + ), + 'foreign keys' => array( + 'uid' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid') + ), + ), + 'primary key' => array('id'), + ); + + return $schema; +} From 99c18dca70c91939d92bf2e53fc90235138653f8 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Tue, 10 Apr 2018 23:47:15 +0200 Subject: [PATCH 03/46] Extend hook_entity_info --- gdpr_consent/gdpr_consent.module | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 9c9dfd6..aa5480b 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -41,11 +41,18 @@ function gdpr_consent_entity_info() { $info = array(); $info['gdpr_consent_agreement'] = array( 'label' => t('GDPR Consent Agreement'), - // Table for storing entity data, defined in hook_schema(). 'base table' => 'gdpr_consent_agreement', + 'entity class' => 'Entity', + 'controller class' => 'ConsentAgreementController', + 'fieldable' => TRUE, 'entity keys' => array( 'id' => 'id', + 'label' => 'title', ), + // Use the default label() and uri() functions + 'label callback' => 'entity_class_label', + 'uri callback' => 'entity_class_uri', + 'module' => 'gdpr_consent', ); return $info; From 87d6c11a633b60a5fc8d5c542717537314659063 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Wed, 11 Apr 2018 15:04:36 +0200 Subject: [PATCH 04/46] Add metadata for entity API via hook_entity_property_info() --- gdpr_consent/gdpr_consent.install | 6 ++++ gdpr_consent/gdpr_consent.module | 59 +++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 968c427..a3fc8a4 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -34,6 +34,12 @@ function gdpr_consent_schema() { 'not null' => TRUE, 'default' => 0, ), + 'changed' => array( + 'description' => 'The Unix timestamp the entity was last edited.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), 'uid' => array( 'type' => 'int', 'unsigned' => TRUE, diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index aa5480b..eefcfe9 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -57,3 +57,62 @@ function gdpr_consent_entity_info() { return $info; } + +/** + * Implements hook_entity_property_info(). + */ +function gdpr_consent_entity_property_info() { + $info = array(); + $properties = &$info['gdpr_consent_agreement']['properties']; + + $properties['id'] = array( + 'label' => t('GDPR Consent Agreement ID'), + 'description' => t('The uniquie ID of the consent agreement entity.'), + 'type' => 'integer', + 'schema field' => 'id', + ); + + $properties['title'] = array( + 'label' => t('Title'), + 'description' => t('Title of the agreement'), + 'type' => 'text', + 'schema field' => 'title', + ); + + $properties['description'] = array( + 'label' => t('Description'), + 'description' => t('Text displayed to the user on the form'), + 'type' => 'text', + 'schema field' => 'description', + ); + + $properties['created'] = array( + 'label' => t('Created date'), + 'description' => t('Date the consent agreement was created'), + 'type' => 'date', + 'schema field' => 'created', + ); + + $properties['changed'] = array( + 'label' => t('Updated date'), + 'description' => t('Date the consent agreement was last edited'), + 'type' => 'date', + 'schema field' => 'changed', + ); + + $properties['uid'] = array( + 'label' => t('Authored by'), + 'description' => t('The user ID of author of the Consent Agreement entity.'), + 'type' => 'user', + 'schema field' => 'uid', + ); + + $properties['status'] = array( + 'label' => t('Publishing status'), + 'description' => t('A boolean indicating whether the Consent Agreement is published.'), + 'type' => 'boolean', + 'schema field' => 'status', + ); + + return $info; +} From 18970599623e3c277d2b94b67f06a74ccd96aefa Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Fri, 13 Apr 2018 00:46:30 +0200 Subject: [PATCH 05/46] Define admin ui and form definition --- gdpr_consent/gdpr_consent.module | 15 ++++++- gdpr_consent/includes/gdpr_consent.admin.inc | 43 +++++++++++++++++++ .../includes/gdpr_consent.agreements.inc | 0 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 gdpr_consent/includes/gdpr_consent.admin.inc create mode 100644 gdpr_consent/includes/gdpr_consent.agreements.inc diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index eefcfe9..7325c04 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -43,7 +43,8 @@ function gdpr_consent_entity_info() { 'label' => t('GDPR Consent Agreement'), 'base table' => 'gdpr_consent_agreement', 'entity class' => 'Entity', - 'controller class' => 'ConsentAgreementController', + 'controller class' => 'EntityAPIController', + //'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, 'entity keys' => array( 'id' => 'id', @@ -52,6 +53,14 @@ function gdpr_consent_entity_info() { // Use the default label() and uri() functions 'label callback' => 'entity_class_label', 'uri callback' => 'entity_class_uri', + // Access callback to determine permisisons. + 'access callback' => 'gdpr_consent_access_callback', + 'admin ui' => array( + 'path' => 'admin/consent', + //'controller class' => 'ConesentAgreementEntityUIController', + 'menu wildcard' => '%entity_object', + 'file' => 'includes/gdpr_consent.admin.inc', + ), 'module' => 'gdpr_consent', ); @@ -116,3 +125,7 @@ function gdpr_consent_entity_property_info() { return $info; } + +function gdpr_consent_access_callback($op, $entity = NULL, $account = NULL) { + return TRUE; +} diff --git a/gdpr_consent/includes/gdpr_consent.admin.inc b/gdpr_consent/includes/gdpr_consent.admin.inc new file mode 100644 index 0000000..a7f1a4b --- /dev/null +++ b/gdpr_consent/includes/gdpr_consent.admin.inc @@ -0,0 +1,43 @@ + t('Title'), + '#type' => 'textfield', + '#default_value' => isset($entity->title) ? $entity->title : '', + '#description' => t('Agreement title.'), + '#required' => TRUE, + '#weight' => -50, + ); + $form['description'] = array( + '#title' => t('Description'), + '#type' => 'textfield', + '#default_value' => isset($entity->description) ? $entity->description : '', + '#description' => t('Agreement description.'), + ); + + field_attach_form('gdpr_consent_agreement', $entity, $form, $form_state); + + $form['actions'] = array( + '#type' => 'actions', + 'submit' => array( + '#type' => 'submit', + '#value' => isset($entity->id) ? t('Update consent agreement') : t('Save consent agreement'), + ), + ); + + return $form; +} + +/** + * Submit handler for consent agreement entity form. + */ +function gdpr_consent_agreement_form_submit($form, &$form_state) { + $entity = entity_ui_form_submit_build_entity($form, $form_state); + $entity->save(); + drupal_set_message(t('@title agreement has been saved.', array('@title' => $entity->title))); + $form_state['redirect'] = 'admin/consent'; +} diff --git a/gdpr_consent/includes/gdpr_consent.agreements.inc b/gdpr_consent/includes/gdpr_consent.agreements.inc new file mode 100644 index 0000000..e69de29 From 601080c5a016433e79af0dc81536e1c104bb4695 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Sat, 14 Apr 2018 00:26:39 +0200 Subject: [PATCH 06/46] Make the gdpr_consent_agreement entity fieldable --- gdpr_consent/gdpr_consent.module | 12 +++++++++- .../includes/gdpr_consent.agreements.inc | 23 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 7325c04..5c83dfb 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -21,11 +21,12 @@ function gdpr_consent_help($path, $arg) { function gdpr_consent_menu() { $items = array(); - $items['user/%user/gdpr/collected_data/agreements'] = array( + $items['user/%user/collected_data/agreements'] = array( 'title' => 'Agreements', 'description' => 'List Agreement Entities', 'access callback' => TRUE, 'page callback' => 'gdpr_consent_collected_agreements', + 'page arguments' => array(1), 'menu_name' => 'navigation', 'file' => 'includes/gdpr_consent.agreements.inc', ); @@ -46,6 +47,15 @@ function gdpr_consent_entity_info() { 'controller class' => 'EntityAPIController', //'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, + 'bundles' => array( + 'gdpr_consent_agreement' => array( + 'label' => t('GDPR Consent Agreement'), + 'admin' => array( + 'path' => 'admin/consent', + 'access arguments' => array('administer site configuration'), + ), + ), + ), 'entity keys' => array( 'id' => 'id', 'label' => 'title', diff --git a/gdpr_consent/includes/gdpr_consent.agreements.inc b/gdpr_consent/includes/gdpr_consent.agreements.inc index e69de29..c712fb8 100644 --- a/gdpr_consent/includes/gdpr_consent.agreements.inc +++ b/gdpr_consent/includes/gdpr_consent.agreements.inc @@ -0,0 +1,23 @@ +entityCondition('entity_type', 'gdpr_consent_agreement'); + //->propertyCondition('uid', $account->uid); + + $result = $query->execute(); + + if (isset($result['gdpr_consent_agreement'])) { + $consent_ids = array_keys($result['gdpr_consent_agreement']); + $consent_items = entity_load('gdpr_consent_agreement', $consent_ids); + + } + + kpr($consent_items); + + return $account->name; +} From 835c4739cc7740407b3160295013250ce23a87c5 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Sat, 14 Apr 2018 00:56:35 +0200 Subject: [PATCH 07/46] Make the gdpr_consent_agreement entity revisionable --- gdpr_consent/gdpr_consent.install | 40 +++++++++++++++++++++++++------ gdpr_consent/gdpr_consent.module | 28 ++++++++++++++++++---- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index a3fc8a4..3e6a3d8 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -14,6 +14,13 @@ function gdpr_consent_schema() { 'unsigned' => TRUE, 'not null' => TRUE, ), + 'revision_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => 'The ID of consent agreement\'s default revision.', + ), 'title' => array( 'description' => 'Title of the consent agreement.', 'type' => 'varchar', @@ -21,13 +28,6 @@ function gdpr_consent_schema() { 'not null' => TRUE, 'default' => '', ), - 'description' => array( - 'description' => 'A description of the consent agreement.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), 'created' => array( 'description' => 'The Unix timestamp of the entity creation time.', 'type' => 'int', @@ -64,5 +64,31 @@ function gdpr_consent_schema() { 'primary key' => array('id'), ); + $schema['gdpr_consent_agreement_revision'] = array( + 'description' => 'GDPR Consent Agreement entity revisions.', + 'fields' => array( + 'id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => 'The ID of the attached entity.', + ), + 'revision_id' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Primary Key: Unique revision ID.', + ), + 'description' => array( + 'description' => 'A description of the consent agreement.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + ), + 'primary key' => array('revision_id'), + ); + return $schema; } diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 5c83dfb..3680997 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -43,9 +43,9 @@ function gdpr_consent_entity_info() { $info['gdpr_consent_agreement'] = array( 'label' => t('GDPR Consent Agreement'), 'base table' => 'gdpr_consent_agreement', + 'revision table' => 'gdpr_consent_agreement_revision', 'entity class' => 'Entity', - 'controller class' => 'EntityAPIController', - //'controller class' => 'ConsentAgreementController', + 'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, 'bundles' => array( 'gdpr_consent_agreement' => array( @@ -59,15 +59,16 @@ function gdpr_consent_entity_info() { 'entity keys' => array( 'id' => 'id', 'label' => 'title', + 'revision' => 'revision_id', ), - // Use the default label() and uri() functions + // Use the default label() and uri() functions. 'label callback' => 'entity_class_label', 'uri callback' => 'entity_class_uri', // Access callback to determine permisisons. 'access callback' => 'gdpr_consent_access_callback', 'admin ui' => array( 'path' => 'admin/consent', - //'controller class' => 'ConesentAgreementEntityUIController', + //'controller class' => 'ConsentAgreementEntityUIController', 'menu wildcard' => '%entity_object', 'file' => 'includes/gdpr_consent.admin.inc', ), @@ -139,3 +140,22 @@ function gdpr_consent_entity_property_info() { function gdpr_consent_access_callback($op, $entity = NULL, $account = NULL) { return TRUE; } + +/** + * Custom controller for the gdpr_consent_agreement entity type. + */ +class ConsentAgreementController extends EntityAPIController { + + public function save($entity, DatabaseTransaction $transaction = NULL) { + if (isset($entity->is_new)) { + $entity->created = REQUEST_TIME; + } + + $entity->changed = REQUEST_TIME; + + // Always save new revisions. + $entity->is_new_revision = TRUE; + + return parent::save($entity, $transaction); + } +} From b86c3e28acc2be29fe5b400125813329b7c35bb9 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Sat, 14 Apr 2018 22:51:13 +0200 Subject: [PATCH 08/46] Add long description property to consent entity --- gdpr_consent/gdpr_consent.install | 18 ++++++++++++------ gdpr_consent/gdpr_consent.module | 7 +++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 3e6a3d8..29d3864 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -9,10 +9,10 @@ function gdpr_consent_schema() { 'description' => 'Base table for GDPR Consent Agreement entity.', 'fields' => array( 'id' => array( - 'description' => 'Primary key of the GDPR Consent Agreement entity.', 'type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, + 'description' => 'Primary key of the GDPR Consent Agreement entity.', ), 'revision_id' => array( 'type' => 'int', @@ -22,23 +22,23 @@ function gdpr_consent_schema() { 'description' => 'The ID of consent agreement\'s default revision.', ), 'title' => array( - 'description' => 'Title of the consent agreement.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '', + 'description' => 'Title of the consent agreement.', ), 'created' => array( - 'description' => 'The Unix timestamp of the entity creation time.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, + 'description' => 'The Unix timestamp of the entity creation time.', ), 'changed' => array( - 'description' => 'The Unix timestamp the entity was last edited.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, + 'description' => 'The Unix timestamp the entity was last edited.', ), 'uid' => array( 'type' => 'int', @@ -48,11 +48,11 @@ function gdpr_consent_schema() { 'description' => "The {users}.uid of the associated user.", ), 'status' => array( - 'description' => 'Entity status.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'tiny', + 'description' => 'Entity status.', ), ), 'foreign keys' => array( @@ -80,12 +80,18 @@ function gdpr_consent_schema() { 'description' => 'Primary Key: Unique revision ID.', ), 'description' => array( - 'description' => 'A description of the consent agreement.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '', + 'description' => 'A description of the consent agreement.', ), + 'long_description' => array( + 'type' => 'text', + 'size' => 'medium', + 'not null' => TRUE, + 'description' => 'A long description of the consent agreement.', + ) ), 'primary key' => array('revision_id'), ); diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 3680997..f96b97f 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -106,6 +106,13 @@ function gdpr_consent_entity_property_info() { 'schema field' => 'description', ); + $properties['long_description'] = array( + 'label' => t('Long Description'), + 'description' => t('Text shown when the user clicks for more details'), + 'type' => 'text', + 'schema field' => 'description', + ); + $properties['created'] = array( 'label' => t('Created date'), 'description' => t('Date the consent agreement was created'), From 530a39ff3fa408ea84391587d79715026ae9ed3e Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Sun, 15 Apr 2018 10:45:17 +0200 Subject: [PATCH 09/46] Add agreement_type property and handle uid on save() --- gdpr_consent/gdpr_consent.install | 9 ++++++++- gdpr_consent/gdpr_consent.module | 14 ++++++++++++-- gdpr_consent/includes/gdpr_consent.admin.inc | 20 ++++++++++++++++++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 29d3864..af3ff69 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -91,7 +91,14 @@ function gdpr_consent_schema() { 'size' => 'medium', 'not null' => TRUE, 'description' => 'A long description of the consent agreement.', - ) + ), + 'agreement_type' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + 'description' => 'Consent agreement\'s type: implicit or explicit.', + ), ), 'primary key' => array('revision_id'), ); diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index f96b97f..7de750d 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -99,6 +99,13 @@ function gdpr_consent_entity_property_info() { 'schema field' => 'title', ); + $properties['agreement_type'] = array( + 'label' => t('Agreement Type'), + 'description' => t('Whether consent is implicit or explicit. Set to "Explicit" if the user needs to explicitly agree, otherwise "Implicit'), + 'type' => 'boolean', + 'schema field' => 'agreement_type', + ); + $properties['description'] = array( 'label' => t('Description'), 'description' => t('Text displayed to the user on the form'), @@ -110,7 +117,7 @@ function gdpr_consent_entity_property_info() { 'label' => t('Long Description'), 'description' => t('Text shown when the user clicks for more details'), 'type' => 'text', - 'schema field' => 'description', + 'schema field' => 'long_description', ); $properties['created'] = array( @@ -129,7 +136,7 @@ function gdpr_consent_entity_property_info() { $properties['uid'] = array( 'label' => t('Authored by'), - 'description' => t('The user ID of author of the Consent Agreement entity.'), + 'description' => t('The user ID of author of the Consent Agreement entity'), 'type' => 'user', 'schema field' => 'uid', ); @@ -155,7 +162,10 @@ class ConsentAgreementController extends EntityAPIController { public function save($entity, DatabaseTransaction $transaction = NULL) { if (isset($entity->is_new)) { + global $user; + $entity->created = REQUEST_TIME; + $entity->uid = $user->uid; } $entity->changed = REQUEST_TIME; diff --git a/gdpr_consent/includes/gdpr_consent.admin.inc b/gdpr_consent/includes/gdpr_consent.admin.inc index a7f1a4b..cfb539f 100644 --- a/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/gdpr_consent/includes/gdpr_consent.admin.inc @@ -8,15 +8,31 @@ function gdpr_consent_agreement_form($form, &$form_state, $entity = NULL) { '#title' => t('Title'), '#type' => 'textfield', '#default_value' => isset($entity->title) ? $entity->title : '', - '#description' => t('Agreement title.'), + '#description' => t('Agreement Title'), '#required' => TRUE, '#weight' => -50, ); + $form['agreement_type'] = array( + '#title' => t('Agreement Type'), + '#type' => 'select', + '#options' => array( + 0 => t('Implicit'), + 1 => t('Explicit'), + ), + '#default_value' => isset($entity->agreement_type) ? $entity->agreement_type : '', + '#description' => t('Whether consent is implicit or explicit. Set to "Explicit" if the user needs to explicitly agree, otherwise "Implicit'), + ); $form['description'] = array( '#title' => t('Description'), '#type' => 'textfield', '#default_value' => isset($entity->description) ? $entity->description : '', - '#description' => t('Agreement description.'), + '#description' => t('Text displayed to the user on the form'), + ); + $form['long_description'] = array( + '#title' => t('Long Description'), + '#type' => 'textarea', + '#default_value' => isset($entity->long_description) ? $entity->long_description : '', + '#description' => t('Text shown when the user clicks for more details'), ); field_attach_form('gdpr_consent_agreement', $entity, $form, $form_state); From 733942b820654ad413bf531762a3d8bd2e177999 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Mon, 16 Apr 2018 00:31:10 +0200 Subject: [PATCH 10/46] Work towards user consent field type --- gdpr_consent/gdpr_consent.module | 130 +++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 7de750d..23743a6 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -155,6 +155,136 @@ function gdpr_consent_access_callback($op, $entity = NULL, $account = NULL) { return TRUE; } +/** + * Implements hook_field_info(). + * + * Provides a user consent field type. + */ +function gdpr_consent_field_info() { + return array( + 'gdpr_user_consent' => array( + 'label' => t('GDPR Consent'), + 'description' => t('Stores user consent for a particular agreement'), + 'default_widget' => 'gdpr_consent_widget', + 'default_formatter' => 'gdpr_consent_formatter', + ), + ); +} + +/** + * Implements hook_field_validate(). + */ +function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { + $valid = TRUE; + + foreach ($items as $delta => $item) { + if (!empty($item['target_id'])) { + if (!$valid) { + $errors[$field['field_name']][$langcode][$delta][] = array( + 'error' => 'gdpr_user_consent_invalid', + 'message' => t('Referenced consent agreement entity is invalid.'), + ); + } + } + } +} + + +/** + * Implements hook_field_is_empty(). + */ +function gdpr_consent_field_is_empty($item, $field) { + return empty($item['target_id']); +} + +/** + * Implements hook_field_formatter_info(). + */ +function gdpr_consent_field_formatter_info() { + return array( + 'gdpr_consent' => array( + 'label' => t('GDPR user consent formatter'), + 'field types' => array('gdpr_user_consent'), + ), + ); +} + +function gdpr_consent_field_settings_form($field, $instance, $has_data) { + $settings = $field['settings']; + + $form['target_id'] = array( + '#type' => 'textfield', + '#title' => t('User consent agreement entity id'), + '#default_value' => $settings['target_id'], + '#required' => FALSE, + '#element_validate' => array( + 'element_validate_integer_positive', + ), + '#description' => t('The GDPR User Consent Agreement to display'), + ); + + return $form; +} + +/** + * Implements hook_field_formatter_view(). + */ +function gdpr_consent_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { + $element = array(); + + switch ($display['type']) { + case 'gdpr_consent': + foreach ($items as $delta => $item) { + $element[$delta] = array( + '#type' => 'html_tag', + '#tag' => 'p', + '#value' => t('User Consent ID: @entity', array('@entity' => $item['target_id'])), + ); + } + break; + } + + return $element; +} + +/** + * Implements hook_field_widget_info(). + * + * Field widget to show consent information. + */ +function gdpr_consent_field_widget_info() { + return array( + 'gdpr_consent_widget' => array( + 'label' => t('GDPR Consent'), + 'field types' => array('gdpr_user_consent'), + ), + ); +} + +/** + * Implements hook_field_widget_form(). + */ +function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { + $value = isset($items[$delta]['target_id']) ? $items[$delta]['target_id'] : ''; + + $widget = $element; + $widget['#delta'] = $delta; + + switch ($instance['widget']['type']) { + + case 'gdpr_consent_widget': + $widget += array( + '#type' => 'checkbox', + '#title' => 'Agree?', + '#description' => 'Description', + ); + break; + } + + $element['agree'] = $widget; + return $element; +} + /** * Custom controller for the gdpr_consent_agreement entity type. */ From 2b0d7e246318fec39048159f3929643f2cec2cc2 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Tue, 17 Apr 2018 22:37:27 +0200 Subject: [PATCH 11/46] Work on multiple property user consent field --- gdpr_consent/gdpr_consent.install | 56 +++++++++++++++++++++++++++++++ gdpr_consent/gdpr_consent.module | 12 +++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index af3ff69..3b08f96 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -105,3 +105,59 @@ function gdpr_consent_schema() { return $schema; } + +/** + * Implements hook_field_schema(). + */ +function gdpr_consent_field_schema($field) { + $columns = array( + 'target_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'target_revision_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'agreed' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'date' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'user_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + ), + 'user_id_accepted' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + ), + 'notes' => array( + 'type' => 'text', + 'size' => 'medium', + 'not null' => FALSE, + ), + ); + $indexes = array( + 'target_id' => array('target_id'), + 'target_revision_id' => array('target_revision_id'), + ); + return array( + 'columns' => $columns, + 'indexes' => $indexes, + ); +} diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 23743a6..c610711 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -209,13 +209,16 @@ function gdpr_consent_field_formatter_info() { ); } +/** + * Implements hook_field_settings_form(). + */ function gdpr_consent_field_settings_form($field, $instance, $has_data) { $settings = $field['settings']; $form['target_id'] = array( '#type' => 'textfield', '#title' => t('User consent agreement entity id'), - '#default_value' => $settings['target_id'], + '#default_value' => (isset($settings['target_id'])) ? $settings['target_id'] : '', '#required' => FALSE, '#element_validate' => array( 'element_validate_integer_positive', @@ -273,15 +276,18 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, switch ($instance['widget']['type']) { case 'gdpr_consent_widget': - $widget += array( + $element['agreed'] = $widget + array( '#type' => 'checkbox', '#title' => 'Agree?', '#description' => 'Description', ); + $element['date'] = $widget + array( + '#type' => 'hidden', + '#value' => REQUEST_TIME, + ); break; } - $element['agree'] = $widget; return $element; } From a570c9ef3dbd83645395246ed71cd6cb0ff9fa8f Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Wed, 18 Apr 2018 23:01:03 +0200 Subject: [PATCH 12/46] Update user's consent path to be in line with D8 version --- gdpr_consent/gdpr_consent.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index c610711..c88c62f 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -21,7 +21,7 @@ function gdpr_consent_help($path, $arg) { function gdpr_consent_menu() { $items = array(); - $items['user/%user/collected_data/agreements'] = array( + $items['user/%user/gdpr/agreements'] = array( 'title' => 'Agreements', 'description' => 'List Agreement Entities', 'access callback' => TRUE, From 0fb972355be6de73bafc9975c1ae5b53e22fccb4 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Wed, 18 Apr 2018 23:05:15 +0200 Subject: [PATCH 13/46] Define permissions --- gdpr_consent/gdpr_consent.module | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index c88c62f..7983f5e 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -34,6 +34,24 @@ function gdpr_consent_menu() { return $items; } +/** + * Implements hook_permission(). + */ +function gdpr_consent_permission() { + $permissions = array( + 'manage gdpr agreements' => array( + 'title' => t('Manage GDPR Agreements'), + ), + 'grant any consent' => array( + 'title' => t('Grant Any Consent'), + ), + 'grant own consent' => array( + 'title' => t('Grant Own Consent'), + ), + ); + + return $permissions; +} /** * Implements hook_entity_info(). From 56a416372141f02bfade770136cf1a911072eb68 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Wed, 18 Apr 2018 23:33:28 +0200 Subject: [PATCH 14/46] Update consent entity admin UI paths This is now in line with the D8 version of this module. --- gdpr_consent/gdpr_consent.module | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 7983f5e..ef76858 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -69,7 +69,7 @@ function gdpr_consent_entity_info() { 'gdpr_consent_agreement' => array( 'label' => t('GDPR Consent Agreement'), 'admin' => array( - 'path' => 'admin/consent', + 'path' => 'admin/gdpr/agreements', 'access arguments' => array('administer site configuration'), ), ), @@ -82,10 +82,9 @@ function gdpr_consent_entity_info() { // Use the default label() and uri() functions. 'label callback' => 'entity_class_label', 'uri callback' => 'entity_class_uri', - // Access callback to determine permisisons. 'access callback' => 'gdpr_consent_access_callback', 'admin ui' => array( - 'path' => 'admin/consent', + 'path' => 'admin/gdpr/agreements', //'controller class' => 'ConsentAgreementEntityUIController', 'menu wildcard' => '%entity_object', 'file' => 'includes/gdpr_consent.admin.inc', From 94a886b512171a063b788d5007beffe4e18b7b35 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Thu, 19 Apr 2018 00:58:46 +0200 Subject: [PATCH 15/46] Make widget form save custom field properties to db --- gdpr_consent/gdpr_consent.module | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index ef76858..10afdce 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -169,7 +169,7 @@ function gdpr_consent_entity_property_info() { } function gdpr_consent_access_callback($op, $entity = NULL, $account = NULL) { - return TRUE; + return user_access('manage gdpr agreements'); } /** @@ -285,23 +285,45 @@ function gdpr_consent_field_widget_info() { * Implements hook_field_widget_form(). */ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { - $value = isset($items[$delta]['target_id']) ? $items[$delta]['target_id'] : ''; + $agreed = isset($items[$delta]['agreed']) ? $items[$delta]['agreed'] : ''; + $notes = isset($items[$delta]['notes']) ? $items[$delta]['notes'] : ''; $widget = $element; $widget['#delta'] = $delta; + // Get current revision of the referenced agreement entity. + $entity_type = 'gdpr_consent_agreement'; + $entity = entity_load_single('gdpr_consent_agreement', $field['settings']['target_id']); + + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + switch ($instance['widget']['type']) { case 'gdpr_consent_widget': $element['agreed'] = $widget + array( '#type' => 'checkbox', '#title' => 'Agree?', + '#default_value' => $agreed, '#description' => 'Description', ); + $element['notes'] = $widget + array( + '#type' => 'textfield', + '#title' => 'GDPR Consent Notes', + '#default_value' => $notes, + ); + $element['target_id'] = $widget + array( + '#type' => 'hidden', + '#value' => $field['settings']['target_id'], + ); + $element['target_revision_id'] = $widget + array( + '#type' => 'hidden', + '#value' => $vid, + ); $element['date'] = $widget + array( '#type' => 'hidden', '#value' => REQUEST_TIME, ); + break; } From 8da8c4f9f3b3d3bb4d9e5f4f46f62211c26cce78 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Thu, 19 Apr 2018 01:19:40 +0200 Subject: [PATCH 16/46] Add user_id_accepted to widget form --- gdpr_consent/gdpr_consent.module | 33 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 10afdce..06627a4 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -285,6 +285,8 @@ function gdpr_consent_field_widget_info() { * Implements hook_field_widget_form(). */ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { + global $user; + $agreed = isset($items[$delta]['agreed']) ? $items[$delta]['agreed'] : ''; $notes = isset($items[$delta]['notes']) ? $items[$delta]['notes'] : ''; @@ -300,29 +302,36 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, switch ($instance['widget']['type']) { case 'gdpr_consent_widget': - $element['agreed'] = $widget + array( + $element['agreed'] = array( '#type' => 'checkbox', - '#title' => 'Agree?', + '#title' => $entity->title, '#default_value' => $agreed, - '#description' => 'Description', - ); - $element['notes'] = $widget + array( + '#weight' => 0, + '#description' => $entity->long_description, + ) + $widget; + $element['notes'] = array( '#type' => 'textfield', '#title' => 'GDPR Consent Notes', '#default_value' => $notes, - ); - $element['target_id'] = $widget + array( + '#weight' => 10, + '#description' => '', + ) + $widget; + $element['target_id'] = array( '#type' => 'hidden', '#value' => $field['settings']['target_id'], - ); - $element['target_revision_id'] = $widget + array( + ) + $widget; + $element['target_revision_id'] = array( '#type' => 'hidden', '#value' => $vid, - ); - $element['date'] = $widget + array( + ) + $widget; + $element['date'] = array( '#type' => 'hidden', '#value' => REQUEST_TIME, - ); + ) + $widget; + $element['user_id_accepted'] = array( + '#type' => 'hidden', + '#value' => $user->uid, + ) + $widget; break; } From 1bea71be0d762782c26d0caeb2953e8f9ba39233 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Thu, 19 Apr 2018 19:33:28 +0200 Subject: [PATCH 17/46] Work towards entity and revision view pages --- gdpr_consent/gdpr_consent.module | 67 ++++++++++++++++++- .../includes/gdpr_consent.agreements.inc | 23 ++++++- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 06627a4..3433ba1 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -24,16 +24,45 @@ function gdpr_consent_menu() { $items['user/%user/gdpr/agreements'] = array( 'title' => 'Agreements', 'description' => 'List Agreement Entities', - 'access callback' => TRUE, 'page callback' => 'gdpr_consent_collected_agreements', 'page arguments' => array(1), + 'access arguments' => array('manage gdpr agreements'), 'menu_name' => 'navigation', 'file' => 'includes/gdpr_consent.agreements.inc', ); + $items['admin/gdpr/agreements/%gdpr_consent_agreement'] = array( + 'title' => 'Consent Agreement', + 'page callback' => 'gdpr_consent_agreement_view_entity', + 'page arguments' => array(3), + 'access callback' => TRUE, + 'file' => 'includes/gdpr_consent.agreements.inc', + ); + $items['admin/gdpr/agreements/%gdpr_consent_agreement/%'] = array( + 'title' => 'Consent Agreement revision', + 'load arguments' => array(4), + 'page callback' => 'gdpr_consent_agreement_view_revision', + 'page arguments' => array(3), + 'access callback' => TRUE, + 'file' => 'includes/gdpr_consent.agreements.inc', + ); return $items; } +/** + * Menu autoloader for gdpr agreements. + */ +function gdpr_consent_agreement_load($id, $revision_id = NULL) { + if (is_numeric($revision_id)) { + $entity = entity_revision_load('gdpr_consent_agreement', $revision_id); + return $entity; + } + else { + $entity = entity_load('gdpr_consent_agreement', array($id)); + return array_pop($entity); + } +} + /** * Implements hook_permission(). */ @@ -62,7 +91,7 @@ function gdpr_consent_entity_info() { 'label' => t('GDPR Consent Agreement'), 'base table' => 'gdpr_consent_agreement', 'revision table' => 'gdpr_consent_agreement_revision', - 'entity class' => 'Entity', + 'entity class' => 'ConsentAgreement', 'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, 'bundles' => array( @@ -339,6 +368,18 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, return $element; } +/** + * Our custom entity class. + */ +class ConsentAgreement extends Entity { + /** + * Implement a custom default URI. + */ + protected function defaultUri() { + return array('path' => 'admin/gdpr/agreements/' . $this->identifier()); + } +} + /** * Custom controller for the gdpr_consent_agreement entity type. */ @@ -359,4 +400,26 @@ class ConsentAgreementController extends EntityAPIController { return parent::save($entity, $transaction); } + + public function buildContent($entity, $view_mode = 'default', $langcode = NULL, $content = array()) { + $build = parent::buildContent($entity, $view_mode, $langcode, $content); + + $build['description'] = array( + '#type' => 'markup', + '#markup' => $entity->description, + ); + + $build['long_description'] = array( + '#type' => 'markup', + '#markup' => $entity->long_description, + ); + + $build['agreement_type'] = array( + '#type' => 'markup', + '#markup' => $entity->agreement_type, + ); + + return $build; + } + } diff --git a/gdpr_consent/includes/gdpr_consent.agreements.inc b/gdpr_consent/includes/gdpr_consent.agreements.inc index c712fb8..209b1fd 100644 --- a/gdpr_consent/includes/gdpr_consent.agreements.inc +++ b/gdpr_consent/includes/gdpr_consent.agreements.inc @@ -17,7 +17,28 @@ function gdpr_consent_collected_agreements($account) { } - kpr($consent_items); + //kpr($consent_items); return $account->name; } + +/** + * Callback for /admin/gdpr/agreements/{gdpr_consent_agreement ID} page. + */ +function gdpr_consent_agreement_view_entity($agreement) { + drupal_set_title($agreement->title); + $entity = entity_view('gdpr_consent_agreement', array($agreement->id => $agreement), 'default'); + //kpr($entity); + + return $entity; +} + +/** + * Callback for /admin/gdpr/agreements/{gdpr_consent_agreement ID}/{revision ID} page. + */ +function gdpr_consent_agreement_view_revision($agreement) { + $output = gdpr_consent_agreement_view_entity($agreement); + drupal_set_title(t('@title revision @id', array('@title' => $agreement->title, '@id' => $agreement->revision_id))); + + return $output; +} From 538fd1d4872e6f8459068b3582d48d3168fefc7a Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Sun, 22 Apr 2018 23:46:00 +0200 Subject: [PATCH 18/46] Add css and js files to handle long description The long description is now hidden with javascript. --- gdpr_consent/css/gdpr_consent.css | 3 +++ gdpr_consent/gdpr_consent.module | 9 +++++++- gdpr_consent/js/gdpr_consent.js | 36 +++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 gdpr_consent/css/gdpr_consent.css create mode 100644 gdpr_consent/js/gdpr_consent.js diff --git a/gdpr_consent/css/gdpr_consent.css b/gdpr_consent/css/gdpr_consent.css new file mode 100644 index 0000000..caf99c8 --- /dev/null +++ b/gdpr_consent/css/gdpr_consent.css @@ -0,0 +1,3 @@ +.gdpr_agreed_toggle { + padding-left: 5px; +} \ No newline at end of file diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 3433ba1..ca4b141 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -336,10 +336,17 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, '#title' => $entity->title, '#default_value' => $agreed, '#weight' => 0, + '#attributes' => array( + 'class' => array('gdpr_consent_agreement'), + ), + '#attached' => array( + 'css' => array(drupal_get_path('module', 'gdpr_consent') . '/css/gdpr_consent.css'), + 'js' => array(drupal_get_path('module', 'gdpr_consent') . '/js/gdpr_consent.js'), + ), '#description' => $entity->long_description, ) + $widget; $element['notes'] = array( - '#type' => 'textfield', + '#type' => 'textarea', '#title' => 'GDPR Consent Notes', '#default_value' => $notes, '#weight' => 10, diff --git a/gdpr_consent/js/gdpr_consent.js b/gdpr_consent/js/gdpr_consent.js new file mode 100644 index 0000000..339d617 --- /dev/null +++ b/gdpr_consent/js/gdpr_consent.js @@ -0,0 +1,36 @@ +(function ($) { + $(function () { + // Hide the description for any GDPR checkboxes. + var container = $('.gdpr_consent_agreement').parent(); + var desc = container.find('.description'); + + if (!desc.length) { + container = container.parent(); + desc = container.find('.description'); + } + + desc.hide(); + + $('?') + .insertAfter(container.find('label')) + .click(function () { + var desc = $(this).find('.description'); + if (!desc.length) { + desc = $(this).parent().find('.description'); + } + + desc.slideToggle() + }); + + // Do the same for implicit + container = $('.gdpr_consent_implicit').parent(); + desc = container.next('.description'); + desc.hide(); + + $('?') + .appendTo(container) + .click(function () { + $(this).next('.description').slideToggle() + }); + }); +})(jQuery); From ae209b0b01d200734bfddb1c796dc4f32099f981 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Thu, 26 Apr 2018 23:56:40 +0200 Subject: [PATCH 19/46] Implement entity view page callback --- .../includes/gdpr_consent.agreements.inc | 56 +++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/gdpr_consent/includes/gdpr_consent.agreements.inc b/gdpr_consent/includes/gdpr_consent.agreements.inc index 209b1fd..221940a 100644 --- a/gdpr_consent/includes/gdpr_consent.agreements.inc +++ b/gdpr_consent/includes/gdpr_consent.agreements.inc @@ -24,13 +24,59 @@ function gdpr_consent_collected_agreements($account) { /** * Callback for /admin/gdpr/agreements/{gdpr_consent_agreement ID} page. + * + * As we load the entity for display, we're responsible for invoking a number + * of hooks in their proper order. + * + * @see hook_entity_prepare_view() + * @see hook_entity_view() + * @see hook_entity_view_alter() */ -function gdpr_consent_agreement_view_entity($agreement) { - drupal_set_title($agreement->title); - $entity = entity_view('gdpr_consent_agreement', array($agreement->id => $agreement), 'default'); - //kpr($entity); +function gdpr_consent_agreement_view_entity($entity, $view_mode = 'default') { + // Our entity type, for convenience. + $entity_type = 'gdpr_consent_agreement'; + // Start setting up the content. + $entity->content = array( + '#view_mode' => $view_mode, + ); + // Build fields content - this is where the Field API really comes in to play. + // The task has very little code here because it all gets taken care of by + // field module. + // field_attach_prepare_view() lets the fields load any data they need + // before viewing. + field_attach_prepare_view($entity_type, array($entity->id => $entity), + $view_mode); + // We call entity_prepare_view() so it can invoke hook_entity_prepare_view() + // for us. + entity_prepare_view($entity_type, array($entity->id => $entity)); + // Now field_attach_view() generates the content for the fields. + $entity->content += field_attach_view($entity_type, $entity, $view_mode); - return $entity; + // OK, Field API done, now we can set up some of our own data. + $entity->content['created'] = array( + '#type' => 'item', + '#title' => t('Created date'), + '#markup' => format_date($entity->created), + ); + $entity->content['description'] = array( + '#type' => 'item', + '#title' => t('Description'), + '#markup' => $entity->description, + ); + + // Now to invoke some hooks. We need the language code for + // hook_entity_view(), so let's get that. + global $language; + $langcode = $language->language; + // And now invoke hook_entity_view(). + module_invoke_all('entity_view', $entity, $entity_type, $view_mode, + $langcode); + // Now invoke hook_entity_view_alter(). + drupal_alter(array('gdpr_consent_agreement_view_entity', 'entity_view'), + $entity->content, $entity_type); + + // And finally return the content. + return $entity->content; } /** From e67e55498a2c010f0eedf6d465b41874f2969594 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Fri, 27 Apr 2018 00:22:25 +0200 Subject: [PATCH 20/46] Work towards agreement entity menu tabs --- gdpr_consent/gdpr_consent.module | 50 ++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index ca4b141..daf8a53 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -37,13 +37,53 @@ function gdpr_consent_menu() { 'access callback' => TRUE, 'file' => 'includes/gdpr_consent.agreements.inc', ); - $items['admin/gdpr/agreements/%gdpr_consent_agreement/%'] = array( - 'title' => 'Consent Agreement revision', - 'load arguments' => array(4), - 'page callback' => 'gdpr_consent_agreement_view_revision', + // 'View' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/view'] = array( + 'title' => 'View', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + // 'Edit' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/edit'] = array( + 'title' => 'Edit', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('gdpr_consent_agreement_form', 3), + 'access arguments' => array('manage gdpr agreements'), + 'type' => MENU_LOCAL_TASK, + 'weight' => -5, + 'file' => 'includes/gdpr_consent.admin.inc', + ); + // 'Revisions' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/revisions'] = array( + 'title' => 'Revisions', + 'load arguments' => array(5), + 'page callback' => 'gdpr_consent_agreement_view_revisions', + 'page arguments' => array(4), + 'type' => MENU_LOCAL_TASK, + 'access arguments' => array('manage gdpr agreements'), + 'file' => 'includes/gdpr_consent.admin.inc', + 'weight' => -3, + ); + // An individual revision view page. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/revisions/%/view'] = array( + 'title' => 'Revision', + 'load arguments' => array(5), + 'page callback' => 'gdpr_consent_agreement_view_entity', 'page arguments' => array(3), - 'access callback' => TRUE, + 'type' => MENU_LOCAL_TASK, + 'access arguments' => array('manage gdpr agreements'), 'file' => 'includes/gdpr_consent.agreements.inc', + 'weight' => -3, + ); + // 'Delete' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/delete'] = array( + 'title' => 'Delete', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('gdpr_consent_agreement_form', 3), + 'access arguments' => array('manage gdpr agreements'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'includes/gdpr_consent.admin.inc', + 'weight' => 0, ); return $items; From a72fbce798602397ab88949f0bdadbb15a832870 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Mon, 30 Apr 2018 01:48:44 +0200 Subject: [PATCH 21/46] Add notes to the Consent agreement entity These are notes for staff to put their rationale for why they have done this for auditors. --- gdpr_consent/gdpr_consent.install | 6 ++++++ gdpr_consent/gdpr_consent.module | 16 ++++++++++++++-- gdpr_consent/includes/gdpr_consent.admin.inc | 6 ++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 3b08f96..75da9cb 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -99,6 +99,12 @@ function gdpr_consent_schema() { 'size' => 'tiny', 'description' => 'Consent agreement\'s type: implicit or explicit.', ), + 'notes' => array( + 'type' => 'text', + 'size' => 'medium', + 'not null' => FALSE, + 'description' => 'Notes for staff to put their rationale for why they have done this for auditors.', + ), ), 'primary key' => array('revision_id'), ); diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index daf8a53..736c3be 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -33,6 +33,7 @@ function gdpr_consent_menu() { $items['admin/gdpr/agreements/%gdpr_consent_agreement'] = array( 'title' => 'Consent Agreement', 'page callback' => 'gdpr_consent_agreement_view_entity', + //'page callback' => 'entity_ui_entity_page_view', 'page arguments' => array(3), 'access callback' => TRUE, 'file' => 'includes/gdpr_consent.agreements.inc', @@ -56,9 +57,8 @@ function gdpr_consent_menu() { // 'Revisions' tab for an individual entity. $items['admin/gdpr/agreements/%gdpr_consent_agreement/revisions'] = array( 'title' => 'Revisions', - 'load arguments' => array(5), 'page callback' => 'gdpr_consent_agreement_view_revisions', - 'page arguments' => array(4), + 'page arguments' => array(3), 'type' => MENU_LOCAL_TASK, 'access arguments' => array('manage gdpr agreements'), 'file' => 'includes/gdpr_consent.admin.inc', @@ -206,6 +206,13 @@ function gdpr_consent_entity_property_info() { 'schema field' => 'long_description', ); + $properties['notes'] = array( + 'label' => t('Notes'), + 'description' => t('This should contain the rationale behind the agreement.'), + 'type' => 'text', + 'schema field' => 'notes', + ); + $properties['created'] = array( 'label' => t('Created date'), 'description' => t('Date the consent agreement was created'), @@ -461,6 +468,11 @@ class ConsentAgreementController extends EntityAPIController { '#markup' => $entity->long_description, ); + $build['notes'] = array( + '#type' => 'markup', + '#markup' => $entity->notes, + ); + $build['agreement_type'] = array( '#type' => 'markup', '#markup' => $entity->agreement_type, diff --git a/gdpr_consent/includes/gdpr_consent.admin.inc b/gdpr_consent/includes/gdpr_consent.admin.inc index cfb539f..8b26196 100644 --- a/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/gdpr_consent/includes/gdpr_consent.admin.inc @@ -34,6 +34,12 @@ function gdpr_consent_agreement_form($form, &$form_state, $entity = NULL) { '#default_value' => isset($entity->long_description) ? $entity->long_description : '', '#description' => t('Text shown when the user clicks for more details'), ); + $form['notes'] = array( + '#title' => t('Notes'), + '#type' => 'textarea', + '#default_value' => isset($entity->notes) ? $entity->notes : '', + '#description' => t('This should contain the rationale behind the agreement.'), + ); field_attach_form('gdpr_consent_agreement', $entity, $form, $form_state); From 88de361c6a87d4c121242aaae07e075d271c314b Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Mon, 30 Apr 2018 02:00:28 +0200 Subject: [PATCH 22/46] Add message type for consent agreement logs --- gdpr_consent/gdpr_consent.module | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 736c3be..3ba3ce9 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -422,6 +422,39 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, return $element; } + +/** + * Implements hook_default_message_type(). + */ +function gdpr_consent_default_message_type() { + $items = array(); + + $items['gdpr_consent_agreement'] = entity_import('message_type', '{ + "name" : "gdpr_consent_agreement", + "description" : "GDPR Consent Agreement", + "argument_keys" : [], + "argument" : [], + "category" : "message_type", + "data" : { + "token options" : { "clear" : 0 }, + "purge" : { "override" : 1, "enabled" : 1, "quota" : "1000", "days" : "30" } + }, + "language" : "", + "arguments" : null, + "message_text" : { "und" : [ + { + "value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022[message:agreement:entity:url]\/revisions\/[message:agreement:target_revision_id]\/view\u0022\u003E[message:agreement:entity:title]\u003C\/a\u003E\u003Cbr \/\u003E\r\nAgreed: [message:agreed]\u003Cbr \/\u003E\r\nNotes: [message:notes]\u003C\/p\u003E", + "format" : "filtered_html", + "safe_value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022url]\/revisions\/[message:agreement:target_revision_id]\/view\u0022\u003E[message:agreement:entity:title]\u003C\/a\u003E\u003Cbr \/\u003E\nAgreed: [message:agreed]\u003Cbr \/\u003E\nNotes: [message:notes]\u003C\/p\u003E\n" + } + ] + }, + "rdf_mapping" : [] + }'); + + return $items; +} + /** * Our custom entity class. */ From c8c5b0c02a10c05edbc97c1040ae63e1dc1f062d Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Mon, 30 Apr 2018 02:30:37 +0200 Subject: [PATCH 23/46] Work towards logs of changes via messages --- gdpr_consent/gdpr_consent.module | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 3ba3ce9..c639f5f 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -283,6 +283,21 @@ function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $ } +/** + * Implements hook_field_insert(). + */ +function gdpr_consent_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { + $message = message_create('consent_agreement_accepted', array('uid' => $entity->uid)); + $wrapper = entity_metadata_wrapper('message', $message); + + $wrapper->user->set($field['user_id_accepted']); + $wrapper->agreement->set(array($field['target_id'], $field['target_revision_id'])); + $wrapper->notes->set($field['notes']); + $wrapper->agreed->set($field['agreed']); + + $wrapper->save(); +} + /** * Implements hook_field_is_empty(). */ @@ -429,8 +444,8 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, function gdpr_consent_default_message_type() { $items = array(); - $items['gdpr_consent_agreement'] = entity_import('message_type', '{ - "name" : "gdpr_consent_agreement", + $items['consent_agreement_accepted'] = entity_import('message_type', '{ + "name" : "consent_agreement_accepted", "description" : "GDPR Consent Agreement", "argument_keys" : [], "argument" : [], From ae84301b8cf885b1d160568f57561e5be83df71c Mon Sep 17 00:00:00 2001 From: yanniboi Date: Mon, 23 Apr 2018 07:25:14 +0000 Subject: [PATCH 24/46] By yanniboi: Updated consent UI controller. --- gdpr_consent/gdpr_consent.module | 50 ++++++++++++++++---- gdpr_consent/includes/gdpr_consent.admin.inc | 2 +- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index c639f5f..734fab7 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -131,6 +131,7 @@ function gdpr_consent_entity_info() { 'label' => t('GDPR Consent Agreement'), 'base table' => 'gdpr_consent_agreement', 'revision table' => 'gdpr_consent_agreement_revision', + 'uri callback' => 'entity_class_uri', 'entity class' => 'ConsentAgreement', 'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, @@ -148,14 +149,11 @@ function gdpr_consent_entity_info() { 'label' => 'title', 'revision' => 'revision_id', ), - // Use the default label() and uri() functions. - 'label callback' => 'entity_class_label', - 'uri callback' => 'entity_class_uri', 'access callback' => 'gdpr_consent_access_callback', 'admin ui' => array( 'path' => 'admin/gdpr/agreements', - //'controller class' => 'ConsentAgreementEntityUIController', - 'menu wildcard' => '%entity_object', + 'controller class' => 'ConsentAgreementEntityUIController', + 'menu wildcard' => '%gdpr_consent_agreement', 'file' => 'includes/gdpr_consent.admin.inc', ), 'module' => 'gdpr_consent', @@ -282,7 +280,6 @@ function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $ } } - /** * Implements hook_field_insert(). */ @@ -323,11 +320,18 @@ function gdpr_consent_field_formatter_info() { function gdpr_consent_field_settings_form($field, $instance, $has_data) { $settings = $field['settings']; + $agreements = array(); + + foreach (entity_load('gdpr_consent_agreement') as $agreement) { + $agreements[$agreement->id] = $agreement->title; + } + $form['target_id'] = array( - '#type' => 'textfield', - '#title' => t('User consent agreement entity id'), + '#type' => 'select', + '#title' => t('User consent agreement'), '#default_value' => (isset($settings['target_id'])) ? $settings['target_id'] : '', '#required' => FALSE, + '#options' => $agreements, '#element_validate' => array( 'element_validate_integer_positive', ), @@ -408,11 +412,12 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, '#description' => $entity->long_description, ) + $widget; $element['notes'] = array( - '#type' => 'textarea', - '#title' => 'GDPR Consent Notes', + '#type' => 'textfield', + '#title' => 'Consent Notes', '#default_value' => $notes, '#weight' => 10, '#description' => '', + '#access' => user_access('grant any consent', $user), ) + $widget; $element['target_id'] = array( '#type' => 'hidden', @@ -503,6 +508,9 @@ class ConsentAgreementController extends EntityAPIController { return parent::save($entity, $transaction); } +} + +class ConsentAgreementEntityUIController extends EntityDefaultUIController { public function buildContent($entity, $view_mode = 'default', $langcode = NULL, $content = array()) { $build = parent::buildContent($entity, $view_mode, $langcode, $content); @@ -529,4 +537,26 @@ class ConsentAgreementController extends EntityAPIController { return $build; } + /** + * {@inheritdoc} + */ + protected function overviewTableHeaders($conditions, $rows, $additional_header = array()) { + $additional_header = array( + t('Type'), + ); + return parent::overviewTableHeaders($conditions, $rows, $additional_header); + } + + /** + * {@inheritdoc} + */ + protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { + $additional_cols = array( + $entity->agreement_type ? 'Explicit' : 'Implicit', + ); + + $row = parent::overviewTableRow($conditions, $id, $entity, $additional_cols); + return $row; + } + } diff --git a/gdpr_consent/includes/gdpr_consent.admin.inc b/gdpr_consent/includes/gdpr_consent.admin.inc index 8b26196..21bddd5 100644 --- a/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/gdpr_consent/includes/gdpr_consent.admin.inc @@ -61,5 +61,5 @@ function gdpr_consent_agreement_form_submit($form, &$form_state) { $entity = entity_ui_form_submit_build_entity($form, $form_state); $entity->save(); drupal_set_message(t('@title agreement has been saved.', array('@title' => $entity->title))); - $form_state['redirect'] = 'admin/consent'; + $form_state['redirect'] = 'admin/gdpr/agreements'; } From 6c4fd45ac462641a314fb01d18fb60947146e706 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Mon, 30 Apr 2018 20:17:13 +0200 Subject: [PATCH 25/46] Implement revisions overview tab --- gdpr_consent/gdpr_consent.module | 5 +- gdpr_consent/includes/gdpr_consent.admin.inc | 63 ++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 734fab7..eeaa941 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -57,7 +57,7 @@ function gdpr_consent_menu() { // 'Revisions' tab for an individual entity. $items['admin/gdpr/agreements/%gdpr_consent_agreement/revisions'] = array( 'title' => 'Revisions', - 'page callback' => 'gdpr_consent_agreement_view_revisions', + 'page callback' => 'gdpr_consent_agreement_revision_overview', 'page arguments' => array(3), 'type' => MENU_LOCAL_TASK, 'access arguments' => array('manage gdpr agreements'), @@ -70,7 +70,6 @@ function gdpr_consent_menu() { 'load arguments' => array(5), 'page callback' => 'gdpr_consent_agreement_view_entity', 'page arguments' => array(3), - 'type' => MENU_LOCAL_TASK, 'access arguments' => array('manage gdpr agreements'), 'file' => 'includes/gdpr_consent.agreements.inc', 'weight' => -3, @@ -131,7 +130,6 @@ function gdpr_consent_entity_info() { 'label' => t('GDPR Consent Agreement'), 'base table' => 'gdpr_consent_agreement', 'revision table' => 'gdpr_consent_agreement_revision', - 'uri callback' => 'entity_class_uri', 'entity class' => 'ConsentAgreement', 'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, @@ -149,6 +147,7 @@ function gdpr_consent_entity_info() { 'label' => 'title', 'revision' => 'revision_id', ), + 'uri callback' => 'entity_class_uri', 'access callback' => 'gdpr_consent_access_callback', 'admin ui' => array( 'path' => 'admin/gdpr/agreements', diff --git a/gdpr_consent/includes/gdpr_consent.admin.inc b/gdpr_consent/includes/gdpr_consent.admin.inc index 21bddd5..0e7ab1e 100644 --- a/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/gdpr_consent/includes/gdpr_consent.admin.inc @@ -63,3 +63,66 @@ function gdpr_consent_agreement_form_submit($form, &$form_state) { drupal_set_message(t('@title agreement has been saved.', array('@title' => $entity->title))); $form_state['redirect'] = 'admin/gdpr/agreements'; } + +/** + * Generates an overview table of older revisions of a Consent Agreement. + * + * @param $agreement + * A consent agreement entity. + * + * @return array + * An array as expected by drupal_render(). + */ +function gdpr_consent_agreement_revision_overview($agreement) { + drupal_set_title(t('Revisions for @title', array('@title' => $agreement->title))); + + $header = array(t('Revision'), array('data' => t('Operations'), 'colspan' => 2)); + + $revisions = gdpr_consent_revision_list($agreement); + + $rows = array(); + + foreach ($revisions as $revision) { + $row = array(); + $operations = array(); + + if ($revision->current_revision > 0) { + $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->changed, 'short'), "admin/gdpr/agreements/$agreement->id"), '!username' => theme('username', array('account' => $revision)))), + 'class' => array('revision-current')); + $operations[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current'), 'colspan' => 2); + } + else { + $row[] = t('!date by !username', array('!date' => l(format_date($revision->changed, 'short'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/view"), '!username' => theme('username', array('account' => $revision)))); + $operations[] = l(t('revert'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/revert"); + $operations[] = l(t('delete'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/delete"); + } + $rows[] = array_merge($row, $operations); + } + + $build['gdpr_consent_revisions_table'] = array( + '#theme' => 'table', + '#rows' => $rows, + '#header' => $header, + ); + + return $build; +} + +/** + * Returns a list of all the existing revision numbers. + * + * @param $agreement + * The Consent Agreement entity. + * + * @return + * An associative array keyed by revision number. + */ +function gdpr_consent_revision_list($agreement) { + $revisions = array(); + $result = db_query('SELECT r.revision_id, a.title, a.uid, a.revision_id AS current_revision, a.changed, u.name FROM {gdpr_consent_agreement_revision} r LEFT JOIN {gdpr_consent_agreement} a ON a.revision_id = r.revision_id LEFT JOIN {users} u ON u.uid = a.uid WHERE r.id = :id ORDER BY r.revision_id DESC', array(':id' => $agreement->id)); + foreach ($result as $revision) { + $revisions[$revision->revision_id] = $revision; + } + + return $revisions; +} From 0c55f2ccf5c438e46f2583af95f8d2bfc422cc1e Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Mon, 30 Apr 2018 23:13:41 +0200 Subject: [PATCH 26/46] Add revision log to schema and entity form --- gdpr_consent/gdpr_consent.install | 26 +++++++++++++++++++ gdpr_consent/gdpr_consent.module | 6 ++--- gdpr_consent/includes/gdpr_consent.admin.inc | 18 ++++++++++--- .../includes/gdpr_consent.agreements.inc | 3 ++- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 75da9cb..443e681 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -79,6 +79,26 @@ function gdpr_consent_schema() { 'not null' => TRUE, 'description' => 'Primary Key: Unique revision ID.', ), + 'title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Title of the consent agreement.', + ), + 'timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'A Unix timestamp indicating when this version was created.', + ), + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => "The {users}.uid of the associated user.", + ), 'description' => array( 'type' => 'varchar', 'length' => 255, @@ -105,6 +125,12 @@ function gdpr_consent_schema() { 'not null' => FALSE, 'description' => 'Notes for staff to put their rationale for why they have done this for auditors.', ), + 'log' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + 'description' => 'The log entry explaining the changes in this version.', + ), ), 'primary key' => array('revision_id'), ); diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index eeaa941..71e3fdf 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -67,9 +67,8 @@ function gdpr_consent_menu() { // An individual revision view page. $items['admin/gdpr/agreements/%gdpr_consent_agreement/revisions/%/view'] = array( 'title' => 'Revision', - 'load arguments' => array(5), - 'page callback' => 'gdpr_consent_agreement_view_entity', - 'page arguments' => array(3), + 'page callback' => 'gdpr_consent_agreement_view_revision', + 'page arguments' => array(5), 'access arguments' => array('manage gdpr agreements'), 'file' => 'includes/gdpr_consent.agreements.inc', 'weight' => -3, @@ -503,6 +502,7 @@ class ConsentAgreementController extends EntityAPIController { // Always save new revisions. $entity->is_new_revision = TRUE; + $entity->timestamp = REQUEST_TIME; return parent::save($entity, $transaction); } diff --git a/gdpr_consent/includes/gdpr_consent.admin.inc b/gdpr_consent/includes/gdpr_consent.admin.inc index 0e7ab1e..2a0eee5 100644 --- a/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/gdpr_consent/includes/gdpr_consent.admin.inc @@ -40,6 +40,18 @@ function gdpr_consent_agreement_form($form, &$form_state, $entity = NULL) { '#default_value' => isset($entity->notes) ? $entity->notes : '', '#description' => t('This should contain the rationale behind the agreement.'), ); + $form['revision'] = array( + '#type' => 'checkbox', + '#title' => t('Create new revision'), + '#default_value' => 1, + ); + $form['log'] = array( + '#type' => 'textarea', + '#title' => t('Revision log message'), + '#rows' => 4, + '#default_value' => !empty($entity->log) ? $entity->log : '', + '#description' => t('Briefly describe the changes you have made.'), + ); field_attach_form('gdpr_consent_agreement', $entity, $form, $form_state); @@ -87,12 +99,12 @@ function gdpr_consent_agreement_revision_overview($agreement) { $operations = array(); if ($revision->current_revision > 0) { - $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->changed, 'short'), "admin/gdpr/agreements/$agreement->id"), '!username' => theme('username', array('account' => $revision)))), + $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "admin/gdpr/agreements/$agreement->id"), '!username' => theme('username', array('account' => $revision)))), 'class' => array('revision-current')); $operations[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current'), 'colspan' => 2); } else { - $row[] = t('!date by !username', array('!date' => l(format_date($revision->changed, 'short'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/view"), '!username' => theme('username', array('account' => $revision)))); + $row[] = t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/view"), '!username' => theme('username', array('account' => $revision)))); $operations[] = l(t('revert'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/revert"); $operations[] = l(t('delete'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/delete"); } @@ -119,7 +131,7 @@ function gdpr_consent_agreement_revision_overview($agreement) { */ function gdpr_consent_revision_list($agreement) { $revisions = array(); - $result = db_query('SELECT r.revision_id, a.title, a.uid, a.revision_id AS current_revision, a.changed, u.name FROM {gdpr_consent_agreement_revision} r LEFT JOIN {gdpr_consent_agreement} a ON a.revision_id = r.revision_id LEFT JOIN {users} u ON u.uid = a.uid WHERE r.id = :id ORDER BY r.revision_id DESC', array(':id' => $agreement->id)); + $result = db_query('SELECT r.revision_id, a.title, r.uid, a.revision_id AS current_revision, r.timestamp, u.name FROM {gdpr_consent_agreement_revision} r LEFT JOIN {gdpr_consent_agreement} a ON a.revision_id = r.revision_id INNER JOIN {users} u ON u.uid = r.uid WHERE r.id = :id ORDER BY r.revision_id DESC', array(':id' => $agreement->id)); foreach ($result as $revision) { $revisions[$revision->revision_id] = $revision; } diff --git a/gdpr_consent/includes/gdpr_consent.agreements.inc b/gdpr_consent/includes/gdpr_consent.agreements.inc index 221940a..0feff0f 100644 --- a/gdpr_consent/includes/gdpr_consent.agreements.inc +++ b/gdpr_consent/includes/gdpr_consent.agreements.inc @@ -82,7 +82,8 @@ function gdpr_consent_agreement_view_entity($entity, $view_mode = 'default') { /** * Callback for /admin/gdpr/agreements/{gdpr_consent_agreement ID}/{revision ID} page. */ -function gdpr_consent_agreement_view_revision($agreement) { +function gdpr_consent_agreement_view_revision($revision_id) { + $agreement = entity_revision_load('gdpr_consent_agreement', $revision_id); $output = gdpr_consent_agreement_view_entity($agreement); drupal_set_title(t('@title revision @id', array('@title' => $agreement->title, '@id' => $agreement->revision_id))); From 628479e25f15b272c1905b76a0b810a1ea4d02c7 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Tue, 1 May 2018 08:09:47 +0100 Subject: [PATCH 27/46] By yanniboi: Make consent agreements exportable. --- gdpr_consent/gdpr_consent.install | 18 ++++++- gdpr_consent/gdpr_consent.module | 83 +++++++++++++++++++++++++------ 2 files changed, 84 insertions(+), 17 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 443e681..aa4e6c7 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -14,6 +14,12 @@ function gdpr_consent_schema() { 'not null' => TRUE, 'description' => 'Primary key of the GDPR Consent Agreement entity.', ), + 'name' => array( + 'description' => 'The machine-readable name of this consent agreement.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + ), 'revision_id' => array( 'type' => 'int', 'unsigned' => TRUE, @@ -54,14 +60,24 @@ function gdpr_consent_schema() { 'size' => 'tiny', 'description' => 'Entity status.', ), + 'module' => array( + 'description' => 'The name of the providing module if the entity has been defined in code.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), ), 'foreign keys' => array( 'uid' => array( 'table' => 'users', - 'columns' => array('uid' => 'uid') + 'columns' => array('uid' => 'uid'), ), ), 'primary key' => array('id'), + 'unique key' => array('name'), + 'indexes' => array( + 'name' => array('name'), + ), ); $schema['gdpr_consent_agreement_revision'] = array( diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 71e3fdf..e1dc637 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -87,20 +87,6 @@ function gdpr_consent_menu() { return $items; } -/** - * Menu autoloader for gdpr agreements. - */ -function gdpr_consent_agreement_load($id, $revision_id = NULL) { - if (is_numeric($revision_id)) { - $entity = entity_revision_load('gdpr_consent_agreement', $revision_id); - return $entity; - } - else { - $entity = entity_load('gdpr_consent_agreement', array($id)); - return array_pop($entity); - } -} - /** * Implements hook_permission(). */ @@ -132,6 +118,7 @@ function gdpr_consent_entity_info() { 'entity class' => 'ConsentAgreement', 'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, + 'exportable' => TRUE, 'bundles' => array( 'gdpr_consent_agreement' => array( 'label' => t('GDPR Consent Agreement'), @@ -143,6 +130,7 @@ function gdpr_consent_entity_info() { ), 'entity keys' => array( 'id' => 'id', + 'name' => 'name', 'label' => 'title', 'revision' => 'revision_id', ), @@ -240,10 +228,46 @@ function gdpr_consent_entity_property_info() { return $info; } +/** + * Access callback for agreements. + */ function gdpr_consent_access_callback($op, $entity = NULL, $account = NULL) { return user_access('manage gdpr agreements'); } +/** + * Menu autoloader for gdpr agreements. + */ +function gdpr_consent_agreement_load($id, $revision_id = NULL) { + if (is_numeric($revision_id)) { + $entity = entity_revision_load('gdpr_consent_agreement', $revision_id); + return $entity; + } + else { + $entity = entity_load_single('gdpr_consent_agreement', $id); + } + return $entity; +} + +/** + * Loads multiple agreements. + */ +function gdpr_consent_agreement_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) { + if (empty($ids)) { + $ids = FALSE; + } + + return entity_load('gdpr_consent_agreement', $ids, $conditions, $reset); +} + +/** + * Gets an array of all agreements, keyed by the machine name. + */ +function gdpr_consent_agreement_load_multiple_by_name($name = NULL) { + $signups = entity_load_multiple_by_name('gdpr_consent_agreement', isset($name) ? array($name) : FALSE); + return isset($name) ? reset($signups) : $signups; +} + /** * Implements hook_field_info(). * @@ -477,19 +501,24 @@ function gdpr_consent_default_message_type() { * Our custom entity class. */ class ConsentAgreement extends Entity { + /** - * Implement a custom default URI. + * {@inheritdoc} */ protected function defaultUri() { return array('path' => 'admin/gdpr/agreements/' . $this->identifier()); } + } /** * Custom controller for the gdpr_consent_agreement entity type. */ -class ConsentAgreementController extends EntityAPIController { +class ConsentAgreementController extends EntityAPIControllerExportable { + /** + * {@inheritdoc} + */ public function save($entity, DatabaseTransaction $transaction = NULL) { if (isset($entity->is_new)) { global $user; @@ -507,9 +536,31 @@ class ConsentAgreementController extends EntityAPIController { return parent::save($entity, $transaction); } + /** + * {@inheritdoc} + */ + public function export($entity, $prefix = '') { + $vars = get_object_vars($entity); + unset($vars[$this->statusKey], $vars[$this->moduleKey], $vars['is_new']); + if ($this->nameKey != $this->idKey) { + unset($vars[$this->idKey]); + } + if ($this->revisionKey) { + unset($vars[$this->revisionKey]); + } + return entity_var_json_export($vars, $prefix); + } + } +/** + * Custom UI controller for the gdpr_consent_agreement entity type. + */ class ConsentAgreementEntityUIController extends EntityDefaultUIController { + + /** + * {@inheritdoc} + */ public function buildContent($entity, $view_mode = 'default', $langcode = NULL, $content = array()) { $build = parent::buildContent($entity, $view_mode, $langcode, $content); From 64895c3cf4cd6025581433c0da308a0ed98ebbde Mon Sep 17 00:00:00 2001 From: yanniboi Date: Tue, 1 May 2018 08:10:06 +0100 Subject: [PATCH 28/46] By yanniboi: PHPCS review. --- gdpr_consent/css/gdpr_consent.css | 2 +- gdpr_consent/gdpr_consent.install | 6 ++++++ gdpr_consent/gdpr_consent.module | 1 - gdpr_consent/includes/gdpr_consent.admin.inc | 5 +++++ gdpr_consent/includes/gdpr_consent.agreements.inc | 12 +++++++----- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/gdpr_consent/css/gdpr_consent.css b/gdpr_consent/css/gdpr_consent.css index caf99c8..0744974 100644 --- a/gdpr_consent/css/gdpr_consent.css +++ b/gdpr_consent/css/gdpr_consent.css @@ -1,3 +1,3 @@ .gdpr_agreed_toggle { padding-left: 5px; -} \ No newline at end of file +} diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index aa4e6c7..dff7a84 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -1,4 +1,10 @@ entityCondition('entity_type', 'gdpr_consent_agreement'); - //->propertyCondition('uid', $account->uid); $result = $query->execute(); if (isset($result['gdpr_consent_agreement'])) { $consent_ids = array_keys($result['gdpr_consent_agreement']); $consent_items = entity_load('gdpr_consent_agreement', $consent_ids); - } - //kpr($consent_items); - - return $account->name; + return MENU_ACCESS_DENIED; } /** From 58c0e2c85430ffb257a62018c17b59b0d3705165 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Tue, 1 May 2018 09:00:19 +0100 Subject: [PATCH 29/46] By yanniboi: Added machine name to consent form. --- gdpr_consent/includes/gdpr_consent.admin.inc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/gdpr_consent/includes/gdpr_consent.admin.inc b/gdpr_consent/includes/gdpr_consent.admin.inc index d5195f9..82c2195 100644 --- a/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/gdpr_consent/includes/gdpr_consent.admin.inc @@ -8,7 +8,7 @@ /** * Form for managing a consent agreement entity. */ -function gdpr_consent_agreement_form($form, &$form_state, $entity = NULL) { +function gdpr_consent_agreement_form($form, &$form_state, ConsentAgreement $entity = NULL) { $form['title'] = array( '#title' => t('Title'), '#type' => 'textfield', @@ -17,6 +17,17 @@ function gdpr_consent_agreement_form($form, &$form_state, $entity = NULL) { '#required' => TRUE, '#weight' => -50, ); + $form['name'] = array( + '#type' => 'machine_name', + '#default_value' => isset($entity->name) ? $entity->name : '', + '#disabled' => $entity->hasStatus(ENTITY_IN_CODE), + '#machine_name' => array( + 'exists' => 'gdpr_consent_agreement_load_multiple_by_name', + 'source' => array('title'), + ), + '#description' => t('A unique machine-readable name for this agreement. It must only contain lowercase letters, numbers, and underscores.'), + '#weight' => -50, + ); $form['agreement_type'] = array( '#title' => t('Agreement Type'), '#type' => 'select', From c6f03fcb82e80d11f9528847f4921b1d432736fc Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Tue, 1 May 2018 14:24:52 +0200 Subject: [PATCH 30/46] Fix entity edit form submit issues By switching to entity_ui_get_form page callback. --- gdpr_consent/gdpr_consent.module | 23 ++++++++------------ gdpr_consent/includes/gdpr_consent.admin.inc | 2 +- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 71e3fdf..2233ddc 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -47,8 +47,8 @@ function gdpr_consent_menu() { // 'Edit' tab for an individual entity. $items['admin/gdpr/agreements/%gdpr_consent_agreement/edit'] = array( 'title' => 'Edit', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('gdpr_consent_agreement_form', 3), + 'page callback' => 'entity_ui_get_form', + 'page arguments' => array('gdpr_consent_agreement', 3), 'access arguments' => array('manage gdpr agreements'), 'type' => MENU_LOCAL_TASK, 'weight' => -5, @@ -129,7 +129,7 @@ function gdpr_consent_entity_info() { 'label' => t('GDPR Consent Agreement'), 'base table' => 'gdpr_consent_agreement', 'revision table' => 'gdpr_consent_agreement_revision', - 'entity class' => 'ConsentAgreement', + 'entity class' => 'Entity', 'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, 'bundles' => array( @@ -146,7 +146,7 @@ function gdpr_consent_entity_info() { 'label' => 'title', 'revision' => 'revision_id', ), - 'uri callback' => 'entity_class_uri', + 'uri callback' => 'gdpr_consent_entity_uri_callback', 'access callback' => 'gdpr_consent_access_callback', 'admin ui' => array( 'path' => 'admin/gdpr/agreements', @@ -473,16 +473,11 @@ function gdpr_consent_default_message_type() { return $items; } -/** - * Our custom entity class. - */ -class ConsentAgreement extends Entity { - /** - * Implement a custom default URI. - */ - protected function defaultUri() { - return array('path' => 'admin/gdpr/agreements/' . $this->identifier()); - } +function gdpr_consent_entity_uri_callback($entity) { + return array( + 'path' => 'admin/gdpr/agreements/' . $entity->id, + 'options' => array(), + ); } /** diff --git a/gdpr_consent/includes/gdpr_consent.admin.inc b/gdpr_consent/includes/gdpr_consent.admin.inc index 2a0eee5..0ca0d53 100644 --- a/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/gdpr_consent/includes/gdpr_consent.admin.inc @@ -69,7 +69,7 @@ function gdpr_consent_agreement_form($form, &$form_state, $entity = NULL) { /** * Submit handler for consent agreement entity form. */ -function gdpr_consent_agreement_form_submit($form, &$form_state) { +function gdpr_consent_agreement_form_submit(&$form, &$form_state) { $entity = entity_ui_form_submit_build_entity($form, $form_state); $entity->save(); drupal_set_message(t('@title agreement has been saved.', array('@title' => $entity->title))); From 4749b6a527ce1e6821fc92bd15f7e7eaf721a878 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Tue, 1 May 2018 15:07:45 +0200 Subject: [PATCH 31/46] Restore ConsentAgreement entity class --- gdpr_consent/gdpr_consent.module | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 7d21a44..ed5a72b 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -115,7 +115,7 @@ function gdpr_consent_entity_info() { 'label' => t('GDPR Consent Agreement'), 'base table' => 'gdpr_consent_agreement', 'revision table' => 'gdpr_consent_agreement_revision', - 'entity class' => 'Entity', + 'entity class' => 'ConsentAgreement', 'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, 'exportable' => TRUE, @@ -134,7 +134,7 @@ function gdpr_consent_entity_info() { 'label' => 'title', 'revision' => 'revision_id', ), - 'uri callback' => 'gdpr_consent_entity_uri_callback', + 'uri callback' => 'entity_class_uri', 'access callback' => 'gdpr_consent_access_callback', 'admin ui' => array( 'path' => 'admin/gdpr/agreements', @@ -496,11 +496,16 @@ function gdpr_consent_default_message_type() { return $items; } -function gdpr_consent_entity_uri_callback($entity) { - return array( - 'path' => 'admin/gdpr/agreements/' . $entity->id, - 'options' => array(), - ); +/** + * Our custom entity class. + */ +class ConsentAgreement extends Entity { + /** + * Implement a custom default URI. + */ + public function defaultUri() { + return array('path' => 'admin/gdpr/agreements/' . $this->identifier()); + } } /** From 83168ec88cdbcc73ba2afbae0b92f0ed6568e0ef Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Wed, 2 May 2018 16:24:03 +0200 Subject: [PATCH 32/46] Add log messages to revision overview page --- gdpr_consent/includes/gdpr_consent.admin.inc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gdpr_consent/includes/gdpr_consent.admin.inc b/gdpr_consent/includes/gdpr_consent.admin.inc index 1f7c2bb..9830c00 100644 --- a/gdpr_consent/includes/gdpr_consent.admin.inc +++ b/gdpr_consent/includes/gdpr_consent.admin.inc @@ -115,12 +115,14 @@ function gdpr_consent_agreement_revision_overview($agreement) { $operations = array(); if ($revision->current_revision > 0) { - $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "admin/gdpr/agreements/$agreement->id"), '!username' => theme('username', array('account' => $revision)))), + $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "admin/gdpr/agreements/$agreement->id"), '!username' => theme('username', array('account' => $revision)))) + . (($revision->log != '') ? '

' . filter_xss($revision->log) . '

' : ''), 'class' => array('revision-current')); $operations[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current'), 'colspan' => 2); } else { - $row[] = t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/view"), '!username' => theme('username', array('account' => $revision)))); + $row[] = t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/view"), '!username' => theme('username', array('account' => $revision)))) + . (($revision->log != '') ? '

' . filter_xss($revision->log) . '

' : ''); $operations[] = l(t('revert'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/revert"); $operations[] = l(t('delete'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/delete"); } @@ -147,7 +149,7 @@ function gdpr_consent_agreement_revision_overview($agreement) { */ function gdpr_consent_revision_list($agreement) { $revisions = array(); - $result = db_query('SELECT r.revision_id, a.title, r.uid, a.revision_id AS current_revision, r.timestamp, u.name FROM {gdpr_consent_agreement_revision} r LEFT JOIN {gdpr_consent_agreement} a ON a.revision_id = r.revision_id INNER JOIN {users} u ON u.uid = r.uid WHERE r.id = :id ORDER BY r.revision_id DESC', array(':id' => $agreement->id)); + $result = db_query('SELECT r.revision_id, a.title, r.log, r.uid, a.revision_id AS current_revision, r.timestamp, u.name FROM {gdpr_consent_agreement_revision} r LEFT JOIN {gdpr_consent_agreement} a ON a.revision_id = r.revision_id INNER JOIN {users} u ON u.uid = r.uid WHERE r.id = :id ORDER BY r.revision_id DESC', array(':id' => $agreement->id)); foreach ($result as $revision) { $revisions[$revision->revision_id] = $revision; } From 59b9be09ebeb4db2b68b79d227733bfdeab3769c Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Thu, 3 May 2018 00:00:24 +0200 Subject: [PATCH 33/46] Add message module as dependency to gdpr_consent --- gdpr_consent/gdpr_consent.info | 1 + 1 file changed, 1 insertion(+) diff --git a/gdpr_consent/gdpr_consent.info b/gdpr_consent/gdpr_consent.info index 6f6edcb..0d0e4be 100644 --- a/gdpr_consent/gdpr_consent.info +++ b/gdpr_consent/gdpr_consent.info @@ -3,3 +3,4 @@ description = Allow tracking of GDPR Consent core = 7.x dependencies[] = entity dependencies[] = gdpr +dependencies[] = message From b6e63af671b281966abb183fbdb813d3b6754271 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Thu, 3 May 2018 00:00:44 +0200 Subject: [PATCH 34/46] Add fields to the consent_agreement_accepted message --- gdpr_consent/gdpr_consent.install | 328 ++++++++++++++++++++++++++++++ 1 file changed, 328 insertions(+) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index dff7a84..2c8e122 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -215,3 +215,331 @@ function gdpr_consent_field_schema($field) { 'indexes' => $indexes, ); } + +/** + * Implements hook_install(). + */ +function gdpr_consent_install() { + $field_bases = array(); + + // Exported field_base: 'field_agreed'. + $field_bases['field_agreed'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'agreed', + 'indexes' => array( + 'value' => array( + 0 => 'value', + ), + ), + 'locked' => 0, + 'module' => 'list', + 'settings' => array( + 'allowed_values' => array( + 0 => 'No', + 1 => 'Yes', + ), + 'allowed_values_function' => '', + ), + 'translatable' => 0, + 'type' => 'list_boolean', + ); + // + // Exported field_base: 'field_agreement'. + $field_bases['field_agreement'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'agreement', + 'indexes' => array( + 'target_id' => array( + 0 => 'target_id', + ), + ), + 'locked' => 0, + 'module' => 'entityreference', + 'settings' => array( + 'handler' => 'base', + 'handler_settings' => array( + 'sort' => array( + 'type' => 'none', + ), + 'target_bundles' => array(), + ), + 'target_type' => 'gdpr_consent_agreement', + ), + 'translatable' => 0, + 'type' => 'entityreference', + ); + + // Exported field_base: 'field_notes'. + $field_bases['field_notes'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'notes', + 'indexes' => array( + 'format' => array( + 0 => 'format', + ), + ), + 'locked' => 0, + 'module' => 'text', + 'settings' => array(), + 'translatable' => 0, + 'type' => 'text_long', + ); + + // Exported field_base: 'field_user'. + $field_bases['field_user'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'user', + 'indexes' => array( + 'target_id' => array( + 0 => 'target_id', + ), + ), + 'locked' => 0, + 'module' => 'entityreference', + 'settings' => array( + 'handler' => 'base', + 'handler_settings' => array( + 'sort' => array( + 'type' => 'none', + ), + 'target_bundles' => array(), + ), + 'target_type' => 'user', + ), + 'translatable' => 0, + 'type' => 'entityreference', + ); + + // Exported field_base: 'field_user_accepted'. + $field_bases['field_user_accepted'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'user_accepted', + 'indexes' => array( + 'target_id' => array( + 0 => 'target_id', + ), + ), + 'locked' => 0, + 'module' => 'entityreference', + 'settings' => array( + 'handler' => 'base', + 'handler_settings' => array( + 'sort' => array( + 'type' => 'none', + ), + 'target_bundles' => array(), + ), + 'target_type' => 'node', + ), + 'translatable' => 0, + 'type' => 'entityreference', + ); + + foreach ($field_bases as $field_base) { + field_create_field($field_base); + } + + $field_instances = array(); + + // Exported field_instance: 'message-consent_agreement_accepted-field_agreed'. + $field_instances['message-consent_agreement_accepted-field_agreed'] = array( + 'bundle' => 'consent_agreement_accepted', + 'default_value' => array( + 0 => array( + 'value' => 0, + ), + ), + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'above', + 'module' => 'list', + 'settings' => array(), + 'type' => 'list_default', + 'weight' => 0, + ), + ), + 'entity_type' => 'message', + 'field_name' => 'agreed', + 'label' => 'Agreed', + 'required' => 0, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'options', + 'settings' => array( + 'display_label' => 0, + ), + 'type' => 'options_onoff', + 'weight' => 1, + ), + ); + + // Exported field_instance: + // 'message-consent_agreement_accepted-field_agreement'. + $field_instances['message-consent_agreement_accepted-field_agreement'] = array( + 'bundle' => 'consent_agreement_accepted', + 'default_value' => NULL, + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'above', + 'module' => 'entityreference', + 'settings' => array( + 'bypass_access' => FALSE, + 'link' => FALSE, + ), + 'type' => 'entityreference_label', + 'weight' => 1, + ), + ), + 'entity_type' => 'message', + 'field_name' => 'agreement', + 'label' => 'Agreement', + 'required' => 0, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'options', + 'settings' => array(), + 'type' => 'options_select', + 'weight' => 2, + ), + ); + + // Exported field_instance: 'message-consent_agreement_accepted-field_notes'. + $field_instances['message-consent_agreement_accepted-field_notes'] = array( + 'bundle' => 'consent_agreement_accepted', + 'default_value' => NULL, + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'above', + 'module' => 'text', + 'settings' => array(), + 'type' => 'text_default', + 'weight' => 2, + ), + ), + 'entity_type' => 'message', + 'field_name' => 'notes', + 'label' => 'Notes', + 'required' => 0, + 'settings' => array( + 'text_processing' => 0, + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'text', + 'settings' => array( + 'rows' => 5, + ), + 'type' => 'text_textarea', + 'weight' => 3, + ), + ); + + // Exported field_instance: 'message-consent_agreement_accepted-field_user'. + $field_instances['message-consent_agreement_accepted-field_user'] = array( + 'bundle' => 'consent_agreement_accepted', + 'default_value' => NULL, + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'above', + 'module' => 'entityreference', + 'settings' => array( + 'bypass_access' => FALSE, + 'link' => FALSE, + ), + 'type' => 'entityreference_label', + 'weight' => 3, + ), + ), + 'entity_type' => 'message', + 'field_name' => 'user', + 'label' => 'User', + 'required' => 0, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'entityreference', + 'settings' => array( + 'match_operator' => 'CONTAINS', + 'path' => '', + 'size' => 60, + ), + 'type' => 'entityreference_autocomplete', + 'weight' => 4, + ), + ); + + // Exported field_instance: + // 'message-consent_agreement_accepted-field_user_accepted'. + $field_instances['message-consent_agreement_accepted-field_user_accepted'] = array( + 'bundle' => 'consent_agreement_accepted', + 'default_value' => NULL, + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'above', + 'module' => 'entityreference', + 'settings' => array( + 'bypass_access' => FALSE, + 'link' => FALSE, + ), + 'type' => 'entityreference_label', + 'weight' => 4, + ), + ), + 'entity_type' => 'message', + 'field_name' => 'user_accepted', + 'label' => 'User Accepted', + 'required' => 0, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'entityreference', + 'settings' => array( + 'match_operator' => 'CONTAINS', + 'path' => '', + 'size' => 60, + ), + 'type' => 'entityreference_autocomplete', + 'weight' => 5, + ), + ); + + foreach ($field_instances as $field_instance) { + field_create_instance($field_instance); + } +} From 74b26d39f8edfa90cca7239911fa69d1b371de32 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Fri, 4 May 2018 00:37:18 +0200 Subject: [PATCH 35/46] Update 'user' field name to 'owner_user' Turned out that a message entity already has a $message->user property. --- gdpr_consent/gdpr_consent.install | 44 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 2c8e122..87eccc7 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -222,8 +222,8 @@ function gdpr_consent_field_schema($field) { function gdpr_consent_install() { $field_bases = array(); - // Exported field_base: 'field_agreed'. - $field_bases['field_agreed'] = array( + // Exported field_base: 'agreed'. + $field_bases['agreed'] = array( 'active' => 1, 'cardinality' => 1, 'deleted' => 0, @@ -247,8 +247,8 @@ function gdpr_consent_install() { 'type' => 'list_boolean', ); // - // Exported field_base: 'field_agreement'. - $field_bases['field_agreement'] = array( + // Exported field_base: 'agreement'. + $field_bases['agreement'] = array( 'active' => 1, 'cardinality' => 1, 'deleted' => 0, @@ -275,8 +275,8 @@ function gdpr_consent_install() { 'type' => 'entityreference', ); - // Exported field_base: 'field_notes'. - $field_bases['field_notes'] = array( + // Exported field_base: 'notes'. + $field_bases['notes'] = array( 'active' => 1, 'cardinality' => 1, 'deleted' => 0, @@ -294,13 +294,13 @@ function gdpr_consent_install() { 'type' => 'text_long', ); - // Exported field_base: 'field_user'. - $field_bases['field_user'] = array( + // Exported field_base: 'owner_user'. + $field_bases['owner_user'] = array( 'active' => 1, 'cardinality' => 1, 'deleted' => 0, 'entity_types' => array(), - 'field_name' => 'user', + 'field_name' => 'owner_user', 'indexes' => array( 'target_id' => array( 0 => 'target_id', @@ -322,8 +322,8 @@ function gdpr_consent_install() { 'type' => 'entityreference', ); - // Exported field_base: 'field_user_accepted'. - $field_bases['field_user_accepted'] = array( + // Exported field_base: 'user_accepted'. + $field_bases['user_accepted'] = array( 'active' => 1, 'cardinality' => 1, 'deleted' => 0, @@ -356,8 +356,8 @@ function gdpr_consent_install() { $field_instances = array(); - // Exported field_instance: 'message-consent_agreement_accepted-field_agreed'. - $field_instances['message-consent_agreement_accepted-field_agreed'] = array( + // Exported field_instance: 'message-consent_agreement_accepted-agreed'. + $field_instances['message-consent_agreement_accepted-agreed'] = array( 'bundle' => 'consent_agreement_accepted', 'default_value' => array( 0 => array( @@ -394,8 +394,8 @@ function gdpr_consent_install() { ); // Exported field_instance: - // 'message-consent_agreement_accepted-field_agreement'. - $field_instances['message-consent_agreement_accepted-field_agreement'] = array( + // 'message-consent_agreement_accepted-agreement'. + $field_instances['message-consent_agreement_accepted-agreement'] = array( 'bundle' => 'consent_agreement_accepted', 'default_value' => NULL, 'deleted' => 0, @@ -428,8 +428,8 @@ function gdpr_consent_install() { ), ); - // Exported field_instance: 'message-consent_agreement_accepted-field_notes'. - $field_instances['message-consent_agreement_accepted-field_notes'] = array( + // Exported field_instance: 'message-consent_agreement_accepted-notes'. + $field_instances['message-consent_agreement_accepted-notes'] = array( 'bundle' => 'consent_agreement_accepted', 'default_value' => NULL, 'deleted' => 0, @@ -462,8 +462,8 @@ function gdpr_consent_install() { ), ); - // Exported field_instance: 'message-consent_agreement_accepted-field_user'. - $field_instances['message-consent_agreement_accepted-field_user'] = array( + // Exported field_instance: 'message-consent_agreement_accepted-owner_user'. + $field_instances['message-consent_agreement_accepted-owner_user'] = array( 'bundle' => 'consent_agreement_accepted', 'default_value' => NULL, 'deleted' => 0, @@ -481,7 +481,7 @@ function gdpr_consent_install() { ), ), 'entity_type' => 'message', - 'field_name' => 'user', + 'field_name' => 'owner_user', 'label' => 'User', 'required' => 0, 'settings' => array( @@ -501,8 +501,8 @@ function gdpr_consent_install() { ); // Exported field_instance: - // 'message-consent_agreement_accepted-field_user_accepted'. - $field_instances['message-consent_agreement_accepted-field_user_accepted'] = array( + // 'message-consent_agreement_accepted-user_accepted'. + $field_instances['message-consent_agreement_accepted-user_accepted'] = array( 'bundle' => 'consent_agreement_accepted', 'default_value' => NULL, 'deleted' => 0, From dc07885e5ba4fa9a369e1344b46f891bd44a6e68 Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Fri, 4 May 2018 00:39:04 +0200 Subject: [PATCH 36/46] WIP: record a message entity on field insert --- gdpr_consent/gdpr_consent.module | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index ed5a72b..5d752e3 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -306,15 +306,22 @@ function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $ * Implements hook_field_insert(). */ function gdpr_consent_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { - $message = message_create('consent_agreement_accepted', array('uid' => $entity->uid)); - $wrapper = entity_metadata_wrapper('message', $message); + $consent_field = reset($items); - $wrapper->user->set($field['user_id_accepted']); - $wrapper->agreement->set(array($field['target_id'], $field['target_revision_id'])); - $wrapper->notes->set($field['notes']); - $wrapper->agreed->set($field['agreed']); + if (!empty($consent_field)) { + $agreement = entity_load_single('gdpr_consent_agreement', $consent_field['target_id']); + $message = message_create('consent_agreement_accepted', array('uid' => $entity->uid)); + $wrapper = entity_metadata_wrapper('message', $message); - $wrapper->save(); + $wrapper->owner_user->set($entity->uid); + $wrapper->user_accepted->set($consent_field['user_id_accepted']); + //$wrapper->agreement->set($agreement); + $wrapper->notes->set($consent_field['notes']); + $wrapper->agreed->set($consent_field['agreed']); + + $wrapper->save(); + + } } /** @@ -484,9 +491,9 @@ function gdpr_consent_default_message_type() { "arguments" : null, "message_text" : { "und" : [ { - "value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022[message:agreement:entity:url]\/revisions\/[message:agreement:target_revision_id]\/view\u0022\u003E[message:agreement:entity:title]\u003C\/a\u003E\u003Cbr \/\u003E\r\nAgreed: [message:agreed]\u003Cbr \/\u003E\r\nNotes: [message:notes]\u003C\/p\u003E", + "value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022\/admin\/gdpr\/agreements\/[message:agreement:id]\u0022\u003E[message:agreement:title]\u003C\/a\u003E\u003Cbr \/\u003E\r\nAgreed: [message:agreed]\u003Cbr \/\u003E\r\nNotes: [message:notes]\u003C\/p\u003E", "format" : "filtered_html", - "safe_value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022url]\/revisions\/[message:agreement:target_revision_id]\/view\u0022\u003E[message:agreement:entity:title]\u003C\/a\u003E\u003Cbr \/\u003E\nAgreed: [message:agreed]\u003Cbr \/\u003E\nNotes: [message:notes]\u003C\/p\u003E\n" + "safe_value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022\/admin\/gdpr\/agreements\/[message:agreement:id]\u0022\u003E[message:agreement:title]\u003C\/a\u003E\u003Cbr \/\u003E\nAgreed: [message:agreed]\u003Cbr \/\u003E\nNotes: [message:notes]\u003C\/p\u003E\n" } ] }, From 8b1e828d6c377145457809f723cfa3451884bc3a Mon Sep 17 00:00:00 2001 From: yanniboi Date: Wed, 9 May 2018 14:42:21 +0000 Subject: [PATCH 37/46] By yanniboi: Log messages when consent is granted or revoked. --- gdpr_consent/gdpr_consent.info | 2 + gdpr_consent/gdpr_consent.install | 18 +++-- gdpr_consent/gdpr_consent.module | 106 +++++++++++++++++++++++++----- 3 files changed, 103 insertions(+), 23 deletions(-) diff --git a/gdpr_consent/gdpr_consent.info b/gdpr_consent/gdpr_consent.info index 0d0e4be..5388034 100644 --- a/gdpr_consent/gdpr_consent.info +++ b/gdpr_consent/gdpr_consent.info @@ -4,3 +4,5 @@ core = 7.x dependencies[] = entity dependencies[] = gdpr dependencies[] = message +dependencies[] = entityreference +dependencies[] = entity_token diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 87eccc7..6729b80 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -351,14 +351,15 @@ function gdpr_consent_install() { ); foreach ($field_bases as $field_base) { - field_create_field($field_base); + if (!field_info_field($field_base['field_name'])) { + field_create_field($field_base); + } } $field_instances = array(); // Exported field_instance: 'message-consent_agreement_accepted-agreed'. $field_instances['message-consent_agreement_accepted-agreed'] = array( - 'bundle' => 'consent_agreement_accepted', 'default_value' => array( 0 => array( 'value' => 0, @@ -396,7 +397,6 @@ function gdpr_consent_install() { // Exported field_instance: // 'message-consent_agreement_accepted-agreement'. $field_instances['message-consent_agreement_accepted-agreement'] = array( - 'bundle' => 'consent_agreement_accepted', 'default_value' => NULL, 'deleted' => 0, 'description' => '', @@ -430,7 +430,6 @@ function gdpr_consent_install() { // Exported field_instance: 'message-consent_agreement_accepted-notes'. $field_instances['message-consent_agreement_accepted-notes'] = array( - 'bundle' => 'consent_agreement_accepted', 'default_value' => NULL, 'deleted' => 0, 'description' => '', @@ -464,7 +463,6 @@ function gdpr_consent_install() { // Exported field_instance: 'message-consent_agreement_accepted-owner_user'. $field_instances['message-consent_agreement_accepted-owner_user'] = array( - 'bundle' => 'consent_agreement_accepted', 'default_value' => NULL, 'deleted' => 0, 'description' => '', @@ -503,7 +501,6 @@ function gdpr_consent_install() { // Exported field_instance: // 'message-consent_agreement_accepted-user_accepted'. $field_instances['message-consent_agreement_accepted-user_accepted'] = array( - 'bundle' => 'consent_agreement_accepted', 'default_value' => NULL, 'deleted' => 0, 'description' => '', @@ -539,7 +536,14 @@ function gdpr_consent_install() { ), ); + $bundles = array('consent_agreement_accepted', 'consent_agreement_revoked'); + foreach ($field_instances as $field_instance) { - field_create_instance($field_instance); + foreach ($bundles as $bundle) { + if (!field_info_instance($field_instance['entity_type'], $field_instance['field_name'], $bundle)) { + $field_instance['bundle'] = $bundle; + field_create_instance($field_instance); + } + } } } diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 5d752e3..5fda956 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -303,25 +303,69 @@ function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $ } /** - * Implements hook_field_insert(). + * Implements hook_field_attach_submit(). + * + * If consent field changes grant/revoke consent and log audit trail. */ -function gdpr_consent_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { - $consent_field = reset($items); +function gdpr_consent_field_attach_submit($entity_type, $entity, $form, &$form_state) { + list($id, , $bundle) = entity_extract_ids($entity_type, $entity); + $fields = gdpr_consent_get_consent_fields($entity_type, $bundle); + + $original = entity_load_unchanged($entity_type, $id); + foreach ($fields as $field) { + if (!empty($values = $form_state['values'][$field])) { + $new = isset($values[LANGUAGE_NONE][0]['agreed']) ? (int) $values[LANGUAGE_NONE][0]['agreed'] : NULL; + $old = isset($original->{$field}[LANGUAGE_NONE][0]['agreed']) ? (int) $original->{$field}[LANGUAGE_NONE][0]['agreed'] : NULL; + + if ($new !== $old) { + global $user; + $agreement = entity_load_single('gdpr_consent_agreement', $values[LANGUAGE_NONE][0]['target_id']); + + if ($new) { + $message = message_create('consent_agreement_accepted', array('uid' => $user->uid)); + } + else { + $message = message_create('consent_agreement_revoked', array('uid' => $user->uid)); + } - if (!empty($consent_field)) { - $agreement = entity_load_single('gdpr_consent_agreement', $consent_field['target_id']); - $message = message_create('consent_agreement_accepted', array('uid' => $entity->uid)); - $wrapper = entity_metadata_wrapper('message', $message); + $wrapper = entity_metadata_wrapper('message', $message); - $wrapper->owner_user->set($entity->uid); - $wrapper->user_accepted->set($consent_field['user_id_accepted']); - //$wrapper->agreement->set($agreement); - $wrapper->notes->set($consent_field['notes']); - $wrapper->agreed->set($consent_field['agreed']); + $wrapper->owner_user->set($user->uid); + $wrapper->user_accepted->set($values[LANGUAGE_NONE][0]['user_id_accepted']); + $wrapper->agreement->set($agreement); + $wrapper->notes->set($values[LANGUAGE_NONE][0]['notes']); + $wrapper->agreed->set($values[LANGUAGE_NONE][0]['agreed']); + $wrapper->save(); + } + } + } +} - $wrapper->save(); +/** + * Fetches a list of consent fields for an entity. + * + * @param string $entity_type + * The entity type to get fields for. + * @param string|null $bundle + * (Optional) the bundle to get fields for. If none provided, all bundles + * will be checked. + * + * @return array + * List of consent field names. + */ +function gdpr_consent_get_consent_fields($entity_type, $bundle = NULL) { + $instances = field_info_instances($entity_type, $bundle); + $fields = array(); + + foreach ($instances as $field_name => $instance) { + $field = field_info_field($field_name); + if ($field['type'] == 'gdpr_user_consent') { + $fields[] = $field_name; + } } + + return $fields; } /** @@ -479,7 +523,30 @@ function gdpr_consent_default_message_type() { $items['consent_agreement_accepted'] = entity_import('message_type', '{ "name" : "consent_agreement_accepted", - "description" : "GDPR Consent Agreement", + "description" : "GDPR Consent Agreement accepted", + "argument_keys" : [], + "argument" : [], + "category" : "message_type", + "data" : { + "token options" : { "clear" : 0 }, + "purge" : { "override" : 1, "enabled" : 1, "quota" : "1000", "days" : "30" } + }, + "language" : "", + "arguments" : null, + "message_text" : { "und" : [ + { + "value" : "\u003Cp\u003EAccepted Agreement: \u003Ca href=\u0022\/admin\/gdpr\/agreements\/[message:agreement:id]\u0022\u003E[message:agreement:title]\u003C\/a\u003E\u003Cbr \/\u003E\r\nAgreed: [message:agreed]\u003Cbr \/\u003E\r\nNotes: [message:notes]\u003C\/p\u003E", + "format" : "filtered_html", + "safe_value" : "\u003Cp\u003Eu003EAccepted Agreement: \u003Ca href=\u0022\/admin\/gdpr\/agreements\/[message:agreement:id]\u0022\u003E[message:agreement:title]\u003C\/a\u003E\u003Cbr \/\u003E\nAgreed: [message:agreed]\u003Cbr \/\u003E\nNotes: [message:notes]\u003C\/p\u003E\n" + } + ] + }, + "rdf_mapping" : [] + }'); + + $items['consent_agreement_revoked'] = entity_import('message_type', '{ + "name" : "consent_agreement_revoked", + "description" : "GDPR Consent Agreement revoked", "argument_keys" : [], "argument" : [], "category" : "message_type", @@ -491,9 +558,9 @@ function gdpr_consent_default_message_type() { "arguments" : null, "message_text" : { "und" : [ { - "value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022\/admin\/gdpr\/agreements\/[message:agreement:id]\u0022\u003E[message:agreement:title]\u003C\/a\u003E\u003Cbr \/\u003E\r\nAgreed: [message:agreed]\u003Cbr \/\u003E\r\nNotes: [message:notes]\u003C\/p\u003E", + "value" : "\u003Cp\u003ERevoked Agreement: \u003Ca href=\u0022\/admin\/gdpr\/agreements\/[message:agreement:id]\u0022\u003E[message:agreement:title]\u003C\/a\u003E\u003Cbr \/\u003E\r\nAgreed: [message:agreed]\u003Cbr \/\u003E\r\nNotes: [message:notes]\u003C\/p\u003E", "format" : "filtered_html", - "safe_value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022\/admin\/gdpr\/agreements\/[message:agreement:id]\u0022\u003E[message:agreement:title]\u003C\/a\u003E\u003Cbr \/\u003E\nAgreed: [message:agreed]\u003Cbr \/\u003E\nNotes: [message:notes]\u003C\/p\u003E\n" + "safe_value" : "\u003Cp\u003ERevoked Agreement: \u003Ca href=\u0022\/admin\/gdpr\/agreements\/[message:agreement:id]\u0022\u003E[message:agreement:title]\u003C\/a\u003E\u003Cbr \/\u003E\nAgreed: [message:agreed]\u003Cbr \/\u003E\nNotes: [message:notes]\u003C\/p\u003E\n" } ] }, @@ -513,6 +580,13 @@ class ConsentAgreement extends Entity { public function defaultUri() { return array('path' => 'admin/gdpr/agreements/' . $this->identifier()); } + + /** + * {@inheritdoc} + */ + public function identifier() { + return $this->internalIdentifier(); + } } /** From ccf2a48101526a4eedfa4489a8d058793487f85c Mon Sep 17 00:00:00 2001 From: Erno Zsemlye Date: Fri, 11 May 2018 23:18:21 +0200 Subject: [PATCH 38/46] Handle logging for newly created entities --- gdpr_consent/gdpr_consent.module | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 5fda956..585f515 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -308,14 +308,30 @@ function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $ * If consent field changes grant/revoke consent and log audit trail. */ function gdpr_consent_field_attach_submit($entity_type, $entity, $form, &$form_state) { + $is_new = FALSE; + list($id, , $bundle) = entity_extract_ids($entity_type, $entity); + // If $entity does not have an id that means it's new. + if (!isset($id)) { + $is_new = TRUE; + } $fields = gdpr_consent_get_consent_fields($entity_type, $bundle); - $original = entity_load_unchanged($entity_type, $id); + if (!$is_new) { + $original = entity_load_unchanged($entity_type, $id); + } + foreach ($fields as $field) { if (!empty($values = $form_state['values'][$field])) { $new = isset($values[LANGUAGE_NONE][0]['agreed']) ? (int) $values[LANGUAGE_NONE][0]['agreed'] : NULL; - $old = isset($original->{$field}[LANGUAGE_NONE][0]['agreed']) ? (int) $original->{$field}[LANGUAGE_NONE][0]['agreed'] : NULL; + + // Newly created entities should only log accepted agreements. + if ($is_new) { + $old = 0; + } + else { + $old = isset($original->{$field}[LANGUAGE_NONE][0]['agreed']) ? (int) $original->{$field}[LANGUAGE_NONE][0]['agreed'] : NULL; + } if ($new !== $old) { global $user; From 69ee952d9965edcafe0f85877af8bb30ff29e186 Mon Sep 17 00:00:00 2001 From: Rob Mumford Date: Tue, 15 May 2018 12:15:41 +0100 Subject: [PATCH 39/46] by rlmumford: Split consent uid field into author_uid and revision_uid. --- gdpr_consent/gdpr_consent.install | 52 +++++++++++++++++++++++++------ gdpr_consent/gdpr_consent.module | 15 +++++++-- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 6729b80..3134d81 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -52,12 +52,19 @@ function gdpr_consent_schema() { 'default' => 0, 'description' => 'The Unix timestamp the entity was last edited.', ), - 'uid' => array( + 'author_uid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => NULL, - 'description' => "The {users}.uid of the associated user.", + 'description' => "The {users}.uid of the consent author.", + ), + 'agreement_type' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + 'description' => 'Consent agreement\'s type: implicit or explicit.', ), 'status' => array( 'type' => 'int', @@ -74,15 +81,19 @@ function gdpr_consent_schema() { ), ), 'foreign keys' => array( - 'uid' => array( + 'author_uid' => array( 'table' => 'users', - 'columns' => array('uid' => 'uid'), + 'columns' => array('author_uid' => 'uid'), ), ), 'primary key' => array('id'), - 'unique key' => array('name'), - 'indexes' => array( + 'unique keys' => array( 'name' => array('name'), + 'revision_id' => array('revision_id'), + ), + 'indexes' => array( + 'author_uid' => array('author_uid'), + 'agreement_type' => array('agreement_type'), ), ); @@ -94,7 +105,7 @@ function gdpr_consent_schema() { 'unsigned' => TRUE, 'not null' => FALSE, 'default' => NULL, - 'description' => 'The ID of the attached entity.', + 'description' => 'The ID of the GDPR Consent Agreement entity.', ), 'revision_id' => array( 'type' => 'serial', @@ -114,12 +125,19 @@ function gdpr_consent_schema() { 'default' => 0, 'description' => 'A Unix timestamp indicating when this version was created.', ), - 'uid' => array( + 'author_uid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => NULL, - 'description' => "The {users}.uid of the associated user.", + 'description' => "The {users}.uid of the consent author.", + ), + 'revision_uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => "The {users}.uid of the revising user.", ), 'description' => array( 'type' => 'varchar', @@ -155,6 +173,22 @@ function gdpr_consent_schema() { ), ), 'primary key' => array('revision_id'), + 'indexes' => array( + 'id' => array('id'), + 'revision_uid' => array('revision_uid'), + 'author_uid' => array('author_uid'), + 'agreement_type' => array('agreement_type'), + ), + 'foreign keys' => array( + 'author_uid' => array( + 'table' => 'users', + 'columns' => array('author_uid' => 'uid'), + ), + 'revision_uid' => array( + 'table' => 'users', + 'columns' => array('revision_uid' => 'uid'), + ), + ), ); return $schema; diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 585f515..6e2cb1f 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -211,13 +211,21 @@ function gdpr_consent_entity_property_info() { 'schema field' => 'changed', ); - $properties['uid'] = array( + $properties['author_uid'] = array( 'label' => t('Authored by'), 'description' => t('The user ID of author of the Consent Agreement entity'), 'type' => 'user', - 'schema field' => 'uid', + 'schema field' => 'author_uid', ); + $properties['revision_uid'] = array( + 'label' => t('Revised by'), + 'description' => t('The user ID of author of the Consent Agreement revision'), + 'type' => 'user', + 'schema field' => 'revision_uid', + ); + + $properties['status'] = array( 'label' => t('Publishing status'), 'description' => t('A boolean indicating whether the Consent Agreement is published.'), @@ -618,7 +626,7 @@ class ConsentAgreementController extends EntityAPIControllerExportable { global $user; $entity->created = REQUEST_TIME; - $entity->uid = $user->uid; + $entity->author_uid = $user->uid; } $entity->changed = REQUEST_TIME; @@ -626,6 +634,7 @@ class ConsentAgreementController extends EntityAPIControllerExportable { // Always save new revisions. $entity->is_new_revision = TRUE; $entity->timestamp = REQUEST_TIME; + $entity->revision_uid = $user->uid; return parent::save($entity, $transaction); } From f881de028af718a27d93b6727e984a477f8b923e Mon Sep 17 00:00:00 2001 From: Rob Mumford Date: Tue, 15 May 2018 12:36:35 +0100 Subject: [PATCH 40/46] by rlmumford: Indexes and foreign keys for field table. --- gdpr_consent/gdpr_consent.install | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 3134d81..90211ef 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -243,10 +243,31 @@ function gdpr_consent_field_schema($field) { $indexes = array( 'target_id' => array('target_id'), 'target_revision_id' => array('target_revision_id'), + 'agreed' => array('agreed' => 'agreed'), + 'user_id' => array('user_id' => 'user_id'), + 'user_id_accepted' => array('user_id_accepted' => 'user_id_accepted'), ); return array( 'columns' => $columns, 'indexes' => $indexes, + 'foreign keys' => array( + 'target_id' => array( + 'table' => 'gdpr_consent_agreement', + 'columns' => array('target_id' => 'id'), + ), + 'target_revision_id' => array( + 'table' => 'gdpr_consent_agreement_revision', + 'columns' => array('target_revision_id' => 'revision_id'), + ), + 'user_id' => array( + 'table' => 'users', + 'columns' => array('user_id' => 'uid'), + ), + 'user_id_accepted' => array( + 'table' => 'users', + 'columns' => array('user_id_accepted' => 'uid'), + ), + ), ); } From e0288c980affc799e0e57d509dce8066f05962fe Mon Sep 17 00:00:00 2001 From: Rob Mumford Date: Tue, 15 May 2018 12:37:41 +0100 Subject: [PATCH 41/46] by rlmumford: Don't expose consent entity to exportable entity problems. --- gdpr_consent/gdpr_consent.module | 1 - 1 file changed, 1 deletion(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 6e2cb1f..2296bca 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -118,7 +118,6 @@ function gdpr_consent_entity_info() { 'entity class' => 'ConsentAgreement', 'controller class' => 'ConsentAgreementController', 'fieldable' => TRUE, - 'exportable' => TRUE, 'bundles' => array( 'gdpr_consent_agreement' => array( 'label' => t('GDPR Consent Agreement'), From 3f514c807d94f88d3ed6afbad91ee3e009f3ab03 Mon Sep 17 00:00:00 2001 From: Rob Mumford Date: Tue, 15 May 2018 12:39:55 +0100 Subject: [PATCH 42/46] by rlmumford: Fix dodgy use of empty. --- gdpr_consent/gdpr_consent.module | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 2296bca..3361738 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -329,7 +329,8 @@ function gdpr_consent_field_attach_submit($entity_type, $entity, $form, &$form_s } foreach ($fields as $field) { - if (!empty($values = $form_state['values'][$field])) { + if (!empty($form_state['values'][$field])) { + $values = $form_state['values'][$field]; $new = isset($values[LANGUAGE_NONE][0]['agreed']) ? (int) $values[LANGUAGE_NONE][0]['agreed'] : NULL; // Newly created entities should only log accepted agreements. From bbc344825356b01f28abdc49a2e03dfe82c1ba81 Mon Sep 17 00:00:00 2001 From: Rob Mumford Date: Tue, 15 May 2018 15:06:04 +0100 Subject: [PATCH 43/46] by rlmumford: Consent field property info. --- gdpr_consent/gdpr_consent.install | 2 + gdpr_consent/gdpr_consent.module | 77 +++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index 90211ef..fd35094 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -227,12 +227,14 @@ function gdpr_consent_field_schema($field) { 'unsigned' => TRUE, 'not null' => FALSE, 'default' => NULL, + 'description' => 'The user uid of the user giving consent', ), 'user_id_accepted' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => NULL, + 'description' => 'The user uid of the user recording the consent', ), 'notes' => array( 'type' => 'text', diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 3361738..9becfcf 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -287,10 +287,87 @@ function gdpr_consent_field_info() { 'description' => t('Stores user consent for a particular agreement'), 'default_widget' => 'gdpr_consent_widget', 'default_formatter' => 'gdpr_consent_formatter', + 'property_type' => 'struct', + 'property_callbacks' => array( + 'gdpr_consent_field_gdpr_user_consent_property_callback', + ), ), ); } +/** + * Property info for gdpr consent fields. + */ +function gdpr_consent_field_gdpr_user_consent_property_callback(&$info, $entity_type, $field, $instance, $field_type) { + $property = &$info[$entity_type]['bundles'][$instance['bundle']['properties'][$field['field_name']]; + + $property['getter callback'] = 'entity_metadata_field_verbatim_get'; + $property['setter callback'] = 'entity_metadata_field_verbatim_set'; + $property['auto creation'] = 'entity_property_create_array'; + $property['type'] = ($field['cardinality'] != 1) ? 'list' : 'struct'; + + $property['property info']['target_id'] = array( + 'label' => 'Agreement ID', + 'type' => 'int', + 'getter callback' => 'entity_metadata_verbatim_get', + 'schema field' => 'target_id', + ); + $property['property info']['target_revision_id'] = array( + 'label' => 'Agreement Revision ID', + 'type' => 'int', + 'getter callback' => 'entity_metadata_verbatim_get', + 'schema field' => 'target_revision_id', + ); + $property['property info']['entity'] = array( + 'label' => 'Consent Agreement', + 'type' => 'gdpr_consent_agreement', + 'getter callback' => 'gdpr_consent_field_property_entity_get', + ); + $property['property info']['date'] = array( + 'label' => 'Date Agreed', + 'type' => 'date', + 'getter callback' = 'entity_metadata_verbatim_get', + 'schema field' => 'date', + ); + $property['property info']['agreed'] = array( + 'label' => 'Agreed', + 'type' => 'boolean', + 'getter callback' => 'entity_metadata_verbatim_get', + 'schema field' => 'agreed', + ); + $property['property info']['user_id'] = array( + 'label' => 'The Consenting User', + 'type' => 'user', + 'getter callback' => 'entity_metadata_verbatim_get', + 'schema field' => 'user_id', + ); + $property['property info']['user_id_accepted'] = array( + 'label' => 'The User recording the Consent', + 'type' => 'user', + 'getter callback' => 'entity_metadata_verbatim_get', + 'schema field' => 'user_id_accepted', + ); + $property['property info']['notes'] = array( + 'label' => 'Notes', + 'type' => 'text', + 'getter callback' => 'entity_metadata_verbatim_get', + 'schema field' => 'notes', + ); +} + +/** + * Getter callback for the consent agreement entity. + */ +function gdpr_consent_field_property_entity_get($data, array $options, $name, $type, $info) { + if ( + !empty($data['target_id']) + && !empty($data['target_revision_id']) + && ($consent = entity_revision_load('gdpr_consent_agreement', $data['target_revision_id'])) + ) { + return $consent; + } +} + /** * Implements hook_field_validate(). */ From ef08d5a1958406ea0b3027b29c2bf08c8a0fa789 Mon Sep 17 00:00:00 2001 From: Rob Mumford Date: Tue, 15 May 2018 15:12:48 +0100 Subject: [PATCH 44/46] by rlmumford: Improve display. --- gdpr_consent/gdpr_consent.install | 2 +- gdpr_consent/gdpr_consent.module | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index fd35094..d04df83 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -64,7 +64,7 @@ function gdpr_consent_schema() { 'not null' => TRUE, 'default' => 0, 'size' => 'tiny', - 'description' => 'Consent agreement\'s type: implicit or explicit.', + 'description' => 'Consent agreement\'s type: implicit (0) or explicit (1).', ), 'status' => array( 'type' => 'int', diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 9becfcf..1e3ed25 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -746,22 +746,22 @@ class ConsentAgreementEntityUIController extends EntityDefaultUIController { $build['description'] = array( '#type' => 'markup', - '#markup' => $entity->description, + '#markup' => check_plain($entity->description), ); $build['long_description'] = array( '#type' => 'markup', - '#markup' => $entity->long_description, + '#markup' => check_plain($entity->long_description), ); $build['notes'] = array( '#type' => 'markup', - '#markup' => $entity->notes, + '#markup' => check_plain($entity->notes), ); $build['agreement_type'] = array( '#type' => 'markup', - '#markup' => $entity->agreement_type, + '#markup' => !empty($entity->agreement_type) ? t('Explicit') : t('Implicit'), ); return $build; From 7e6af6cddb2090f756d3b9928fb6106c96b9c174 Mon Sep 17 00:00:00 2001 From: Rob Mumford Date: Tue, 15 May 2018 15:15:54 +0100 Subject: [PATCH 45/46] by rlmumford: Added todo to fix validate function. --- gdpr_consent/gdpr_consent.module | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index 1e3ed25..e0bb563 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -370,6 +370,8 @@ function gdpr_consent_field_property_entity_get($data, array $options, $name, $t /** * Implements hook_field_validate(). + * + * @TODO: This validate function isn't doing anything. Please fix. */ function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { $valid = TRUE; From f9fdd619d80d1ad9a19104ecb11474fdedbdbdc5 Mon Sep 17 00:00:00 2001 From: Rob Mumford Date: Tue, 15 May 2018 17:27:36 +0100 Subject: [PATCH 46/46] by rlmumford: Rename consenter and recorder fields to be more clear. --- gdpr_consent/gdpr_consent.install | 16 ++++++++-------- gdpr_consent/gdpr_consent.module | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/gdpr_consent/gdpr_consent.install b/gdpr_consent/gdpr_consent.install index d04df83..4638757 100644 --- a/gdpr_consent/gdpr_consent.install +++ b/gdpr_consent/gdpr_consent.install @@ -222,14 +222,14 @@ function gdpr_consent_field_schema($field) { 'not null' => TRUE, 'default' => 0, ), - 'user_id' => array( + 'consenter' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => NULL, 'description' => 'The user uid of the user giving consent', ), - 'user_id_accepted' => array( + 'recorder' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, @@ -246,8 +246,8 @@ function gdpr_consent_field_schema($field) { 'target_id' => array('target_id'), 'target_revision_id' => array('target_revision_id'), 'agreed' => array('agreed' => 'agreed'), - 'user_id' => array('user_id' => 'user_id'), - 'user_id_accepted' => array('user_id_accepted' => 'user_id_accepted'), + 'consenter' => array('consenter' => 'consenter'), + 'recorder' => array('recorder' => 'recorder'), ); return array( 'columns' => $columns, @@ -261,13 +261,13 @@ function gdpr_consent_field_schema($field) { 'table' => 'gdpr_consent_agreement_revision', 'columns' => array('target_revision_id' => 'revision_id'), ), - 'user_id' => array( + 'consenter' => array( 'table' => 'users', - 'columns' => array('user_id' => 'uid'), + 'columns' => array('consenter' => 'uid'), ), - 'user_id_accepted' => array( + 'recorder' => array( 'table' => 'users', - 'columns' => array('user_id_accepted' => 'uid'), + 'columns' => array('recorder' => 'uid'), ), ), ); diff --git a/gdpr_consent/gdpr_consent.module b/gdpr_consent/gdpr_consent.module index e0bb563..5065c66 100644 --- a/gdpr_consent/gdpr_consent.module +++ b/gdpr_consent/gdpr_consent.module @@ -335,17 +335,17 @@ function gdpr_consent_field_gdpr_user_consent_property_callback(&$info, $entity_ 'getter callback' => 'entity_metadata_verbatim_get', 'schema field' => 'agreed', ); - $property['property info']['user_id'] = array( + $property['property info']['consenter'] = array( 'label' => 'The Consenting User', 'type' => 'user', 'getter callback' => 'entity_metadata_verbatim_get', - 'schema field' => 'user_id', + 'schema field' => 'consenter', ); - $property['property info']['user_id_accepted'] = array( + $property['property info']['recorder'] = array( 'label' => 'The User recording the Consent', 'type' => 'user', 'getter callback' => 'entity_metadata_verbatim_get', - 'schema field' => 'user_id_accepted', + 'schema field' => 'recorder', ); $property['property info']['notes'] = array( 'label' => 'Notes', @@ -434,7 +434,7 @@ function gdpr_consent_field_attach_submit($entity_type, $entity, $form, &$form_s $wrapper = entity_metadata_wrapper('message', $message); $wrapper->owner_user->set($user->uid); - $wrapper->user_accepted->set($values[LANGUAGE_NONE][0]['user_id_accepted']); + $wrapper->user_accepted->set($values[LANGUAGE_NONE][0]['recorder']); $wrapper->agreement->set($agreement); $wrapper->notes->set($values[LANGUAGE_NONE][0]['notes']); $wrapper->agreed->set($values[LANGUAGE_NONE][0]['agreed']); @@ -607,7 +607,7 @@ function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, '#type' => 'hidden', '#value' => REQUEST_TIME, ) + $widget; - $element['user_id_accepted'] = array( + $element['recorder'] = array( '#type' => 'hidden', '#value' => $user->uid, ) + $widget;