You are here

public function GeneralLinkFormatter::viewElements in Formatter Suite 8

Builds a renderable array for a field value.

Parameters

\Drupal\Core\Field\FieldItemListInterface $items: The field values to be rendered.

string $langcode: The language that should be used to render the field.

Return value

array A renderable array for $items, as an array of child elements keyed by consecutive numeric indexes starting from 0.

Overrides FormatterInterface::viewElements

File

src/Plugin/Field/FieldFormatter/GeneralLinkFormatter.php, line 675

Class

GeneralLinkFormatter
Formats a link field as one or more links.

Namespace

Drupal\formatter_suite\Plugin\Field\FieldFormatter

Code

public function viewElements(FieldItemListInterface $items, $langcode) {
  if ($items
    ->isEmpty() === TRUE) {
    return [];
  }
  $this
    ->sanitizeSettings();
  $showLink = $this
    ->getSetting('showLink');
  $entity = $items
    ->getEntity();

  // Prepare custom classes, if any.
  $classes = $this
    ->getSetting('classes');
  if (empty($classes) === TRUE) {
    $classes = [];
  }
  else {

    // Security: Class names are entered by an administrator.
    // They may not include anything but CSS-compatible words, and
    // certainly no HTML.
    //
    // Here, the class text is stripped of HTML tags as a start.
    // A regex tosses unacceptable characters in a CSS class name.
    $classes = strip_tags($classes);
    $classes = mb_ereg_replace('[^_a-zA-Z0-9- ]', '', $classes);
    if ($classes === FALSE) {
      $classes = [];
    }
    $classes = explode(' ', $classes);
  }
  $elements = [];
  foreach ($items as $delta => $item) {

    //
    // Get the URL.
    // ------------
    // Get the URL from the field and get an initial set of options,
    // including those that may have been set in the field.
    // Build the URL from the field.
    $url = $item
      ->getUrl();
    if (empty($url) === TRUE) {
      $url = Url::fromRoute('<none>');
    }
    $urlOptions = $item->options + $url
      ->getOptions();
    $url
      ->setOptions($urlOptions);

    //
    // Get the link title.
    // -------------------
    // Use custom text, text from the link, or the URL. If custom text
    // is selected, but there isn't any, fall through to text from the
    // link. If there is none of that, fall through to the URL.
    switch ($this
      ->getSetting('titleStyle')) {
      case 'text_custom':

        // Security: A custom title is entered by an administrator.
        // It may legitimately include HTML entities and minor HTML, but
        // it should not include dangerous HTML.
        //
        // Because it may include HTML, we cannot pass it directly as
        // the '#title' on a link, which will escape the HTML.
        //
        // We therefore use an Xss admin filter to remove any egreggious
        // HTML (such as scripts and styles), and then FormattableMarkup()
        // to mark the resulting text as safe.
        $title = Xss::filterAdmin($this
          ->getSetting('titleCustomText'));
        if (empty($title) === FALSE) {
          $title = new FormattableMarkup($title, []);
          break;
        }

      // Fall-through and use the field's title as the title text.
      default:
      case 'text_from_link':

        // Security: Link text from the link field is entered by
        // a user. It may legitimately include HTML entities and minor
        // HTML, but it should not include dangerous HTML.
        //
        // Because it may include HTML, we cannot pass it directly as the
        // '#title' on a link, which will escape the HTML.
        //
        // We therefore use an Xss strict filter to remove any egreggious
        // HTML (such as scripts and styles, broken HTML, etc), and then
        // FormattableMarkup() to mark the resulting text as safe.
        $title = $item->title;
        if (empty($title) === FALSE) {
          $title = new FormattableMarkup($title, []);
          break;
        }

      // Fall-through and use the URL as the title text.
      case 'text_from_url':

        // Use the URL as entered in the field, BEFORE this formatter adds
        // additional attributes.
        //
        // Security: URL text from the link field is entered by a user.
        // It should be strictly a valid URL.
        //
        // Below we pass it as the '#title' on a link, which will escape
        // any HTML the URL might contain.
        $title = $url
          ->toString();
        break;
    }

    //
    // Build the link.
    // ---------------
    // If the link is disabled, show the title text within a <span>.
    // Otherwise, build a URL and create a link.
    if ($showLink === FALSE) {
      $elements[$delta] = [
        '#type' => 'html_tag',
        '#tag' => 'span',
        '#value' => $title,
        '#attributes' => [
          'class' => $classes,
        ],
        '#cache' => [
          'tags' => $entity
            ->getCacheTags(),
        ],
      ];
    }
    else {
      $rel = '';
      $target = '';
      $download = FALSE;
      switch ($this
        ->getSetting('openLinkIn')) {
        case '_self':
          $target = '_self';
          break;
        case '_blank':
          $target = '_blank';
          break;
        case 'download':
          $download = TRUE;
          break;
      }
      if ($this
        ->getSetting('noreferrer') === TRUE) {
        $rel .= 'noreferrer ';
      }
      if ($this
        ->getSetting('noopener') === TRUE) {
        $rel .= 'noopener ';
      }
      if ($this
        ->getSetting('nofollow') === TRUE) {
        $rel .= 'nofollow ';
      }
      $topic = $this
        ->getSetting('linkTopic');
      if ($topic !== 'any') {
        $rel .= $topic;
      }
      if (empty($rel) === FALSE) {
        $urlOptions['attributes']['rel'] = $rel;
      }
      if (empty($target) === FALSE) {
        $urlOptions['attributes']['target'] = $target;
      }
      if ($download === TRUE) {
        $urlOptions['attributes']['download'] = '';
      }
      $url
        ->setOptions($urlOptions);
      $elements[$delta] = [
        '#type' => 'link',
        '#title' => $title,
        '#options' => $url
          ->getOptions(),
        '#url' => $url,
        '#attributes' => [
          'class' => $classes,
        ],
        '#cache' => [
          'tags' => $entity
            ->getCacheTags(),
        ],
      ];
      if (empty($items[$delta]->_attributes) === FALSE) {

        // There are item attributes. Add them to the link's options.
        $elements[$delta]['#options'] += [
          'attributes' => $items[$delta]->_attributes,
        ];

        // And remove them from the item since they are now included
        // on the link.
        unset($items[$delta]->_attributes);
      }
    }
  }
  if (empty($elements) === TRUE) {
    return [];
  }

  //
  // Add multi-value field processing.
  // ---------------------------------
  // If the field has multiple values, redirect to a theme and pass
  // the list style and separator.
  $isMultiple = $this->fieldDefinition
    ->getFieldStorageDefinition()
    ->isMultiple();
  if ($isMultiple === TRUE) {

    // Replace the 'field' theme with one that supports lists.
    $elements['#theme'] = 'formatter_suite_field_list';

    // Set the list style.
    $elements['#list_style'] = $this
      ->getSetting('listStyle');

    // Set the list separator.
    //
    // Security: The list separator is entered by an administrator.
    // It may legitimately include HTML entities and minor HTML, but
    // it should not include dangerous HTML.
    //
    // Because it may include HTML, we cannot pass it as-is and let a TWIG
    // template use {{ }}, which will process the text and corrupt any
    // entered HTML or HTML entities.
    //
    // We therefore use an Xss admin filter to remove any egreggious HTML
    // (such as scripts and styles), and then FormattableMarkup() to mark the
    // resulting text as safe.
    $listSeparator = Xss::filterAdmin($this
      ->getSetting('listSeparator'));
    $elements['#list_separator'] = new FormattableMarkup($listSeparator, []);
  }
  return $elements;
}