diff --git a/composer.json b/composer.json index ecd8c6bb2b4a2ce3de42e80f191dc647cb569357..4eacbf169bbb24dbadfb4973f555eae7ce6c7791 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ "email": "christian.wiedemann@key-tec.de" } ], - "require": { - "drupal/ui_patterns": "^2.0" + "require-dev": { + "drupal/ui_patterns": "2.0.x-dev", + "drupal/field_group": "^3.0" } } diff --git a/modules/ui_patterns_ui_fieldgroups/src/Element/DisplayFormFieldGroup.php b/modules/ui_patterns_ui_fieldgroups/src/Element/DisplayFormFieldGroup.php new file mode 100644 index 0000000000000000000000000000000000000000..326bb65f2bb1d84910f4b9b1a646e44b8a3d8d33 --- /dev/null +++ b/modules/ui_patterns_ui_fieldgroups/src/Element/DisplayFormFieldGroup.php @@ -0,0 +1,67 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\ui_patterns_ui_fieldgroups\Element; + +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Security\TrustedCallbackInterface; +use Drupal\Core\Theme\ComponentPluginManager; + +/** + * Our additions to the SDC render element. + */ +class DisplayFormFieldGroup implements TrustedCallbackInterface { + + /** + * Constructs a ComponentElementAlter. + */ + public function __construct(protected ComponentPluginManager $componentPluginManager) {} + + /** + * {@inheritdoc} + */ + public static function trustedCallbacks() { + return ['process']; + + } + + /** + * Alter SDC component element. + * + * The ::normalizeProps() methods logic has been moved to + * TwigExtension::normalizeProps() in order to be executed also when + * components are loaded from Twig include or embed. + */ + public static function process(array $element, FormStateInterface $form_state): array { + /** @var \Drupal\ui_patterns_ui\Entity\ComponentFormDisplay $display */ + $display = $element['#display'] ?? NULL; + if ($display === NULL) { + return $element; + } + + $display_options = $display->getPropSlotOptions(); + $groups = ui_patterns_ui_fieldgroups_info($display); + foreach ($display_options as $display_option_id => $option) { + if (isset($groups[$display_option_id])) { + $element[$display_option_id] = []; + $group = $groups[$display_option_id]; + $complete_form = $form_state->getCompleteForm(); + field_group_field_group_form_process($element[$display_option_id], $group, $complete_form); + } + if (!empty($option['parent'])) { + $parents = $element['#parents']; + $parents[] = $option['parent']; + if (str_starts_with($display_option_id, 'group_')) { + $element[$display_option_id]['#group'] = $option['parent']; + } + else { + $element[$display_option_id]['#group'] = implode('][', $parents); + } + } + } + + return $element; + } + +} diff --git a/modules/ui_patterns_ui_fieldgroups/src/FieldGroupFormTrait.php b/modules/ui_patterns_ui_fieldgroups/src/FieldGroupFormTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..208b704df75eaf436da1b654f316c6576824b0fc --- /dev/null +++ b/modules/ui_patterns_ui_fieldgroups/src/FieldGroupFormTrait.php @@ -0,0 +1,33 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\ui_patterns_ui_fieldgroups; + +use Drupal\ui_patterns_ui\ComponentFormDisplayInterface; +use Drupal\ui_patterns_ui\Entity\ComponentFormDisplay; + +/** + * Trait for plugins (sources and prop types) handling attributes. + */ +trait FieldGroupFormTrait { + + /** + * Returns the current display. + */ + protected function getDisplay():?ComponentFormDisplayInterface { + $component_id = \Drupal::routeMatch()->getParameter('component_id'); + $form_mode_name = \Drupal::routeMatch()->getParameter('form_mode_name'); + return ComponentFormDisplay::loadByFormMode($component_id, $form_mode_name); + } + + /** + * Returns the current group. + */ + public function getGroup(): mixed { + $group_id = \Drupal::routeMatch()->getParameter('group_id'); + $groups = ui_patterns_ui_fieldgroups_info($this->getDisplay()); + return $groups[$group_id] ?? NULL; + } + +} diff --git a/modules/ui_patterns_ui_fieldgroups/src/Form/FieldGroupAddForm.php b/modules/ui_patterns_ui_fieldgroups/src/Form/FieldGroupAddForm.php new file mode 100644 index 0000000000000000000000000000000000000000..8cb3bde21b0e1fab3b087dcdac0a7f58ab41cec2 --- /dev/null +++ b/modules/ui_patterns_ui_fieldgroups/src/Form/FieldGroupAddForm.php @@ -0,0 +1,114 @@ +<?php + +namespace Drupal\ui_patterns_ui_fieldgroups\Form; + +use Drupal\Core\Form\FormStateInterface; +use Drupal\field_group\Form\FieldGroupAddForm as BaseFieldGroupAddForm; +use Drupal\ui_patterns_ui\Entity\ComponentFormDisplay; +use Drupal\ui_patterns_ui_fieldgroups\FieldGroupFormTrait; + +/** + * Provides a form for adding a fieldgroup to a bundle. + */ +class FieldGroupAddForm extends BaseFieldGroupAddForm { + use FieldGroupFormTrait; + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, mixed $entity_type_id = NULL, mixed $bundle = NULL, mixed $context = NULL) { + $group_id = $this->getRouteMatch()->getParameter('group_id'); + $this->context = 'form'; + if ($group_id !== NULL) { + return $this->buildEditConfigurationForm($form, $form_state); + } + else { + return parent::buildForm($form, $form_state, $entity_type_id, $bundle, 'form'); + } + } + + /** + * {@inheritdoc} + */ + public function buildEditConfigurationForm(array &$form, FormStateInterface $form_state): array { + $group = $this->getGroup(); + $manager = $this->fieldGroupFormatterPluginManager; + $plugin = $manager->getInstance([ + 'format_type' => $group->format_type, + 'configuration' => [ + 'label' => $group->label, + 'settings' => $group->format_settings, + ], + 'group' => $group, + ]); + $form['format_settings'] = [ + '#type' => 'container', + '#tree' => TRUE, + ]; + + $form['format_settings'] += $plugin->settingsForm(); + + $form['actions'] = ['#type' => 'actions']; + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Update group'), + '#button_type' => 'primary', + ]; + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state): void { + $group_id = $this->getRouteMatch()->getParameter('group_id'); + $group_name = $group_id ?? $form_state->get('group_name'); + if ($form_state->get('step') == 'formatter') { + $form_state->set('step', 'configuration'); + $form_state->set('group_label', $form_state->getValue('label')); + $form_state->set('group_name', $form_state->getValue('group_name')); + $form_state->set('group_formatter', $form_state->getValue('group_formatter')); + $form_state->setRebuild(); + } + else { + + if ($group_id === NULL) { + $new_group = (object) [ + 'group_name' => $group_name, + 'context' => 'form', + 'children' => [], + 'parent_name' => '', + 'weight' => 20, + 'format_type' => $form_state->get('group_formatter'), + 'region' => 'hidden', + ]; + } + else { + $new_group = $this->getGroup(); + } + + $new_group->format_settings = $form_state->getValue('format_settings'); + $new_group->label = $new_group->format_settings['label']; + unset($new_group->format_settings['label']); + $new_group->format_settings += $this->fieldGroupFormatterPluginManager->getDefaultSettings($form_state->get('group_formatter'), $this->context); + + $data = (array) $new_group; + unset($data['group_name'], $data['entity_type'], $data['bundle'], $data['mode'], $data['form'], $data['context']); + $form_mode_name = $this->getRouteMatch()->getParameter('form_mode_name'); + $component_id = $this->getRouteMatch()->getParameter('component_id'); + $display = ComponentFormDisplay::loadByFormMode($component_id, $form_mode_name); + $display->setThirdPartySetting('ui_patterns_ui_fieldgroups', $new_group->group_name, $data); + $display->save(); + // Store new group information for any additional submit handlers. + $groups_added = $form_state->get('groups_added'); + $groups_added['_add_new_group'] = $new_group->group_name; + $this->messenger->addMessage($this->t('New group %label successfully created.', ['%label' => $new_group->label])); + + $form_state->setRedirectUrl($display->toUrl('edit-form')); + $this->cacheBackend->invalidate('field_groups'); + + } + + } + +} diff --git a/modules/ui_patterns_ui_fieldgroups/src/Form/FieldGroupDeleteForm.php b/modules/ui_patterns_ui_fieldgroups/src/Form/FieldGroupDeleteForm.php new file mode 100644 index 0000000000000000000000000000000000000000..aa1ea6888c52e9c4ece83b8755d957784526d8a9 --- /dev/null +++ b/modules/ui_patterns_ui_fieldgroups/src/Form/FieldGroupDeleteForm.php @@ -0,0 +1,55 @@ +<?php + +namespace Drupal\ui_patterns_ui_fieldgroups\Form; + +use Drupal\Core\Form\ConfirmFormBase; +use Drupal\Core\Form\FormStateInterface; +use Drupal\ui_patterns_ui_fieldgroups\FieldGroupFormTrait; + +/** + * Provides a form for removing a fieldgroup from a component. + */ +class FieldGroupDeleteForm extends ConfirmFormBase { + use FieldGroupFormTrait; + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return $this->getDisplay()->toUrl('edit-form'); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state): void { + $display = $this->getDisplay(); + $group_id = $this->getRouteMatch()->getParameter('group_id'); + $display->unsetThirdPartySetting('ui_patterns_ui_fieldgroups', $group_id); + $display->removePropSlotOption($group_id); + foreach ($display->getPropSlotOptions() as $prop_id => $options) { + if ($options['parent'] === $group_id) { + $options['parent'] = ''; + $display->setPropSlotOptions($prop_id, $options); + } + } + $display->save(); + $form_state->setRedirectUrl($this->getCancelUrl()); + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + $group = $this->getGroup(); + return $this->t('Are you sure you want to delete the group %group?', ['%group' => $group->label ?? '']); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'ui_patterns_ui_fieldgroups_delete_form'; + } + +} diff --git a/modules/ui_patterns_ui_fieldgroups/src/Plugin/Derivative/UiPatternsUiFieldgroupLocalAction.php b/modules/ui_patterns_ui_fieldgroups/src/Plugin/Derivative/UiPatternsUiFieldgroupLocalAction.php new file mode 100644 index 0000000000000000000000000000000000000000..56fc8a013f275d14104477edf9dec2dd91e509d5 --- /dev/null +++ b/modules/ui_patterns_ui_fieldgroups/src/Plugin/Derivative/UiPatternsUiFieldgroupLocalAction.php @@ -0,0 +1,64 @@ +<?php + +namespace Drupal\ui_patterns_ui_fieldgroups\Plugin\Derivative; + +use Drupal\Component\Plugin\Derivative\DeriverBase; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; +use Drupal\Core\Routing\RouteProviderInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\Theme\ComponentPluginManager; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Provides local action definitions for all component forms. + */ +class UiPatternsUiFieldgroupLocalAction extends DeriverBase implements ContainerDeriverInterface { + + use StringTranslationTrait; + + /** + * Constructs a UiPatternsUiLocalAction object. + */ + public function __construct(protected RouteProviderInterface $routeProvider, protected ComponentPluginManager $componentPluginManager, protected EntityTypeManagerInterface $entityTypeManager) { + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $container->get('router.route_provider'), + $container->get('plugin.manager.sdc'), + $container->get('entity_type.manager'), + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + $this->derivatives = []; + + /** @var \Drupal\ui_patterns_ui\Entity\ComponentFormDisplay[] $displays */ + $displays = $this->entityTypeManager->getStorage('component_form_display')->loadMultiple(); + foreach ($displays as $display) { + $this->derivatives["component_form_display.{$display->getComponentId()}.fieldgroup.add"] = [ + 'route_name' => "entity.component_form_display.add_group", + 'title' => $this->t('Add Fieldgroup'), + 'appears_on' => ["entity.component_form_display.{$display->getComponentId()}.edit_form"], + 'parameters' => [ + 'form_mode_name' => $display->getFormModeName(), + 'component_id' => $display->getComponentId(), + ], + ]; + } + + foreach ($this->derivatives as &$entry) { + $entry += $base_plugin_definition; + } + + return $this->derivatives; + } + +} diff --git a/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.info.yml b/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..a74a85e71df053eacbb351282063762254cc3f8d --- /dev/null +++ b/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.info.yml @@ -0,0 +1,9 @@ +name: UI Patterns UI fieldgroups +type: module +description: Configure patterns with fieldgroups +package: User interface +core_version_requirement: ^10.3 || ^11 +dependencies: + - ui_patterns:ui_patterns (>=2.0) + - ui_patterns:ui_patterns_ui (>=2.0) + - field_group:field_group diff --git a/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.links.action.yml b/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.links.action.yml new file mode 100644 index 0000000000000000000000000000000000000000..44f64583853560b81922cd1a96e31f6c9ae7743b --- /dev/null +++ b/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.links.action.yml @@ -0,0 +1,3 @@ +ui_patterns_ui_fieldgroups.fieldgroups: + class: \Drupal\Core\Menu\LocalActionDefault + deriver: \Drupal\ui_patterns_ui_fieldgroups\Plugin\Derivative\UiPatternsUiFieldgroupLocalAction diff --git a/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.module b/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.module new file mode 100644 index 0000000000000000000000000000000000000000..42bb3c8ec8b3ab30cc6ab46e58e84c82d5a33982 --- /dev/null +++ b/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.module @@ -0,0 +1,112 @@ +<?php + +/** + * @file + * Contains ui_patterns_ui_fieldgroups.module. + */ + +use Drupal\Core\Url; +use Drupal\ui_patterns_ui\ComponentFormDisplayInterface; +use Drupal\Core\Link; +use Drupal\ui_patterns_ui_fieldgroups\Element\DisplayFormFieldGroup; +use Drupal\Core\Form\FormStateInterface; + +/** + * Implements hook_component_form_display_groups(). + */ +function ui_patterns_ui_fieldgroups_component_form_display_groups(ComponentFormDisplayInterface $display): array { + return $display->getThirdPartySettings('ui_patterns_ui_fieldgroups'); +} + +/** + * Implements hook_element_info_alter(). + */ +function ui_patterns_ui_fieldgroups_element_info_alter(array &$types): void { + if (isset($types['uip_display_form'])) { + $types['uip_display_form']['#process'][] = [DisplayFormFieldGroup::class, 'process']; + } + +} + +/** + * Build group objects. + * + * @param \Drupal\ui_patterns_ui\ComponentFormDisplayInterface $display + * The component display. + * + * @return mixed + * Info array. + */ +function ui_patterns_ui_fieldgroups_info(ComponentFormDisplayInterface $display) { + $data = $display->getThirdPartySettings('ui_patterns_ui_fieldgroups'); + $groups = []; + if (isset($data) && is_array($data)) { + foreach ($data as $group_name => $definition) { + $definition += [ + 'group_name' => $group_name, + 'context' => 'form', + ]; + $groups[$group_name] = (object) $definition; + } + } + $options = $display->getPropSlotOptions(); + foreach ($options as $group_name => $display_option) { + if (isset($groups[$group_name])) { + $group = $groups[$group_name]; + $group->region = $display_option['region']; + $group->mode = 'form'; + $group->entity_type = NULL; + $group->bundle = NULL; + $group->parent = $display_option['parent'] ?? NULL; + } + + if (isset($groups[$display_option['parent']])) { + $parent_group = $groups[$display_option['parent']]; + if (!isset($parent_group->children)) { + $parent_group->children = []; + } + $parent_group->children[] = $group_name; + } + + } + return $groups; +} + +/** + * Implements hook_component_form_display_group_row_alter(). + */ +function ui_patterns_ui_fieldgroups_component_form_display_group_row_alter(array &$row, ComponentFormDisplayInterface $display, array $group): void { + $row['human_name']['#markup'] = $group['label']; + $row['settings_edit'] = [ + '#type' => 'link', + '#title' => ['#markup' => '<img src="/core/misc/icons/787878/cog.svg"/>'], + '#url' => Url::fromRoute('entity.component_form_display.edit_group', + [ + 'component_id' => $display->getComponentId(), + 'form_mode_name' => $display->getFormModeName(), + 'group_id' => $group['id'], + ]), + '#src' => 'core/misc/icons/787878/cog.svg', + '#attributes' => [ + 'class' => ['field-plugin-settings-edit'], + 'alt' => t('Edit'), + ], + '#prefix' => '<div class="field-plugin-settings-edit-wrapper">', + '#suffix' => '</div>', + ]; + $delete_route = Url::fromRoute('entity.component_form_display.delete_group', + [ + 'component_id' => $display->getComponentId(), + 'form_mode_name' => $display->getFormModeName(), + 'group_id' => $group['id'], + ]); + $row['settings_edit']['#suffix'] .= Link::fromTextAndUrl(t('delete'), $delete_route)->toString(); + +} + +/** + * Implements hook_form_component_form_display_edit_form_alter(). + */ +function ui_patterns_ui_fieldgroups_form_component_form_display_edit_form_alter(array &$form, FormStateInterface $form_state): void { + $form['#attached']['library'][] = 'field_group/field_ui'; +} diff --git a/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.routing.yml b/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.routing.yml new file mode 100644 index 0000000000000000000000000000000000000000..2a6d154a19a2a931b69b252199e1962962134e9b --- /dev/null +++ b/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.routing.yml @@ -0,0 +1,30 @@ +entity.component_form_display.add_group: + path: '/admin/structure/component/{component_id}/form-display/{form_mode_name}/add-group' + defaults: + entity_type_id: 'component_form_display' + context: form + _form: \Drupal\ui_patterns_ui_fieldgroups\Form\FieldGroupAddForm + _title: 'Add field group' + requirements: + _permission: 'administer component_form_display' + +entity.component_form_display.edit_group: + path: '/admin/structure/component/{component_id}/form-display/{form_mode_name}/{group_id}/edit-group' + defaults: + entity_type_id: 'component_form_display' + context: form + _form: \Drupal\ui_patterns_ui_fieldgroups\Form\FieldGroupAddForm + _title: 'Edit field group' + requirements: + _permission: 'administer component_form_display' + + +entity.component_form_display.delete_group: + path: '/admin/structure/component/{component_id}/form-display/{form_mode_name}/{group_id}/delete-group' + defaults: + entity_type_id: 'component_form_display' + context: form + _form: \Drupal\ui_patterns_ui_fieldgroups\Form\FieldGroupDeleteForm + _title: 'Edit field group' + requirements: + _permission: 'administer component_form_display' diff --git a/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.services.yml b/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.services.yml new file mode 100644 index 0000000000000000000000000000000000000000..f703ccb4b72e06c7d2be1c030cbb5a9b9554e5e1 --- /dev/null +++ b/modules/ui_patterns_ui_fieldgroups/ui_patterns_ui_fieldgroups.services.yml @@ -0,0 +1,5 @@ +services: + ui_patterns_ui_fieldgroups.display_forms_alter: + class: Drupal\ui_patterns_ui_fieldgroups\Element\DisplayFormFieldGroup + arguments: + - "@plugin.manager.sdc"