Skip to content

Commit

Permalink
Data Dictionary Field Widget (#4138)
Browse files Browse the repository at this point in the history
  • Loading branch information
kaise-lafrai committed Apr 16, 2024
1 parent 24bd853 commit 008e376
Show file tree
Hide file tree
Showing 18 changed files with 2,134 additions and 0 deletions.
1 change: 1 addition & 0 deletions dkan.info.yml
Expand Up @@ -10,6 +10,7 @@ dependencies:
- dkan:datastore
- dkan:harvest
- dkan:json_form_widget
- dkan:data_dictionary_widget
- drupal:config
- drupal:field
- drupal:file
Expand Down
52 changes: 52 additions & 0 deletions modules/data_dictionary_widget/css/dataDictionaryWidget.css
@@ -0,0 +1,52 @@
.table {
display: table;
width: auto;
background-color: #eee;
border: 1px solid #666666;
border-spacing: 5px; /* cellspacing:poor IE support for this */
}

.sticky-header {
min-width: 0;
}
.caption {
text-align: left; /* LTR */
}
.thead {
display: table-header-group;
vertical-align: middle;
border-color: inherit;
}
.thead .tr {
border: 0;
}

.th {
position: relative;
box-sizing: border-box;
height: 3rem;
padding: 0.5rem 1rem;
text-align: left; /* LTR */
color: #292323;
background: #f3f4f9;
line-height: 1.25rem; /* 20px */
}
[dir="rtl"] .th {
text-align: right;
}
.row {
display: table-row;
width: auto;
clear: both;
}
.td {
float: left; /* fix for buggy browsers */
display: table-column;
width: 200px;
background-color: #ccc;
}
#edit-field-json-metadata-0-identifier {
background-color: #f4f4f4;
color: #666;
cursor: not-allowed;
}
@@ -0,0 +1,8 @@
name: 'Data Dictionary Widget'
description: 'Provides a field widget to generate a data-dictionary form'
package: DKAN
type: module
core_version_requirement: ^9.4 || ^10
dependencies:
- drupal:field
- metastore
@@ -0,0 +1,5 @@
dataDictionaryWidget:
version: 1.x
css:
theme:
css/dataDictionaryWidget.css: {}
133 changes: 133 additions & 0 deletions modules/data_dictionary_widget/data_dictionary_widget.module
@@ -0,0 +1,133 @@
<?php

/**
* @file
* Module for creating Data Dictionary Widget.
*/

use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\data_dictionary_widget\Fields\FieldOperations;
use Drupal\node\NodeInterface;

/**
* Implements hook_theme().
*/
function data_dictionary_widget_theme($existing, $type, $theme, $path) {
return [
'custom_table' => [
'variables' => [
'header' => [],
'rows' => [],
'attributes' => [],
],
'template' => 'custom-table',
],
];
}

/**
* Implements hook_entity_form_display_alter().
*
* Dynamically set the widget type for the field_json_metadata field.
*/
function data_dictionary_widget_entity_form_display_alter(EntityFormDisplayInterface $form_display, array $context) {
if (data_dictionary_widget__data_dictionary_data_type_checker($context) === 'data-dictionary') {
$form_display->setComponent('field_json_metadata', [
'type' => 'data_dictionary_widget',
]);
}
}

/**
* Find entity field_data_type or schema based on the context.
*
* @param array $context
* An associative array containing entity_type, bundle and form_mode.
*
* @return string
* Schema/field_data_type value depending on form_mode.
*/
function data_dictionary_widget__data_dictionary_data_type_checker($context) {

if ($context['form_mode'] === "edit") {
$node = \Drupal::routeMatch()->getParameter('node');
if ($node instanceof NodeInterface && $node->hasField('field_data_type')) {
return $node->get('field_data_type')->value;
}
}

if ($context['form_mode'] === "default") {
return \Drupal::request()->query->get('schema');
}
}

/**
* Implements hook_form_alter().
*
* Setting form validation for unique identifier.
*
* Modifying current_fields array structure to prevent errors in element render array.
*
* Attaching module library to render theme changes.
*/
function data_dictionary_widget_form_alter(&$form, &$form_state, $form_id) {
$formObject = $form_state->getFormObject();
if ($formObject instanceof \Drupal\Core\Entity\EntityFormInterface) {
$entity = $formObject->getEntity();
if (isset($form["field_json_metadata"]["widget"][0]["dictionary_fields"])) {
if ($entity->getEntityTypeId() === 'node' && in_array($entity->bundle(), ['data'])) {
$form['#attached']['library'][] = 'data_dictionary_widget/dataDictionaryWidget';
}
}
}

if ($form_id == 'node_data_edit_form' || $form_id == 'node_data_form') {
$form['#validate'][] = 'data_dictionary_widget_validate_unique_identifier';
$current_fields = !empty($form["field_json_metadata"]["widget"][0]["dictionary_fields"]["current_fields"]) ? $form["field_json_metadata"]["widget"][0]["dictionary_fields"]["current_fields"] : NULL;

// The form element render array prefers child keys to be stored as arrays with a #value property.
if ($current_fields) {
foreach ($current_fields as $key => $value) {
$keys = array_keys($value);
$formatted_current_fields[$key] = [];

foreach ($keys as $attr) {
$formatted_current_fields[$key][$attr] = [
'#value' => $value[$attr]
];
}
}
$form["field_json_metadata"]["widget"][0]["dictionary_fields"]["current_fields"] = $formatted_current_fields;
}

// Set the default value of the identifier field to a randomly generated uuid.
$identifier_uuid = $form["field_json_metadata"]["widget"][0]["identifier"]["#default_value"] ?? NULL;
if (!$identifier_uuid) {
$uuid = \Drupal::service('uuid')->generate();
$form["field_json_metadata"]["widget"][0]["identifier"]['#default_value'] = $uuid;
$form["field_json_metadata"]["widget"][0]["identifier"]['#description'] = t('<div class="form-item__description">This is the UUID of this Data Dictionary. To assign this data dictionary to a specific distribution use this <a href="/api/1/metastore/schemas/data-dictionary/items/' .$uuid. '" target="_blank">URL</a>.</div>');
}
}
}

