diff --git a/layout_builder_ids.info.yml b/layout_builder_ids.info.yml index 42eac067e1ebb01e1e26d01e33eaf50785adfb97..0840651a1f4294ec45a7f0fbc29a21c7e522f3aa 100644 --- a/layout_builder_ids.info.yml +++ b/layout_builder_ids.info.yml @@ -4,4 +4,3 @@ type: module core_version_requirement: ^10.4 || ^11.1 dependencies: - drupal:layout_builder - - hook_event_dispatcher:core_event_dispatcher diff --git a/layout_builder_ids.module b/layout_builder_ids.module index b5fa315417bb39b5ad9271f8e5af87149f0ea1f0..cedfb94dc52a791f7d3ccf9f410f22a45dd88305 100644 --- a/layout_builder_ids.module +++ b/layout_builder_ids.module @@ -5,6 +5,10 @@ * Functions for layout_builder_ids. */ +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Hook\Attribute\LegacyHook; +use Drupal\layout_builder_ids\Hook\LayoutBuilderIdsFormAlterHooks; + /** * Implements template_preprocess_layout. */ @@ -18,3 +22,21 @@ function layout_builder_ids_preprocess_layout(&$variables) { $variables['attributes']['id'] = $variables['content']['#settings']['layout_builder_id']; } } + +/** + * Implements hook_form_alter(). + */ +#[LegacyHook] +function layout_builder_ids_form_alter(array &$form, FormStateInterface $form_state, string $form_id) { + return \Drupal::service(LayoutBuilderIdsFormAlterHooks::class) + ->formAlter($form, $form_state, $form_id); +} + +/** + * Implements hook_form_FORM_ID_alter(). + */ +#[LegacyHook] +function layout_builder_ids_form_layout_builder_configure_section_alter(array &$form, FormStateInterface $form_state, string $form_id) { + return \Drupal::service(LayoutBuilderIdsFormAlterHooks::class) + ->formLayoutBuilderConfigureSectionAlter($form, $form_state, $form_id); +} diff --git a/layout_builder_ids.services.yml b/layout_builder_ids.services.yml index 35e2f680a1ee48602ac8f1842364712bf0ffa429..d94dd2da16212e705577cbd2b7166b83c46922de 100644 --- a/layout_builder_ids.services.yml +++ b/layout_builder_ids.services.yml @@ -1,17 +1,14 @@ services: layout_builder_ids.layout_builder_ids_service: class: '\Drupal\layout_builder_ids\Service\LayoutBuilderIdsService' + + Drupal\layout_builder_ids\Service\LayoutBuilderIdsServiceInterface: '@layout_builder_ids.layout_builder_ids_service' + layout_builder_ids.render_block_component_subscriber: class: 'Drupal\layout_builder_ids\EventSubscriber\LayoutBuilderIdsRenderSubscriber' tags: - { name: event_subscriber } - layout_builder_ids.configure_section_form: - class: '\Drupal\layout_builder_ids\EventSubscriber\LayoutBuilderIdsConfigureSection' - arguments: ['@layout_builder_ids.layout_builder_ids_service'] - tags: - - { name: 'event_subscriber' } - layout_builder_ids.configure_block_form: - class: '\Drupal\layout_builder_ids\EventSubscriber\LayoutBuilderIdsConfigureBlock' - arguments: ['@layout_builder_ids.layout_builder_ids_service'] - tags: - - { name: 'event_subscriber' } + + Drupal\layout_builder_ids\Hook\LayoutBuilderIdsFormAlterHooks: + class: '\Drupal\layout_builder_ids\Hook\LayoutBuilderIdsFormAlterHooks' + autowire: true diff --git a/src/EventSubscriber/LayoutBuilderIdsConfigureBlock.php b/src/EventSubscriber/LayoutBuilderIdsConfigureBlock.php deleted file mode 100644 index d33f78f62e6a8cee1b10e1c728e3a9ce44bfd1a5..0000000000000000000000000000000000000000 --- a/src/EventSubscriber/LayoutBuilderIdsConfigureBlock.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php - -namespace Drupal\layout_builder_ids\EventSubscriber; - -use Drupal\Component\Utility\Html; -use Drupal\Core\Form\FormStateInterface; -use Drupal\core_event_dispatcher\Event\Form\FormAlterEvent; -use Drupal\core_event_dispatcher\FormHookEvents; -use Drupal\layout_builder_ids\Service\LayoutBuilderIdsService; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Add block ids to layout builder sections. - */ -class LayoutBuilderIdsConfigureBlock implements EventSubscriberInterface { - - /** - * Layout builder ids service. - * - * @var \Drupal\layout_builder_ids\Service\LayoutBuilderIdsService - */ - protected $layoutBuilderIdsService; - - /** - * Constructor for event subscriber for configure block. - * - * @param \Drupal\layout_builder_ids\Service\LayoutBuilderIdsService $layoutBuilderIdsService - * Layout builder ids service. - */ - public function __construct(LayoutBuilderIdsService $layoutBuilderIdsService) { - $this->layoutBuilderIdsService = $layoutBuilderIdsService; - } - - /** - * Alter form. - * - * @param \Drupal\core_event_dispatcher\Event\Form\FormAlterEvent $event - * The event. - */ - public static function alterForm(FormAlterEvent $event): void { - // Ignore form alter according to module configuration. - $config = \Drupal::config('layout_builder_ids.settings'); - if (!$config->get('block_id')) { - return; - } - - // Get the form from the event. - $form = &$event->getForm(); - - // If we are on a configure section form, alter it. - if ( - in_array( - $form['#form_id'], - ['layout_builder_add_block', 'layout_builder_update_block'], - TRUE - ) - ) { - - // Pull out the layout_builder_id from config. - $layout_builder_id = $event->getFormState()->getFormObject()->getCurrentComponent()->get('layout_builder_id'); - - // Add the section id to the configure form. - $form['settings']['layout_builder_id'] = [ - '#type' => 'textfield', - '#title' => 'Block ID', - '#weight' => 99, - '#default_value' => $layout_builder_id ?: NULL, - '#description' => t('Block ID is an optional setting which is used to support an anchor link to this block. For example, entering "feature" lets you link directly to this block by adding "#feature" to the end of the URL.</br>IDs should start with a letter, may only contain letters, numbers, underscores, hyphens, and periods, and should be unique on the page.'), - ]; - - // Add the form validation for configure block. - $form['#validate'][] = 'Drupal\layout_builder_ids\EventSubscriber\LayoutBuilderIdsConfigureBlock::layoutBuilderIdsConfigureBlockFormValidation'; - - // Add our custom submit function. - array_unshift( - $form['#submit'], - 'Drupal\layout_builder_ids\EventSubscriber\LayoutBuilderIdsConfigureBlock::layoutBuilderIdsConfigureBlockSubmitForm' - ); - } - } - - /** - * {@inheritdoc} - */ - public static function layoutBuilderIdsConfigureBlockFormValidation(array &$form, FormStateInterface $form_state) { - - // Get the layout builder id from the form, - // also put the id through the HTML getId to make sure - // that we form a valid id. - $layout_builder_id = Html::getId( - $form_state->getValue(['settings', 'layout_builder_id']) - ); - - // Ensure that we have an actual id to check. - if ($layout_builder_id !== '' && $layout_builder_id !== NULL) { - - // Check if we have a duplicate id somewhere. - $found_id = LayoutBuilderIdsService::layoutBuilderIdsCheckIds( - $layout_builder_id, - $form_state, - 'block' - ); - - // If we have a duplicate id, then set the form error. - if ($found_id) { - - // Set the form error on the layout builder id element. - $form_state->setError($form['settings']['layout_builder_id'], 'There is already a block or section with the ID "' . $layout_builder_id . '".'); - } - } - } - - /** - * {@inheritdoc} - */ - public static function layoutBuilderIdsConfigureBlockSubmitForm(array &$form, FormStateInterface $form_state) { - - // Load in the layout_builder_id. - $layout_builder_id = $form_state->getValue(['settings', 'layout_builder_id']); - - // If there is in id, save it in config. - if ($layout_builder_id !== NULL) { - - // Load in the component/block. - $component = $form_state->getFormObject()->getCurrentComponent(); - - // Set the layout_builder_id. - $component->set('layout_builder_id', Html::getId($layout_builder_id)); - } - - } - - /** - * {@inheritdoc} - */ - public static function getSubscribedEvents(): array { - - return [ - FormHookEvents::FORM_ALTER => 'alterForm', - ]; - } - -} diff --git a/src/EventSubscriber/LayoutBuilderIdsConfigureSection.php b/src/EventSubscriber/LayoutBuilderIdsConfigureSection.php deleted file mode 100644 index f9abc7bfb368e1ac2f6d4497c62979d08fa38d69..0000000000000000000000000000000000000000 --- a/src/EventSubscriber/LayoutBuilderIdsConfigureSection.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php - -namespace Drupal\layout_builder_ids\EventSubscriber; - -use Drupal\Component\Utility\Html; -use Drupal\Core\Form\FormStateInterface; -use Drupal\core_event_dispatcher\Event\Form\FormAlterEvent; -use Drupal\core_event_dispatcher\FormHookEvents; -use Drupal\layout_builder_ids\Service\LayoutBuilderIdsService; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Add section id to layout builder sections. - */ -class LayoutBuilderIdsConfigureSection implements EventSubscriberInterface { - - /** - * Layout builder ids service. - * - * @var \Drupal\layout_builder_ids\Service\LayoutBuilderIdsService - */ - protected $layoutBuilderIdsService; - - /** - * Constructor for event subscriber for configure block. - * - * @param \Drupal\layout_builder_ids\Service\LayoutBuilderIdsService $layoutBuilderIdsService - * Layout builder ids service. - */ - public function __construct(LayoutBuilderIdsService $layoutBuilderIdsService) { - $this->layoutBuilderIdsService = $layoutBuilderIdsService; - } - - /** - * Alter form. - * - * @param \Drupal\core_event_dispatcher\Event\Form\FormAlterEvent $event - * The event. - */ - public static function alterForm(FormAlterEvent $event): void { - // Ignore form alter according to module configuration. - $config = \Drupal::config('layout_builder_ids.settings'); - if (!$config->get('section_id')) { - return; - } - - // Get the form from the event. - $form = &$event->getForm(); - - // If we are on a configure section form, alter it. - if ($form['#form_id'] == 'layout_builder_configure_section') { - - // These next two lines are needed until this issue gets fixed: - // https://www.drupal.org/project/drupal/issues/3103812. - // Once this issue gets fixed in core then we can use the - // proper validate procedures. Until then we need to add the - // form id without the random value. - $form_state = $event->getFormState(); - $form['#id'] = Html::getId($form_state->getBuildInfo()['form_id']); - - // Get the config for the section. - $config = $event->getFormState()->getFormObject()->getCurrentLayout()->getConfiguration(); - - // Add the section id to the configure form. - $form['layout_settings']['layout_builder_id'] = [ - '#type' => 'textfield', - '#title' => 'Section ID', - '#weight' => 99, - '#default_value' => $config['layout_builder_id'] ?? NULL, - '#description' => t('Section ID is an optional setting which is used to support an anchor link to this block. For example, entering "feature" lets you link directly to this section by adding "#feature" to the end of the URL.</br>IDs should start with a letter, may only contain letters, numbers, hyphens, and should be unique on the page (underscores will be replaced by hyphens, and periods will be removed from the rendered ID).'), - ]; - - // Add the form validation for configure block. - $form['#validate'][] = 'Drupal\layout_builder_ids\EventSubscriber\LayoutBuilderIdsConfigureSection::layoutBuilderIdsConfigureSectionFormValidation'; - - // Add our custom submit function. - array_unshift( - $form['#submit'], - 'Drupal\layout_builder_ids\EventSubscriber\LayoutBuilderIdsConfigureSection::layoutBuilderIdsConfigureSectionSubmitForm' - ); - } - } - - /** - * {@inheritdoc} - */ - public static function layoutBuilderIdsConfigureSectionFormValidation(array &$form, FormStateInterface $form_state) { - - // Get the layout builder id from the form. - $layout_builder_id = $form_state->getValue( - [ - 'layout_settings', - 'layout_builder_id', - ] - ); - - // Ensure that we are not checking for blank layout builder id. - if ($layout_builder_id !== '') { - - // Put the id through the HTML getId to make sure - // that we form a valid id. - $layout_builder_id = Html::getId($layout_builder_id); - - // Check if we have a duplicate id somewhere. - $found_id = layoutBuilderIdsService::layoutBuilderIdsCheckIds($layout_builder_id, $form_state, 'section'); - - // If we have a duplicate id, then set the form error. - if ($found_id) { - - // Set the form error on the layout builder id form element. - $form_state->setError($form['layout_settings']['layout_builder_id'], 'There is already a block or section with the ID "' . $layout_builder_id . '".'); - } - } - } - - /** - * {@inheritdoc} - */ - public static function layoutBuilderIdsConfigureSectionSubmitForm(array &$form, FormStateInterface $form_state) { - - // Get the layout builder id from the form. - $layout_builder_id = $form_state->getValue( - [ - 'layout_settings', - 'layout_builder_id', - ], - NULL - ); - - // If there is a layout builder id, store it. - if ($layout_builder_id !== NULL) { - - // Get the form object from the form state. - $formObject = $form_state->getFormObject(); - - // Get the layout. - $layout = $formObject->getCurrentLayout(); - - // Load in the config for this section. - $configuration = $layout->getConfiguration(); - - // Set the layout builder id in config variable. - $configuration['layout_builder_id'] = Html::getId($layout_builder_id); - - // Set the config for this section. - $layout->setConfiguration($configuration); - } - - } - - /** - * {@inheritdoc} - */ - public static function getSubscribedEvents() { - return [ - FormHookEvents::FORM_ALTER => 'alterForm', - ]; - } - -} diff --git a/src/Hook/LayoutBuilderIdsFormAlterHooks.php b/src/Hook/LayoutBuilderIdsFormAlterHooks.php new file mode 100644 index 0000000000000000000000000000000000000000..24e92abc9c9e0de2e101426bf5ffac255b4c887f --- /dev/null +++ b/src/Hook/LayoutBuilderIdsFormAlterHooks.php @@ -0,0 +1,260 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\layout_builder_ids\Hook; + +use Drupal\Component\Utility\Html; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\DependencyInjection\DependencySerializationTrait; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\layout_builder_ids\Service\LayoutBuilderIdsService; +use Drupal\layout_builder_ids\Service\LayoutBuilderIdsServiceInterface; + +/** + * Form Alter hooks for layout builder ids. + */ +class LayoutBuilderIdsFormAlterHooks { + + use DependencySerializationTrait; + use StringTranslationTrait; + + /** + * Constructs a LayoutBuilderIdsFormAlterHooks object. + * + * @param \Drupal\layout_builder_ids\Service\LayoutBuilderIdsServiceInterface $layoutBuilderIdsService + * Layout builder ids service. + * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory + * Config factory service. + */ + public function __construct( + protected LayoutBuilderIdsServiceInterface $layoutBuilderIdsService, + protected ConfigFactoryInterface $configFactory, + ) { + + } + + /** + * Implements hook_form_alter(). + */ + #[Hook('form_alter')] + public function formAlter(array &$form, FormStateInterface &$form_state, string $form_id): void { + // Ignore form alter according to module configuration. + $config = $this->configFactory->get('layout_builder_ids.settings'); + if (!$config->get('block_id')) { + return; + } + + // If we are on a configure section form, alter it. + if ( + in_array( + $form['#form_id'], + ['layout_builder_add_block', 'layout_builder_update_block'], + TRUE + ) + ) { + + // Pull out the layout_builder_id from config. + $layout_builder_id = $form_state->getFormObject()->getCurrentComponent()->get('layout_builder_id'); + + // Add the section id to the configure form. + $form['settings']['layout_builder_id'] = [ + '#type' => 'textfield', + '#title' => 'Block ID', + '#weight' => 99, + '#default_value' => $layout_builder_id ?: NULL, + '#description' => $this->t('Block ID is an optional setting which is used to support an anchor link to this block. For example, entering "feature" lets you link directly to this block by adding "#feature" to the end of the URL.</br>IDs should start with a letter, may only contain letters, numbers, underscores, hyphens, and periods, and should be unique on the page.'), + ]; + + // Add the form validation for configure block. + $form['#validate'][] = [$this, 'layoutBuilderIdsConfigureBlockFormValidation']; + + // Add our custom submit function. + array_unshift( + $form['#submit'], + [$this, 'layoutBuilderIdsConfigureBlockSubmitForm'] + ); + } + } + + /** + * Implements hook_form_FORM_ID_alter(). + */ + #[Hook('layout_builder_configure_section_alter')] + public function formLayoutBuilderConfigureSectionAlter(array &$form, FormStateInterface $form_state, string $form_id): void { + // Ignore form alter according to module configuration. + if (!$this->configFactory->get('section_id')) { + return; + } + + // These next two lines are needed until this issue gets fixed: + // https://www.drupal.org/project/drupal/issues/3103812. + // Once this issue gets fixed in core then we can use the + // proper validate procedures. Until then we need to add the + // form id without the random value. + $form['#id'] = Html::getId($form_state->getBuildInfo()['form_id']); + + // Get the config for the section. + $config = $form_state->getFormObject() + ->getCurrentLayout() + ->getConfiguration(); + + // Add the section id to the configure form. + $form['layout_settings']['layout_builder_id'] = [ + '#type' => 'textfield', + '#title' => 'Section ID', + '#weight' => 99, + '#default_value' => $config['layout_builder_id'] ?? NULL, + '#description' => $this->t('Section ID is an optional setting which is used to support an anchor link to this block. For example, entering "feature" lets you link directly to this section by adding "#feature" to the end of the URL.</br>IDs should start with a letter, may only contain letters, numbers, hyphens, and should be unique on the page (underscores will be replaced by hyphens, and periods will be removed from the rendered ID).'), + ]; + + // Add the form validation for configure block. + $form['#validate'][] = [$this, 'layoutBuilderIdsConfigureSectionFormValidation']; + + // Add our custom submit function. + array_unshift( + $form['#submit'], + [$this, 'layoutBuilderIdsConfigureSectionSubmitForm'] + ); + } + + /** + * Block form validation. + * + * @param array $form + * The renderable form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function layoutBuilderIdsConfigureBlockFormValidation(array &$form, FormStateInterface $form_state): void { + + // Get the layout builder id from the form, + // also put the id through the HTML getId to make sure + // that we form a valid id. + $layout_builder_id = Html::getId( + $form_state->getValue(['settings', 'layout_builder_id']) + ); + + // Ensure that we have an actual id to check. + if ($layout_builder_id !== '' && $layout_builder_id !== NULL) { + + // Check if we have a duplicate id somewhere. + $found_id = LayoutBuilderIdsService::layoutBuilderIdsCheckIds( + $layout_builder_id, + $form_state, + 'block' + ); + + // If we have a duplicate id, then set the form error. + if ($found_id) { + + // Set the form error on the layout builder id element. + $form_state->setError($form['settings']['layout_builder_id'], 'There is already a block or section with the ID "' . $layout_builder_id . '".'); + } + } + } + + /** + * Handles the submission for configuring the block with a layout builder ID. + * + * @param array $form + * The renderable form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function layoutBuilderIdsConfigureBlockSubmitForm(array &$form, FormStateInterface $form_state): void { + // Load in the layout_builder_id. + $layout_builder_id = $form_state->getValue(['settings', 'layout_builder_id']); + + // If there is in id, save it in config. + if ($layout_builder_id !== NULL) { + + // Load in the component/block. + $component = $form_state->getFormObject()->getCurrentComponent(); + + // Set the layout_builder_id. + $component->set('layout_builder_id', Html::getId($layout_builder_id)); + } + } + + /** + * Section form validation. + * + * @param array $form + * The renderable form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function layoutBuilderIdsConfigureSectionFormValidation(array &$form, FormStateInterface $form_state): void { + + // Get the layout builder id from the form. + $layout_builder_id = $form_state->getValue( + [ + 'layout_settings', + 'layout_builder_id', + ] + ); + + // Ensure that we are not checking for blank layout builder id. + if ($layout_builder_id !== '') { + + // Put the id through the HTML getId to make sure + // that we form a valid id. + $layout_builder_id = Html::getId($layout_builder_id); + + // Check if we have a duplicate id somewhere. + $found_id = layoutBuilderIdsService::layoutBuilderIdsCheckIds($layout_builder_id, $form_state, 'section'); + + // If we have a duplicate id, then set the form error. + if ($found_id) { + + // Set the form error on the layout builder id form element. + $message = 'There is already a block or section with the ID "' . $layout_builder_id . '".'; + $form_state->setError($form['layout_settings']['layout_builder_id'], $message); + } + } + } + + /** + * Handles the submission for configuring section with a layout builder ID. + * + * @param array $form + * The renderable form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function layoutBuilderIdsConfigureSectionSubmitForm(array &$form, FormStateInterface $form_state): void { + + // Get the layout builder id from the form. + $layout_builder_id = $form_state->getValue( + [ + 'layout_settings', + 'layout_builder_id', + ], + NULL + ); + + // If there is a layout builder id, store it. + if ($layout_builder_id !== NULL) { + + // Get the form object from the form state. + $formObject = $form_state->getFormObject(); + + // Get the layout. + $layout = $formObject->getCurrentLayout(); + + // Load in the config for this section. + $configuration = $layout->getConfiguration(); + + // Set the layout builder id in config variable. + $configuration['layout_builder_id'] = Html::getId($layout_builder_id); + + // Set the config for this section. + $layout->setConfiguration($configuration); + } + + } + +}