htools-8.x-1.x-dev/modules/htools_migrate/src/Plugin/migrate/process/RemoteMedia.php

modules/htools_migrate/src/Plugin/migrate/process/RemoteMedia.php
<?php

namespace Drupal\htools_migrate\Plugin\migrate\process;

use Drupal\Component\Utility\Crypt;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\file\FileInterface;
use Drupal\media\MediaInterface;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\MigrateSkipProcessException;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser;

/**
 * Process remote URL images and create them as media entities.
 *
 * @MigrateProcessPlugin(
 *   id = "remote_media"
 * )
 *
 * This plugin fetch image file from remote URL's and migrate them as media
 * entities. By default if the download fails during the migration process the
 * media entity is created and the file downloading process to a queue, so it
 * can be done later unattended. Also can be configured to always delegate to
 * the queue instead of downloading the file.
 *
 * Example usage with default values configuration:
 *
 * @code
 * destination:
 *   plugin: 'entity:node'
 * process:
 *   images:
 *     plugin: htools_media_image
 *     source: urlImages
 * @endcode
 *
 * Example usage with queue option configuration:
 * @code
 * destination:
 *   plugin: 'entity:node'
 * process:
 *   images:
 *     plugin: htools_media_image
 *     source: urlImages
 *     name: longName
 *     queue: true
 *     media_field: image
 *     bundle: image
 * @endcode
 *
 * @see \Drupal\htools_migrate\Plugin\QueueWorker\DownloadRemoteFile
 */
class RemoteMedia extends ProcessPluginBase implements ContainerFactoryPluginInterface {

  /**
   * The migration instance..
   *
   * @var \Drupal\migrate\Plugin\MigrationInterface
   */
  protected $migration;

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

  /**
   * HTTP client service.
   *
   * @var \GuzzleHttp\ClientInterface
   */
  protected $httpClient;

  /**
   * The language manager service.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

  /**
   * Current user.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $account;

  /**
   * Extension guesser.
   *
   * @var \Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser
   */
  protected $extensionGuesser;