/**
* Checking if identifier is already used.
*/
function data_dictionary_widget_validate_unique_identifier($form, &$form_state) {
$op = $form_state->getFormObject()->getOperation();
$current_nid = ($op != "default") ? \Drupal::routeMatch()->getParameter('node')->id() : false;
$existing_data_dictionary_nodes = FieldOperations::getDataDictionaries();

if (isset($form["field_json_metadata"]["widget"][0]["identifier"]["#value"])) {
$submitted_identifier = $form["field_json_metadata"]["widget"][0]["identifier"]["#value"];

foreach ($existing_data_dictionary_nodes as $node) {
if ($current_nid !== $node["nid"] && strtolower($submitted_identifier) === strtolower($node["identifier"])) {
$form_state->setError($form["field_json_metadata"]["widget"][0]["identifier"], 'The identifier you entered is taken. Please choose another one.');
return $form_state;
}
}
}

return;
}
145 changes: 145 additions & 0 deletions modules/data_dictionary_widget/src/Fields/FieldAddCreation.php
@@ -0,0 +1,145 @@
<?php

namespace Drupal\data_dictionary_widget\Fields;

/**
* Various operations for creating Data Dictionary Widget add fields.
*/
class FieldAddCreation {

/**
* Create add fields for Data Dictionary Widget.
*/
public static function addFields() {
$add_fields['#access'] = FALSE;
$add_fields['group'] = [
'#type' => 'fieldset',
'#title' => t('Add new field'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
];

$add_fields['group']['name'] = [
'#name' => 'field_json_metadata[0][dictionary_fields][field_collection][group][name]',
'#type' => 'textfield',
'#required' => TRUE,
'#title' => 'Name',
'#description' => t('Machine name of the field/column in the data table.'),
];
$add_fields['group']['title'] = self::createTitle();
$add_fields['group']['type'] = self::createType();
$add_fields['group']['format'] = self::createFormat();
$add_fields['group']['format_other'] = self::createFormatOther();
$add_fields['group']['description'] = self::createDescriptionField();
$add_fields['group']['actions'] = self::createActionFields();

return $add_fields;
}

/**
* Create Type field.
*/
private static function createTitle() {
return [
'#name' => 'field_json_metadata[0][dictionary_fields][field_collection][group][title]',
'#type' => 'textfield',
'#required' => TRUE,
'#title' => 'Title',
'#description' => t('A human-readable title.'),
];
}

/**
* Create Type field.
*/
private static function createType() {
return [
'#name' => 'field_json_metadata[0][dictionary_fields][field_collection][group][type]',
'#type' => 'select',
'#required' => TRUE,
'#title' => 'Data type',
'#default_value' => 'string',
'#op' => 'type',
'#options' => [
'string' => t('String'),
'date' => t('Date'),
'integer' => t('Integer'),
'number' => t('Number'),
],
'#ajax' => [
'callback' => '\Drupal\data_dictionary_widget\Fields\FieldCallbacks::updateFormatOptions',
'method' => 'replace',
'wrapper' => 'field-json-metadata-format',
],
];
}

/**
* Create Format field.
*/
private static function createFormat() {
return [
'#name' => 'field_json_metadata[0][dictionary_fields][field_collection][group][format]',
'#type' => 'select',
'#required' => TRUE,
'#title' => 'Format',
'#description' => FieldOperations::generateFormatDescription("string"),
'#default_value' => 'default',
'#prefix' => '<div id = field-json-metadata-format>',
'#suffix' => '</div>',
'#validated' => TRUE,
'#options' => [
'default' => t('default'),
'email' => t('email'),
'uri' => t('uri'),
'binary' => t('binary'),
'uuid' => t('uuid'),
],
];
}

/**
* Create Format Other field.
*/
private static function createFormatOther() {
return [
'#name' => 'field_json_metadata[0][dictionary_fields][field_collection][group][format_other]',
'#type' => 'textfield',
'#title' => t('Other format'),
'#description' => t('A supported format'),
'#states' => [
'visible' => [
':input[name="field_json_metadata[0][dictionary_fields][field_collection][group][format]"]' => ['value' => 'other'],
],
'required' => [
':input[name="field_json_metadata[0][dictionary_fields][field_collection][group][format]"]' => ['value' => 'other'],
],
],
];
}

/**
* Create Action buttons.
*/
private static function createActionFields() {
return [
'#type' => 'actions',
'save_settings' => FieldButtons::submitButton('add', NULL),
'cancel_settings' => FieldButtons::cancelButton('cancel', NULL),
];
}

/**
* Create Description field.
*/
private static function createDescriptionField() {
return [
'#name' => 'field_json_metadata[0][dictionary_fields][field_collection][group][description]',
'#type' => 'textfield',
'#required' => TRUE,
'#title' => 'Description',
'#description' => t('Information about the field data.'),
];
}

}

0 comments on commit 008e376

Please sign in to comment.