toolshed-8.x-1.x-dev/modules/toolshed_media/src/Plugin/Field/FieldFormatter/FileInfoFormatter.php

modules/toolshed_media/src/Plugin/Field/FieldFormatter/FileInfoFormatter.php
<?php

namespace Drupal\toolshed_media\Plugin\Field\FieldFormatter;

use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Entity\EntityMalformedException;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
use Drupal\Core\Field\Attribute\FieldFormatter;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceFormatterBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Logger\LoggerChannelTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\toolshed_media\Utility\FileHelper;
use Drupal\toolshed_media\Utility\MimeHelperInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * A Field formatter for displaying file information for file or media entities.
 *
 * @FieldFormatter(
 *   id = "toolshed_file_info",
 *   label = @Translation("File info link"),
 *   field_types = {
 *     "file",
 *     "entity_reference",
 *   },
 * )
 */
#[FieldFormatter(
  id: 'toolshed_file_info',
  label: new TranslatableMarkup('File info link'),
  field_types: [
    "file",
    "entity_reference",
  ],
)]
class FileInfoFormatter extends EntityReferenceFormatterBase {

  use LoggerChannelTrait;

  // Entity types that can be used with the file info formatter works with.
  const ALLOWED_ENTITY_TYPES = ['file', 'media'];

  /**
   * Mime helper to transform extensions to friendly display types.
   *
   * @var \Drupal\toolshed_media\Utility\MimeHelperInterface
   */
  protected MimeHelperInterface $mimeManager;

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

  /**
   * Creates a new instance of the FileInfoFormatter formatter plugin.
   *
   * @param array $configuration
   *   Array of plugin configuration options which includes the field formatter
   *   definition information:
   *     - $field_definition
   *     - $settings
   *     - $label
   *     - $view_mode
   *     - $third_party_settings
   *   These are the usual values for constructing a standard field formatter.
   * @param string $plugin_id
   *   The plugin ID.
   * @param mixed $plugin_definition
   *   The plugin definition.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\toolshed_media\Utility\MimeHelperInterface $mime_helper
   *   Toolshed media mime manager. Maintains a map of file extensions to
   *   configured mime display settings.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, MimeHelperInterface $mime_helper) {
    parent::__construct(
      $plugin_id,
      $plugin_definition,
      $configuration['field_definition'],
      $configuration['settings'],
      $configuration['label'],
      $configuration['view_mode'],
      $configuration['third_party_settings']
    );

    $this->entityTypeManager = $entity_type_manager;
    $this->mimeManager = $mime_helper;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity_type.manager'),
      $container->get('toolshed.media.mime_manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public static function isApplicable(FieldDefinitionInterface $fieldDef): bool {
    $storageDef = $fieldDef->getFieldStorageDefinition();

    switch ($storageDef->getType()) {
      case 'entity_reference':
        $fieldType = $storageDef->getSetting('target_type');
        return in_array($fieldType, static::ALLOWED_ENTITY_TYPES);

      case 'file':
        return TRUE;
    }

    return FALSE;
  }

  /**
   * Get the list of available options for linking the entity reference field.
   *
   * For media it is valid for the resulting link to point to either the file
   * or the original media object. For file, the entity is the file, so they
   * actually both generate the same link.
   *
   * @return array
   *   Return a key/value list of available options for linking the field.
   */
  public function getLinkOptions(): array {
    try {
      $targetType = $this->fieldDefinition->getSetting('target_type');
      $typeLabel = $this->entityTypeManager->getDefinition($targetType)->getLabel();
    }
    catch (PluginNotFoundException $e) {
      // Should never happen, but just incase there are invalid types.
      $typeLabel = $this->t('Entity');
    }

    $options = [
      'file' => $this->t('Directly to file path'),
      'entity' => $this->t('@type_label entity URL', [
        '@type_label' => $typeLabel,
      ]),
    ];

    if ('media' === $this->fieldDefinition->getTargetEntityTypeId()) {
      $options['parent'] = $this->t('Parent media URL');
    }

    return $options;
  }

  /**
   * Get a list of link title display options.
   *
   * @return array
   *   Available link title options formatted to be used as options for a
   *   "select" or "radios" form element.
   */
  public function getTitleDisplayOptions(): array {
    $options = [
      'file' => $this->t('Filename'),
      'custom' => $this->t('Custom title text'),
    ];

    $targetType = $this->fieldDefinition->getSetting('target_type');
    if ('media' === $this->fieldDefinition->getTargetEntityTypeId() || 'media' === $targetType) {
      $options = ['media' => $this->t('Media name')] + $options;
    }

    return $options;
  }

