acquia_dam-1.0.0-rc1/src/Plugin/media/Source/Asset.php

src/Plugin/media/Source/Asset.php
<?php

declare(strict_types=1);

namespace Drupal\acquia_dam\Plugin\media\Source;

use Drupal\acquia_dam\Entity\MediaSourceField;
use Drupal\acquia_dam\Plugin\Field\FieldType\AssetItem;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\media\MediaInterface;
use Drupal\media\MediaSourceBase;
use Drupal\media\MediaTypeInterface;
use GuzzleHttp\Exception\TransferException;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Media source for DAM assets.
 *
 * @MediaSource(
 *   id = "acquia_dam_asset",
 *   label = @Translation("Asset"),
 *   description = @Translation("Use an asset from the Acquia DAM"),
 *   allowed_field_types = {"acquia_dam_asset"},
 *   default_thumbnail_filename = "no-thumbnail.png",
 *   deriver = "Drupal\acquia_dam\Plugin\media\Source\AssetDeriver",
 *   asset_search_key = "",
 *   asset_search_value = "",
 * )
 */
final class Asset extends MediaSourceBase {

  /**
   * Asset storage.
   *
   * @var array
   */
  protected $assetData = [];

  /**
   * The DAM client factory.
   *
   * @var \Drupal\acquia_dam\Client\AcquiaDamClientFactory
   */
  private $clientFactory;

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

  /**
   * The file system.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  private $fileSystem;

  /**
   * The token replacement service.
   *
   * @var \Drupal\Core\Utility\Token
   */
  private $token;

  /**
   * Logger channel interface.
   *
   * @var \Drupal\Core\Logger\LoggerChannelInterface
   */
  private $damLoggerChannel;

  /**
   * System date config.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->clientFactory = $container->get('acquia_dam.client.factory');
    $instance->httpClient = $container->get('http_client');
    $instance->fileSystem = $container->get('file_system');
    $instance->token = $container->get('token');
    $instance->damLoggerChannel = $container->get('logger.channel.acquia_dam');
    $instance->config = $container->get('config.factory')->get('system.date');
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function getMetadataAttributes(): array {
    $attributes = [
      'created_date' => $this->t('Created date'),
      'filename' => $this->t('File name'),
      'size' => $this->t('Size'),
      'last_update_date' => $this->t('Last updated date'),
      'file_upload_date' => $this->t('File upload date'),
      'expiration_date' => $this->t('Expiration date'),
      'release_date' => $this->t('Release date'),
      'deleted_date' => $this->t('Deleted date'),
      'format_type' => $this->t('Format Type'),
      'format' => $this->t('Format'),
    ];
    if ($this->configFactory->get('acquia_dam.settings')->get('allowed_metadata')) {
      return array_merge($attributes, $this->configFactory->get('acquia_dam.settings')->get('allowed_metadata'));
    }
    return $attributes;
  }

  /**
   * {@inheritdoc}
   *
   * Disable PHPMD.CyclomaticComplexity due to the switch statement, which is
   * a pattern used in all the implementations of this method.
   *
   * @SuppressWarnings(PHPMD.CyclomaticComplexity)
   */
  public function getMetadata(MediaInterface $media, $attribute_name) {
    [$asset_id, $version_id, $external_id] = array_values($this->getSourceFieldValue($media));

    if (empty($asset_id)) {
      return NULL;
    }
    if ($version_id === NULL) {
      $version_id = '';
    }
    if ($external_id === NULL) {
      $external_id = '';
    }

    $asset = $this->assetData;
    if ($asset === []) {
      try {
        $asset = $this->clientFactory->getSiteClient()->getAsset($asset_id, $version_id);
      }
      catch (\Exception $exception) {
        $this->damLoggerChannel->error(sprintf(
            'Following error occurred while trying to get asset from dam. Asset: %s, error: %s',
            $asset_id,
            $exception->getMessage()
          )
        );
        return NULL;
      }
    }

    // The field mapping is used by some attributes to transform values for
    // better storage compatibility.
    $field_map = $media->bundle->entity->getFieldMap();
    $field_definition = NULL;
    if (isset($field_map[$attribute_name])) {
      $field_definition = $media->getFieldDefinition($field_map[$attribute_name]);
    }

    switch ($attribute_name) {
      case 'filename':
      case 'default_name':
        return $asset['filename'];

      case 'id':
      case 'version_id':
        return $asset['id'];

      case 'size':
        return $asset['file_properties']['size_in_kbytes'] * 1024;

      case 'thumbnail_uri':
        return $this->getLocalThumbnailUri($asset_id, $version_id, $asset);

      case 'embeds':
        return $asset['embeds'];

      case 'external_id':
        return $asset['external_id'];

      case 'thumbnails':
        return $asset['thumbnails'];

      case 'image_properties':
        return $asset['file_properties']['image_properties'];

      case 'format_type':
        return $asset['file_properties']['format_type'];

      case 'format':
        return $asset['file_properties']['format'];

      case 'video_properties':
        return $asset['file_properties']['video_properties'];

      case 'created_date':
      case 'last_update_date':
      case 'file_upload_date':
      case 'deleted_date':
        return $asset[$attribute_name] ? $this->transformMetadataForStorage($asset[$attribute_name], 'datetime', $field_definition) : NULL;

      case 'expiration_date':
      case 'release_date':
        return $asset['security'][$attribute_name] ? $this->transformMetadataForStorage($asset['security'][$attribute_name], 'datetime', $field_definition) : NULL;

      default:
        if (!array_key_exists($attribute_name, $asset['metadata']['fields'])) {
          return NULL;
        }
        $value = $asset['metadata']['fields'][$attribute_name];
        if (count($asset['metadata']['fields'][$attribute_name]) === 0) {
          return NULL;
        }

        $is_multiple = $field_definition && $field_definition->getFieldStorageDefinition()->isMultiple();

        if (isset($asset['metadata_info']) && $field_definition !== NULL) {
          $metadata_type = self::getMetadataFieldType($asset['metadata_info'], $attribute_name);
          if ($metadata_type !== NULL) {
            $value = $this->transformMetadataForStorage($value, $metadata_type, $field_definition);
          }
        }
        return $is_multiple ? $value : implode(', ', $value);
    }
  }

