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;