custom_field-1.0.x-dev/src/Plugin/CustomField/FieldFormatter/UriLinkFormatter.php

src/Plugin/CustomField/FieldFormatter/UriLinkFormatter.php
<?php

declare(strict_types=1);

namespace Drupal\custom_field\Plugin\CustomField\FieldFormatter;

use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\TranslatableInterface;
use Drupal\Core\Field\Attribute\FieldFormatter;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\custom_field\Plugin\CustomFieldFormatterBase;
use Drupal\link\AttributeXss;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Plugin implementation of the 'uri_link' formatter.
 */
#[FieldFormatter(
  id: 'uri_link',
  label: new TranslatableMarkup('Link'),
  field_types: [
    'uri',
  ],
)]
class UriLinkFormatter extends CustomFieldFormatterBase {

  /**
   * The entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The entity repository.
   *
   * @var \Drupal\Core\Entity\EntityRepositoryInterface
   */
  protected $entityRepository;

  /**
   * The token service.
   *
   * @var \Drupal\Core\Utility\Token
   */
  protected $tokenService;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->entityTypeManager = $container->get('entity_type.manager');
    $instance->entityRepository = $container->get('entity.repository');
    $instance->tokenService = $container->get('token');

    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings(): array {
    return [
      'link_text' => '',
      'trim_length' => '80',
      'url_plain' => FALSE,
      'url_only' => FALSE,
      'rel' => '',
      'noopener' => '',
      'noreferrer' => '',
      'target' => '',
      'title' => '',
      'class' => '',
      'aria-label' => '',
      'accesskey' => '',
      'name' => '',
      'id' => '',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state): array {
    $visibility_path = $form['#visibility_path'];
    $elements['link_text'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Link text'),
      '#description' => $this->t('This field can serve as the link text.'),
      '#default_value' => $this->getSetting('link_text'),
      '#placeholder' => $this->t('e.g. Learn More'),
    ];
    $elements['trim_length'] = [
      '#type' => 'number',
      '#title' => $this->t('Trim link text length'),
      '#field_suffix' => $this->t('characters'),
      '#default_value' => $this->getSetting('trim_length'),
      '#min' => 1,
      '#description' => $this->t('Leave blank to allow unlimited link text lengths.'),
    ];
    $elements['url_only'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('URL only'),
      '#default_value' => $this->getSetting('url_only'),
      '#access' => $this->getPluginId() == 'link',
    ];
    $elements['url_plain'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Show URL as plain text'),
      '#default_value' => $this->getSetting('url_plain'),
    ];
    $elements['rel'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Add rel="nofollow" to links'),
      '#return_value' => 'nofollow',
      '#default_value' => $this->getSetting('rel'),
    ];
    $elements['target'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Open external link in new window'),
      '#description' => $this->t('Adds target="_blank" to external links.'),
      '#return_value' => '_blank',
      '#default_value' => $this->getSetting('target'),
    ];
    $elements['noopener'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Add rel="noopener" to links'),
      '#description' => $this->t('Recommended when "Open external link in new window" is checked.'),
      '#states' => [
        'visible' => [
          ':input[name="' . $visibility_path . '[target]"]' => ['checked' => TRUE],
        ],
      ],
      '#return_value' => 'noopener',
      '#default_value' => $this->getSetting('noopener'),
    ];
    $elements['noreferrer'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Add rel="noreferrer" to links'),
      '#description' => $this->t('Recommended when "Open external link in new window" is checked.'),
      '#states' => [
        'visible' => [
          ':input[name="' . $visibility_path . '[target]"]' => ['checked' => TRUE],
        ],
      ],
      '#return_value' => 'noreferrer',
      '#default_value' => $this->getSetting('noreferrer'),
    ];
    $elements['title'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Title'),
      '#default_value' => $this->getSetting('title'),
      '#maxlength' => 255,
    ];
    $elements['aria-label'] = [
      '#type' => 'textfield',
      '#title' => $this->t('ARIA label'),
      '#default_value' => $this->getSetting('aria-label'),
      '#maxlength' => 255,
    ];
    $elements['class'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Class'),
      '#description' => $this->t('Separate multiple classes by a single space.'),
      '#default_value' => $this->getSetting('class'),
    ];
    $elements['id'] = [
      '#type' => 'textfield',
      '#title' => $this->t('ID'),
      '#default_value' => $this->getSetting('id'),
    ];
    $elements['name'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Name'),
      '#default_value' => $this->getSetting('name'),
      '#maxlength' => 255,
    ];
    $elements['accesskey'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Access key'),
      '#description' => $this->t('Must be a single alphanumeric character. Each access key on a page should be unique to avoid browser conflicts.'),
      '#default_value' => $this->getSetting('accesskey'),
      '#maxlength' => 1,
      '#size' => 1,
      '#pattern' => '[a-zA-Z0-9]',
    ];

    return $elements;
  }

  /**
   * Builds the \Drupal\Core\Url object for a link field item.
   *
   * @param array $value
   *   The value to build the url from.
   *
   * @return \Drupal\Core\Url
   *   A Url object.
   */
  protected function buildUrl(array $value): Url {
    $settings = $this->getSettings();
    try {
      $url = $this->getUrl($value['uri']);
    }
    catch (\InvalidArgumentException $e) {
      // @todo Add logging here in https://www.drupal.org/project/drupal/issues/3348020
      $url = Url::fromRoute('<none>');
    }

    $options = $value['options'] ?? [];
    $options += $url->getOptions();

    // Check for widget attributes.
    $rel = explode(' ', $options['attributes']['rel'] ?? '');
    $class = $options['attributes']['class'] ?? [];
    $id = trim($options['attributes']['id'] ?? '');
    $target = $options['attributes']['target'] ?? '';
    $accesskey = trim($options['attributes']['accesskey'] ?? '');
    $ariaLabel = trim($options['attributes']['aria-label'] ?? '');
    $name = trim($options['attributes']['name'] ?? '');
    $title = trim($options['attributes']['title'] ?? '');

    // Check for rel attributes from settings.
    if (!empty($settings['rel'])) {
      $rel[] = $settings['rel'];
    }
    // Set ID attribute if not already set.
    if (empty($id) && !empty($settings['id'])) {
      $options['attributes']['id'] = $settings['id'];
    }
    // Merge classes.
    if (!empty($settings['class'])) {
      $class = array_merge($class, explode(' ', $settings['class']));
      $options['attributes']['class'] = $class;
    }
    // Set 'target' if not already set and external rel attributes.
    if (empty($target) && !empty($settings['target']) && $url->isExternal()) {
      $options['attributes']['target'] = $settings['target'];
      if (!empty($settings['noopener'])) {
        $rel[] = $settings['noopener'];
      }
      if (!empty($settings['noreferrer'])) {
        $rel[] = $settings['noreferrer'];
      }
    }
    // Set 'accesskey' if not already set.
    if (empty($accesskey) && !empty($settings['accesskey'])) {
      $options['attributes']['accesskey'] = $settings['accesskey'];
    }
    // Set 'aria-label' if not already set.
    if (empty($ariaLabel) && !empty($settings['aria-label'])) {
      $options['attributes']['aria-label'] = $settings['aria-label'];
    }
    // Set 'name' if not already set.
    if (empty($name) && !empty($settings['name'])) {
      $options['attributes']['name'] = $settings['name'];
    }
    // Set 'title' if not already set.
    if (empty($title) && !empty($settings['title'])) {
      $options['attributes']['title'] = $title;
    }
    // Merge all rel attributes as string.
    if (!empty($rel)) {
      $options['attributes']['rel'] = implode(' ', $rel);
    }
    if (!empty($options['attributes'])) {
      $options['attributes'] = AttributeXss::sanitizeAttributes($options['attributes']);
    }
    $url->setOptions($options);

    return $url;
  }

  /**
   * Helper function to get a Url from given string value.
   *
   * @param string $value
   *   The field value.
   *
   * @return \Drupal\Core\Url
   *   The Url object.
   */
  protected function getUrl(string $value): Url {
    return Url::fromUri($value);
  }

  /**
   * {@inheritdoc}
   */
  public function formatValue(FieldItemInterface $item, mixed $value): ?array {
    $settings = $this->getSettings();
    $entity = $item->getEntity();
    $langcode = $entity->language()->getId();
    $url = $this->buildUrl($value);
    // Use the full URL as the link title by default.
    $link_title = $url->toString();
    $title = $value['title'] ?? $settings['link_text'];

    // Check for access for linked entities.
    $link_entity = NULL;
    if ($url->isRouted() && preg_match('/^entity\.(\w+)\.canonical$/', $url->getRouteName(), $matches)) {
      // Check access to the canonical entity route.
      $link_entity_type = $matches[1];
      if (!empty($url->getRouteParameters()[$link_entity_type])) {
        $link_entity_param = $url->getRouteParameters()[$link_entity_type];
        if ($link_entity_param instanceof EntityInterface) {
          $link_entity = $link_entity_param;
        }
        elseif (is_string($link_entity_param) || is_numeric($link_entity_param)) {
          try {
            $link_entity_type_storage = $this->entityTypeManager->getStorage($link_entity_type);
            $link_entity = $link_entity_type_storage->load($link_entity_param);
          }
          catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
          }
        }
        // Set the entity in the correct language for display.
        if ($link_entity instanceof TranslatableInterface) {
          $link_entity = $this->entityRepository->getTranslationFromContext($link_entity, $langcode);
        }
        if ($link_entity instanceof EntityInterface) {
          $access = $link_entity->access('view', NULL, TRUE);
          if (!$access->isAllowed()) {
            return NULL;
          }
          if (empty($title)) {
            $title = $link_entity->label();
          }
        }
      }
    }

    // If the title field value is available, use it for the link text.
    if (empty($settings['url_only']) && !empty($title)) {
      // Unsanitized token replacement here because the entire link title
      // gets auto-escaped during link generation in
      // \Drupal\Core\Utility\LinkGenerator::generate().
      $link_title = $this->tokenService->replace($title, [$entity->getEntityTypeId() => $entity], ['clear' => TRUE]);
    }

    // Trim the link text to the desired length.
    if (!empty($settings['trim_length'])) {
      $link_title = Unicode::truncate($link_title, $settings['trim_length'], FALSE, TRUE);
    }

    // For link formatter.
    if ($this->getPluginId() === 'link' && !empty($settings['url_only']) && !empty($settings['url_plain'])) {
      $build = [
        '#plain_text' => $link_title,
      ];
    }
    elseif ($this->getPluginId() === 'uri' && !empty($settings['url_plain'])) {
      $build = [
        '#plain_text' => $link_title,
      ];
    }
    else {
      $build = [
        '#type' => 'link',
        '#title' => $link_title,
        '#options' => $url->getOptions(),
        '#url' => $url,
      ];
    }

    return $build;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc