class FileThemeHooks

Theme hooks for the file module.

Hierarchy

Expanded class hierarchy of FileThemeHooks

1 file declares its use of FileThemeHooks
file.module in core/modules/file/file.module

File

core/modules/file/src/Hook/FileThemeHooks.php, line 21

Namespace

Drupal\file\Hook
View source
class FileThemeHooks {
  use StringTranslationTrait;
  public function __construct(protected FileUrlGeneratorInterface $fileUrlGenerator, protected RendererInterface $renderer) {
  }
  
  /**
   * Implements hook_theme().
   */
  public function theme() : array {
    return [
      // From file.module.
'file_link' => [
        'variables' => [
          'file' => NULL,
          'description' => NULL,
          'attributes' => [],
        ],
        'initial preprocess' => static::class . ':preprocessFileLink',
      ],
      'file_managed_file' => [
        'render element' => 'element',
        'initial preprocess' => static::class . ':preprocessManagedFile',
      ],
      'file_audio' => [
        'variables' => [
          'files' => [],
          'attributes' => NULL,
        ],
      ],
      'file_video' => [
        'variables' => [
          'files' => [],
          'attributes' => NULL,
        ],
      ],
      'file_widget_multiple' => [
        'render element' => 'element',
        'initial preprocess' => static::class . ':preprocessFileWidgetMultiple',
      ],
      'file_upload_help' => [
        'variables' => [
          'description' => NULL,
          'upload_validators' => NULL,
          'cardinality' => NULL,
        ],
        'initial preprocess' => static::class . ':preprocessFileUploadHelp',
      ],
    ];
  }
  
  /**
   * Prepares variables for file link templates.
   *
   * Default template: file-link.html.twig.
   *
   * @param array<string,mixed> $variables
   *   An associative array containing:
   *   - file: A File entity to which the link will be created.
   *   - icon_directory: (optional) A path to a directory of icons to be used
   *     for files. Defaults to the value of the "icon.directory" variable.
   *   - description: A description to be displayed instead of the filename.
   *   - attributes: An associative array of attributes to be placed in the a
   *     tag.
   */
  public function preprocessFileLink(array &$variables) : void {
    $file = $variables['file'];
    $options = [];
    $url = $this->fileUrlGenerator
      ->generate($file->getFileUri());
    $mime_type = $file->getMimeType();
    $options['attributes']['type'] = $mime_type;
    // Use the description as the link text if available.
    if (empty($variables['description'])) {
      $link_text = $file->getFilename();
    }
    else {
      $link_text = $variables['description'];
      $options['attributes']['title'] = $file->getFilename();
    }
    // Classes to add to the file field for icons.
    $classes = [
      'file',
      // Add a specific class for each and every mime type.
'file--mime-' . strtr($mime_type, [
        '/' => '-',
        '.' => '-',
      ]),
      // Add a more general class for groups of well known MIME types.
'file--' . IconMimeTypes::getIconClass($mime_type),
    ];
    // Set file classes to the options array.
    $variables['attributes'] = new Attribute($variables['attributes']);
    $variables['attributes']->addClass($classes);
    $variables['file_size'] = $file->getSize() !== NULL ? ByteSizeMarkup::create($file->getSize()) : '';
    $variables['link'] = (new Link($link_text, $url->mergeOptions($options)))
      ->toRenderable();
  }
  
  /**
   * Prepares variables for file upload help text templates.
   *
   * Default template: file-upload-help.html.twig.
   *
   * @param array<string,mixed> $variables
   *   An associative array containing:
   *   - description: The normal description for this field, specified by the
   *     user.
   *   - upload_validators: An array of upload validators as used in
   *     $element['#upload_validators'].
   */
  public function preprocessFileUploadHelp(array &$variables) : void {
    $description = $variables['description'];
    $upload_validators = $variables['upload_validators'];
    $cardinality = $variables['cardinality'];
    $descriptions = [];
    if (!empty($description)) {
      $descriptions[] = FieldFilteredMarkup::create($description);
    }
    if (isset($cardinality)) {
      if ($cardinality == -1) {
        $descriptions[] = $this->t('Unlimited number of files can be uploaded to this field.');
      }
      else {
        $descriptions[] = $this->formatPlural($cardinality, 'One file only.', 'Maximum @count files.');
      }
    }
    if (isset($upload_validators['FileSizeLimit'])) {
      $descriptions[] = $this->t('@size limit.', [
        '@size' => ByteSizeMarkup::create($upload_validators['FileSizeLimit']['fileLimit']),
      ]);
    }
    if (isset($upload_validators['FileExtension'])) {
      $descriptions[] = $this->t('Allowed types: @extensions.', [
        '@extensions' => $upload_validators['FileExtension']['extensions'],
      ]);
    }
    if (isset($upload_validators['FileImageDimensions'])) {
      $max = $upload_validators['FileImageDimensions']['maxDimensions'];
      $min = $upload_validators['FileImageDimensions']['minDimensions'];
      if ($min && $max && $min == $max) {
        $descriptions[] = $this->t('Images must be exactly <strong>@size</strong> pixels.', [
          '@size' => $max,
        ]);
      }
      elseif ($min && $max) {
        $descriptions[] = $this->t('Images must be larger than <strong>@min</strong> pixels. Images larger than <strong>@max</strong> pixels will be resized.', [
          '@min' => $min,
          '@max' => $max,
        ]);
      }
      elseif ($min) {
        $descriptions[] = $this->t('Images must be larger than <strong>@min</strong> pixels.', [
          '@min' => $min,
        ]);
      }
      elseif ($max) {
        $descriptions[] = $this->t('Images larger than <strong>@max</strong> pixels will be resized.', [
          '@max' => $max,
        ]);
      }
    }
    $variables['descriptions'] = $descriptions;
  }
  
