media_acquiadam-8.x-1.46/src/Service/AssetFileEntityHelper.php

src/Service/AssetFileEntityHelper.php
<?php

namespace Drupal\media_acquiadam\Service;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Utility\Token;
use Drupal\file\FileInterface;
use Drupal\file\FileRepositoryInterface;
use Drupal\media_acquiadam\AcquiadamInterface;
use Drupal\media_acquiadam\Entity\Asset;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Class AssetFileEntityHelper.
 *
 * Abstracts out primarily file entity and system file related functionality.
 */
class AssetFileEntityHelper implements ContainerInjectionInterface {

  use StringTranslationTrait;

  /**
   * Entity Type Manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Entity Field Manager service.
   *
   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
   */
  protected $entityFieldManager;

  /**
   * Drupal config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * Acquia DAM config.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * Drupal filesystem service.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  protected $fileSystem;

  /**
   * Drupal token service.
   *
   * @var \Drupal\Core\Utility\Token
   */
  protected $token;

  /**
   * Acquia DAM asset image helper service.
   *
   * @var \Drupal\media_acquiadam\Service\AssetImageHelper
   */
  protected $assetImageHelper;

  /**
   * Acquia DAM client.
   *
   * @var \Drupal\media_acquiadam\AcquiadamInterface
   */
  protected $acquiaDamClient;

  /**
   * Acquia DAM factory for wrapping media entities.
   *
   * @var \Drupal\media_acquiadam\Service\AssetMediaFactory
   */
  protected $assetMediaFactory;

  /**
   * Acquia DAM logger channel.
   *
   * @var \Drupal\Core\Logger\LoggerChannelInterface
   */
  protected $loggerChannel;

  /**
   * The HTTP client.
   *
   * @var \GuzzleHttp\Client
   */
  protected $httpClient;

  /**
   * The file repository.
   *
   * @var \Drupal\file\FileRepositoryInterface
   */
  protected $fileRepository;

  /**
   * The messenger service.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * @var \Symfony\Component\DependencyInjection\ContainerInterface
   */
  private ContainerInterface $container;

  /**
   * AssetFileEntityHelper constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   Entity Type Manager service.
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
   *   Entity Field Manager service.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   Drupal config factory.
   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
   *   Drupal filesystem service.
   * @param \Drupal\Core\Utility\Token $token
   *   Drupal token service.
   * @param \Drupal\media_acquiadam\Service\AssetImageHelper $assetImageHelper
   *   Acquia DAM asset image helper service.
   * @param \Drupal\media_acquiadam\AcquiadamInterface $acquiaDamClient
   *   Acquia DAM client.
   * @param \Drupal\media_acquiadam\Service\AssetMediaFactory $assetMediaFactory
   *   Acquia DAM Asset Media Factory service.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $loggerChannelFactory
   *   The Drupal LoggerChannelFactory service.
   * @param \GuzzleHttp\Client $client
   *   The HTTP client.
   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
   * @param \Drupal\Core\Messenger\MessengerInterface|null $messenger
   *   Drupal core messenger service.
   */
  public function __construct(
    EntityTypeManagerInterface    $entityTypeManager,
    EntityFieldManagerInterface   $entityFieldManager,
    ConfigFactoryInterface        $configFactory,
    FileSystemInterface           $fileSystem,
    Token                         $token,
    AssetImageHelper              $assetImageHelper,
    AcquiadamInterface            $acquiaDamClient,
    AssetMediaFactory             $assetMediaFactory,
    LoggerChannelFactoryInterface $loggerChannelFactory,
    Client                        $client,
    MessengerInterface            $messenger = NULL) {
    $this->entityTypeManager = $entityTypeManager;
    $this->entityFieldManager = $entityFieldManager;
    $this->configFactory = $configFactory;
    $this->config = $configFactory->get('media_acquiadam.settings');
    $this->fileSystem = $fileSystem;
    $this->token = $token;
    $this->assetImageHelper = $assetImageHelper;
    $this->acquiaDamClient = $acquiaDamClient;
    $this->assetMediaFactory = $assetMediaFactory;
    $this->loggerChannel = $loggerChannelFactory->get('media_acquiadam');
    $this->httpClient = $client;
    $this->messenger = $messenger ?? \Drupal::service('messenger');
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    $instance = new static(
      $container->get('entity_type.manager'),
      $container->get('entity_field.manager'),
      $container->get('config.factory'),
      $container->get('file_system'),
      $container->get('token'),
      $container->get('media_acquiadam.asset_image.helper'),
      $container->get('media_acquiadam.acquiadam'),
      $container->get('media_acquiadam.asset_media.factory'),
      $container->get('logger.factory'),
      $container->get('http_client'),
      $container->get('messenger')
    );
    if ($container->has('file.repository')) {
      $instance->setFileRepository($container->get('file.repository'));
    }
    return $instance;
  }

