Skip to content
Snippets Groups Projects
Commit 05a83035 authored by Mikael Meulle's avatar Mikael Meulle
Browse files

Merge branch '3487548-2.0.0-rc1-check-hookfieldformatterthirdpartysettingsform' into '2.0.x'

support 3rd party settings in field formatters

See merge request !311
parents 2db97f81 d931bebc
No related branches found
No related tags found
No related merge requests found
Pipeline #376093 passed
......@@ -27,7 +27,7 @@ settings:
value: ''
props:
attributes:
source_id: class_attribute
source_id: attributes
source:
value: ''
string:
......@@ -93,15 +93,15 @@ settings:
source:
value: ''
attributes_implicit:
source_id: class_attribute
source_id: attributes
source:
value: ''
attributes_ui_patterns:
source_id: class_attribute
source_id: attributes
source:
value: ''
attributes_class:
source_id: class_attribute
source_id: attributes
source:
value: ''
slots:
......
......@@ -56,7 +56,7 @@ third_party_settings:
_weight: '0'
props:
attributes:
source_id: class_attribute
source_id: attributes
source:
value: ''
string:
......@@ -122,15 +122,15 @@ third_party_settings:
source:
value: ''
attributes_implicit:
source_id: class_attribute
source_id: attributes
source:
value: ''
attributes_ui_patterns:
source_id: class_attribute
source_id: attributes
source:
value: ''
attributes_class:
source_id: class_attribute
source_id: attributes
source:
value: ''
weight: 0
......
......@@ -66,20 +66,6 @@ abstract class ComponentFormatterBase extends FormatterBase {
*/
protected $context = NULL;
/**
* Use for settings form, to know where the form is used exactly (Optional)
*
* @var array<string>|null
*/
protected $formArrayParents = NULL;
/**
* Set the internal property formArrayParents.
*/
public function setFormArrayParents(array $form_array_parents) : void {
$this->formArrayParents = $form_array_parents;
}
/**
* {@inheritdoc}
*/
......
......@@ -56,11 +56,16 @@ trait FieldFormatterFormTrait {
$subformKeys = $triggeringElement['#array_parents'];
// Remove the triggering element itself and add the 'settings' below key.
array_pop($subformKeys);
$subformKeys[] = 'settings';
// Return the subform:
$subform = NestedArray::getValue($form, $subformKeys);
// $form_state->setRebuild(TRUE);
return $subform ?? [];
$subform_settings_wrapper = NestedArray::getValue($form, array_merge($subformKeys, ['settings_wrapper']));
$subform_settings = NestedArray::getValue($form, array_merge($subformKeys, ['settings']));
$subform_third_party_settings = NestedArray::getValue($form, array_merge($subformKeys, ['third_party_settings']));
return [
'#prefix' => $subform_settings_wrapper['#prefix'],
'settings' => $subform_settings,
'third_party_settings' => $subform_third_party_settings,
'#suffix' => $subform_settings_wrapper['#suffix'],
];
}
return [];
}
......
......@@ -6,6 +6,7 @@ namespace Drupal\ui_patterns_field_formatters\Plugin\UiPatterns\Source;
use Drupal\Component\Plugin\Definition\PluginDefinitionInterface;
use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityDisplayBase;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Field\FieldItemListInterface;
......@@ -16,6 +17,7 @@ use Drupal\Core\Field\FormatterPluginManager;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Logger\LoggerChannelTrait;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\Exception\MissingDataException;
use Drupal\ui_patterns\Attribute\Source;
......@@ -34,7 +36,14 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
description: new TranslatableMarkup('Entity Field formatted with a field formatter'),
deriver: FieldFormatterSourceDeriver::class
)]
class FieldFormatterSource extends FieldValueSourceBase {
class FieldFormatterSource extends FieldValueSourceBase implements TrustedCallbackInterface {
/**
* {@inheritdoc}
*/
public static function trustedCallbacks() {
return ['preRenderFormatterSettingsForm'];
}
use LoggerChannelTrait;
......@@ -129,20 +138,14 @@ class FieldFormatterSource extends FieldValueSourceBase {
*/
private function generateFieldFormatterForm(array &$form, FormStateInterface $form_state, FieldDefinitionInterface $field_definition, FieldStorageDefinitionInterface $field_storage): bool {
$formatter_options = $this->getAvailableFormatterOptions($field_storage, $field_definition);
if (empty($formatter_options)) {
return FALSE;
}
// @todo remove ui patterns formatters from the list of options ?
// Get the formatter type from configuration.
$type_path = [
'settings',
'type',
];
$formatter_type = $this->getSettingsFromConfiguration($type_path);
$formatter_type = $this->getSettingsFromConfiguration(["settings", "type"]);
$uniqueID = Html::getId(implode("_", $this->formArrayParents ?? []) . "_field-formatter-settings-ajax");
// Get the formatter settings from configuration.
$settings = $this->getSettingsFieldFormatter($form_state, $field_storage, $formatter_options, $formatter_type);
$form['type'] = [
'#type' => 'select',
'#required' => TRUE,
......@@ -158,44 +161,79 @@ class FieldFormatterSource extends FieldValueSourceBase {
'method' => 'replaceWith',
],
];
$form['settings'] = [];
$form['third_party_settings'] = [];
$form['settings_wrapper'] = [
'#prefix' => '<div id="' . $uniqueID . '">',
'#suffix' => '</div>',
];
$options = [
'field_definition' => $field_definition,
'configuration' => [
'type' => $formatter_type,
'settings' => $settings,
'label' => '',
'weight' => 0,
],
'view_mode' => '_custom',
'configuration' => $this->getFormatterConfiguration($form_state, $field_storage, $formatter_options, $formatter_type),
'view_mode' => EntityDisplayBase::CUSTOM_MODE,
'prepare' => TRUE,
];
// Get the formatter settings form.
$form['settings'] = [
'#value' => [],
'#attributes' => [
'id' => $uniqueID,
],
];
if ($formatter = $this->formatterPluginManager->getInstance($options)) {
if ($formatter instanceof ComponentFormatterBase) {
// The Source is giving its context to the field formatter.
$formatter->setContext($this->context);
$formatter->setFormArrayParents(array_merge($this->formArrayParents, ["settings"]));
}
$subform_state = SubformState::createForSubform($form["settings"], $form, $form_state);
$form['settings'] = $formatter->settingsForm($form['settings'], $subform_state);
$form['settings']['#prefix'] = '<div id="' . $uniqueID . '" style="padding: 20px; background: red;">' . ($form['settings']['#prefix'] ?? '');
$form['settings']['#suffix'] = ($form['settings']['#suffix'] ?? '') . '</div>';
// Settings and third_party_settings are rendered into settings_wrapper.
// see the preRenderFormatterSettingsForm() method.
// but they are created in the form array at the root level.
// to ensure configuration structure does not add settings_wrapper.
$settings_subform_state = SubformState::createForSubform($form["settings"], $form, $form_state);
$form['settings'] = $formatter->settingsForm($form, $settings_subform_state);
$form['third_party_settings'] = $this->thirdPartySettingsForm($formatter, $field_definition, $form, $form_state);
// Should we use FormHelper::rewriteStatesSelector() ?
// like in FieldBlock::formatterSettingsProcessCallback.
}
$form['#pre_render'][] = [static::class, 'preRenderFormatterSettingsForm'];
return TRUE;
}
/**
* Get the formatter settings from configuration.
* Customize slot or prop form elements (pre-render).
*
* @param array $element
* Element to process.
*
* @return array
* Processed element
*/
public static function preRenderFormatterSettingsForm(array $element) : array {
$element['settings_wrapper']['settings'] = $element['settings'];
$element['settings']["#printed"] = TRUE;
$element['settings_wrapper']['third_party_settings'] = $element['third_party_settings'];
$element['third_party_settings']["#printed"] = TRUE;
return $element;
}
/**
* Retrieve the settings from the configuration.
*
* @return array
* The settings.
*/
private function getFormatterBaseSettingsFromConfiguration() : array {
$base_container = $this->getSettingsFromConfiguration(["settings"]) ?? [];
if (isset($base_container['settings'])) {
// Backward compatibility.
return [
'settings' => is_array($base_container['settings']) ? $base_container['settings'] : [],
'third_party_settings' => $base_container['third_party_settings'] ?? [],
];
}
return [];
}
/**
* Gets the formatter configuration.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state.
* The current state of the form.
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage
* Field storage.
* @param array $formatter_options
......@@ -203,33 +241,75 @@ class FieldFormatterSource extends FieldValueSourceBase {
* @param string|null $formatter_type
* The formatter name.
*
* @return array|mixed|null
* The field formatter settings.
*
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @return array
* The formatter configuration.
*/
private function getSettingsFieldFormatter(FormStateInterface $form_state, FieldStorageDefinitionInterface $field_storage, array $formatter_options, ?string $formatter_type = '') {
private function getFormatterConfiguration(FormStateInterface $form_state, FieldStorageDefinitionInterface $field_storage, array $formatter_options, ?string $formatter_type = ''): array {
$settings = [];
$third_party_settings = [];
if (!empty($formatter_type)) {
$settings_path = [
'settings',
'settings',
];
$settings = $this->getSettingsFromConfiguration($settings_path);
$formatter_configuration = $this->getFormatterBaseSettingsFromConfiguration();
$settings = $formatter_configuration["settings"] ?? [];
$third_party_settings = $formatter_configuration["third_party_settings"] ?? [];
}
// Get default formatter type.
if (empty($formatter_type) || !isset($formatter_options[$formatter_type])) {
$formatter_type = $this->fieldTypePluginManager->getDefinition($field_storage->getType())['default_formatter'] ?? key($formatter_options);
$settings = $this->formatterPluginManager->getDefaultSettings($field_storage->getType());
$third_party_settings = [];
}
// Reset settings if we change the formatter.
$triggering_element = $form_state->getTriggeringElement();
if (!empty($triggering_element) && $triggering_element['#value'] === $formatter_type) {
$settings = $this->formatterPluginManager->getDefaultSettings($formatter_type);
$third_party_settings = [];
}
if (empty($settings) && !empty($formatter_type)) {
$settings = $this->formatterPluginManager->getDefaultSettings($formatter_type);
$third_party_settings = [];
}
return $settings;
return [
'settings' => $settings,
'third_party_settings' => $third_party_settings,
'type' => $formatter_type,
'label' => '',
'weight' => 0,
];
}
/**
* Adds the formatter third party settings forms.
*
* @param \Drupal\Core\Field\FormatterInterface $plugin
* The formatter.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
* @param array $form
* The (entire) configuration form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return array
* The formatter third party settings form.
*/
protected function thirdPartySettingsForm(FormatterInterface $plugin, FieldDefinitionInterface $field_definition, array $form, FormStateInterface $form_state) {
$settings_form = [];
// Invoke hook_field_formatter_third_party_settings_form(), keying resulting
// subforms by module name.
$this->moduleHandler->invokeAllWith(
'field_formatter_third_party_settings_form',
function (callable $hook, string $module) use (&$settings_form, $plugin, $field_definition, $form, $form_state) {
$settings_form[$module] = $hook(
$plugin,
$field_definition,
EntityDisplayBase::CUSTOM_MODE,
$form,
$form_state,
);
}
);
return $settings_form;
}
/**
......@@ -309,20 +389,22 @@ class FieldFormatterSource extends FieldValueSourceBase {
private function viewFieldItems(FieldItemListInterface $items, $field_delta = NULL): array {
$returned = [];
$configuration = $this->getConfiguration();
if (empty($configuration['settings']['type'])) {
$formatter_type = $configuration['settings']['type'] ?? '';
$formatter_settings_wrapper = $this->getFormatterBaseSettingsFromConfiguration();
if (empty($formatter_type)) {
// No formatter has been configured.
return $returned;
}
// We use third_party_settings to propagate context to the formatter.
// Only our formatter will know how to use it.
$formatter_config = [
'type' => $configuration['settings']['type'],
'settings' => $configuration['settings']['settings'] ?? [],
'third_party_settings' => [
'type' => $formatter_type,
'settings' => $formatter_settings_wrapper['settings'] ?? [],
'third_party_settings' => array_merge($formatter_settings_wrapper['third_party_settings'] ?? [], [
'ui_patterns' => [
'context' => $this->context,
],
],
]),
];
if ($field_delta === NULL) {
$rendered_field = $items->view($formatter_config);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment