htools-8.x-1.x-dev/modules/htools_migrate/src/Plugin/QueueWorker/DownloadRemoteFile.php

modules/htools_migrate/src/Plugin/QueueWorker/DownloadRemoteFile.php
<?php

namespace Drupal\htools_migrate\Plugin\QueueWorker;

use Drupal\Component\Utility\Crypt;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\QueueWorkerBase;
use Drupal\file\FileInterface;
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;
use function count;

/**
 * Implements a queue to download file from remote.
 *
 * @QueueWorker(
 *   id = "download_remote_file",
 *   title = @Translation("Download file from remote"),
 *   cron = {"time" = 30}
 * )
 *
 * This plugin uses queue data to download a remote file and link it to an
 * entity. The entity metadata is provided in the queue item data.
 *
 * Usage example:
 *
 * @code
 * $queue->createItem([
 *   'type' => 'node',
 *   'bundle' => 'article',
 *   'id' => 15,
 *   'source' => 'http://my.cdn.com/files/awesome-image.png',
 *   'field' => 'header_image',
 *   'properties' => [
 *     'alt' => 'Header image',
 *     'title' => 'My node title',
 *   ],
 *   'attempts' => 60,
 * ]);
 * @endcode
 *
 * The attempts is the number of failed download attempts after which the item
 * will be discarded. Leave it empty or 0 to try just one time.
 */
class DownloadRemoteFile extends QueueWorkerBase implements ContainerFactoryPluginInterface {

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

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

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

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

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entityTypeManager, QueueFactory $queue, ClientInterface $http_client) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->entityTypeManager = $entityTypeManager;
    $this->queue = $queue->get($plugin_id);
    $this->httpClient = $http_client;
    $this->extensionGuesser = ExtensionGuesser::getInstance();
    $this->extensionGuesser->register(new MimeTypeExtensionGuesser());
  }

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

  /**
   * {@inheritdoc}
   */
  public function processItem($data) {
    if (!$this->validateData($data)) {
      // If thr item is not valid log an exception and release the item.
      watchdog_exception('cron', new \InvalidArgumentException(), 'Invalid item data supplied for %plugin: @data', [
        '%plugin' => $this->getPluginId(),
        '@data' => str_replace("\n", '', var_export($data, TRUE)),
      ]);

      return;
    }

    $attempts = 0;

    if (!empty((int) $data['attempts'])) {
      $attempts = (int) $data['attempts'];
    }

    try {
      $storage = $this->entityTypeManager->getStorage($data['type']);
      /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
      if (!empty($data['id'])) {
        $entity = $storage->load($data['id']);
      }
      else {
        $entities = $storage->loadByProperties(['remote_source' => $data['source']]);
        $entity = reset($entities);
      }


      if ($entity === NULL || empty($entity)) {
        throw new \InvalidArgumentException('The entity does not exists.');
      }

      $file = $this->fetchFile($data['source'], $data['keep_original_file_name']);

      if (!($file instanceof FileInterface)) {
        throw new \RuntimeException('The file could not be downloaded. Check the logs for more information.');
      }

      if (!$entity->hasField($data['field'])) {
        throw new \InvalidArgumentException('The field does not exists.');
      }

      $value = [
        'target_id' => $file->id(),
      ];
      $entity->get($data['field'])->setValue($value + $data['properties']);
      $entity->save();
      $attempts = 0;
    } catch (GuzzleException $e) {
      --$attempts;
      $this->handleError($data, $e);
    } catch (\Exception $e) {
      --$attempts;
      $this->handleError($data, $e);
    } finally {
      // Check if we have any attempts left.
      if ($attempts > 0) {
        $data['attempts'] = $attempts;
        $this->queue->createItem($data);
      }
    }
  }

  /**
   * Validate queue item data.
   *
   * @param mixed $data
   *   The queue item data.
   *
   * @return bool
   *   Returns TRUE if the data is valid.
   */
  protected function validateData($data): bool {
    $pattern = [
      'type',
      'bundle',
      'id',
      'source',
      'field',
      'properties',
      'keep_original_file_name',
      'attempts',
    ];

    $keys = array_keys($data + ['attempts' => 0]);

    return count($pattern) === count($keys) && !empty(array_intersect($pattern, $keys));
  }

  /**
   * 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.
   *
   * @throws \GuzzleHttp\Exception\GuzzleException
   */
  protected function fetchFile($url, $keep_original = FALSE) {
    $response = $this->httpClient->request('GET', $url, ['verify' => FALSE]);
    $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;
    }

    if ($keep_original === FALSE) {
      $path .= '/' . Crypt::hashBase64($url);
    }
    else {
      $info = pathinfo($url);
      $path .= '/' . $info['filename'];
    }
    $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);
  }

  /**
   * Handle exceptions.
   *
   * @param mixed $data
   *   The item data.
   * @param \Exception|\GuzzleHttp\Exception\GuzzleException $e
   *   The exception instance.
   */
  protected function handleError($data, $e): void {
    watchdog_exception('cron', $e, 'Download of %file failed: @error', [
      '%file' => $data['source'],
      '@error' => $e->getMessage(),
    ]);
  }

}

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

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