  /**
   * Sets the file repository service, introduced in 9.3.
   *
   * @param \Drupal\file\FileRepositoryInterface $file_repository
   *   The file repository.
   */
  public function setFileRepository(FileRepositoryInterface $file_repository) {
    $this->fileRepository = $file_repository;
  }

  /**
   * Get a destination uri from the given entity and field combo.
   *
   * Following will be concatenated to create the path:
   *  - scheme
   *  - acquiadam_assets
   *  - Upload year from DAM
   *  - Upload month from DAM.
   *
   * Example: public://acquiadam_assets/2022-12
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity to check the field configuration on.
   * @param string $fileField
   *   The name of the file field.
   * @param string $upload_date
   *   Upload date of the asset from DAM (ISO8601).
   *
   * @return string
   *   The uri to use.
   */
  public function getDestinationFromEntity(EntityInterface $entity, string $fileField, string $upload_date): string {
    $scheme = $this->configFactory->get('system.file')->get('default_scheme');
    $file_directory = 'acquiadam_assets/[date:custom:Y]-[date:custom:m]';

    // Load the field definitions for this bundle.
    $field_definitions = $this->entityFieldManager->getFieldDefinitions(
      $entity->getEntityTypeId(),
      $entity->bundle()
    );

    if (!empty($field_definitions[$fileField])) {
      $definition = $field_definitions[$fileField]->getItemDefinition();
      $scheme = $definition->getSetting('uri_scheme');
      $file_directory = $definition->getSetting('file_directory');
    }

    // Replace the token for file directory.
    if (!empty($file_directory)) {
      $file_directory = $this->token->replace($file_directory);
    }

    return sprintf('%s://%s', $scheme, $file_directory);
  }

  /**
   * Creates a new file for an asset.
   *
   * @param \Drupal\media_acquiadam\Entity\Asset $asset
   *   The asset to save a new file for.
   * @param string $destination_folder
   *   The path to save the asset into.
   *
   * @return bool|\Drupal\file\FileInterface
   *   The created file or FALSE on failure.
   */
  public function createNewFile(Asset $asset, $destination_folder) {
    // By default, we use the filename attribute as the file name. However,
    // because the actual file format may differ than the file name (specially
    // for the images which are downloaded as png), we pass the filename
    // as a parameter so it can be overridden.
    $filename = $asset->filename;
    $file_contents = $this->fetchRemoteAssetData($asset, $filename);
    if ($file_contents === FALSE) {
      return FALSE;
    }

    $file = $this->assetMediaFactory->getFileEntity($asset->id);
    if ($file instanceof FileInterface) {
      $uri = $this->fileSystem->saveData($file_contents, $file->getFileUri(), FileSystemInterface::EXISTS_REPLACE);
      $file->setFileUri($uri);
      $file->setFilename($filename);
      $file->save();
    }
    else {
      // Ensure we can write to our destination directory.
      if (!$this->fileSystem->prepareDirectory($destination_folder, FileSystemInterface::CREATE_DIRECTORY)) {
        $message = 'Unable to save file for asset ID @asset_id on directory @destination_folder.';
        $context = [
          '@asset_id' => $asset->id,
          '@destination_folder' => $destination_folder,
        ];
        $this->loggerChannel->warning($message, $context);
        if ($this->config->get('debug') === TRUE) {
          $this->messenger->addWarning($this->t($message, $context));
        }
        return FALSE;
      }
      $destination_path = sprintf('%s/%s', $destination_folder, $filename);
      $file = $this->drupalFileSaveData($file_contents, $destination_path);
    }

    if ($file instanceof FileInterface) {
      return $file;
    }

    $message = 'Unable to save file for asset ID @asset_id.';
    $context = [
      '@asset_id' => $asset->id,
    ];
    $this->loggerChannel->warning($message, $context);
    if ($this->config->get('debug') === TRUE) {
      $this->messenger->addWarning($this->t($message, $context));
    }

    return FALSE;
  }