  /**
   * Transforms metadata values for field storage.
   *
   * @param string|array $value
   *   The metadata's value.
   * @param string $metadata_type
   *   The metadata's type.
   * @param \Drupal\Core\Field\FieldDefinitionInterface|null $field_definition
   *   The field definition, if metadata is mapped to a field.
   *
   * @return string|array
   *   The transformed metadata values.
   */
  private function transformMetadataForStorage($value, string $metadata_type, ?FieldDefinitionInterface $field_definition) {
    if ($field_definition === NULL) {
      return $value;
    }
    $field_type = $field_definition->getType();
    $field_storage_definition = $field_definition->getFieldStorageDefinition();

    if ($field_type === 'string') {
      $max_length = $field_definition->getSetting('max_length');
      if (is_array($value)) {
        $value = array_map(function ($value) use ($max_length) {
          return $this->formatStringValues($value, $max_length);
        }, $value);
      }
      else {
        $value = $this->formatStringValues($value, $max_length);
      }
    }

    if (in_array($metadata_type, ['date', 'datetime'])) {
      $source_format = $metadata_type === 'date' ? 'Y-m-d' : \DateTimeInterface::ATOM;
      if ($field_type === 'datetime') {
        $datetime_type = $field_storage_definition->getSetting('datetime_type');
        $format = $datetime_type === DateTimeItem::DATETIME_TYPE_DATETIME ? DateTimeItemInterface::DATETIME_STORAGE_FORMAT : DateTimeItemInterface::DATE_STORAGE_FORMAT;
      }
      elseif ($field_type === 'timestamp') {
        $format = 'U';
      }
      else {
        return $value;
      }

      if (is_array($value)) {
        $value = array_map(function ($value) use ($source_format, $format) {
          return $this->formatDateForDateField($value, $source_format, $format);
        }, $value);
      }
      else {
        $value = $this->formatDateForDateField($value, $source_format, $format);
      }
    }

    return $value;
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
    $form = parent::buildConfigurationForm($form, $form_state);
    $form['source_field']['#default_value'] = MediaSourceField::SOURCE_FIELD_NAME;
    $form['source_field']['#disabled'] = TRUE;
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function getSourceFieldValue(MediaInterface $media): array {
    $items = $media->get($this->getSourceFieldName());
    if ($items->isEmpty()) {
      return [
        'asset_id' => '',
        'version_id' => '',
        'external_id' => '',
      ];
    }
    $field_item = $items->first();
    assert($field_item instanceof AssetItem);
    // The ::getValue method on FieldItem only returns an array where properties
    // have been initiated with a value. It does not return properties that have
    // no value. Using ::toArray ensures the result has `version_id`, which may
    // be empty when a media item is first saved.
    return $field_item->toArray();
  }

  /**
   * {@inheritdoc}
   */
  public function createSourceField(MediaTypeInterface $type) {
    return $this->getSourceFieldDefinition($type);
  }

  /**
   * {@inheritdoc}
   */
  public function getSourceFieldDefinition(MediaTypeInterface $type) {
    return MediaSourceField::getFieldDefinition('media', $type->id(), $type->label());
  }

  /**
   * {@inheritdoc}
   */
  protected function getSourceFieldName() {
    return MediaSourceField::SOURCE_FIELD_NAME;
  }

  /**
   * Returns the local URI for a resource thumbnail.
   *
   * @param string $asset_id
   *   The asset ID.
   * @param string $version_id
   *   The version ID, an empty string is allowed if version unknown.
   * @param array $asset
   *   The asset.
   *
   * @return string
   *   The local thumbnail URI, or NULL if it could not be downloaded.
   *
   * @throws \GuzzleHttp\Exception\GuzzleException
   *
   * @see \Drupal\media\Plugin\media\Source\OEmbed::getLocalThumbnailUri
   */
  protected function getLocalThumbnailUri(string $asset_id, string $version_id, array $asset): string {
    if ($version_id === '') {
      // The version ID was not yet set. This happens when the media is saved
      // without a version ID and Media::prepareSave is invoked before preSave
      // where the version is populated. Give a generic version ID for the
      // filename of this thumbnail.
      $version_id = 'default';
    }
    $directory = 'public://acquia_dam_thumbnails/' . $asset_id;
    if (!$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
      return '';
    }
    // If there is an existing local thumbnail for the version, return it.
    $files = $this->fileSystem->scanDirectory($directory, "/^$version_id\..*/");
    if (count($files) > 0) {
      return reset($files)->uri;
    }

    $thumbnail_url = $asset['thumbnails']['160px']['url'];
    try {
      $response = $this->httpClient->request('GET', $thumbnail_url);
      if ($response->getStatusCode() === 200) {
        $local_thumbnail_uri = $directory . DIRECTORY_SEPARATOR . "$version_id.png";
        $this->fileSystem->saveData((string) $response->getBody(), $local_thumbnail_uri, FileSystemInterface::EXISTS_REPLACE);
        return $local_thumbnail_uri;
      }
    }
    catch (TransferException $e) {
      $this->damLoggerChannel->error(sprintf(
          'Unable to download thumbnail at %s: %s %s',
          $thumbnail_url,
          get_class($e),
          $e->getMessage()
        ));
      return '';
    }
    catch (FileException $e) {
      $this->damLoggerChannel->error(sprintf(
        'Unable to download thumbnail at %s: %s %s',
        $thumbnail_url,
        get_class($e),
        $e->getMessage()
      ));
      return '';
    }
    return '';
  }

  /**
   * Formats date coming from DAM to save into storage.
   *
   * @param string $value
   *   Date string coming from API in ISO8601 format.
   * @param string $source_format
   *   The source date time format.
   * @param string $format
   *   The date time format.
   *
   * @return string
   *   The formatted date.
   */
  protected function formatDateForDateField(string $value, string $source_format, string $format): string {
    try {
      $date = DrupalDateTime::createFromFormat(
        $source_format,
        $value,
        new \DateTimeZone($this->config->get('timezone.default')),
        [
          // We do not want to validate the format. Incoming ISO8601 has the Z
          // timezone offset, while PHP may return +00:00 when comparing the
          // output with the `P` option.
          'validate_format' => FALSE,
        ]
      );
      // If the format did not include an explicit time portion, then the time
      // will be set from the current time instead. Provide a default for
      // consistent values.
      if (!str_contains($value, 'T')) {
        $date->setDefaultDateTime();
      }
    }
    catch (\InvalidArgumentException | \UnexpectedValueException $exception) {
      return $value;
    }
    return $date->format($format);
  }

  /**
   * Cuts last part of the string if it is longer than allowed.
   *
   * @param string $value
   *   String value.
   * @param int $max_length
   *   Allowed max length on field.
   *
   * @return string
   *   Formatted string.
   */
  protected function formatStringValues(string $value, int $max_length): string {
    if ($max_length < strlen($value)) {
      return Unicode::truncate($value, $max_length - 3, TRUE, TRUE);
    }
    return $value;
  }

  /**
   * Sets the asset data.
   *
   * @param array $data
   *   The asset data.
   */
  public function setAssetData(array $data) {
    $this->assetData = $data;
  }

  /**
   * Gets the field type for a metadata field.
   *
   * @param array $metadata_info
   *   The asset's metadata info.
   * @param string $field_name
   *   The metadata field name.
   *
   * @return string|null
   *   The field type.
   */
  private static function getMetadataFieldType(array $metadata_info, string $field_name): ?string {
    $mapping = [];
    foreach ($metadata_info['field_set_fields'] as $field) {
      $mapping[$field['key']] = $field['type'];
    }
    return $mapping[$field_name] ?? NULL;
  }

}

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

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