  /**
   * The queue instance.
   *
   * @var \Drupal\Core\Queue\QueueInterface
   */
  protected $queue;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityTypeManagerInterface $entity_type_manager, ClientInterface $http_client, LanguageManagerInterface $language_manager, QueueFactory $queue, AccountProxyInterface $account = NULL) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->migration = $migration;
    $this->entityTypeManager = $entity_type_manager;
    $this->httpClient = $http_client;
    $this->languageManager = $language_manager;
    $this->queue = $queue->get('download_remote_file');
    $this->account = $account;
    $this->extensionGuesser = ExtensionGuesser::getInstance();
    $this->extensionGuesser->register(new MimeTypeExtensionGuesser());
    $this->configuration['queue'] = !empty($this->configuration['queue']);
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $migration,
      $container->get('entity_type.manager'),
      $container->get('http_client'),
      $container->get('language_manager'),
      $container->get('queue'),
      $container->get('current_user')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    try {
      if (empty($value)) {
        throw new \Exception('Value is empty');
      }
      // @FIXME This is a workaround for URL's with no schema.
      if (!(bool) preg_match('/^(?:ftp|https?|feed):/xi', $value)) {
        $value = 'http:' . $value;
      }
      // @FIXME Use a configuration option to pass a collection of source fields to generate the alt property.
      $alt = $row->getSourceProperty($this->configuration['name']);
      $title = $alt;
      $explode_value = !empty($this->configuration['explode']) ? explode($this->configuration['explode']['delimiter'], $value) : NULL;
      if ($explode_value !== NULL && is_array($explode_value) && is_numeric($this->configuration['explode']['value_key'])) {
        $value = $explode_value[$this->configuration['explode']['value_key']];
        $title = !empty($this->configuration['explode']['title_key']) ? $explode_value[$this->configuration['explode']['title_key']] : $title;
        $alt = !empty($this->configuration['explode']['alt_key']) ? $explode_value[$this->configuration['explode']['alt_key']] : $alt;
      }
      if (empty($value)) {
        throw new \Exception('Value is empty');
      }
      $this->configuration['bundle'] = !empty($this->configuration['bundle']) ? $this->configuration['bundle'] : 'image';
      // Replace encoded characters.
      $value = urldecode($value);
      // Check if the image is already imported using the URL.
      $media = $this->lookupEntity($value, $this->configuration['bundle']);
      if ($media !== NULL) {
        $this->updateTranslations($media, $alt);
        return $media->id();
      }

      $file = FALSE;

      if (empty($this->configuration['queue'])) {
        $file = $this->fetchFile($value);
      }

      $properties = [
        'media_field' => $this->configuration['media_field'],
        'bundle' => $this->configuration['bundle'],
      ];
      $media = \Drupal::service('htools.media_image_utils')
        ->createMedia($value, $file, $alt, $properties);
      // Queue the media entity for file downloading.
      if (!($file instanceof FileInterface)) {
        $this->queue->createItem([
          'type' => $media->getEntityTypeId(),
          'bundle' => $media->bundle(),
          'id' => $media->id(),
          'source' => $value,
          'field' => $this->configuration['media_field'],
          'keep_original_file_name' => isset($this->configuration['keep_original_file_name']) ? $this->configuration['keep_original_file_name'] : FALSE,
          'properties' => [
            'alt' => $alt,
            'title' => $title,
          ],
          // @FIXME Use DI.
          'attempts' => \Drupal::state()
            ->get('remote_file_download_attempts', 288),
        ]);
      }

      return $media->id();
    } catch (\Exception $e) {
      watchdog_exception('migrate', $e, 'Process plugin %plugin failed for value %value with error @error', [
        '%plugin' => $this->getPluginId(),
        '%value' => var_export($value, TRUE),
        '@error' => $e->getMessage(),
      ]);
      /* @noinspection PhpUnhandledExceptionInspection */
      throw new MigrateSkipProcessException($this->t('An error occurred while executing %plugin plugin. Check the logs for more information.', [
        '%plugin' => $this->getPluginId(),
      ]));
    }
  }

  /**
   * Lookup the media entity using the source URL.
   *
   * @param string $url
   *   The source URL.
   * @param string $bundle
   *   The entity bundle.
   *
   * @return \Drupal\media\MediaInterface|null
   *   The media entity instance, or NULL if cannot be found.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function lookupEntity($url, $bundle): ?MediaInterface {
    $storage = $this->entityTypeManager->getStorage('media');
    $query = $storage->getQuery()
      ->condition('remote_source', $url)
      ->condition('bundle', $bundle);
    $result = $query->execute();
    $result = reset($result);

    if (empty($result)) {
      return NULL;
    }

    /** @var \Drupal\media\MediaInterface $media */
    $media = $storage->load($result);

    return $media;
  }

  /**
   * Update media entity translations.
   *
   * @param \Drupal\media\MediaInterface $media
   *   The media instance.
   * @param string $alt
   *   The alt field.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  protected function updateTranslations(MediaInterface $media, $alt): void {
    if (empty($this->configuration['langcode'])) {
      // If no langcode is configured use current language.
      $this->configuration['langcode'] = $this->languageManager->getCurrentLanguage()
        ->getId();
    }

    if ($this->languageManager->getLanguage($this->configuration['langcode']) === NULL) {
      $e = new \InvalidArgumentException('Cannot update the entity translations, the configured langcode is invalid.');
      watchdog_exception('migrate', $e);
      return;
    }

    $media_field = $this->configuration['media_field'];
    $langcode = $this->configuration['langcode'];
    $original = $media->language()->getId();

    if ((string) $langcode === $original ) {
      $value = $media->get($media_field)->getValue();
      if (!$media->get($media_field)->isEmpty() && array_key_exists('alt', $value[0]) && $value[0]['alt'] !== $alt) {
        $value[0]['alt'] = $alt;
        $value[0]['title'] = $alt;
        $media->set($media_field, $value);
        $media->save();
      }
      return;
    }

    // Remove the previous translation if exists.
    if ($media->hasTranslation($langcode)) {
      $media->removeTranslation($langcode);
    }

    $translation = $media->getTranslation($original)->toArray();
    $translation[$media_field][0]['alt'] = $alt;
    $translation[$media_field][0]['title'] = $alt;
    $translation['content_translation_source'][0]['value'] = $original;

    $media
      ->addTranslation($langcode, $translation)
      ->save();
  }

  /**
   * Fetch file from remote URL and stores it.
   *
   * @param string $url
   *   The file url.
   *
   * @return bool|\Drupal\file\FileInterface
   *   The file instance or FALSE if cannot be fetched.
   */
  protected function fetchFile($url) {
    try {
      $response = $this->httpClient->request('GET', $url);
      $path = file_build_uri('migrate');
      $writable = \Drupal::service('file_system')
        ->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);

      if ($writable === FALSE) {
        $e = new \RuntimeException('Cannot create or modify permissions for ' . $path . ' directory.');
        watchdog_exception('migrate', $e);

        return NULL;
      }

      $path .= '/' . Crypt::hashBase64($url);
      $header = $response->getHeader('Content-Type');
      $extension = $this->extensionGuesser->guess($header[0]);

      if (!empty($extension)) {
        $path .= '.' . $extension;
      }

      $data = (string) $response->getBody();

      return file_save_data($data, $path, FileSystemInterface::EXISTS_REPLACE);
    } catch (GuzzleException $e) {
      // Just log the exception for debugging purposes.
      watchdog_exception('migrate', $e);
    } catch (\Exception $e) {
      // Just log the exception for debugging purposes.
      watchdog_exception('migrate', $e);
    }

    return FALSE;
  }
}

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

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