From 8fe23f0b5fa01a8bfb99302300baac2f22a845b7 Mon Sep 17 00:00:00 2001 From: "Christian.wiedemann" <7688-Christian.wiedemann@users.noreply.drupalcode.org> Date: Mon, 26 Feb 2024 18:37:00 +0000 Subject: [PATCH] Issue #3414291 by pdureau, Christian.wiedemann: Source plugins, methods naming and interfaces use --- .../src/Plugin/Block/ComponentBlock.php | 8 +- .../src/Plugin/Layout/ComponentLayout.php | 14 +- .../ui_patterns_layouts.module | 3 +- .../ui_patterns_legacy.module | 8 +- src/ComponentPluginManager.php | 2 +- src/Element/ComponentElementAlter.php | 3 + src/Element/ComponentElementBuilder.php | 33 ++--- src/Element/ComponentForm.php | 123 ++++++++---------- src/Element/ComponentFormBase.php | 5 +- src/Element/ComponentPropsForm.php | 4 +- src/Element/ComponentSlotsForm.php | 9 +- src/Form/ComponentFormBuilderTrait.php | 49 ++++++- .../ComponentSettingsFormBuilderTrait.php | 12 +- .../UiPatterns/Source/AttributesWidget.php | 2 +- .../UiPatterns/Source/ChecboxesWidget.php | 10 +- .../UiPatterns/Source/CheckboxWidget.php | 8 +- src/Plugin/UiPatterns/Source/ColorWidget.php | 8 +- .../UiPatterns/Source/ListTextareaWidget.php | 2 +- src/Plugin/UiPatterns/Source/MenuSource.php | 60 ++++++--- src/Plugin/UiPatterns/Source/NumberWidget.php | 8 +- src/Plugin/UiPatterns/Source/PathSource.php | 10 +- src/Plugin/UiPatterns/Source/RadiosWidget.php | 10 +- src/Plugin/UiPatterns/Source/SelectWidget.php | 4 +- .../UiPatterns/Source/TextfieldWidget.php | 12 +- src/Plugin/UiPatterns/Source/TokenSource.php | 10 +- src/Plugin/UiPatterns/Source/UrlWidget.php | 10 +- .../UiPatterns/Source/WysiwygWidget.php | 2 +- src/PluginSettingsInterface.php | 77 +++++++++++ src/SourceInterface.php | 66 +--------- src/SourcePluginManager.php | 2 +- tests/src/Kernel/SourcePluginManagerTest.php | 10 +- ui_patterns.services.yml | 4 +- 32 files changed, 331 insertions(+), 257 deletions(-) create mode 100644 src/PluginSettingsInterface.php diff --git a/modules/ui_patterns_blocks/src/Plugin/Block/ComponentBlock.php b/modules/ui_patterns_blocks/src/Plugin/Block/ComponentBlock.php index 6d831958b..022a8307c 100644 --- a/modules/ui_patterns_blocks/src/Plugin/Block/ComponentBlock.php +++ b/modules/ui_patterns_blocks/src/Plugin/Block/ComponentBlock.php @@ -32,8 +32,7 @@ class ComponentBlock extends BlockBase { * {@inheritdoc} */ public function build() { - $configuration = $this->getConfiguration(); - $build = $this->buildComponentRenderable($configuration); + $build = $this->buildComponentRenderable(); return $build; } @@ -41,15 +40,14 @@ class ComponentBlock extends BlockBase { * {@inheritdoc} */ public function blockForm($form, FormStateInterface $form_state) { - $configuration = $this->getConfiguration(); - return $this->buildComponentsForm($form_state, $configuration['ui_patterns'], NULL, TRUE, TRUE); + return $this->buildComponentsForm($form_state, NULL, TRUE, TRUE); } /** * {@inheritdoc} */ public function blockSubmit($form, FormStateInterface $form_state) { - $this->configuration['ui_patterns'] = $form_state->getValue('ui_patterns'); + $this->submitComponentsForm($form_state); } } diff --git a/modules/ui_patterns_layouts/src/Plugin/Layout/ComponentLayout.php b/modules/ui_patterns_layouts/src/Plugin/Layout/ComponentLayout.php index 65e66e276..ef94b949d 100644 --- a/modules/ui_patterns_layouts/src/Plugin/Layout/ComponentLayout.php +++ b/modules/ui_patterns_layouts/src/Plugin/Layout/ComponentLayout.php @@ -42,14 +42,9 @@ class ComponentLayout extends LayoutDefault implements PluginFormInterface { * {@inheritdoc} */ public function build(array $regions) { - $build = parent::build($regions); - $configuration = $this->getConfiguration(); - $data = $this->buildComponentRenderable($configuration, $this->getPluginId()); + $build = $this->buildComponentRenderable($this->getPluginDefinition()->id()); $build['#layout'] = $this; - $build['#slots'] = $regions; - $build['#props'] = $data['#props'] ?? []; - $build['#type'] = $data['#type']; - $build['#component'] = $data['#component']; + $regions = parent::build($regions); foreach ($this->getPluginDefinition()->getRegionNames() as $region_name) { if (array_key_exists($region_name, $regions)) { $build['#slots'][$region_name] = $regions[$region_name]; @@ -70,9 +65,8 @@ class ComponentLayout extends LayoutDefault implements PluginFormInterface { */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $form = parent::buildConfigurationForm($form, $form_state); - $configuration = $this->getConfiguration(); $component_id = $this->getPluginDefinition()->id(); - return $form + $this->buildComponentsForm($form_state, $configuration['ui_patterns'], $component_id, FALSE, TRUE); + return $form + $this->buildComponentsForm($form_state, $component_id, FALSE, TRUE); } /** @@ -85,7 +79,7 @@ class ComponentLayout extends LayoutDefault implements PluginFormInterface { * {@inheritdoc} */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state):void { - $this->configuration['ui_patterns'] = $form_state->getValue('ui_patterns'); + $this->submitComponentsForm($form_state); parent::submitConfigurationForm($form, $form_state); } diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.module b/modules/ui_patterns_layouts/ui_patterns_layouts.module index 0095c3e75..d5681b0d1 100644 --- a/modules/ui_patterns_layouts/ui_patterns_layouts.module +++ b/modules/ui_patterns_layouts/ui_patterns_layouts.module @@ -17,10 +17,9 @@ use Drupal\Core\Layout\LayoutDefinition; * Implements hook_layout_alter(). */ function ui_patterns_layouts_layout_alter(&$definitions) { - /** @var \Drupal\sdc\ComponentPluginManager $plugin_manager */ $plugin_manager = \Drupal::service('plugin.manager.sdc'); /** @var \Drupal\sdc\Component\ComponentMetadata[] $components */ - $components = $plugin_manager->getDefinitions(); + $components = $plugin_manager->getSortedDefinitions(); foreach ($components as $component) { $definition = [ 'label' => $component['name'] ?? $component['id'], diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.module b/modules/ui_patterns_legacy/ui_patterns_legacy.module index bd7fac732..a1557eba4 100644 --- a/modules/ui_patterns_legacy/ui_patterns_legacy.module +++ b/modules/ui_patterns_legacy/ui_patterns_legacy.module @@ -14,6 +14,10 @@ function ui_patterns_legacy_element_info_alter(array &$types) { $types = _ui_patterns_legacy_clone_component_element($types, "pattern"); $types = _ui_patterns_legacy_clone_component_element($types, "pattern_preview"); } + $moduleHandler = \Drupal::service('module_handler'); + if ($moduleHandler->moduleExists('ui_patterns_library')) { + array_unshift($types["pattern_preview"]['#pre_render'], 'ui_patterns_library.component_element_alter:alter'); + } } /** @@ -22,10 +26,6 @@ function ui_patterns_legacy_element_info_alter(array &$types) { function _ui_patterns_legacy_clone_component_element(array $types, string $element_id): array { $types[$element_id] = $types['component']; array_unshift($types[$element_id]['#pre_render'], 'ui_patterns.component_element_alter:alter'); - $moduleHandler = \Drupal::service('module_handler'); - if ($moduleHandler->moduleExists('ui_patterns_library')) { - array_unshift($types[$element_id]['#pre_render'], 'ui_patterns_library.component_element_alter:alter'); - } array_unshift($types[$element_id]['#pre_render'], 'ui_patterns_legacy.component_element_alter:convert'); return $types; } diff --git a/src/ComponentPluginManager.php b/src/ComponentPluginManager.php index b7c9da955..88a621c76 100644 --- a/src/ComponentPluginManager.php +++ b/src/ComponentPluginManager.php @@ -170,7 +170,7 @@ class ComponentPluginManager extends SdcPluginManager implements CategorizingPlu $label_key = 'name'; uasort($definitions, function ($a, $b) use ($label_key) { if ((string) $a['group'] != (string) $b['group']) { - return strnatcasecmp($a['group'], $b['group']); + return strnatcasecmp($a['group'] ?? '', $b['group'] ?? ''); } return strnatcasecmp($a[$label_key], $b[$label_key]); }); diff --git a/src/Element/ComponentElementAlter.php b/src/Element/ComponentElementAlter.php index 9ba701c9c..28a516ca0 100644 --- a/src/Element/ComponentElementAlter.php +++ b/src/Element/ComponentElementAlter.php @@ -45,6 +45,9 @@ class ComponentElementAlter implements TrustedCallbackInterface { elseif (is_string($slot)) { $element['#slots'][$slot_id] = ['#markup' => $slot]; } + elseif (is_string($slot)) { + $element['#slots'][$slot_id] = ['#markup' => $slot]; + } // Because SDC validator is sometimes confused by a null slot. if (is_null($slot)) { diff --git a/src/Element/ComponentElementBuilder.php b/src/Element/ComponentElementBuilder.php index 5c51b630f..b19c4b106 100644 --- a/src/Element/ComponentElementBuilder.php +++ b/src/Element/ComponentElementBuilder.php @@ -39,15 +39,16 @@ class ComponentElementBuilder implements TrustedCallbackInterface { * Build component data provided to the SDC element. */ public function build(array $element): array { - if (!isset($element['#configuration'])) { + if (!isset($element['#ui_patterns'])) { return $element; } - $configuration = $element['#configuration']; + $configuration = $element['#ui_patterns']; $contexts = $element['#source_contexts'] ?? []; $component = $this->componentPluginManager->find($element['#component']); $element = $this->buildProps($element, $component, $configuration, $contexts); $element = $this->buildSlots($element, $component, $configuration, $contexts); - return array_filter($element); + $element['#propsAlter'] = []; + return $element; } /** @@ -69,6 +70,10 @@ class ComponentElementBuilder implements TrustedCallbackInterface { * Add a single prop to the renderable. */ protected function buildProp(array $build, string $prop_id, array $definition, array $configuration, array $contexts): array { + if (isset($build["#props"][$prop_id])) { + // Keep existing props. No known use case yet. + return $build; + } $prop_type = $definition['ui_patterns']['type_definition']; $source_id = array_key_exists("source_id", $configuration) ? $configuration["source_id"] : $this->sourcesManager->getPropTypeDefault($prop_type->getPluginId(), $contexts); if (!$source_id) { @@ -76,11 +81,7 @@ class ComponentElementBuilder implements TrustedCallbackInterface { } $source = $this->sourcesManager->createInstance( $source_id, - [ - 'prop_id' => $prop_id, - 'prop_definition' => $definition, - 'settings' => $configuration, - ] + SourcePluginManager::buildPluginConfiguration($prop_id, $definition, $configuration) ); if (!$source) { return $build; @@ -122,6 +123,10 @@ class ComponentElementBuilder implements TrustedCallbackInterface { * Add a single slot to the renderable. */ protected function buildSlot(array $build, string $slot_id, array $definition, array $configuration, array $contexts): array { + if (isset($build["#slots"][$slot_id])) { + // Keep existing slots. Used by ComponentLayout for example. + return $build; + } if (!isset($configuration["sources"])) { return $build; } @@ -130,8 +135,8 @@ class ComponentElementBuilder implements TrustedCallbackInterface { foreach ($configuration["sources"] as $source_configuration) { $build = $this->addSlotSource($build, $slot_id, $definition, $source_configuration, $contexts); } - if (count($build["#props"][$slot_id]) === 1) { - $build["#props"][$slot_id] = $build["#props"][$slot_id][0]; + if (count($build["#slots"][$slot_id]) === 1) { + $build["#slots"][$slot_id] = $build["#slots"][$slot_id][0]; } return $build; } @@ -146,14 +151,10 @@ class ComponentElementBuilder implements TrustedCallbackInterface { } $source = $this->sourcesManager->createInstance( $source_id, - [ - 'prop_id' => $slot_id, - 'prop_definition' => $definition, - 'settings' => $configuration, - ] + SourcePluginManager::buildPluginConfiguration($slot_id, $definition, $configuration) ); $build = $source->alterComponent($build); - $build["#props"][$slot_id][] = $source->getData(); + $build["#slots"][$slot_id][] = $source->getData(); return $build; } diff --git a/src/Element/ComponentForm.php b/src/Element/ComponentForm.php index 501ac7712..41efec21a 100644 --- a/src/Element/ComponentForm.php +++ b/src/Element/ComponentForm.php @@ -2,7 +2,6 @@ namespace Drupal\ui_patterns\Element; -use Drupal\Component\Utility\Html; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Form\FormStateInterface; @@ -75,6 +74,7 @@ class ComponentForm extends ComponentFormBase { if ($input) { $value = [ 'component_id' => $input['component_id'] ?? NULL, + 'variant_id' => $input['component_form']['variant_id'] ?? NULL, 'props' => $input['component_form']['props'] ?? [], 'slots' => $input['component_form']['slots'] ?? [], ]; @@ -84,6 +84,7 @@ class ComponentForm extends ComponentFormBase { else { return [ 'component_id' => NULL, + 'variant_id' => NULL, 'props' => [], 'slots' => [], ]; @@ -113,8 +114,7 @@ class ComponentForm extends ComponentFormBase { $element["component_form"] = self::buildComponentForm( $element, $wrapper_id, - $component_id, - $form_state + $component_id ); $element['#tree'] = TRUE; return $element; @@ -127,70 +127,27 @@ class ComponentForm extends ComponentFormBase { * The component form. */ private static function buildComponentForm( - &$element, - $wrapper_id, - $component_id, - FormStateInterface $form_state + array $element, + string $wrapper_id, + ?string $component_id ): array { - + if (!$component_id) { + return $element; + } $form = [ '#type' => 'container', '#prefix' => '<div id="' . $wrapper_id . '">', '#suffix' => '</div>', ]; - if ($component_id !== NULL) { - $form['variant_id'] = self::buildComponentVariantSelectorForm( - $element, - $element['#default_value']['variant_id'] ?? NULL, - ); - $form += self::buildComponentVariantForm( - $wrapper_id, - $element - ); - } - + $form['variant_id'] = self::buildComponentVariantSelectorForm( + $element, + $element['#default_value']['variant_id'] ?? NULL, + ); + $form['slots'] = self::buildSlotsForm($element, $component_id); + $form['props'] = self::buildPropsForm($element, $component_id); return $form; } - /** - * Build the component variant form. - * - * @return array - * The component variant form. - */ - private static function buildComponentVariantForm( - $wrapper_id, - $element - ): array { - $component_id = $element['#default_value']['component_id'] ?? $element['#component_id'] ?? NULL; - - return [ - '#type' => 'container', - '#prefix' => '<div id="' . $wrapper_id . '">', - '#suffix' => '</div>', - 'slots' => [ - '#title' => 'Slots', - '#type' => 'component_slots_form', - '#component_id' => $component_id, - '#source_context' => $element['#source_context'], - '#ajax_url' => $element['#ajax_url'], - '#access' => $element['#render_slots'] ?? TRUE, - '#default_value' => [ - 'slots' => $element['#default_value']['slots'], - ], - ], - 'props' => [ - '#title' => 'Props', - '#type' => 'component_props_form', - '#component_id' => $component_id, - '#source_context' => $element['#source_context'], - '#ajax_url' => $element['#ajax_url'], - '#access' => $element['#render_props'] ?? TRUE, - '#default_value' => ['props' => $element['#default_value']['props']], - ], - ]; - } - /** * Build components selector widget. * @@ -198,8 +155,8 @@ class ComponentForm extends ComponentFormBase { * The component select. */ private static function buildComponentSelectorForm( - $wrapper_id, - $selected_component_id + ?string $wrapper_id, + ?string $selected_component_id ): array { $definition_groups = \Drupal::service("plugin.manager.sdc")->getGroupedDefinitions(); $options = []; @@ -211,7 +168,6 @@ class ComponentForm extends ComponentFormBase { } $options[$definition_group_id] = $group_options; } - return [ "#type" => "select", "#title" => t("Component"), @@ -244,17 +200,46 @@ class ComponentForm extends ComponentFormBase { foreach ($definition["variants"] as $variant_id => $variant) { $options[$variant_id] = $variant["title"]; } - $id = strtr(Html::getId('variant_id'), '-', '_'); - - return self::expandAjax([ + return [ "#type" => "select", - '#name' => $id, - '#id' => $id, "#title" => t("Variant"), "#options" => $options, - '#ui_patterns' => ['id' => 'variant_id'], '#default_value' => $default_variant_id, - ]); + ]; + } + + /** + * Build slots form. + */ + private static function buildSlotsForm(array $element, string $component_id): array { + return [ + '#title' => 'Slots', + '#type' => 'component_slots_form', + '#component_id' => $component_id, + '#source_context' => $element['#source_context'], + '#ajax_url' => $element['#ajax_url'], + '#access' => $element['#render_slots'] ?? TRUE, + '#default_value' => [ + 'slots' => $element['#default_value']['slots'], + ], + ]; + } + + /** + * Build props form. + */ + private static function buildPropsForm(array $element, string $component_id): array { + return [ + '#title' => 'Props', + '#type' => 'component_props_form', + '#component_id' => $component_id, + '#source_context' => $element['#source_context'], + '#ajax_url' => $element['#ajax_url'], + '#access' => $element['#render_props'] ?? TRUE, + '#default_value' => [ + 'props' => $element['#default_value']['props'], + ], + ]; } /** @@ -273,7 +258,7 @@ class ComponentForm extends ComponentFormBase { /** * Form element validation handler. */ - public static function validateFormElement(&$element, FormStateInterface $form_state, &$complete_form) { + public static function validateFormElement(array &$element, FormStateInterface $form_state) { $trigger_element = $form_state->getTriggeringElement(); if (isset($trigger_element['#ui_patterns']) === FALSE) { $form_state->setValueForElement($element, $element['#value']); diff --git a/src/Element/ComponentFormBase.php b/src/Element/ComponentFormBase.php index 67bcdef73..68abb4708 100644 --- a/src/Element/ComponentFormBase.php +++ b/src/Element/ComponentFormBase.php @@ -3,7 +3,6 @@ namespace Drupal\ui_patterns\Element; use Drupal\Component\Utility\Html; -use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\Render\Element\FormElement; use Drupal\Core\Url; @@ -70,13 +69,11 @@ abstract class ComponentFormBase extends FormElement { * * @param array $button_base * Button base render array. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The form state. * * @return array * Button render array. */ - protected static function expandComponentButton(array $button_base, FormStateInterface $form_state) { + protected static function expandComponentButton(array $button_base) { // Do not expand elements that do not have submit handler. if (empty($button_base['#submit'])) { return $button_base; diff --git a/src/Element/ComponentPropsForm.php b/src/Element/ComponentPropsForm.php index 75e8bc04a..9f6b85f7c 100644 --- a/src/Element/ComponentPropsForm.php +++ b/src/Element/ComponentPropsForm.php @@ -100,7 +100,7 @@ class ComponentPropsForm extends ComponentFormBase { } } if ($selected_source) { - $element['value'] = $selected_source->settingsForm( + $element['plugin'] = $selected_source->settingsForm( $element, $form_state ); @@ -143,7 +143,7 @@ class ComponentPropsForm extends ComponentFormBase { 'effect' => 'fade', ], ]; - $actions[$source_id] = self::expandComponentButton($button, $form_state); + $actions[$source_id] = self::expandComponentButton($button); } } if (count($actions) === 0) { diff --git a/src/Element/ComponentSlotsForm.php b/src/Element/ComponentSlotsForm.php index e1de4cf41..822a0fc24 100644 --- a/src/Element/ComponentSlotsForm.php +++ b/src/Element/ComponentSlotsForm.php @@ -128,7 +128,7 @@ class ComponentSlotsForm extends ComponentFormBase { $configuration['source_id'], SourcePluginManager::buildPluginConfiguration($slot_id, $definition, $configuration) ); - $element['value'] = $source->settingsForm([], $form_state); + $element['plugin'] = $source->settingsForm([], $form_state); $element['source_id'] = [ '#type' => 'hidden', '#value' => $source->getPluginId(), @@ -173,10 +173,7 @@ class ComponentSlotsForm extends ComponentFormBase { '#type' => 'ui_patterns_actions', '#ui_patterns_header' => TRUE, 'dropdown_actions' => [ - self::expandComponentButton( - $delete_action, - $form_state - ), + self::expandComponentButton($delete_action), ], ]; } @@ -206,7 +203,7 @@ class ComponentSlotsForm extends ComponentFormBase { 'wrapper' => $wrapper_id, 'effect' => 'fade', ], - ], $form_state); + ]); } return self::buildComponentDropbutton($action_buttons); } diff --git a/src/Form/ComponentFormBuilderTrait.php b/src/Form/ComponentFormBuilderTrait.php index 0f0ff52d8..7bc1f603f 100644 --- a/src/Form/ComponentFormBuilderTrait.php +++ b/src/Form/ComponentFormBuilderTrait.php @@ -13,6 +13,32 @@ use Drupal\Core\Url; */ trait ComponentFormBuilderTrait { + /** + * Adapter function to get plugin configuration. + * + * Overwrite to return settings/options of the + * current plugin. + * + * @return array + * The plugin settings/options. + */ + protected function getComponentConfiguration(): array { + return $this->configuration['ui_patterns'] ?? []; + } + + /** + * Adapter function to set plugin configuration. + * + * Overwrite to return settings/options of the + * current plugin. + * + * @param mixed $configuration + * The configuration to store. + */ + protected function setComponentConfiguration($configuration): void { + $this->configuration['ui_patterns'] = $configuration; + } + /** * Get component form default. * @@ -70,8 +96,6 @@ trait ComponentFormBuilderTrait { * * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state. - * @param array $configuration - * The stored configuration. * @param string|null $initial_component_id * The initial_component_id. If provided the component is changeable. * @param bool $render_slots @@ -81,7 +105,6 @@ trait ComponentFormBuilderTrait { */ protected function buildComponentsForm( FormStateInterface $form_state, - array $configuration, string $initial_component_id = NULL, bool $render_slots = TRUE, bool $render_props = TRUE @@ -93,21 +116,33 @@ trait ComponentFormBuilderTrait { '#component_id' => $initial_component_id, '#ajax_url' => $this->getAjaxUrl($form_state), '#source_context' => $this->getComponentSourceContexts(), - '#default_value' => $configuration, + '#default_value' => $this->getComponentConfiguration(), '#render_slots' => $render_slots, '#render_props' => $render_props, ], ]; } + /** + * Submit the component form. + * + * The form contains. + * + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + */ + public function submitComponentsForm(FormStateInterface $form_state) { + $this->setComponentConfiguration($form_state->getValue('ui_patterns')); + } + /** * Build component renderable (a SDC render element). */ - public function buildCompnentRenderable(array $configuration, $component_id = NULL) { + public function buildComponentRenderable($component_id = NULL) { return [ - '#component' => $component_id ?? $configuration['component_id'], '#type' => 'component', - '#ui_patterns' => $configuration, + '#component' => $component_id ?? $this->getComponentConfiguration()['component_id'], + '#ui_patterns' => $this->getComponentConfiguration(), ]; } diff --git a/src/Form/ComponentSettingsFormBuilderTrait.php b/src/Form/ComponentSettingsFormBuilderTrait.php index 06582af22..26dfde3c9 100644 --- a/src/Form/ComponentSettingsFormBuilderTrait.php +++ b/src/Form/ComponentSettingsFormBuilderTrait.php @@ -14,7 +14,7 @@ trait ComponentSettingsFormBuilderTrait { use ComponentFormBuilderTrait; /** - * PropTypeAdapter function for plugin settings/options. + * Adapter function for plugin settings/options. * * Overwrite to return settings/options of the * current plugin. @@ -24,6 +24,13 @@ trait ComponentSettingsFormBuilderTrait { */ abstract protected function getComponentSettings(): array; + /** + * {@inheritdoc} + */ + protected function getComponentConfiguration(): array { + return $this->getComponentSettings()['ui_patterns'] ?? []; + } + /** * {@inheritdoc} */ @@ -31,8 +38,7 @@ trait ComponentSettingsFormBuilderTrait { array $form, FormStateInterface $form_state ): array { - $configuration = $this->getComponentSettings()['ui_patterns'] ?? []; - return $this->buildComponentsForm($form_state, $configuration); + return $this->buildComponentsForm($form_state); } } diff --git a/src/Plugin/UiPatterns/Source/AttributesWidget.php b/src/Plugin/UiPatterns/Source/AttributesWidget.php index e765344cd..02ef6336b 100644 --- a/src/Plugin/UiPatterns/Source/AttributesWidget.php +++ b/src/Plugin/UiPatterns/Source/AttributesWidget.php @@ -43,7 +43,7 @@ class AttributesWidget extends SourcePluginBase { // Attributes are associative arrays, but this source plugin is storing // them as string in config. // It would be better to use something else than a textfield one day. - $form = [ + $form['value'] = [ '#type' => 'textfield', '#title' => $this->propDefinition['title'], '#default_value' => $this->getSetting('value'), diff --git a/src/Plugin/UiPatterns/Source/ChecboxesWidget.php b/src/Plugin/UiPatterns/Source/ChecboxesWidget.php index 480af424a..7cefbf05f 100644 --- a/src/Plugin/UiPatterns/Source/ChecboxesWidget.php +++ b/src/Plugin/UiPatterns/Source/ChecboxesWidget.php @@ -38,10 +38,12 @@ class ChecboxesWidget extends SourcePluginBase { } // @todo max & min return [ - '#type' => 'checkboxes', - '#title' => $this->propDefinition['title'], - '#default_value' => $this->getSetting('value') ?? [], - "#options" => $options, + 'value' => [ + '#type' => 'checkboxes', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->getSetting('value') ?? [], + "#options" => $options, + ], ]; } diff --git a/src/Plugin/UiPatterns/Source/CheckboxWidget.php b/src/Plugin/UiPatterns/Source/CheckboxWidget.php index 75fa05769..15069ded8 100644 --- a/src/Plugin/UiPatterns/Source/CheckboxWidget.php +++ b/src/Plugin/UiPatterns/Source/CheckboxWidget.php @@ -33,9 +33,11 @@ class CheckboxWidget extends SourcePluginBase { */ public function settingsForm(array $form, FormStateInterface $form_state): array { return [ - '#type' => 'checkbox', - '#title' => $this->propDefinition['title'], - '#default_value' => $this->getSetting('value'), + 'value' => [ + '#type' => 'checkbox', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->getSetting('value'), + ], ]; } diff --git a/src/Plugin/UiPatterns/Source/ColorWidget.php b/src/Plugin/UiPatterns/Source/ColorWidget.php index f17c0a1ef..d1ac9582f 100644 --- a/src/Plugin/UiPatterns/Source/ColorWidget.php +++ b/src/Plugin/UiPatterns/Source/ColorWidget.php @@ -26,9 +26,11 @@ class ColorWidget extends SourcePluginBase { */ public function settingsForm(array $form, FormStateInterface $form_state): array { return [ - '#type' => 'color', - '#title' => $this->propDefinition['title'], - '#default_value' => $this->getSetting('value'), + 'value' => [ + '#type' => 'color', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->getSetting('value'), + ], ]; } diff --git a/src/Plugin/UiPatterns/Source/ListTextareaWidget.php b/src/Plugin/UiPatterns/Source/ListTextareaWidget.php index 3002cf089..9eb45bed6 100644 --- a/src/Plugin/UiPatterns/Source/ListTextareaWidget.php +++ b/src/Plugin/UiPatterns/Source/ListTextareaWidget.php @@ -37,7 +37,7 @@ class ListTextareaWidget extends SourcePluginBase { if (is_array($items)) { $items = implode("\r", $items); } - $form = [ + $form['value'] = [ '#type' => 'textarea', '#title' => $this->propDefinition['title'], '#default_value' => $items, diff --git a/src/Plugin/UiPatterns/Source/MenuSource.php b/src/Plugin/UiPatterns/Source/MenuSource.php index 055d4afe7..a7464663e 100644 --- a/src/Plugin/UiPatterns/Source/MenuSource.php +++ b/src/Plugin/UiPatterns/Source/MenuSource.php @@ -55,44 +55,67 @@ class MenuSource extends SourcePluginBase { /** * {@inheritdoc} */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - $plugin = parent::create($container, $configuration, $plugin_id, $plugin_definition); + public static function create( + ContainerInterface $container, + array $configuration, + $plugin_id, + $plugin_definition + ) { + $plugin = parent::create( + $container, + $configuration, + $plugin_id, + $plugin_definition + ); $plugin->menuLinkTree = $container->get('menu.link_tree'); $plugin->entityTypeManager = $container->get('entity_type.manager'); $plugin->moduleHandler = $container->get('module_handler'); return $plugin; } + /** + * {@inheritdoc} + */ + public function defaultSettings() { + return [ + 'menu' => NULL, + 'level' => 1, + 'depth' => 0, + ]; + } + /** * {@inheritdoc} */ public function getData(): mixed { - $value = parent::getData(); - if (!$value) { + $menu_id = $this->getSetting('menu'); + if (!$menu_id) { return []; } - $this->menuId = $value["menu"]; - return $this->getMenuItems($value); + $this->menuId = $menu_id; + return $this->getMenuItems(); } /** * {@inheritdoc} */ - public function settingsForm(array $form, FormStateInterface $form_state): array { - $value = $this->getSetting('value') ?? []; + public function settingsForm( + array $form, + FormStateInterface $form_state + ): array { $form = []; $form["menu"] = [ '#type' => 'select', '#title' => $this->propDefinition['title'] . ": " . $this->t("Menu"), '#options' => $this->getMenuList(), - '#default_value' => \array_key_exists("menu", $value) ? $value["menu"] : "", + '#default_value' => $this->getSetting('menu'), ]; $options = range(0, $this->menuLinkTree->maxDepth()); unset($options[0]); $form['level'] = [ '#type' => 'select', '#title' => $this->t('Initial visibility level'), - '#default_value' => \array_key_exists("level", $value) ? $value["level"] : 1, + '#default_value' => $this->getSetting('level'), '#options' => $options, '#required' => TRUE, ]; @@ -100,13 +123,14 @@ class MenuSource extends SourcePluginBase { $form['depth'] = [ '#type' => 'select', '#title' => $this->t('Number of levels to display'), - '#default_value' => \array_key_exists("depth", $value) ? $value["depth"] : 0, + '#default_value' => $this->getSetting('depth'), '#options' => $options, - '#description' => $this->t('This maximum number includes the initial level and the final display is dependant of the pattern template.'), + '#description' => $this->t( + 'This maximum number includes the initial level and the final display is dependant of the pattern template.' + ), '#required' => TRUE, ]; return $form; - } /** @@ -133,10 +157,10 @@ class MenuSource extends SourcePluginBase { * @return array * List of items. */ - private function getMenuItems($value): array { + private function getMenuItems(): array { $menuLinkTree = $this->menuLinkTree; - $level = (int) \array_key_exists("level", $value) ? $value["level"] : 1; - $depth = (int) \array_key_exists("depth", $value) ? $value["depth"] : 0; + $level = (int) $this->getSetting('level'); + $depth = (int) $this->getSetting('depth'); $parameters = new MenuTreeParameters(); $parameters->setMinDepth($level); @@ -145,7 +169,9 @@ class MenuSource extends SourcePluginBase { // Hence this is a relative depth that we must convert to an actual // (absolute) depth, that may never exceed the maximum depth. if ($depth > 0) { - $parameters->setMaxDepth(min($level + $depth - 1, $menuLinkTree->maxDepth())); + $parameters->setMaxDepth( + min($level + $depth - 1, $menuLinkTree->maxDepth()) + ); } $tree = $menuLinkTree->load($this->menuId, $parameters); diff --git a/src/Plugin/UiPatterns/Source/NumberWidget.php b/src/Plugin/UiPatterns/Source/NumberWidget.php index d834839bf..75bceea96 100644 --- a/src/Plugin/UiPatterns/Source/NumberWidget.php +++ b/src/Plugin/UiPatterns/Source/NumberWidget.php @@ -37,20 +37,20 @@ class NumberWidget extends SourcePluginBase { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - $form = [ + $form['value'] = [ '#type' => 'number', '#title' => $this->propDefinition['title'], '#default_value' => $this->getSetting('value'), '#step' => 0.01, ]; if ($this->propDefinition["type"] === "integer") { - $form['#step'] = 1; + $form['value']['#step'] = 1; } if (isset($this->propDefinition["minimum"])) { - $form['#min'] = $this->propDefinition["minimum"]; + $form['value']['#min'] = $this->propDefinition["minimum"]; } if (isset($this->propDefinition["maximum"])) { - $form['#max'] = $this->propDefinition["maximum"]; + $form['value']['#max'] = $this->propDefinition["maximum"]; } return $form; } diff --git a/src/Plugin/UiPatterns/Source/PathSource.php b/src/Plugin/UiPatterns/Source/PathSource.php index f0d80c495..b02c34e43 100644 --- a/src/Plugin/UiPatterns/Source/PathSource.php +++ b/src/Plugin/UiPatterns/Source/PathSource.php @@ -51,10 +51,12 @@ class PathSource extends SourcePluginBase { $value = $this->getSetting('value'); $value = $this->getUrlFromRoute($value); return [ - '#type' => 'path', - '#title' => $this->propDefinition['title'], - '#default_value' => $value, - '#description' => $this->t("Enter an internal path"), + 'value' => [ + '#type' => 'path', + '#title' => $this->propDefinition['title'], + '#default_value' => $value, + '#description' => $this->t("Enter an internal path"), + ], ]; } diff --git a/src/Plugin/UiPatterns/Source/RadiosWidget.php b/src/Plugin/UiPatterns/Source/RadiosWidget.php index 6c3cba9b2..97250b097 100644 --- a/src/Plugin/UiPatterns/Source/RadiosWidget.php +++ b/src/Plugin/UiPatterns/Source/RadiosWidget.php @@ -37,10 +37,12 @@ class RadiosWidget extends SourcePluginBase { $options[$item] = $item; } return [ - '#type' => 'radios', - '#title' => $this->propDefinition['title'], - '#default_value' => $this->getSetting('value'), - "#options" => $options, + 'value' => [ + '#type' => 'radios', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->getSetting('value'), + "#options" => $options, + ], ]; } diff --git a/src/Plugin/UiPatterns/Source/SelectWidget.php b/src/Plugin/UiPatterns/Source/SelectWidget.php index 106a37001..f165d740a 100644 --- a/src/Plugin/UiPatterns/Source/SelectWidget.php +++ b/src/Plugin/UiPatterns/Source/SelectWidget.php @@ -36,7 +36,7 @@ class SelectWidget extends SourcePluginBase { foreach ($this->propDefinition['enum'] as $item) { $options[$item] = $item; } - $form = [ + $form['value'] = [ '#type' => 'select', '#title' => $this->propDefinition['title'], '#default_value' => $this->getSetting('value'), @@ -44,7 +44,7 @@ class SelectWidget extends SourcePluginBase { ]; // With Firefox, autocomplete may override #default_value. // https://drupal.stackexchange.com/questions/257732/default-value-not-working-in-select-field - $form['#attributes']['autocomplete'] = 'off'; + $form['value']['#attributes']['autocomplete'] = 'off'; return $form; } diff --git a/src/Plugin/UiPatterns/Source/TextfieldWidget.php b/src/Plugin/UiPatterns/Source/TextfieldWidget.php index e318a276f..e4c691d5c 100644 --- a/src/Plugin/UiPatterns/Source/TextfieldWidget.php +++ b/src/Plugin/UiPatterns/Source/TextfieldWidget.php @@ -27,27 +27,27 @@ class TextfieldWidget extends SourcePluginBase { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - $form = [ + $form['value'] = [ '#type' => 'textfield', '#title' => $this->propDefinition['title'], '#default_value' => $this->getSetting('value'), ]; $description = []; if (isset($this->propDefinition["pattern"])) { - $form['#pattern'] = $this->propDefinition["pattern"]; + $form['value']['#pattern'] = $this->propDefinition["pattern"]; $description[] = $this->t("Constraint: @pattern", ["@pattern" => $this->propDefinition["pattern"]]); } if (isset($this->propDefinition["maxLength"])) { - $form['#maxlength'] = $this->propDefinition["maxLength"]; - $form['#size'] = $this->propDefinition["maxLength"]; + $form['value']['#maxlength'] = $this->propDefinition["maxLength"]; + $form['value']['#size'] = $this->propDefinition["maxLength"]; $description[] = $this->t("Max length: @length", ["@length" => $this->propDefinition["maxLength"]]); } if (!isset($this->propDefinition["pattern"]) && isset($this->propDefinition["minLength"])) { // @todo Cover also the use case pattern + minLength. - $form['#pattern'] = "^.{" . $this->propDefinition["minLength"] . ",}$"; + $form['value']['#pattern'] = "^.{" . $this->propDefinition["minLength"] . ",}$"; $description[] = $this->t("Min length: @length", ["@length" => $this->propDefinition["minLength"]]); } - $form["#description"] = implode("; ", $description); + $form['value']["#description"] = implode("; ", $description); return $form; } diff --git a/src/Plugin/UiPatterns/Source/TokenSource.php b/src/Plugin/UiPatterns/Source/TokenSource.php index 9f0e0456f..8febc2bf2 100644 --- a/src/Plugin/UiPatterns/Source/TokenSource.php +++ b/src/Plugin/UiPatterns/Source/TokenSource.php @@ -41,11 +41,13 @@ class TokenSource extends SourcePluginBase { */ public function settingsForm(array $form, FormStateInterface $form_state): array { return [ - '#type' => 'textfield', - '#title' => $this->t("@prop, with token", ["@prop" => $this->propDefinition['title']]), - '#default_value' => $this->getSetting('value'), + 'value' => [ + '#type' => 'textfield', + '#title' => $this->t("@prop, with token", ["@prop" => $this->propDefinition['title']]), + '#default_value' => $this->getSetting('value'), // Tokens always start with a [ and end with a ]. - '#pattern' => '^\[.+\]$', + '#pattern' => '^\[.+\]$', + ], ]; } diff --git a/src/Plugin/UiPatterns/Source/UrlWidget.php b/src/Plugin/UiPatterns/Source/UrlWidget.php index f4caa5537..d8175dd3c 100644 --- a/src/Plugin/UiPatterns/Source/UrlWidget.php +++ b/src/Plugin/UiPatterns/Source/UrlWidget.php @@ -26,10 +26,12 @@ class UrlWidget extends SourcePluginBase { */ public function settingsForm(array $form, FormStateInterface $form_state): array { return [ - '#type' => 'url', - '#title' => $this->propDefinition['title'], - '#default_value' => $this->getSetting('value'), - '#description' => $this->t("Enter an external URL"), + 'value' => [ + '#type' => 'url', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->getSetting('value'), + '#description' => $this->t("Enter an external URL"), + ], ]; } diff --git a/src/Plugin/UiPatterns/Source/WysiwygWidget.php b/src/Plugin/UiPatterns/Source/WysiwygWidget.php index 728c94a0e..8d0695285 100644 --- a/src/Plugin/UiPatterns/Source/WysiwygWidget.php +++ b/src/Plugin/UiPatterns/Source/WysiwygWidget.php @@ -38,7 +38,7 @@ class WysiwygWidget extends SourcePluginBase { */ public function settingsForm(array $form, FormStateInterface $form_state): array { $value = $this->getSetting('value'); - $form = [ + $form['value'] = [ '#type' => 'text_format', '#default_value' => $value["value"], '#format' => $value["format"], diff --git a/src/PluginSettingsInterface.php b/src/PluginSettingsInterface.php new file mode 100644 index 000000000..7e24a3781 --- /dev/null +++ b/src/PluginSettingsInterface.php @@ -0,0 +1,77 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\ui_patterns; + +use Drupal\Core\Form\FormStateInterface; + +/** + * Interface for source plugins. + */ +interface PluginSettingsInterface { + + /** + * Defines the default settings for this plugin. + * + * @return array + * A list of default settings, keyed by the setting name. + */ + public function defaultSettings(); + + /** + * Returns the array of settings, including defaults for missing settings. + * + * @return array + * The array of settings. + */ + public function getSettings(); + + /** + * Returns the value of a setting, or its default value if absent. + * + * @param string $key + * The setting name. + * + * @return mixed + * The setting value. + */ + public function getSetting($key); + + /** + * Sets the settings for the plugin. + * + * @param array $settings + * The array of settings, keyed by setting names. Missing settings will be + * assigned their default values. + * + * @return $this + */ + public function setSettings(array $settings); + + /** + * Sets the value of a setting for the plugin. + * + * @param string $key + * The setting name. + * @param mixed $value + * The setting value. + * + * @return $this + */ + public function setSetting($key, $value); + + /** + * Returns a form to configure settings for the source plugins. + * + * @param array $form + * The form where the settings form is being included in. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * The form elements for the source settings. + */ + public function settingsForm(array $form, FormStateInterface $form_state): array; + +} diff --git a/src/SourceInterface.php b/src/SourceInterface.php index 88346f944..639a43732 100644 --- a/src/SourceInterface.php +++ b/src/SourceInterface.php @@ -6,62 +6,11 @@ namespace Drupal\ui_patterns; use Drupal\Component\Plugin\ConfigurableInterface; use Drupal\Component\Plugin\PluginInspectionInterface; -use Drupal\Core\Form\FormStateInterface; /** * Interface for source plugins. */ -interface SourceInterface extends ConfigurableInterface, PluginInspectionInterface { - - /** - * Defines the default settings for this plugin. - * - * @return array - * A list of default settings, keyed by the setting name. - */ - public function defaultSettings(); - - /** - * Returns the array of settings, including defaults for missing settings. - * - * @return array - * The array of settings. - */ - public function getSettings(); - - /** - * Returns the value of a setting, or its default value if absent. - * - * @param string $key - * The setting name. - * - * @return mixed - * The setting value. - */ - public function getSetting($key); - - /** - * Sets the settings for the plugin. - * - * @param array $settings - * The array of settings, keyed by setting names. Missing settings will be - * assigned their default values. - * - * @return $this - */ - public function setSettings(array $settings); - - /** - * Sets the value of a setting for the plugin. - * - * @param string $key - * The setting name. - * @param mixed $value - * The setting value. - * - * @return $this - */ - public function setSetting($key, $value); +interface SourceInterface extends ConfigurableInterface, PluginInspectionInterface, PluginSettingsInterface { /** * Returns the translated plugin label. @@ -73,19 +22,6 @@ interface SourceInterface extends ConfigurableInterface, PluginInspectionInterfa */ public function getData(): mixed; - /** - * Returns a form to configure settings for the source plugins. - * - * @param array $form - * The form where the settings form is being included in. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - * - * @return array - * The form elements for the source settings. - */ - public function settingsForm(array $form, FormStateInterface $form_state): array; - /** * Returns the associated prop id. */ diff --git a/src/SourcePluginManager.php b/src/SourcePluginManager.php index f7d7cf813..0dc77e1f5 100644 --- a/src/SourcePluginManager.php +++ b/src/SourcePluginManager.php @@ -75,7 +75,7 @@ class SourcePluginManager extends DefaultPluginManager implements ContextAwarePl return [ 'prop_id' => $prop_id, 'prop_definition' => $prop_definition, - 'settings' => $settings, + 'settings' => $settings['plugin'] ?? [], ]; } diff --git a/tests/src/Kernel/SourcePluginManagerTest.php b/tests/src/Kernel/SourcePluginManagerTest.php index 85d9f4d5f..ef7e4e8ee 100644 --- a/tests/src/Kernel/SourcePluginManagerTest.php +++ b/tests/src/Kernel/SourcePluginManagerTest.php @@ -33,9 +33,15 @@ final class SourcePluginManagerTest extends KernelTestBase { * Test callback. */ public function testGetSourcePlugins(): void { - /** @var \Drupal\ui_patterns\SourcePluginManager $source_provider_plugin_manager */ $source_plugin_manager = \Drupal::service('plugin.manager.ui_patterns_source'); - $sources = $source_plugin_manager->getSourcePlugins('string', 'test', ['title' => 'test title']); + $source_ids = array_keys($source_plugin_manager->getDefinitionsForPropType('string')); + $configuration = [ + 'prop_id' => 'test', + 'prop_definition' => ['title' => 'test title'], + 'form_value' => [], + ]; + $source_ids = array_combine($source_ids, $source_ids); + $sources = $source_plugin_manager->createInstances($source_ids, $configuration); $plugin_ids = []; /** @var \Drupal\ui_patterns\SourcePluginBase $source */ foreach ($sources as $source) { diff --git a/ui_patterns.services.yml b/ui_patterns.services.yml index 1b03a88ee..07a6c9969 100644 --- a/ui_patterns.services.yml +++ b/ui_patterns.services.yml @@ -30,17 +30,17 @@ services: arguments: - "@plugin.manager.ui_patterns_prop_type" - # Builders + # Render elements management. ui_patterns.component_element_builder: class: Drupal\ui_patterns\Element\ComponentElementBuilder arguments: - "@plugin.manager.ui_patterns_source" - "@plugin.manager.ui_patterns_prop_type_adapter" - "@plugin.manager.sdc" - ui_patterns.component_element_alter: class: Drupal\ui_patterns\Element\ComponentElementAlter arguments: [] + # JSON schema management. ui_patterns.schema_stream_wrapper: class: Drupal\ui_patterns\SchemaManager\StreamWrapper -- GitLab