From aa1d11ee6c90990e196cea019e1bb0027d59a7a9 Mon Sep 17 00:00:00 2001 From: berliner Date: Wed, 20 Mar 2024 10:00:18 +0100 Subject: [PATCH] HPC-9391: Use Optional link trait logic also in carousel element --- .../ghi_blocks/src/Element/CarouselItem.php | 78 +++++-------------- .../src/Plugin/Block/Generic/LinkCarousel.php | 17 ++-- .../CarouselItem.php | 45 ++++++++++- 3 files changed, 74 insertions(+), 66 deletions(-) diff --git a/html/modules/custom/ghi_blocks/src/Element/CarouselItem.php b/html/modules/custom/ghi_blocks/src/Element/CarouselItem.php index 5ebb43983..e28a709ca 100644 --- a/html/modules/custom/ghi_blocks/src/Element/CarouselItem.php +++ b/html/modules/custom/ghi_blocks/src/Element/CarouselItem.php @@ -2,10 +2,11 @@ namespace Drupal\ghi_blocks\Element; -use Drupal\Core\Entity\Element\EntityAutocomplete; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\Render\Element\FormElement; +use Drupal\ghi_form_elements\Traits\OptionalLinkTrait; +use Drupal\link\LinkItemInterface; use Drupal\link\Plugin\Field\FieldWidget\LinkWidget; /** @@ -15,6 +16,8 @@ */ class CarouselItem extends FormElement { + use OptionalLinkTrait; + const THUMBNAIL_DIRECTORY = 'public://content-panes/carousel-items/'; /** @@ -77,11 +80,13 @@ public static function processCarouselItem(array &$element, FormStateInterface $ '#type' => 'textfield', '#title' => t('Tag line'), '#default_value' => $element['#default_value']['tag_line'] ?? NULL, + '#description' => t('Enter a tag line that will appear above the title.'), ]; $element['description'] = [ '#type' => 'textarea', '#title' => t('Description'), '#default_value' => $element['#default_value']['description'] ?? NULL, + '#description' => t('Enter a short description for this link.'), ]; $element['image'] = [ '#type' => 'managed_file', @@ -96,17 +101,29 @@ public static function processCarouselItem(array &$element, FormStateInterface $ '#type' => 'textfield', '#title' => t('Image credit'), '#default_value' => $element['#default_value']['image_credit'] ?? NULL, + '#description' => t('Enter a credit for the image. This will appear on top of the image.'), ]; $element['image_caption'] = [ '#type' => 'textfield', '#title' => t('Image caption'), '#default_value' => $element['#default_value']['image_caption'] ?? NULL, + '#description' => t('Enter a caption for the image. This will appear together with the image credit on top of the image.'), ]; $element['url'] = [ - '#type' => 'path', + '#type' => 'entity_autocomplete', '#title' => t('Url'), - '#default_value' => static::getUriAsDisplayableString($element['#default_value']['url']) ?? NULL, + '#default_value' => self::getUriAsDisplayableString($element['#default_value']['url']) ?? NULL, + '#description' => t('Start typing the title of a piece of content to select it. You can also enter an external URL such as %url.', [ + '%url' => 'http://example.com', + ]), + '#link_type' => LinkItemInterface::LINK_GENERIC, + '#target_type' => 'node', + '#attributes' => [ + 'data-autocomplete-first-character-blacklist' => '/#?', + ], + '#process_default_value' => FALSE, '#element_validate' => [[LinkWidget::class, 'validateUriElement']], + '#maxlength' => 256, ]; $element['button_label'] = [ '#type' => 'textfield', @@ -130,59 +147,4 @@ public static function preRenderCarouselItem(array $element) { return $element; } - /** - * Gets the URI without the 'internal:' or 'entity:' scheme. - * - * The following two forms of URIs are transformed: - * - 'entity:' URIs: to entity autocomplete ("label (entity id)") strings; - * - 'internal:' URIs: the scheme is stripped. - * - * This method is the inverse of ::getUserEnteredStringAsUri(). - * - * This method has been copied from LinkWidget::getUriAsDisplayableString(). - * - * @param string $uri - * The URI to get the displayable string for. - * - * @return string - * The uri as a displayable (human-readably) string. - * - * @see LinkWidget::getUserEnteredStringAsUri() - */ - protected static function getUriAsDisplayableString($uri) { - $scheme = parse_url($uri, PHP_URL_SCHEME); - - // By default, the displayable string is the URI. - $displayable_string = $uri; - - // A different displayable string may be chosen in case of the 'internal:' - // or 'entity:' built-in schemes. - if ($scheme === 'internal') { - $uri_reference = explode(':', $uri, 2)[1]; - - // @todo '' is valid input for BC reasons, may be removed by - // https://www.drupal.org/node/2421941 - $path = parse_url($uri, PHP_URL_PATH); - if ($path === '/') { - $uri_reference = '' . substr($uri_reference, 1); - } - - $displayable_string = $uri_reference; - } - elseif ($scheme === 'entity') { - [$entity_type, $entity_id] = explode('/', substr($uri, 7), 2); - // Show the 'entity:' URI as the entity autocomplete would. - // @todo Support entity types other than 'node'. Will be fixed in - // https://www.drupal.org/node/2423093. - if ($entity_type == 'node' && $entity = \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id)) { - $displayable_string = EntityAutocomplete::getEntityLabels([$entity]); - } - } - elseif ($scheme === 'route') { - $displayable_string = ltrim($displayable_string, 'route:'); - } - - return $displayable_string; - } - } diff --git a/html/modules/custom/ghi_blocks/src/Plugin/Block/Generic/LinkCarousel.php b/html/modules/custom/ghi_blocks/src/Plugin/Block/Generic/LinkCarousel.php index 1276bc11c..05b2542e5 100644 --- a/html/modules/custom/ghi_blocks/src/Plugin/Block/Generic/LinkCarousel.php +++ b/html/modules/custom/ghi_blocks/src/Plugin/Block/Generic/LinkCarousel.php @@ -4,14 +4,15 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Link; use Drupal\Core\Render\Markup; use Drupal\file\Entity\File; use Drupal\ghi_blocks\Interfaces\ConfigurableTableBlockInterface; use Drupal\ghi_blocks\Plugin\Block\GHIBlockBase; use Drupal\ghi_blocks\Plugin\Block\ImageProviderBlockInterface; +use Drupal\ghi_blocks\Plugin\ConfigurationContainerItem\CarouselItem; use Drupal\ghi_blocks\Traits\ManagedFileBlockTrait; use Drupal\ghi_form_elements\Traits\ConfigurationContainerTrait; +use Drupal\ghi_form_elements\Traits\OptionalLinkTrait; use Drupal\hpc_api\Query\EndpointQuery; use Drupal\hpc_common\Helpers\ArrayHelper; @@ -29,6 +30,7 @@ class LinkCarousel extends GHIBlockBase implements ConfigurableTableBlockInterfa use ConfigurationContainerTrait; use ManagedFileBlockTrait; + use OptionalLinkTrait; /** * {@inheritdoc} @@ -62,18 +64,19 @@ public function buildContent() { /** @var \Drupal\ghi_blocks\Plugin\ConfigurationContainerItem\CarouselItem $item_type */ $item_type = $this->getItemTypePluginForColumn($item, $context); + if (!$item_type instanceof CarouselItem) { + continue; + } $file = $item_type->getImage(); if (!$file) { continue; } - $link = Link::fromTextAndUrl($item_type->getButtonLabel() ?? $this->t('Read more'), $item_type->getUrl()); - $link->getUrl()->setOptions([ - 'attributes' => [ - 'class' => ['cd-button', 'read-more'], - ], - ]); + $link = $item_type->getLink(); + $attributes = $link->getUrl()->getOption('attributes'); + $attributes['class'] = ['cd-button', 'read-more']; + $link->getUrl()->setOption('attributes', $attributes); $carousel_items[] = [ 'tag_line' => Markup::create($item_type->getTagLine()), 'title' => Markup::create($item_type->getLabel()), diff --git a/html/modules/custom/ghi_blocks/src/Plugin/ConfigurationContainerItem/CarouselItem.php b/html/modules/custom/ghi_blocks/src/Plugin/ConfigurationContainerItem/CarouselItem.php index c6f3eefb4..58910db1a 100644 --- a/html/modules/custom/ghi_blocks/src/Plugin/ConfigurationContainerItem/CarouselItem.php +++ b/html/modules/custom/ghi_blocks/src/Plugin/ConfigurationContainerItem/CarouselItem.php @@ -7,6 +7,7 @@ use Drupal\Core\Url; use Drupal\file\Entity\File; use Drupal\ghi_form_elements\ConfigurationContainerItemPluginBase; +use Drupal\ghi_form_elements\Traits\OptionalLinkTrait; /** * Provides a carousel item for configuration containers. @@ -19,6 +20,8 @@ */ class CarouselItem extends ConfigurationContainerItemPluginBase { + use OptionalLinkTrait; + /** * {@inheritdoc} */ @@ -29,9 +32,37 @@ public function buildForm($element, FormStateInterface $form_state) { '#type' => 'carousel_item', '#default_value' => array_key_exists('value', $this->config) ? $this->config['value'] : NULL, ]; + + $element['#element_validate'] = [ + [static::class, 'validateElement'], + ]; return $element; } + /** + * Validate the link form element. + * + * @param array $element + * The element build in ::buildForm(). + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state object. + * @param array $complete_form + * The complete form that contains the element. + */ + public static function validateElement(&$element, FormStateInterface $form_state, &$complete_form) { + // @todo This repeats logic from + // \Drupal\ghi_form_elements\Element\OptionalLink::elementValidate(). + $url = $form_state->getValue($element['#parents'])['value']['url']; + $transformed_url = self::transformUrl($url); + if (!$transformed_url) { + $form_state->setError($element['value']['url'], t('The link URL must be valid and accessible.')); + } + if (!$form_state->hasAnyErrors() && $transformed_url !== $url) { + $form_state->setValue(array_merge($element['#parents'], ['value', 'url']), $transformed_url); + } + + } + /** * Get the tag line. */ @@ -84,6 +115,17 @@ public function getImageCaption() { return $caption ?? NULL; } + /** + * Get the link. + * + * @return \Drupal\Core\Link + * A link object. + */ + public function getLink() { + $label = $this->getButtonLabel() ?? $this->t('Read more'); + return $this->getLinkFromUri($this->config['value']['url'], $label); + } + /** * Get the url. * @@ -92,7 +134,8 @@ public function getImageCaption() { */ public function getUrl() { try { - return Url::fromUri($this->config['value']['url']); + $link = $this->getLink(); + return $link->getUrl(); } catch (\InvalidArgumentException $e) { return NULL;