view_mode_crop-1.0.x-dev/src/CropImageHelper.php

src/CropImageHelper.php
<?php

namespace Drupal\view_mode_crop;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\EntityReferenceFieldItemList;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Image\ImageInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\file\FileInterface;
use Drupal\image\Entity\ImageStyle;
use Drupal\media\MediaInterface;
use Drupal\view_mode_crop\Plugin\Field\FieldType\ViewModeCropEntityReferenceItem;
use Drupal\view_mode_crop\Plugin\Field\FieldType\ViewModeCropImageItem;
use Drupal\view_mode_crop\StreamWrapper\CropPrivateStreamWrapper;
use Drupal\view_mode_crop\StreamWrapper\CropPublicStreamWrapper;

/**
 * Helper functions for cropping images.
 */
class CropImageHelper {

  /**
   * Generate a derivated (cropped) image.
   *
   * @param \Drupal\view_mode_crop\Plugin\Field\FieldType\ViewModeCropImageItem $imageItem
   *   THe field item.
   * @param string $viewMode
   *   The view mode.
   * @param string $derivative_uri
   *   The derivate uri to save to.
   * @param array $crop_data
   *   The crop data.
   *
   * @return bool
   *   Succeeded or not.
   */
  public static function createDerivate(ViewModeCropImageItem $imageItem, string $viewMode, string $derivative_uri, array $crop_data) {
    /**
     * @var \Drupal\Core\File\FileSystemInterface $file_system
     */
    $file_system = \Drupal::service('file_system');
    /**
     * @var \Drupal\Core\Image\ImageFactory $image_factory
     */
    $image_factory = \Drupal::service('image.factory');

    if (!isset($imageItem->entity) || !$imageItem->entity instanceof FileInterface) {
      return FALSE;
    }

    $original_uri = $imageItem->entity->_initialUri ?? $imageItem->entity->getFileUri();

    if (!$original_uri || !file_exists($original_uri)) {
      return FALSE;
    }

    // If the source file doesn't exist, return FALSE without creating folders.
    $image = $image_factory->get($original_uri);
    if (!$image->isValid()) {
      return FALSE;
    }

    $is_cropped = self::cropImage($image, $viewMode, $crop_data);

    // Get the folder for the final location of this image.
    $directory = $file_system->dirname($derivative_uri);

    // Build the destination folder tree if it doesn't already exist.
    if (!$file_system->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
      \Drupal::logger('view_mode_crop')
        ->error('Failed to create style directory: %directory', ['%directory' => $directory]);
      return FALSE;
    }

    if (!$is_cropped) {
      // Not cropped.
      $file_system->delete($derivative_uri);
    }
    else {
      if (!$image->save($derivative_uri)) {
        if (file_exists($derivative_uri)) {
          \Drupal::logger('view_mode_crop')
            ->error('Cached image file %destination already exists. There may be an issue with your rewrite configuration.', ['%destination' => $derivative_uri]);
        }
        return FALSE;
      }
    }
    $module_handler = \Drupal::moduleHandler();
    if ($module_handler->moduleExists('image')) {
      image_path_flush($derivative_uri);
      if ($module_handler->moduleExists('webp')) {
        // If the webp module exists, we should also clear the webp generated
        // files.
        /**
         * @var \Drupal\webp\Webp $webp
         */
        $webp = \Drupal::service('webp.webp');

        $webp_derivative_uri = $webp->getWebpSrcset($derivative_uri);

        if (file_exists($webp_derivative_uri)) {
          $file_system->delete($webp_derivative_uri);
          clearstatcache(TRUE, $webp_derivative_uri);
        }

        $styles = ImageStyle::loadMultiple();
        foreach ($styles as $style) {
          $webp_derivative_uri = $webp->getWebpSrcset($style->buildUri($derivative_uri));
          if (file_exists($webp_derivative_uri)) {
            $file_system->delete($webp_derivative_uri);
            clearstatcache(TRUE, $webp_derivative_uri);
          }
        }
      }
    }
    clearstatcache(TRUE, $derivative_uri);

    return TRUE;
  }

