From 3351939bac4623465a7c16360617a9842ef2ac2b Mon Sep 17 00:00:00 2001 From: yanniboi Date: Wed, 21 Feb 2018 14:23:28 +0000 Subject: [PATCH 1/3] Started working on GDPR fields contrib API. --- .../config/schema/gdpr_field.schema.yml | 7 ++ modules/gdpr_fields/gdpr_fields.info.yml | 8 +++ modules/gdpr_fields/gdpr_fields.module | 71 +++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 modules/gdpr_fields/config/schema/gdpr_field.schema.yml create mode 100644 modules/gdpr_fields/gdpr_fields.info.yml create mode 100644 modules/gdpr_fields/gdpr_fields.module diff --git a/modules/gdpr_fields/config/schema/gdpr_field.schema.yml b/modules/gdpr_fields/config/schema/gdpr_field.schema.yml new file mode 100644 index 0000000..02479d9 --- /dev/null +++ b/modules/gdpr_fields/config/schema/gdpr_field.schema.yml @@ -0,0 +1,7 @@ +field.field.*.*.*.third_party.gdpr_fields: + type: mapping + label: 'GDPR field' + mapping: + gdpr_fields_settings: + type: boolean + label: 'Whether this field is user sensitive or not.' diff --git a/modules/gdpr_fields/gdpr_fields.info.yml b/modules/gdpr_fields/gdpr_fields.info.yml new file mode 100644 index 0000000..8ce156e --- /dev/null +++ b/modules/gdpr_fields/gdpr_fields.info.yml @@ -0,0 +1,8 @@ +name: General Data Protection Regulation (GDPR) - Fields +description: Allow tracking and removing of user data in all fields. +core: 8.x +type: module +package: General Data Protection Regulation + +dependencies: + - gdpr:gdpr diff --git a/modules/gdpr_fields/gdpr_fields.module b/modules/gdpr_fields/gdpr_fields.module new file mode 100644 index 0000000..00d17a3 --- /dev/null +++ b/modules/gdpr_fields/gdpr_fields.module @@ -0,0 +1,71 @@ +getThirdPartySetting('gdpr_fields', 'gdpr_fields_settings', FALSE); + if ($is_private) { + // @todo Do access check based on field settings. + return AccessResult::forbiddenIf(!$account->hasPermission('administer gdpr fields')); + } + } + } + return AccessResult::neutral(); +} + +/** + * Implements hook_form_FORM_ID_alter(). + */ +function gdpr_fields_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) { + /* @var \Drupal\Core\Field\FieldConfigInterface $field */ + $field = $form_state->getFormObject()->getEntity(); + // @todo Check that target entity is a content entity. + + $form['field']['gdpr_fields']['gdpr_fields_settings'] = [ + '#type' => 'checkbox', + '#title' => t('This is a GDPR field.'), + '#default_value' => $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_settings', FALSE), + ]; + $form['actions']['submit']['#submit'][] = 'gdpr_fields_form_field_config_edit_form_submit'; +} + +/** + * Form submission handler for gdpr_fields_form_field_config_edit_form_alter. + * + * @param array $form + * The form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + */ +function gdpr_fields_form_field_config_edit_form_submit(array $form, FormStateInterface $form_state) { + /* @var \Drupal\Core\Field\FieldConfigInterface $field */ + $field = $form_state->getFormObject()->getEntity(); + $form_fields = &$form_state->getValues(); + + // If the private option is checked, update settings. + if ($form_fields['gdpr_fields_settings']) { + $field->setThirdPartySetting('gdpr_fields', 'gdpr_fields_settings', TRUE); + $field->save(); + } + else { + $field->unsetThirdPartySetting('gdpr_fields', 'gdpr_fields_settings'); + $field->save(); + } + +} From 0d26e3adbd488222429fc3b046088525cfa2b159 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 2 Mar 2018 08:44:38 +0000 Subject: [PATCH 2/3] Field table showing GDPR fields for all user related entity types. --- .../config/schema/gdpr_field.schema.yml | 7 +- modules/gdpr_fields/gdpr_fields.info.yml | 1 + .../gdpr_fields/gdpr_fields.links.task.yml | 4 + modules/gdpr_fields/gdpr_fields.module | 63 +++--- .../gdpr_fields/gdpr_fields.permissions.yml | 5 + modules/gdpr_fields/gdpr_fields.routing.yml | 7 + modules/gdpr_fields/gdpr_fields.services.yml | 4 + .../src/Controller/GDPRController.php | 110 +++++++++++ modules/gdpr_fields/src/GDPRCollector.php | 179 ++++++++++++++++++ ...edDataEntityRelationshipReverseDeriver.php | 41 ++++ .../TypedDataEntityRelationshipReverse.php | 53 ++++++ 11 files changed, 444 insertions(+), 30 deletions(-) create mode 100644 modules/gdpr_fields/gdpr_fields.links.task.yml create mode 100644 modules/gdpr_fields/gdpr_fields.permissions.yml create mode 100644 modules/gdpr_fields/gdpr_fields.routing.yml create mode 100644 modules/gdpr_fields/gdpr_fields.services.yml create mode 100644 modules/gdpr_fields/src/Controller/GDPRController.php create mode 100644 modules/gdpr_fields/src/GDPRCollector.php create mode 100644 modules/gdpr_fields/src/Plugin/Deriver/TypedDataEntityRelationshipReverseDeriver.php create mode 100644 modules/gdpr_fields/src/Plugin/Relationship/TypedDataEntityRelationshipReverse.php diff --git a/modules/gdpr_fields/config/schema/gdpr_field.schema.yml b/modules/gdpr_fields/config/schema/gdpr_field.schema.yml index 02479d9..fca48dd 100644 --- a/modules/gdpr_fields/config/schema/gdpr_field.schema.yml +++ b/modules/gdpr_fields/config/schema/gdpr_field.schema.yml @@ -2,6 +2,9 @@ field.field.*.*.*.third_party.gdpr_fields: type: mapping label: 'GDPR field' mapping: - gdpr_fields_settings: - type: boolean + gdpr_fields_rta: + type: string label: 'Whether this field is user sensitive or not.' + gdpr_fields_rtf: + type: string + label: 'Whether this field is user sensitive or not.' \ No newline at end of file diff --git a/modules/gdpr_fields/gdpr_fields.info.yml b/modules/gdpr_fields/gdpr_fields.info.yml index 8ce156e..8b9bdd7 100644 --- a/modules/gdpr_fields/gdpr_fields.info.yml +++ b/modules/gdpr_fields/gdpr_fields.info.yml @@ -6,3 +6,4 @@ package: General Data Protection Regulation dependencies: - gdpr:gdpr + - ctools:ctools (>=8.x-3.0-beta1) diff --git a/modules/gdpr_fields/gdpr_fields.links.task.yml b/modules/gdpr_fields/gdpr_fields.links.task.yml new file mode 100644 index 0000000..3787fc9 --- /dev/null +++ b/modules/gdpr_fields/gdpr_fields.links.task.yml @@ -0,0 +1,4 @@ +gdpr_fields.fields_list: + route_name: gdpr_fields.fields_list + title: 'Used in GDPR' + base_route: entity.field_storage_config.collection diff --git a/modules/gdpr_fields/gdpr_fields.module b/modules/gdpr_fields/gdpr_fields.module index 00d17a3..5579d33 100644 --- a/modules/gdpr_fields/gdpr_fields.module +++ b/modules/gdpr_fields/gdpr_fields.module @@ -6,41 +6,44 @@ */ use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Field\FieldDefinitionInterface; -use Drupal\field\FieldConfigInterface; -use Drupal\Core\Access\AccessResult; -use Drupal\Core\Session\AccountInterface; -use Drupal\Core\Field\FieldItemListInterface; - -/** - * Implements hook_entity_field_access(). - */ -function gdpr_fields_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) { - // @todo Check that target entity is a content entity. - if ($operation == 'view' && $items) { - if ($field_definition instanceof FieldConfigInterface) { - $is_private = $field_definition->getThirdPartySetting('gdpr_fields', 'gdpr_fields_settings', FALSE); - if ($is_private) { - // @todo Do access check based on field settings. - return AccessResult::forbiddenIf(!$account->hasPermission('administer gdpr fields')); - } - } - } - return AccessResult::neutral(); -} /** * Implements hook_form_FORM_ID_alter(). + * + * @todo Find a better way of editing field GDPR settings as lots of fields are not UI configurable. */ function gdpr_fields_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) { /* @var \Drupal\Core\Field\FieldConfigInterface $field */ $field = $form_state->getFormObject()->getEntity(); // @todo Check that target entity is a content entity. - $form['field']['gdpr_fields']['gdpr_fields_settings'] = [ + $form['field']['gdpr_fields']['gdpr_fields_enabled'] = [ '#type' => 'checkbox', - '#title' => t('This is a GDPR field.'), - '#default_value' => $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_settings', FALSE), + '#title' => t('This is a GDPR field'), + '#default_value' => $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_enabled', FALSE), + ]; + + $form['field']['gdpr_fields']['gdpr_fields_rta'] = [ + '#type' => 'select', + '#title' => t('Right to access'), + '#options' => [ + 'inc' => 'Included', + 'maybe' => 'Maybe', + 'no' => 'Not', + ], + '#default_value' => $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_rta', 'no'), + ]; + + $form['field']['gdpr_fields']['gdpr_fields_rtf'] = [ + '#type' => 'select', + '#title' => t('Right to be forgotten'), + '#options' => [ + 'obfuscate' => 'Obfuscate', + 'remove' => 'Remove', + 'maybe' => 'Maybe', + 'no' => 'Not', + ], + '#default_value' => $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_rtf', 'no'), ]; $form['actions']['submit']['#submit'][] = 'gdpr_fields_form_field_config_edit_form_submit'; } @@ -59,12 +62,16 @@ function gdpr_fields_form_field_config_edit_form_submit(array $form, FormStateIn $form_fields = &$form_state->getValues(); // If the private option is checked, update settings. - if ($form_fields['gdpr_fields_settings']) { - $field->setThirdPartySetting('gdpr_fields', 'gdpr_fields_settings', TRUE); + if ($form_fields['gdpr_fields_enabled']) { + $field->setThirdPartySetting('gdpr_fields', 'gdpr_fields_enabled', TRUE); + $field->setThirdPartySetting('gdpr_fields', 'gdpr_fields_rta', $form_fields['gdpr_fields_rta']); + $field->setThirdPartySetting('gdpr_fields', 'gdpr_fields_rtf', $form_fields['gdpr_fields_rtf']); $field->save(); } else { - $field->unsetThirdPartySetting('gdpr_fields', 'gdpr_fields_settings'); + $field->unsetThirdPartySetting('gdpr_fields', 'gdpr_fields_enabled'); + $field->unsetThirdPartySetting('gdpr_fields', 'gdpr_fields_rta'); + $field->unsetThirdPartySetting('gdpr_fields', 'gdpr_fields_rtf'); $field->save(); } diff --git a/modules/gdpr_fields/gdpr_fields.permissions.yml b/modules/gdpr_fields/gdpr_fields.permissions.yml new file mode 100644 index 0000000..79f6247 --- /dev/null +++ b/modules/gdpr_fields/gdpr_fields.permissions.yml @@ -0,0 +1,5 @@ +view gdpr fields: + title: 'View GDPR fields' + +edit gdpr fields: + title: 'Edit GDPR fields' diff --git a/modules/gdpr_fields/gdpr_fields.routing.yml b/modules/gdpr_fields/gdpr_fields.routing.yml new file mode 100644 index 0000000..0b79a36 --- /dev/null +++ b/modules/gdpr_fields/gdpr_fields.routing.yml @@ -0,0 +1,7 @@ +gdpr_fields.fields_list: + path: '/admin/reports/fields/gdpr-fields' + defaults: + _controller: '\Drupal\gdpr_fields\Controller\GDPRController::fieldsList' + _title: 'Used in GDPR' + requirements: + _permission: 'view gdpr fields' diff --git a/modules/gdpr_fields/gdpr_fields.services.yml b/modules/gdpr_fields/gdpr_fields.services.yml new file mode 100644 index 0000000..cf64343 --- /dev/null +++ b/modules/gdpr_fields/gdpr_fields.services.yml @@ -0,0 +1,4 @@ +services: + gdpr_fields.collector: + class: Drupal\gdpr_fields\GDPRCollector + arguments: ['@entity_type.manager', '@plugin.manager.ctools.relationship'] diff --git a/modules/gdpr_fields/src/Controller/GDPRController.php b/modules/gdpr_fields/src/Controller/GDPRController.php new file mode 100644 index 0000000..94d55e8 --- /dev/null +++ b/modules/gdpr_fields/src/Controller/GDPRController.php @@ -0,0 +1,110 @@ +collector = $collector; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('gdpr_fields.collector') + ); + } + + /** + * Lists all fields with GDPR sensitivity. + * + * @return array + * The Views plugins report page. + */ + public function fieldsList() { + $output = []; + $entities = []; + $this->collector->getEntities($entities); + + foreach ($entities as $entity_type => $bundles) { + $output[$entity_type] = array( + '#type' => 'details', + '#title' => t($entity_type), + '#description' => t('@configure entity @entity_type for GDPR.', [ + // @todo Create ability to exclude entity type from GDPR in configuration. + '@configure' => Link::fromTextAndUrl('Configure', Url::fromUri('internal:/'))->toString(), + '@entity_type' => ucfirst($entity_type), + ]), + '#open' => TRUE, + ); + + if (count($bundles) > 1) { + foreach ($bundles as $bundle_id) { + $output[$entity_type][$bundle_id] = array( + '#type' => 'details', + '#title' => t($bundle_id), + '#open' => TRUE, + ); + $output[$entity_type][$bundle_id]['fields'] = $this->buildFieldTable($entity_type, $bundle_id); + } + } + else { + // Don't add another collapsible wrapper around single bundle entities. + $bundle_id = reset($bundles); + $output[$entity_type][$bundle_id]['fields'] = $this->buildFieldTable($entity_type, $bundle_id); + } + } + + return $output; + } + + /** + * Build a table for entity field list. + * + * @param string $entity_type + * The entity type id. + * @param string $bundle_id + * The entity bundle id. + * + * @return array + * Renderable array for field list table. + */ + protected function buildFieldTable($entity_type, $bundle_id) { + $rows = $this->collector->listFields($entity_type, $bundle_id); + // Sort rows by field name. + ksort($rows); + + return [ + '#type' => 'table', + '#header' => [t('Name'), t('Type'), t('Right to access'), t('Right to be forgotten'), ''], + '#rows' => $rows, + '#sticky' => TRUE, + '#empty' => t('There are no GDPR fields for this entity.'), + ]; + } + +} diff --git a/modules/gdpr_fields/src/GDPRCollector.php b/modules/gdpr_fields/src/GDPRCollector.php new file mode 100644 index 0000000..f91edf7 --- /dev/null +++ b/modules/gdpr_fields/src/GDPRCollector.php @@ -0,0 +1,179 @@ +entityTypeManager = $entity_type_manager; + $this->relationshipManager = $relationship_manager; + } + + /** + * Get entity tree for GDPR. + * + * @param $entity_list + * List of all gotten entities keyed by entity type and bundle id. + * @param string $entity_type + * The entity type id. + * @param string|null $bundle_id + * The entity bundle id, NULL if bundles should be loaded. + */ + public function getEntities(&$entity_list, $entity_type = 'user', $bundle_id = NULL) { + $definition = $this->entityTypeManager->getDefinition($entity_type); + + if ($definition instanceof ConfigEntityTypeInterface) { + return; + } + + if (!$bundle_id) { + if ($definition->getBundleEntityType()) { + $bundle_storage = $this->entityTypeManager->getStorage($definition->getBundleEntityType()); + foreach (array_keys($bundle_storage->loadMultiple()) as $bundle_id) { + $this->getEntities($entity_list, $entity_type, $bundle_id); + } + } + else { + $this->getEntities($entity_list, $entity_type, $entity_type); + } + + return; + } + + // Check for recursion. + if (isset($entity_list[$entity_type][$bundle_id])) { + return; + } + + // Set entity. + $entity_list[$entity_type][$bundle_id] = $bundle_id; + + // Find relationships. + $context = new Context(new ContextDefinition("entity:{$entity_type}")); + $definitions = $this->relationshipManager->getDefinitionsForContexts([$context]); + + foreach ($definitions as $definition_id => $definition) { + list($type, , , $field) = explode(':', $definition_id); + + if ($type == 'typed_data_entity_relationship') { + if (isset($definition['target_entity_type'])) { + $this->getEntities($entity_list, $definition['target_entity_type']); + } + } + elseif ($type == 'typed_data_entity_relationship_reverse') { + if (isset($definition['source_entity_type'])) { + $this->getEntities($entity_list, $definition['source_entity_type']); + } + } + else { + continue; + } + } + } + + /** + * List fields on entity including their GDPR values. + * + * @param string $entity_type + * The entity type id. + * @param string $bundle_id + * The entity bundle id. + * + * @return array + * GDPR entity field list. + */ + public function listFields($entity_type = 'user', $bundle_id) { + $storage = $this->entityTypeManager->getStorage($entity_type); + $entity_definition = $this->entityTypeManager->getDefinition($entity_type); + $bundle_type = $entity_definition->getBundleEntityType(); + + // Create a blank entity. + $values = []; + if ($entity_definition->hasKey('bundle')) { + $bundle_key = $entity_definition->getKey('bundle'); + $values[$bundle_key] = $bundle_id; + } + $entity = $storage->create($values); + + // Get fields for entity. + $fields = []; + foreach ($entity as $field_id => $field) { + /** @var \Drupal\Core\Field\FieldItemListInterface $field */ + $field_definition = $field->getFieldDefinition(); + $key = "$entity_type.$bundle_id.$field_id"; + $route_name = "entity.field_config.{$entity_type}_field_edit_form"; + $route_params = [ + 'field_config' => $key, + ]; + + if (isset($bundle_key)) { + $route_params[$bundle_type] = $bundle_id; + } + + $fields[$key] = [ + 'title' => $field_definition->getLabel(), + 'type' => $field_definition->getType(), + 'gdpr_rta' => 'None', + 'gdpr_rtf' => 'None', + 'edit' => '', + ]; + + if ($entity_definition->get('field_ui_base_route')) { + $url = Url::fromRoute($route_name, $route_params); + + if ($url->access()) { + $fields[$key]['edit'] = Link::fromTextAndUrl('edit', $url); + } + } + $config = $field_definition->getConfig($bundle_id); + + if ($config->getThirdPartySetting('gdpr_fields', 'gdpr_fields_enabled', FALSE)) { + $fields[$key]['gdpr_rta'] = $config->getThirdPartySetting('gdpr_fields', 'gdpr_fields_rta', 'no'); + $fields[$key]['gdpr_rtf'] = $config->getThirdPartySetting('gdpr_fields', 'gdpr_fields_rtf', 'no'); + } + } + + return $fields; + } + +} diff --git a/modules/gdpr_fields/src/Plugin/Deriver/TypedDataEntityRelationshipReverseDeriver.php b/modules/gdpr_fields/src/Plugin/Deriver/TypedDataEntityRelationshipReverseDeriver.php new file mode 100644 index 0000000..b1c4638 --- /dev/null +++ b/modules/gdpr_fields/src/Plugin/Deriver/TypedDataEntityRelationshipReverseDeriver.php @@ -0,0 +1,41 @@ +getType() == 'entity_reference') { + parent::generateDerivativeDefinition($base_plugin_definition, $data_type_id, $data_type_definition, $base_definition, $property_name, $property_definition); + + // Provide the entity type. + $derivative_id = $data_type_id . ':' . $property_name; + if (isset($this->derivatives[$derivative_id])) { + if ($base_definition->getConstraint('EntityType')) { + $this->derivatives[$derivative_id]['source_entity_type'] = $base_definition->getConstraint('EntityType'); + } + + $target_data_type = 'entity:' . $this->derivatives[$derivative_id]['target_entity_type']; + $context_definition = new ContextDefinition($target_data_type, $this->typedDataManager->createDataDefinition($target_data_type)); + // Add the constraints of the base definition to the context definition. + if ($property_definition->getFieldStorageDefinition()->getPropertyDefinition('entity')->getConstraint('Bundle')) { + $context_definition->addConstraint('Bundle', $property_definition->getFieldStorageDefinition()->getPropertyDefinition('entity')->getConstraint('Bundle')); + } + $this->derivatives[$derivative_id]['context']['base'] = $context_definition; + } + } + } + +} diff --git a/modules/gdpr_fields/src/Plugin/Relationship/TypedDataEntityRelationshipReverse.php b/modules/gdpr_fields/src/Plugin/Relationship/TypedDataEntityRelationshipReverse.php new file mode 100644 index 0000000..9e7aa61 --- /dev/null +++ b/modules/gdpr_fields/src/Plugin/Relationship/TypedDataEntityRelationshipReverse.php @@ -0,0 +1,53 @@ +entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static($configuration, $plugin_id, $plugin_definition, + $container->get('entity_type.manager') + ); + } + +} From a6a7c2b7b4232a66269487b506433961da8bea7a Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 2 Mar 2018 08:53:21 +0000 Subject: [PATCH 3/3] Better form UI for gdpr field configuration. --- modules/gdpr_fields/gdpr_fields.module | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/gdpr_fields/gdpr_fields.module b/modules/gdpr_fields/gdpr_fields.module index 5579d33..fb145ad 100644 --- a/modules/gdpr_fields/gdpr_fields.module +++ b/modules/gdpr_fields/gdpr_fields.module @@ -11,16 +11,24 @@ use Drupal\Core\Form\FormStateInterface; * Implements hook_form_FORM_ID_alter(). * * @todo Find a better way of editing field GDPR settings as lots of fields are not UI configurable. + * @todo Check user edit permission for GDPR fields. */ function gdpr_fields_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) { /* @var \Drupal\Core\Field\FieldConfigInterface $field */ $field = $form_state->getFormObject()->getEntity(); // @todo Check that target entity is a content entity. + $enabled = $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_enabled', FALSE); + $form['field']['gdpr_fields'] = [ + '#type' => 'details', + '#title' => t('GDPR field settings'), + '#open' => $enabled, + ]; + $form['field']['gdpr_fields']['gdpr_fields_enabled'] = [ '#type' => 'checkbox', '#title' => t('This is a GDPR field'), - '#default_value' => $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_enabled', FALSE), + '#default_value' => $enabled, ]; $form['field']['gdpr_fields']['gdpr_fields_rta'] = [ @@ -32,6 +40,13 @@ function gdpr_fields_form_field_config_edit_form_alter(&$form, FormStateInterfac 'no' => 'Not', ], '#default_value' => $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_rta', 'no'), + '#states' => array( + 'visible' => array( + ':input[name="gdpr_fields_enabled"]' => array( + 'checked' => TRUE, + ), + ), + ), ]; $form['field']['gdpr_fields']['gdpr_fields_rtf'] = [ @@ -44,6 +59,13 @@ function gdpr_fields_form_field_config_edit_form_alter(&$form, FormStateInterfac 'no' => 'Not', ], '#default_value' => $field->getThirdPartySetting('gdpr_fields', 'gdpr_fields_rtf', 'no'), + '#states' => array( + 'visible' => array( + ':input[name="gdpr_fields_enabled"]' => array( + 'checked' => TRUE, + ), + ), + ), ]; $form['actions']['submit']['#submit'][] = 'gdpr_fields_form_field_config_edit_form_submit'; }