fdk-8.x-1.0/src/FDKHelper.php
src/FDKHelper.php
<?php
namespace Drupal\fdk;
use Drupal\Core\Field\FormatterInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\CacheBackendInterface;
class FDKHelper {
public static function prepareConfigurationSettings($settings) {
$defaultSettings = [
'field_wrapper' => [
'html_element' => '_default',
'html_element_other' => '',
'html_element_classes' => '',
'html_element_attributes' => '',
'force_multiple' => FALSE,
],
'field_item_wrapper' => [
'html_element' => '_default',
'html_element_other' => '',
'html_element_link_href' => '',
'html_element_classes' => '',
'html_element_attributes' => '',
],
'label' => [
'html_element' => '_default',
'html_element_other' => '',
'html_element_classes' => '',
'html_element_attributes' => '',
'value' => '',
],
'field_delimiter' => [
'enabled' => FALSE,
'value' => ', ',
'include_and' => TRUE,
],
];
return $settings = NestedArray::mergeDeep($defaultSettings, $settings);
}
public static function settingsForm(FormatterInterface $plugin, FieldDefinitionInterface $field_definition, $view_mode, $form, FormStateInterface $form_state) {
$elements = [];
$field_info = $field_definition->getFieldStorageDefinition();
if (!empty($field_info)) {
$token_types = [$field_definition->getTargetEntityTypeId()];
$field_name = $field_info->getName();
$settings = self::prepareConfigurationSettings($plugin->getThirdPartySettings('fdk'));
$wrapper_options = ['div'];
$wrapper_options = ['_default' => t('Default')] + array_combine($wrapper_options, $wrapper_options) + ['_other' => t('Other')];
$elements['fdk'] = [
'#type' => 'details',
'#title' => t('Field Display Kit Settings'),
// Default to open if we have any values to show.
'#open' => $settings['field_wrapper']['html_element'] != '_default' || $settings['field_item_wrapper']['html_element'] != '_default',
];
$elements['fdk']['field_wrapper'] = [
'#type' => 'details',
'#title' => t('Field wrapper'),
// Default to open if we have any values to show.
'#open' => $settings['field_wrapper']['html_element'] != '_default',
];
$elements['fdk']['field_wrapper']['html_element'] = [
'#type' => 'select',
'#title' => t('Wrapper element'),
'#description' => t('Select a wrapper element to be used for entire field output.'),
'#default_value' => $settings['field_wrapper']['html_element'],
'#options' => $wrapper_options,
];
$elements['fdk']['field_wrapper']['html_element_other'] = [
'#type' => 'textfield',
'#description' => t('Enter HTML tag name of the wrapper element.'),
'#default_value' => $settings['field_wrapper']['html_element_other'],
'#element_validate' => [ [self::class, 'validateHtmlTagName'] ],
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_wrapper][html_element]"]' => ['value' => '_other'],
],
],
'#depended_field' => 'html_element',
];
$elements['fdk']['field_wrapper']['html_element_classes'] = [
'#type' => 'textfield',
'#title' => t('Classes'),
'#description' => t('Enter one or more HTML class names separated by spaces. It can be used to style the field with CSS.'),
'#default_value' => $settings['field_wrapper']['html_element_classes'],
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_wrapper][html_element]"]' => ['!value' => '_default'],
],
],
];
$elements['fdk']['field_wrapper']['html_element_attributes'] = [
'#title' => t('Attributes'),
'#type' => 'textarea',
'#description' => t('Set attributes for this wrapper. Enter one value per line, in the format attribute|value. The value is optional. Tokens are supported in attribute values.'),
'#default_value' => $settings['field_wrapper']['html_element_attributes'],
'#element_validate' => [ [self::class, 'validateHtmlAttributesList'] ],
'#depended_field' => 'field_wrapper',
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_wrapper][html_element]"]' => ['!value' => '_default'],
],
],
];
if (\Drupal::moduleHandler()->moduleExists('token')) {
$elements['fdk']['field_wrapper']['html_element_attributes_token'] = [
'#type' => 'item',
'#theme' => 'token_tree_link',
'#token_types' => $token_types,
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_wrapper][html_element]"]' => ['!value' => '_default'],
],
],
];
}
$elements['fdk']['field_wrapper']['force_multiple'] = [
'#type' => 'checkbox',
'#title' => t('Include field wrapper even if this field has only one value and label is hidden'),
'#description' => t('By default field wrapper will not be present in output when field has only one value. Enable this checkbox to force inlcuding the field wrapper.'),
'#default_value' => $settings['field_wrapper']['force_multiple'],
];
$elements['fdk']['label'] = [
'#type' => 'details',
'#title' => t('Field label wrapper'),
// Default to open if we have any values to show.
'#open' => $settings['label']['html_element'] != '_default',
];
// Layout Builder provides facility to override title.
// So, not to show in the Layout Builder configuration.
if (isset($form['#form_id']) && strpos($form['#form_id'], "layout_builder") !== 0) {
$elements['fdk']['label']['value'] = [
'#type' => 'textfield',
'#title' => t('Override field title'),
'#description' => t('Enter a custom label value to overwrite the default.'),
'#default_value' => $settings['label']['value'],
];
}
$elements['fdk']['label']['html_element'] = [
'#type' => 'select',
'#title' => t('Wrapper element'),
'#description' => t('Select a wrapper element to be used for field label.'),
'#default_value' => $settings['label']['html_element'],
'#options' => $wrapper_options,
];
$elements['fdk']['label']['html_element_other'] = [
'#type' => 'textfield',
'#description' => t('Enter HTML tag name of the wrapper element.'),
'#default_value' => $settings['label']['html_element_other'],
'#element_validate' => [ [self::class, 'validateHtmlTagName'] ],
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][label][html_element]"]' => ['value' => '_other'],
],
],
'#depended_field' => 'html_element',
];
$elements['fdk']['label']['html_element_classes'] = [
'#type' => 'textfield',
'#title' => t('Classes'),
'#description' => t('Enter one or more HTML class names separated by spaces. It can be used to style with CSS.'),
'#default_value' => $settings['label']['html_element_classes'],
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][label][html_element]"]' => ['!value' => '_default'],
],
],
];
$elements['fdk']['label']['html_element_attributes'] = [
'#title' => t('Attributes'),
'#type' => 'textarea',
'#description' => t('Set attributes for this wrapper. Enter one value per line, in the format attribute|value. The value is optional. Tokens are supported in attribute values.'),
'#default_value' => $settings['label']['html_element_attributes'],
'#element_validate' => [ [self::class, 'validateHtmlAttributesList'] ],
'#depended_field' => 'html_element',
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][label][html_element]"]' => ['!value' => '_default'],
],
],
];
if (\Drupal::moduleHandler()->moduleExists('token')) {
$elements['fdk']['label']['html_element_attributes_token'] = [
'#type' => 'item',
'#theme' => 'token_tree_link',
'#token_types' => $token_types,
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][label][html_element]"]' => ['!value' => '_default'],
],
],
];
}
$elements['fdk']['field_item_wrapper'] = [
'#type' => 'details',
'#title' => t('Field value wrapper'),
// Default to open if we have any values to show.
'#open' => $settings['field_item_wrapper']['html_element'] != '_default',
];
$item_wrapper_options = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'span', 'p', 'strong'];
$item_wrapper_options = ['_default' => t('Default')] + ['a' => t('Link')] + array_combine($item_wrapper_options, $item_wrapper_options) + ['_other' => t('Other')];
$elements['fdk']['field_item_wrapper']['html_element'] = [
'#type' => 'select',
'#title' => t('Wrapper element'),
'#description' => t('Select a wrapper element to be used for each field item value output.'),
'#default_value' => $settings['field_item_wrapper']['html_element'],
'#options' => $item_wrapper_options,
];
$elements['fdk']['field_item_wrapper']['html_element_other'] = [
'#type' => 'textfield',
// '#title' => t('Field wrapper'),
'#description' => t('Enter HTML tag name of the wrapper element.'),
'#default_value' => $settings['field_item_wrapper']['html_element_other'],
'#element_validate' => [ [self::class, 'validateHtmlTagName'] ],
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_item_wrapper][html_element]"]' => ['value' => '_other'],
],
],
'#depended_field' => 'html_element',
];
$elements['fdk']['field_item_wrapper']['html_element_link_href'] = [
'#type' => 'textfield',
'#title' => t('Link destination'),
'#description' => t('Enter destination for the link. Tokens are supported.'),
'#default_value' => $settings['field_item_wrapper']['html_element_link_href'],
'#element_validate' => [ [self::class, 'validateHref'] ],
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_item_wrapper][html_element]"]' => ['value' => 'a'],
],
],
'#depended_field' => 'html_element',
];
if (\Drupal::moduleHandler()->moduleExists('token')) {
$elements['fdk']['field_item_wrapper']['html_element_link_href_token'] = [
'#type' => 'item',
'#theme' => 'token_tree_link',
'#token_types' => $token_types,
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_item_wrapper][html_element]"]' => ['value' => 'a'],
],
],
];
}
$elements['fdk']['field_item_wrapper']['html_element_classes'] = [
'#type' => 'textfield',
'#title' => t('Classes'),
'#description' => t('Enter one or more HTML class names separated by spaces. It can be used to style the field items with CSS.'),
'#default_value' => $settings['field_item_wrapper']['html_element_classes'],
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_item_wrapper][html_element]"]' => ['!value' => '_default'],
],
],
];
$elements['fdk']['field_item_wrapper']['html_element_attributes'] = [
'#title' => t('Attributes'),
'#type' => 'textarea',
'#description' => t('Set attributes for each field item wrapper. Enter one value per line, in the format attribute|value. The value is optional. Tokens are supported in attribute values.'),
'#default_value' => $settings['field_item_wrapper']['html_element_attributes'],
'#element_validate' => [ [self::class, 'validateHtmlAttributesList'] ],
'#depended_field' => 'field_item_wrapper',
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_item_wrapper][html_element]"]' => ['!value' => '_default'],
],
],
];
if (\Drupal::moduleHandler()->moduleExists('token')) {
$elements['fdk']['field_item_wrapper']['html_element_attributes_token'] = [
'#type' => 'item',
'#theme' => 'token_tree_link',
'#token_types' => $token_types,
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_item_wrapper][html_element]"]' => ['!value' => '_default'],
],
],
];
}
$elements['fdk']['field_delimiter'] = [
'#type' => 'details',
'#title' => t('Field delimiter'),
// Default to open if we have any values to show.
'#open' => $settings['field_item_wrapper']['html_element'] != '_default',
];
$elements['fdk']['field_delimiter']['enabled'] = [
'#type' => 'checkbox',
'#title' => t('Add comma between field items'),
'#default_value' => $settings['field_delimiter']['enabled'],
];
// $elements['fdk']['field_delimiter']['after'] = [
// '#type' => 'textfield',
// '#title' => t('After'),
// '#description' => t('Enter a string to be added after each of the field item value. You can enter comma here to construct a comma separated list.'),
// '#default_value' => $settings['field_delimiter']['after'],
// ];
$elements['fdk']['field_delimiter']['include_and'] = [
'#type' => 'checkbox',
'#title' => t('Include "and" before the last item'),
'#description' => t('If enabled "and" will be added before the last item if there are more than one field values to display.'),
'#default_value' => $settings['field_delimiter']['include_and'],
'#states' => [
'visible' => [
':input[name="fields[' . $field_name . '][settings_edit_form][third_party_settings][fdk][field_delimiter][enabled]"]' => ['checked' => TRUE],
],
],
];
}
return $elements['fdk'];
}
public static function validateHtmlTagName(&$element, FormStateInterface $form_state, &$complete_form) {
$input_exists = FALSE;
$input = NestedArray::getValue($form_state->getValues(), $element['#parents'], $input_exists);
$parents = $element['#parents'];
array_pop($parents);
$parents[] = $element['#depended_field'];
$wrapper = NestedArray::getValue($form_state->getValues(), $parents);
if ($wrapper == '_other' && empty($input)) {
$form_state->setError($element, t('Please enter a wrapper tag name.'));
}
else if (!empty($input) && !preg_match('/^[a-zA-Z]+\d?$/', $input)) {
$form_state->setError($element, t('Enter a valid HTML tag name. %name is not a valid tag name.', ['%name' => $input]));
}
}
public static function validateHtmlAttributesList(&$element, FormStateInterface $form_state, &$complete_form) {
$input_exists = FALSE;
$input = NestedArray::getValue($form_state->getValues(), $element['#parents'], $input_exists);
$list = explode("\n", $input);
$list = array_map('trim', $list);
$list = array_filter($list, 'strlen');
$generated_keys = $explicit_keys = FALSE;
foreach ($list as $position => $text) {
// Check for an explicit key.
$matches = [];
if (preg_match('/^([\w]+(\-[\w]+)*)\|(.*)$/', $text, $matches)) {
// Trim key and value to avoid unwanted spaces issues.
$key = trim($matches[1]);
$value = trim($matches[3]);
}
else {
$form_state->setError($element, t('Invalid attribute setting(s).'));
break;
}
}
}
public static function validateHref(&$element, FormStateInterface $form_state, &$complete_form) {
$input_exists = FALSE;
$input = NestedArray::getValue($form_state->getValues(), $element['#parents'], $input_exists);
$parents = $element['#parents'];
array_pop($parents);
$parents = array_merge($parents, [$element['#depended_field']]);
$select_field_value = NestedArray::getValue($form_state->getValues(), $parents);
if ($select_field_value == 'a' && empty($input)) {
$form_state->setError($element, t('Please enter a destination for the link.'));
}
}
public static function formatter_settings_summary_alter(array &$summary, array $context) {
$formatter_plugin = $context['formatter'];
$settings = self::prepareConfigurationSettings($formatter_plugin->getThirdPartySettings('fdk'));
$summary[] = [
'#theme' => 'fdk_settings_summary',
'#settings' => $settings,
];
}
public static function getAttributesFromConfig($attributes_string, $token_data = NULL) {
$attribute_list = explode("\n", $attributes_string);
$attribute_list = array_map('trim', $attribute_list);
$attribute_list = array_filter($attribute_list, 'strlen');
$attributes = [];
foreach ($attribute_list as $text) {
// Check for an explicit key.
$matches = [];
if (preg_match('/^([\w]+(\-[\w]+)*)\|(.*)$/', $text, $matches)) {
// Trim key and value to avoid unwanted spaces issues.
$key = trim($matches[1]);
$value = trim($matches[3]);
if ($token_data) {
$attributes[$key] = \Drupal::token()->replace($value, $token_data, ['clear' => TRUE]);
}
else {
$attributes[$key] = $value;
}
}
}
return $attributes;
}
/**
* The FDK module proivides its own version of field.html.twig templates.
* All themes are supposed to override the FDK version of the template than Drupal core.
* This function scans over all active themes and finds incompatible field templates.
* @return array
* Array of file info keyed by theme names.
*/
public static function getIncompatibleFieldTemplates() {
$cache = \Drupal::cache();
$cache_key = 'fdk_incompatible_template_files';
if ($cached = $cache->get($cache_key)) {
$files_info = $cached->data;
}
else {
// Get all active themes.
$active_themes = array_map(function($theme) {
if ($theme->status) {
return $theme;
}
}, \Drupal::service('theme_handler')->listInfo());
$files_info = [];
foreach ($active_themes as $theme_name => $theme) {
$theme_dir = \Drupal::root() . '/' . $theme->getPath();
$files = \Drupal::service('file_system')->scanDirectory($theme_dir . '/templates', '/^field(\-\-.*)?\.html\.twig$/', ['key' => 'filename']);
if (!empty($files)) {
$incompatible_files = [];
foreach ($files as $file_info) {
$file_content = file_get_contents($file_info->uri);
// Check whether the template contains any of FDK specific variables.
$field_wrapper_tag_exists = (bool) preg_match('/{{\s*field_wrapper_tag\s*}}/', $file_content);
$label_tag_exists = (bool) preg_match('/{{\s*label_tag\s*}}/', $file_content);
$field_item_wrapper_tag_exists = (bool) preg_match('/{{\s*field_item_wrapper_tag\s*}}/', $file_content);
if (!($field_wrapper_tag_exists || $label_tag_exists || $field_item_wrapper_tag_exists)) {
// No FDK specific variables found. Add to incompatible list.
$incompatible_files[] = $file_info;
}
}
if (!empty($incompatible_files)) {
$files_info[$theme_name] = $incompatible_files;
}
}
}
$cache->set($cache_key, $files_info, CacheBackendInterface::CACHE_PERMANENT);
}
return $files_info;
}
}