  /**
   * Fetches binary asset data from a remote source.
   *
   * @param \Drupal\media_acquiadam\Entity\Asset $asset
   *   The asset to fetch data for.
   * @param string $filename
   *   The filename as a reference so it can be overridden.
   *
   * @return false|string
   *   The remote asset contents or FALSE on failure.
   */
  protected function fetchRemoteAssetData(Asset $asset, &$filename) {
    if ($this->config->get('transcode') === 'original') {
      $download_url = $asset->links->download;
    }
    elseif ($asset->file_properties->format_type === 'image') {
      // If the module was configured to enforce an image size limit then we
      // need to grab the nearest matching pre-created size.
      $size_limit = $this->config->get('size_limit');

      $download_url = $this->assetImageHelper->getThumbnailUrlBySize($asset, $size_limit);

      if (empty($download_url)) {
        $message = 'Unable to save file for asset ID @asset_id. Thumbnail for request size (@size px) has not been found.';
        $context = [
          '@asset_id' => $asset->id,
          '@size' => $size_limit,
        ];
        $this->loggerChannel->warning($message, $context);
        if ($this->config->get('debug') === TRUE) {
          $this->messenger->addWarning($this->t($message, $context));
        }
        return FALSE;
      }
    }
    else {
      $original_url = $asset->embeds->original->url ?? '';
      if ($original_url === '') {
        return FALSE;
      }
      $download_url = str_replace('&download=true', '', $original_url);
    }

    try {
      $response = $this->httpClient->get($download_url, [
        'allow_redirects' => [
          'track_redirects' => TRUE,
        ],
      ]);
      $size = $response->getBody()->getSize();
      if ($size === NULL || $size === 0) {
        $message = 'Unable to download contents for asset ID @asset_id. Received zero-byte response for download URL @url with redirects to @history';
        $context = [
          '@asset_id' => $asset->id,
          '@url' => $download_url,
          '@history' => $response->getHeaderLine('X-Guzzle-Redirect-History'),
        ];
        $this->loggerChannel->error($message, $context);
        if ($this->config->get('debug') === TRUE) {
          $this->messenger->addError($this->t($message, $context));
        }
        return FALSE;
      }
      $file_contents = (string) $response->getBody();
      if ($response->hasHeader('Content-Disposition')) {
        $disposition = $response->getHeader('Content-Disposition')[0];
        preg_match('/filename="(.*)"/', $disposition, $matches);
        if (count($matches) > 1) {
          $filename = $matches[1];
        }
      }
    }
    catch (RequestException $exception) {
      $message = 'Unable to download contents for asset ID @asset_id: %message. Attempted download URL @url with redirects to @history';
      $context = [
        '@asset_id' => $asset->id,
        '%message' => $exception->getMessage(),
        '@url' => $download_url,
        '@history' => '[empty request, cannot determine redirects]',
      ];
      $response = $exception->getResponse();
      if ($response) {
        $context['@history'] = $response->getHeaderLine('X-Guzzle-Redirect-History');
      }
      $this->loggerChannel->error($message, $context);
      if ($this->config->get('debug') === TRUE) {
        $this->messenger->addError($this->t($message, $context));
      }
      return FALSE;
    }

    return $file_contents;
  }

  /**
   * Gets a new filename for an asset based on the URL returned by the DAM.
   *
   * @param string $destination
   *   The destination folder the asset is being saved to.
   * @param string $uri
   *   The URI that was returned by the DAM API for the asset.
   * @param string $original_name
   *   The original asset filename.
   *
   * @return string
   *   The updated destination path with the new filename.
   */
  protected function getNewDestinationByUri($destination, $uri, $original_name) {
    $path = parse_url($uri, PHP_URL_PATH);
    $path = basename($path);
    $ext = pathinfo($path, PATHINFO_EXTENSION);

    $base_file_name = pathinfo($original_name, PATHINFO_FILENAME);
    return sprintf('%s/%s.%s', $destination, $base_file_name, $ext);
  }

  /**
   * Saves a file to the specified destination and creates a database entry.
   *
   * This method exists so the functionality can be overridden in unit tests.
   *
   * @param string $data
   *   A string containing the contents of the file.
   * @param string|null $destination
   *   (optional) A string containing the destination URI. This must be a stream
   *   wrapper URI. If no value or NULL is provided, a randomized name will be
   *   generated and the file will be saved using Drupal's default files scheme,
   *   usually "public://".
   *
   * @return \Drupal\file\FileInterface|false
   *   A file entity, or FALSE on error.
   */
  protected function drupalFileSaveData($data, $destination = NULL) {
    if ($this->fileRepository instanceof FileRepositoryInterface) {
      return $this->fileRepository->writeData($data, $destination, FileSystemInterface::EXISTS_REPLACE);
    }
    return file_save_data($data, $destination, FileSystemInterface::EXISTS_REPLACE);
  }

}

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

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