  /**
   * Convert the number of raw bytes into a human friendly readable format.
   *
   * This will convert the bytes integer to a string of the file size with
   * the appropriate unit suffix.
   *
   * @param int $bytes
   *   The size of a file in number of bytes.
   * @param int $decimals
   *   Number of decimal places to keep in the returned string.
   *
   * @return string
   *   Return the file size with a unit suffix, and up to the number
   *   of decimal places requested.
   */
  protected function readableFileSize($bytes, $decimals = 2): string {
    $i = 0;
    $divisor = 1;
    $next = 1024;
    $suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

    while (($bytes / $next) > 1.0 && isset($suffixes[$i])) {
      $divisor = $next;
      $next *= 1024;
      ++$i;
    }

    return number_format($bytes / $divisor, $decimals) . ' ' . $suffixes[$i];
  }

  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items, $langcode): array {
    /** @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items */
    $entityType = $this->fieldDefinition->getSetting('target_type');

    if (in_array($entityType, static::ALLOWED_ENTITY_TYPES)) {
      $elements = [];
      $parentEntity = $items->getEntity();
      $linkTo = $this->getSetting('link_to');
      $displayInfo = $this->getSetting('info_shown');
      $textDisplay = $this->getSetting('link_title_display');
      $linkText = $this->getSetting('link_text');

      // When not configured value, fallback depending on if custom link text
      // has a value or not. This is mostly for backwards compatibility for
      // before the text display option was available.
      if (empty($textDisplay)) {
        $textDisplay = empty($linkText) ? 'file' : 'custom';
      }

      /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
      foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
        $fileHelper = FileHelper::fromEntity($entity);
        $itemUrl = NULL;

        // Determine the label for the text display setting.
        switch ($textDisplay) {
          case 'custom':
            $label = $linkText ?: $entity->label();
            break;

          case 'media':
            $label = 'media' === $entity->getEntityTypeId() ? $entity->label() : $parentEntity->label();
            break;

          default:
            // Use the filename but fallback to the entity label.
            $label = $fileHelper ? $fileHelper->getFilename() : $entity->label();
        }

        if ($fileHelper) {
          $info = [];

          if (!empty($displayInfo['mime'])) {
            $filenameParts = explode('.', $fileHelper->getFilename());
            $ext = end($filenameParts);

            $info[] = $this->mimeManager->getDisplayName($ext) ?: $fileHelper->getMime();
          }
          if (!empty($displayInfo['size'])) {
            $info[] = $this->readableFileSize($fileHelper->getSize());
          }

          $label .= (empty($info) ? '' : ' (' . implode(', ', $info) . ')');
        }

        try {
          switch ($linkTo) {
            case 'file':
              $itemUrl = $fileHelper ? Url::fromUri($fileHelper->buildRawUrl()) : $entity->toUrl('canonical');
              break;

            case 'entity':
              $itemUrl = $entity->toUrl('canonical');
              break;

            case 'parent':
              $itemUrl = $parentEntity->toUrl('canonical');
              break;
          }
        }
        catch (EntityMalformedException | UndefinedLinkTemplateException $e) {
          // Unable to get the URL report the error, but don't crash.
          $this->getLogger('toolshed_media')->error('Unable to create URL for @fieldname on @entity_type (ID: @entity_id).', [
            '@fieldname' => $this->fieldDefinition->getName(),
            '@entity_type' => $parentEntity->getEntityTypeId(),
            '@entity_id' => $parentEntity->id(),
          ]);
        }

        if (!empty($itemUrl)) {
          $elements[$delta] = [
            '#type' => 'link',
            '#title' => $label,
            '#url' => $itemUrl,
          ];
        }
        else {
          $elements[$delta] = [
            '#prefix' => '<span>',
            '#suffix' => '</span>',
            '#plain_text' => $label,
          ];
        }
      }

      return $elements;
    }

    // Unrecognized entity type, so we return an empty render array.
    return [];
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings(): array {
    return [
      'link_to' => 'file',
      'link_title_display' => 'file',
      'link_text' => NULL,
      'info_shown' => [
        'mime' => 'mime',
        'size' => 'size',
      ],
    ] + parent::defaultSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSummary(): array {
    $summary = [];

    $infoDisplay = array_filter($this->getSetting('info_shown'));
    if (!empty($infoDisplay)) {
      $summary[] = $this->t('Displaying file info: %display', [
        '%display' => implode(', ', $infoDisplay),
      ]);
    }

    $linkTo = $this->getSetting('link_to');
    $linkOpts = $this->getLinkOptions();

    if (!empty($linkTo) && isset($linkOpts[$linkTo])) {
      $summary[] = $this->t('Linked to <strong>@target</strong>', [
        '@target' => $linkOpts[$linkTo],
      ]);
    }

    return $summary;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state): array {
    $form = parent::settingsForm($form, $form_state);

    $form['link_to'] = [
      '#type' => 'select',
      '#title' => $this->t('Link to'),
      '#options' => ['' => $this->t('No link')] + $this->getLinkOptions(),
      '#default_value' => $this->getSetting('link_to'),
    ];

    $form['link_title_display'] = [
      '#type' => 'select',
      '#title' => $this->t('Link text'),
      '#options' => $this->getTitleDisplayOptions(),
      '#default_value' => $this->getSetting('link_title_display'),
    ];

    $form['link_text'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Custom text'),
      '#default_value' => $this->getSetting('link_text'),
      '#description' => $this->t('Leave blank to use default'),
      '#process' => [static::class . '::addFormLinkTextStates'],
    ];

    $form['info_shown'] = [
      '#type' => 'checkboxes',
      '#title' => $this->t('Info Shown'),
      '#description' => $this->t('Use the options to determine what will be displayed'),
      '#options' => [
        'mime' => $this->t('File mime'),
        'size' => $this->t('File size'),
      ],
      '#default_value' => $this->getSetting('info_shown'),
    ];

    return $form;
  }

  /**
   * Adds conditional display FAPI state information.
   *
   * The "link_text" setting is only relevant when the link_title_display is
   * set to "custom" and so only setup the "link_text" to appear in this case.
   *
   * @param array $element
   *   The link_text field formatter settings element to modify.
   *
   * @return array
   *   The updated link_text setting element.
   */
  public static function addFormLinkTextStates(array $element): array {
    $parents = $element['#parents'];
    array_pop($parents);
    $name = array_shift($parents);
    $parents[] = 'link_title_display';

    $name .= '[' . implode('][', $parents) . ']';
    $element['#states'] = [
      'visible' => [
        'select[name="' . $name . '"]' => ['value' => 'custom'],
      ],
      'required' => [
        'select[name="' . $name . '"]' => ['value' => 'custom'],
      ],
    ];

    return $element;
  }

}

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

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