You are here

public function GeneralImageFormatter::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 ImageFormatter::viewElements

File

src/Plugin/Field/FieldFormatter/GeneralImageFormatter.php, line 666

Class

GeneralImageFormatter
Formats an image.

Namespace

Drupal\formatter_suite\Plugin\Field\FieldFormatter

Code

public function viewElements(FieldItemListInterface $items, $langCode) {

  // Get the parent entities containing the field items. However, if
  // there are no items, then fill in the default image, if any.
  //
  // Here we override behavior of the parent class. That class implements
  // getEntitiesToView() and checks for an empty $items list. When empty,
  // it gets the default image, if any, and creates a temporary items list
  // containing the image. With a new temporary items list, it calls the
  // parent's parent class getEntitiesToView() to get the associated
  // entities for those items.
  //
  // This works for the image formaters BUT it doesn't expose that new
  // temporary items list. And without that, we don't have some of the
  // information we need for this formatter.
  //
  // So, here we check for an empty item list explicitly, fill in the
  // default AND retain the new $items list for further use. The code is
  // based upon getEntitiesToView() in ImageFormatterBase.
  if ($items
    ->isEmpty() === TRUE) {

    // The item list is empty. Look for a default image.
    $defaultImage = $this
      ->getFieldSetting('default_image');

    // If there is no default image for this field, look in the field
    // definition instead.
    $fieldDef = $this->fieldDefinition;
    if (empty($defaultImage['uuid']) === TRUE && $fieldDef instanceof FieldConfigInterface) {
      $storageDef = $fieldDef
        ->getFieldStorageDefinition();
      $defaultImage = $storageDef
        ->getSetting('default_image');
    }

    // If there is still no default image found, then there is nothing
    // to format.
    if (empty($defaultImage['uuid']) === TRUE) {
      return [];
    }

    // Get the image's File entity.
    $imageFile = $this->entityRepository
      ->loadEntityByUuid('file', $defaultImage['uuid']);

    // If that didn't work, then something is wrong with the image and,
    // again, there is nothing to format.
    if ($imageFile === NULL) {
      return [];
    }

    // Create a new temporary FieldItemList for use in this formatter only.
    $items = clone $items;

    // Add the default image as a field item. Use the title, etc., from
    // the default image.
    $items
      ->setValue([
      'target_id' => $imageFile
        ->id(),
      'alt' => $defaultImage['alt'],
      'title' => $defaultImage['title'],
      'width' => $defaultImage['width'],
      'height' => $defaultImage['height'],
      'entity' => $imageFile,
      '_loaded' => TRUE,
      '_is_default' => TRUE,
    ]);
    $imageFile->_referringItem = $items[0];
  }

  // Load the entities for the item list, using either the original item
  // list or the temporary item list created above for the default image.
  $files = $this
    ->getEntitiesToView($items, $langCode);
  if (empty($files) === TRUE) {
    return [];
  }
  $this
    ->sanitizeSettings();

  //
  // Get settings.
  // -------------
  // Get the formatter configuration.
  $classes = $this
    ->getSetting('classes');
  $openLinkIn = $this
    ->getSetting('openLinkIn');
  $captionLocation = $this
    ->getSetting('captionLocation');
  $captionIncludeTitle = $this
    ->getSetting('captionIncludeTitle');
  $captionIncludeFilename = $this
    ->getSetting('captionIncludeFilename');
  $captionIncludeSize = $this
    ->getSetting('captionIncludeSize');
  $captionIncludeDimensions = $this
    ->getSetting('captionIncludeDimensions');
  $captionIncludeMime = $this
    ->getSetting('captionIncludeMime');

  // If the settings do not enable any of the possible caption components,
  // then there is no caption.
  if ($captionIncludeTitle === FALSE && $captionIncludeFilename === FALSE && $captionIncludeSize === FALSE && $captionIncludeDimensions === FALSE && $captionIncludeMime === FALSE) {
    $captionLocation = 'none';
  }

  //
  // Format each image.
  // ------------------
  // The parent image formatter does very little processing within the
  // formatter. Instead, it sets a theme template and later processing
  // of the template sets up the image's URL, including possibly use of
  // an image style.
  //
  // Let the parent class do its processing. The returned array has one
  // entry per item and a configuration that invokes the image module's
  // 'image_formatter' theme.
  $parentElements = parent::viewElements($items, $langCode);
  $classes = explode(' ', $classes);
  $classes[] = 'formatter_suite-general-image';

  //
  // Create render elements.
  // -----------------------
  // The parent elements created above only contain the field's images.
  // To this we need to add:
  // - A container wrapper that has the given classes.
  // - A caption above or below each image, if enabled.
  //   - A title, if enabled.
  //   - A file name, if enabled.
  //   - A file size, if enabled.
  //   - Image dimensions, if enabled.
  //   - A file MIME type, if enabled.
  //
  // If linking is enabled, the image and caption need to be links using
  // the indicated link attributes.
  //
  // The parent element needs to be nested within a wrapper that adds
  // the above items. If the parent element has a URL, then that URL's
  // options need to be adjusted to include the link attributes.
  $elements = [];
  foreach ($items as $delta => $item) {
    if (isset($parentElements[$delta]) !== TRUE) {

      // The parent formatter skipped this one? Skip it too.
      continue;
    }

    // Get the URL, if any, from the parent. If there is a URL, add
    // link attributes and update the parent elements.
    $url = $parentElements[$delta]['#url'];
    $mime = $files[$delta]
      ->getMimeType();
    if ($url !== NULL) {
      $urlOptions = $url
        ->getOptions();
      if (isset($urlOptions['attributes']) === FALSE) {
        $urlOptions['attributes'] = [];
      }
      $urlOptions['attributes']['type'] = $mime;
      switch ($openLinkIn) {
        case '_self':
          $urlOptions['attributes']['target'] = '_self';
          break;
        case '_blank':
          $urlOptions['attributes']['target'] = '_blank';
          break;
        case 'download':
          $urlOptions['attributes']['download'] = '';
          break;
      }
      $url
        ->setOptions($urlOptions);
      $parentElements[$delta]['#url'] = $url;
    }

    // Assemble the caption's render elements.
    $imageWeight = 0;
    $captionWeight = 0;
    $itemClasses = array_merge([], $classes);
    $caption = [];
    if ($captionLocation !== 'none') {
      switch ($captionLocation) {
        case 'above':
          $captionWeight = 0;
          $imageWeight = 1000;
          $itemClasses[] = 'formatter_suite-general-image-above';
          break;
        default:
        case 'below':
          $imageWeight = 0;
          $captionWeight = 1000;
          $itemClasses[] = 'formatter_suite-general-image-below';
          break;
      }
      $title = '';
      if ($captionIncludeTitle === TRUE) {

        // Prefer the title from the field, if any. Otherwise use the
        // file's label.
        if (isset($item->title) === TRUE) {
          $title = $item->title;
        }
        else {

          // Fall back to the file entity's label/name. But this too
          // may be empty.
          $title = $files[$delta]
            ->label();
        }
        if (empty($title) === FALSE) {
          if ($url === NULL) {
            $caption['title'] = [
              '#type' => 'html_tag',
              '#tag' => 'div',
              '#value' => Html::escape($title),
              '#weight' => $captionWeight++,
              '#attributes' => [
                'class' => [
                  'formatter_suite-image-caption-title',
                ],
              ],
            ];
          }
          else {

            // Clone the URL because the render element modifies it
            // to add attributes.
            $localUrl = clone $url;
            $caption['title'] = [
              '#type' => 'link',
              '#title' => $title,
              '#options' => $localUrl
                ->getOptions(),
              '#url' => $localUrl,
              '#weight' => $captionWeight++,
              '#attributes' => [
                'class' => [
                  'formatter_suite-image-caption-title',
                ],
              ],
            ];
          }
        }
      }
      if ($captionIncludeFilename === TRUE) {

        // The name of the original image file, not the styled image.
        $filename = $files[$delta]
          ->getFilename();

        // Only show the file name if it differs from the title, if the
        // title was shown.
        if (empty($title) === TRUE || $title !== $filename) {
          if ($url === NULL) {
            $caption['filename'] = [
              '#type' => 'html_tag',
              '#tag' => 'div',
              '#value' => Html::escape($filename),
              '#weight' => $captionWeight++,
              '#attributes' => [
                'class' => [
                  'formatter_suite-image-caption-filename',
                ],
              ],
            ];
          }
          else {

            // Clone the URL because the render element modifies it
            // to add attributes.
            $localUrl = clone $url;
            $caption['filename'] = [
              '#type' => 'link',
              '#title' => $filename,
              '#options' => $localUrl
                ->getOptions(),
              '#url' => $localUrl,
              '#weight' => $captionWeight++,
              '#attributes' => [
                'class' => [
                  'formatter_suite-image-caption-filename',
                ],
              ],
            ];
          }
        }
      }
      if ($captionIncludeSize === TRUE) {

        // The size of the original image, not the styled image.
        $bytes = Utilities::formatBytes($files[$delta]
          ->getSize(), 1000, FALSE, 2);
        if ($url === NULL) {
          $caption['size'] = [
            '#type' => 'html_tag',
            '#tag' => 'div',
            '#value' => $bytes,
            '#weight' => $captionWeight++,
            '#attributes' => [
              'class' => [
                'formatter_suite-image-caption-size',
              ],
            ],
          ];
        }
        else {

          // Clone the URL because the render element modifies it
          // to add attributes.
          $localUrl = clone $url;
          $caption['size'] = [
            '#type' => 'link',
            '#title' => $bytes,
            '#options' => $localUrl
              ->getOptions(),
            '#url' => $localUrl,
            '#weight' => $captionWeight++,
            '#attributes' => [
              'class' => [
                'formatter_suite-image-caption-size',
              ],
            ],
          ];
        }
      }
      if ($captionIncludeDimensions === TRUE) {

        // The dimensions of the original image, not the styled image.
        $x = " ⨉ ";
        $text = $item->width . $x . $item->height;
        if ($url === NULL) {
          $caption['dimensions'] = [
            '#type' => 'html_tag',
            '#tag' => 'div',
            '#value' => $text,
            '#weight' => $captionWeight++,
            '#attributes' => [
              'class' => [
                'formatter_suite-image-caption-dimensions',
              ],
            ],
          ];
        }
        else {

          // Clone the URL because the render element modifies it
          // to add attributes.
          $localUrl = clone $url;
          $caption['dimensions'] = [
            '#type' => 'link',
            '#title' => $text,
            '#options' => $localUrl
              ->getOptions(),
            '#url' => $localUrl,
            '#weight' => $captionWeight++,
            '#attributes' => [
              'class' => [
                'formatter_suite-image-caption-dimensions',
              ],
            ],
          ];
        }
      }
      if ($captionIncludeMime === TRUE) {

        // The MIME type of the original image, not the styled image.
        if ($url === NULL) {
          $caption['mime'] = [
            '#type' => 'html_tag',
            '#tag' => 'div',
            '#value' => Html::escape($mime),
            '#weight' => $captionWeight++,
            '#attributes' => [
              'class' => [
                'formatter_suite-image-caption-mime',
              ],
            ],
          ];
        }
        else {

          // Clone the URL because the render element modifies it
          // to add attributes.
          $localUrl = clone $url;
          $caption['mime'] = [
            '#type' => 'link',
            '#title' => $mime,
            '#options' => $localUrl
              ->getOptions(),
            '#url' => $localUrl,
            '#weight' => $captionWeight++,
            '#attributes' => [
              'class' => [
                'formatter_suite-image-caption-mime',
              ],
            ],
          ];
        }
      }
    }

    // Modify the image module's render elements.
    //
    // - Add a weight to order the image and caption properly.
    //
    // - Swap out the theme to go to our own theme, which is identical to
    //   the Image module's, except that it includes attributes on the
    //   link surrounding the image.
    //
    // - Add the URL options, such as the target and download attributes.
    $parentElements[$delta]['#weight'] = $imageWeight;
    $parentElements[$delta]['#theme'] = 'formatter_suite_general_image_formatter';
    if ($url === NULL) {

      // There is no URL. Add the class to the image itself.
      $parentElements[$delta]['#item_attributes']['class'] = [
        'formatter_suite-image',
      ];
    }
    else {

      // There is a URL. Add the class to the link around the image.
      $urlOptions = $url
        ->getOptions();
      if (isset($urlOptions['attributes']) === TRUE) {

        // Add the URL's attributes, such as target and download.
        $urlAttributes = $urlOptions['attributes'];
        $parentElements[$delta]['#attributes'] = $urlAttributes + [
          'class' => [
            'formatter_suite-image',
          ],
        ];
      }
      else {
        $parentElements[$delta]['#attributes']['class'] = [
          'formatter_suite-image',
        ];
      }
    }

    // Create a container for the image and caption.
    $elements[$delta] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => $itemClasses,
      ],
      '#attached' => [
        'library' => [
          'formatter_suite/formatter_suite.usage',
        ],
      ],
      'image' => $parentElements[$delta],
    ];
    if (empty($caption) === FALSE) {
      $elements[$delta] += $caption;
    }
  }
  return $elements;
}