  /**
   * Prepares variables for file form widget templates.
   *
   * Default template: file-managed-file.html.twig.
   *
   * @param array<string,array> $variables
   *   An associative array containing:
   *   - element: A render element representing the file.
   */
  public function preprocessManagedFile(array &$variables) : void {
    $element = $variables['element'];
    $variables['attributes'] = [];
    if (isset($element['#id'])) {
      $variables['attributes']['id'] = $element['#id'];
    }
    if (!empty($element['#attributes']['class'])) {
      $variables['attributes']['class'] = (array) $element['#attributes']['class'];
    }
  }
  
  /**
   * Prepares variables for multi file form widget templates.
   *
   * Default template: file-widget-multiple.html.twig.
   *
   * @param array<string,mixed> $variables
   *   An associative array containing:
   *   - element: A render element representing the widgets.
   */
  public function preprocessFileWidgetMultiple(array &$variables) : void {
    $element = $variables['element'];
    // Special ID and classes for draggable tables.
    $weight_class = $element['#id'] . '-weight';
    $table_id = $element['#id'] . '-table';
    // Build up a table of applicable fields.
    $headers = [];
    $headers[] = $this->t('File information');
    if ($element['#display_field']) {
      $headers[] = [
        'data' => $this->t('Display'),
        'class' => [
          'checkbox',
        ],
      ];
    }
    $headers[] = $this->t('Weight');
    $headers[] = $this->t('Operations');
    // Get our list of widgets in order (needed when the form comes back after
    // preview or failed validation).
    $widgets = [];
    foreach (Element::children($element) as $key) {
      $widgets[] =& $element[$key];
    }
    usort($widgets, function ($a, $b) {
      // Sorts using ['_weight']['#value'].
      $a_weight = is_array($a) && isset($a['_weight']['#value']) ? $a['_weight']['#value'] : 0;
      $b_weight = is_array($b) && isset($b['_weight']['#value']) ? $b['_weight']['#value'] : 0;
      return $a_weight - $b_weight;
    });
    $rows = [];
    foreach ($widgets as &$widget) {
      // Save the uploading row for last.
      if (empty($widget['#files'])) {
        $widget['#title'] = $element['#file_upload_title'];
        $widget['#description'] = $this->renderer
          ->renderInIsolation($element['#file_upload_description']);
        continue;
      }
      // Delay rendering of the buttons, so that they can be rendered later in
      // the "operations" column.
      $operations_elements = [];
      foreach (Element::children($widget) as $key) {
        if (isset($widget[$key]['#type']) && $widget[$key]['#type'] == 'submit') {
          hide($widget[$key]);
          $operations_elements[] =& $widget[$key];
        }
      }
      // Delay rendering of the "Display" option and the weight selector, so
      // that each can be rendered later in its own column.
      if ($element['#display_field']) {
        hide($widget['display']);
      }
      hide($widget['_weight']);
      $widget['_weight']['#attributes']['class'] = [
        $weight_class,
      ];
      // Render everything else together in a column, without the normal
      // wrappers.
      $row = [];
      $widget['#theme_wrappers'] = [];
      $row[] = $this->renderer
        ->render($widget);
      // Arrange the row with the rest of the rendered columns.
      if ($element['#display_field']) {
        unset($widget['display']['#title']);
        $row[] = [
          'data' => $widget['display'],
          'class' => [
            'checkbox',
          ],
        ];
      }
      $row[] = [
        'data' => $widget['_weight'],
      ];
      // Show the buttons that had previously been marked as hidden in this
      // preprocess function. We use show() to undo the earlier hide().
      foreach (Element::children($operations_elements) as $key) {
        show($operations_elements[$key]);
      }
      $row[] = [
        'data' => $operations_elements,
      ];
      $rows[] = [
        'data' => $row,
        'class' => isset($widget['#attributes']['class']) ? array_merge($widget['#attributes']['class'], [
          'draggable',
        ]) : [
          'draggable',
        ],
      ];
    }
    $variables['table'] = [
      '#type' => 'table',
      '#header' => $headers,
      '#rows' => $rows,
      '#attributes' => [
        'id' => $table_id,
      ],
      '#tabledrag' => [
        [
          'action' => 'order',
          'relationship' => 'sibling',
          'group' => $weight_class,
        ],
      ],
      '#access' => !empty($rows),
    ];
    $variables['element'] = $element;
  }

}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.