You are here

public function GeneralUserReferenceFormatter::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/GeneralUserReferenceFormatter.php, line 784

Class

GeneralUserReferenceFormatter
Formats a user entity reference as one or more links.

Namespace

Drupal\formatter_suite\Plugin\Field\FieldFormatter

Code

public function viewElements(FieldItemListInterface $items, $langcode) {
  $this
    ->sanitizeSettings();

  //
  // Get entities.
  // -------------
  // From the item list, get the entities to process. If there aren't
  // any, return nothing.
  $elements = [];
  $entities = $this
    ->getEntitiesToView($items, $langcode);
  if (empty($entities) === TRUE) {
    return [];
  }

  // Prepare custom classes.
  // -----------------------
  // If the admin entered custom classes, add them.
  $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);
  }

  //
  // Prepare token pattern.
  // ----------------------
  // Get the pattern and pre-process it. If the admin entered a custom
  // pattern, clean it of dangerous HTML. Otherwise get a well-known
  // pattern.
  $userReferenceStyle = $this
    ->getSetting('userReferenceStyle');
  if ($userReferenceStyle === 'custom') {

    // Custom token pattern.
    $pattern = $this
      ->getSetting('titleCustomText');
    if (empty($pattern) === TRUE) {

      // If the custom pattern is empty, revert to the ID.
      $pattern = '[user:uid]';
    }
    else {

      // Security: A custom pattern is entered by an administrator.
      // It may legitimately include HTML entities and minor HTML, but
      // it should not include dangerous HTML.
      $pattern = Xss::filterAdmin($this
        ->getSetting('titleCustomText'));
    }
  }
  else {

    // User a pre-defined token pattern.
    $userReferencePatterns = self::getUserReferenceTokenPatterns();
    $pattern = $userReferencePatterns[$userReferenceStyle];
  }

  //
  // Replace tokens.
  // ---------------
  // Loop through the entities and do token replacement for each one.
  foreach ($entities as $delta => &$entity) {
    $url = NULL;
    if ($entity
      ->isAnonymous() === FALSE) {
      $url = $entity
        ->toUrl();
      $urlOptions = $url
        ->getOptions();
    }
    $title = '';
    if ($entity
      ->isAnonymous() === TRUE) {

      // For anonymous users, some tokens cannot be replaced because the
      // user entity has no account name, display name, etc. Fill those in.
      $updatedPattern = $this
        ->replaceTokensForAnonymous($pattern);
    }
    else {
      $updatedPattern = $pattern;
    }

    // Replace tokens in the pattern.
    $title = $this->tokenService
      ->replace($updatedPattern, [
      'user' => $entity,
    ], [
      'langcode' => $langcode,
      'clear' => TRUE,
    ]);

    // Because replaced text may include HTML, we cannot pass it directly as
    // the '#title' on a link, which will escape the HTML. Instead we
    // use FormattableMarkup.
    $title = new FormattableMarkup($title, []);

    // If the link is disabled, show the title text within a <span>.
    // Otherwise, build a URL and create a link.
    if ($this
      ->getSetting('showLink') === FALSE || $url === NULL) {
      $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;
      }
      $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 ours, which 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;
}