  /**
   * Crop the given image.
   *
   * @param \Drupal\Core\Image\ImageInterface $image
   *   The image.
   * @param string $view_mode
   *   The view mode.
   * @param array $crop_data
   *   The crop data.
   *
   * @return bool
   *   Returns true when the image was cropped.
   */
  public static function cropImage(ImageInterface $image, string $view_mode, array $crop_data) {
    if (!isset($crop_data[$view_mode])) {
      // View mode is not set. Fall back to default.
      $view_mode = 'default';
    }
    if (isset($crop_data[$view_mode]) && $crop_data[$view_mode]->w >= 0 && $crop_data[$view_mode]->h >= 0) {

      if (
        $crop_data[$view_mode]->x == 0 &&
        $crop_data[$view_mode]->y == 0 &&
        $crop_data[$view_mode]->w == $image->getWidth() &&
        $crop_data[$view_mode]->h == $image->getHeight()
      ) {
        // No cropping.
        return FALSE;
      }

      $image->crop(
        $crop_data[$view_mode]->x,
        $crop_data[$view_mode]->y,
        $crop_data[$view_mode]->w,
        $crop_data[$view_mode]->h
      );
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Get the uri to the cropped image.
   *
   * @param \Drupal\view_mode_crop\Plugin\Field\FieldType\ViewModeCropImageItem $imageItem
   *   The field item.
   * @param int $delta
   *   The field delta.
   * @param string $viewMode
   *   The view mode.
   *
   * @return string
   *   The uri.
   */
  public static function getUri(ViewModeCropImageItem $imageItem, int $delta, string $viewMode): string {

    $image_item_entity = $imageItem->getEntity();
    if ($image_item_entity instanceof MediaInterface) {
      $referring_item = $image_item_entity->_referringItem;

      if (
        $referring_item instanceof ViewModeCropEntityReferenceItem
      ) {
        // Node.
        $parent_entity = $referring_item->getEntity();
        $field_name = $referring_item->getFieldDefinition()
          // Node field.
          ->getName();
        $crop_data = $referring_item->getViewModeCropData();
        // Get the delta from the parent entity of the referencing field.
        foreach ($referring_item->getParent() as $parent_entity_field_item_delta => $parent_entity_field_item) {
          if ($parent_entity_field_item->getPropertyPath() === $referring_item->getPropertyPath()) {
            $delta = $parent_entity_field_item_delta;
            break;
          }
        }
      }
      else {
        throw new \RuntimeException('Cannot handle ' . get_class($referring_item));
      }
    }
    else {
      $parent_entity = $imageItem->getEntity();
      $field_name = $imageItem->getFieldDefinition()
        ->getName();
      $crop_data = $imageItem->getViewModeCropData();
    }

    /**
     * @var \Drupal\file\FileInterface $file
     */
    $file = $imageItem->entity;

    if (isset($file->_initialUri)) {
      $file_uri = $file->_initialUri;
    }
    else {
      $file_uri = $file->getFileUri();
    }

    $target = StreamWrapperManager::getTarget($file_uri);
    $scheme = StreamWrapperManager::getScheme($file_uri);

    if (stripos($scheme, 'crop') === 0) {
      // Already in crop scheme.
      return $file_uri;
    }

    $uri = 'crop-' . $scheme . '://' . $parent_entity->getEntityTypeId() . '/' . $parent_entity->id() . '/' . $field_name . '/' . $delta . '/' . $viewMode . '/' . $target;

    if (!file_exists($uri)) {
      static $generating = [];
      if (!isset($generating[$uri])) {
        $generating[$uri] = TRUE;
        CropImageHelper::createDerivate($imageItem, $viewMode, $uri, $crop_data);
        unset($generating[$uri]);
      }
    }
    return $uri;
  }

  /**
   * Parse the given path or uri.
   *
   * This returns an array containing all view mode crop properties contained
   * in the path or uri.
   *
   * @param string $path_or_uri
   *   The path or uri.
   *
   * @return array|null
   *   The parsed path or uri.
   */
  public static function parsePathOrUri(string $path_or_uri): ?array {
    /**
     * @var \Drupal\Core\StreamWrapper\PublicStream $public_stream_wrapper
     */
    $public_stream_wrapper = \Drupal::service('stream_wrapper_manager')
      ->getViaScheme('public');
    $public_directory_path = $public_stream_wrapper->getDirectoryPath();

    $scheme = NULL;
    if (strpos($path_or_uri, '/' . $public_directory_path . '/crop/') === 0 || strpos($path_or_uri, 'crop-public://') === 0) {
      $path_prefix = '/' . $public_directory_path . '/crop/';
      $path_or_uri = str_replace($path_prefix, '', '/' . StreamWrapperManager::getTarget($path_or_uri));
      $scheme = 'public';
    }
    elseif (strpos($path_or_uri, '/system/files/crop/') === 0 || strpos($path_or_uri, 'crop-private://') === 0) {
      $path_prefix = '/system/files/crop/';
      $path_or_uri = StreamWrapperManager::getTarget($path_or_uri);
      $scheme = 'private';
    }
    else {
      $path_prefix = '';
    }

    if ($scheme === NULL) {
      $rest = $path_or_uri;
      $scheme = 'public';
    }
    else {
      // Strip out path prefix.
      $rest = preg_replace('|^' . preg_quote($path_prefix, '|') . '|', '', $path_or_uri);
    }

    // Get the image style, scheme and path.
    if (substr_count($rest, '/') >= 5) {
      [
        $entity_type_id,
        $entity_id,
        $field_name,
        $delta,
        $view_mode,
        $file,
      ] = explode('/', $rest, 6);

      return [
        'path' => $path_or_uri,
        'path_prefix' => $path_prefix,
        'scheme' => $scheme,
        'entity_type_id' => $entity_type_id,
        'entity_id' => $entity_id,
        'field_name' => $field_name,
        'delta' => $delta,
        'view_mode' => $view_mode,
        'file' => $file,
      ];
    }

    return NULL;
  }

  /**
   * Get the crop data.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
   *   The entity.
   * @param string $field_name
   *   The field name.
   * @param \Drupal\view_mode_crop\ViewModeCropData[] $view_mode_crop_data
   *   The crop data.
   *
   * @return \Drupal\view_mode_crop\ViewModeCropData[]
   *   The crop data
   */
  public static function getCropData(ContentEntityInterface $entity, string $field_name, array $view_mode_crop_data) {
    /**
     * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
     */
    $entity_display_repository = \Drupal::service('entity_display.repository');
    $all_view_modes = $entity_display_repository->getViewModeOptionsByBundle($entity->getEntityTypeId(), $entity->bundle());

    $field = $entity->get($field_name);
    $entity_reference_entity_type_id = NULL;
    $entity_reference_entity_type_bundles = [];
    if ($field instanceof EntityReferenceFieldItemList) {
      $definition = $field->getFieldDefinition();
      if (($handler = $definition->getSetting('handler')) === 'default:media') {
        // This references a media entity, to determine if cropping is available
        // we should examine the used media view mode.
        $entity_reference_entity_type_id = str_replace('default:', '', $handler);
        $entity_reference_entity_type_bundles = array_keys($definition->getSetting('handler_settings')['target_bundles']);
      }
    }

    $crop_data = [];

    /**
     * @var \Drupal\Core\Field\FormatterPluginManager $field_formatter_plugin_manager
     */
    $field_formatter_plugin_manager = \Drupal::service('plugin.manager.field.formatter');

    foreach ($all_view_modes as $view_mode_id => $view_mode_label) {
      $view_mode_display = $entity_display_repository->getViewDisplay($entity->getEntityTypeId(), $entity->bundle(), $view_mode_id);
      $hidden_fields = $view_mode_display->get('hidden');

      if (isset($hidden_fields[$field_name])) {
        // Field is not visible in this display mode.
        continue;
      }

      $field_name_component = $view_mode_display->getComponent($field_name);

      if ($field_name_component['type'] === 'entity_reference_entity_view' && !empty($entity_reference_entity_type_id)) {
        // Check the referenced view_mode if cropping is available.
        $view_mode_crop_enabled = FALSE;
        foreach ($entity_reference_entity_type_bundles as $entity_reference_entity_type_bundle) {
          $referenced_entity_view_mode_display = $entity_display_repository->getViewDisplay($entity_reference_entity_type_id, $entity_reference_entity_type_bundle, $field_name_component['settings']['view_mode']);

          foreach ($referenced_entity_view_mode_display->getComponents() as $referenced_entity_view_mode_display_component) {
            $field_formatter_plugin_definition = $field_formatter_plugin_manager->getDefinition($referenced_entity_view_mode_display_component['type'], FALSE);
            if (!$field_formatter_plugin_definition) {
              continue;
            }
            if (isset($field_formatter_plugin_definition['view_mode_crop_enabled']) && $field_formatter_plugin_definition['view_mode_crop_enabled']) {
              $view_mode_crop_enabled = TRUE;
            }
          }
        }

        if (!$view_mode_crop_enabled) {
          continue;
        }
      }
      else {
        $field_formatter_plugin_definition = $field_formatter_plugin_manager->getDefinition($field_name_component['type'], FALSE);
        if (!$field_formatter_plugin_definition) {
          continue;
        }
        if (!isset($field_formatter_plugin_definition['view_mode_crop_enabled']) || !$field_formatter_plugin_definition['view_mode_crop_enabled']) {
          continue;
        }
      }

      if (isset($view_mode_crop_data[$view_mode_id])) {
        $crop_data[$view_mode_id] = $view_mode_crop_data[$view_mode_id];
      }

      if (!isset($crop_data[$view_mode_id])) {
        $crop_data[$view_mode_id] = new ViewModeCropData($view_mode_id, $view_mode_label);
      }
      $crop_data[$view_mode_id]->label = $view_mode_label;
    }

    return $crop_data;
  }

  /**
   * Delete cropped images for the given entity.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity.
   */
  public static function deleteCroppedImagesForEntity(EntityInterface $entity): void {
    if (!$entity instanceof ContentEntityInterface) {
      return;
    }
    /**
     * @var \Drupal\Core\File\FileSystemInterface $file_system
     */
    $file_system = \Drupal::service('file_system');

    $public_path = CropPublicStreamWrapper::basePath() . '/' . $entity->getEntityTypeId() . '/' . $entity->id();
    $private_path = CropPrivateStreamWrapper::basePath() . '/' . $entity->getEntityTypeId() . '/' . $entity->id();

    if (is_dir($public_path)) {
      $file_system->deleteRecursive($public_path);
    }
    if (is_dir($private_path)) {
      $file_system->deleteRecursive($private_path);
    }

    $cache_tags_to_invalidate = [];

    foreach ($entity->getFields() as $field_item_list) {

      foreach ($field_item_list as $item) {
        if ($item instanceof ViewModeCropImageItem) {
          $cache_tags_to_invalidate = array_merge($cache_tags_to_invalidate, $item->entity->getCacheTags());
        }
        if ($item instanceof ViewModeCropEntityReferenceItem) {
          $settings = $item->getFieldDefinition()->getSettings();
          if ($settings['target_type'] === 'media' && $item->entity instanceof MediaInterface) {
            $cache_tags_to_invalidate = array_merge($cache_tags_to_invalidate, $item->entity->getCacheTags());
          }
        }
      }
    }

    Cache::invalidateTags($cache_tags_to_invalidate);
  }

}

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

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