webdam-1.0.x-dev/src/Plugin/EntityBrowser/Widget/WebdamUpload.php

src/Plugin/EntityBrowser/Widget/WebdamUpload.php
<?php

namespace Drupal\webdam\Plugin\EntityBrowser\Widget;

use Drupal\Core\Utility\Error;
use Drupal\webdam\WebdamConnector;
use Drupal\webdam\Exception\BundleNotWebdamException;
use Drupal\webdam\Exception\BundleNotExistException;
use Drupal\webdam\Exception\WebdamException;
use Drupal\webdam\Exception\UploadFailedException;
use Drupal\webdam\Plugin\Field\FieldType\WebdamMetadataItem;
use Drupal\webdam\Plugin\media\Source\Webdam;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Url;
use Drupal\entity_browser\WidgetValidationManager;
use Drupal\media\Entity\Media;
use Drupal\media\MediaInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Drupal\Core\Entity\EntityStorageException;

/**
 * Uses upload to create media entities.
 *
 * @EntityBrowserWidget(
 *   id = "webdam_upload",
 *   label = @Translation("Webdam upload"),
 *   description = @Translation("Uploads files to Webdam and creates wrapping media entities."),
 *   provider = "dropzonejs",
 * )
 */
class WebdamUpload extends WebdamWidgetBase {

  /**
   * Number of times to try fetching an asset during the batch.
   */
  const FAIL_LIMIT = 30;

  /**
   * The session service.
   *
   * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
   */
  protected $session;

  /**
   * Upload constructor.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
   *   Event dispatcher service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager service.
   * @param \Drupal\entity_browser\WidgetValidationManager $validation_manager
   *   The Widget Validation Manager service.
   * @param \Drupal\webdam\WebdamConnector $webdam_connector
   *   Webdam connector.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Config factory.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   Logger factory.
   * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
   *   The session service.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The language manager.
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, EntityTypeManagerInterface $entity_type_manager, WidgetValidationManager $validation_manager, WebdamConnector $webdam_connector, ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory, SessionInterface $session, LanguageManagerInterface $language_manager, RequestStack $request_stack) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $event_dispatcher, $entity_type_manager, $validation_manager, $webdam_connector, $logger_factory, $language_manager, $request_stack, $config_factory);
    $this->session = $session;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('event_dispatcher'),
      $container->get('entity_type.manager'),
      $container->get('plugin.manager.entity_browser.widget_validation'),
      $container->get('webdam.connector'),
      $container->get('config.factory'),
      $container->get('logger.factory'),
      $container->get('session'),
      $container->get('language_manager'),
      $container->get('request_stack')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return [
      'extensions' => 'jpg jpeg png gif',
      'dropzone_description' => $this->t('Drop files here to upload them.'),
      'tags' => [],
    ] + parent::defaultConfiguration();
  }

  /**
   * {@inheritdoc}
   */
  public function getForm(array &$original_form, FormStateInterface $form_state, array $additional_widget_parameters) {
    $form = parent::getForm($original_form, $form_state, $additional_widget_parameters);

    if ($form_state->getValue('errors')) {
      $form['actions']['submit']['#access'] = FALSE;
      return $form;
    }

    if ($form_state->getValue('errors')) {
      $form['actions']['submit']['#access'] = FALSE;
      return $form;
    }

    $form['upload'] = [
      '#title' => $this->t('File upload'),
      '#type' => 'dropzonejs',
      '#dropzone_description' => $this->getConfiguration()['settings']['dropzone_description'],
    ];

    $folder_options = [];
    // @todo Deprecation notice: Return type of Bynder\webdam\Entity\Folder::jsonSerialize() should either be compatible with JsonSerializable::jsonSerialize():
    // We suppress the errors for the moment.
    try {
      $client = $this->webdamConnector->getClient();
      $top_folders = @$client->getTopLevelFolders();

      // @todo Cache this? Ask if we cache.
      $this->buildFolderOptions($top_folders, $folder_options);
    }
    catch (\Exception $exception) {
      $this->messenger()->addError($exception->getMessage());
    }

    $form['folder'] = [
      '#type' => 'select',
      '#options' => $folder_options,
      '#title' => $this->t('Folder'),
      '#required' => TRUE,
    ];

    if ($uploaded_assets = $this->session->get('webdam_upload_batch_result', [])) {
      $form_state->set('uploaded_entities', $uploaded_assets);
      $this->session->remove('webdam_upload_batch_result');
      $form['upload']['#access'] = FALSE;
      $form['#attached']['library'][] = 'webdam/upload';
      $form['actions']['submit']['#attributes']['class'][] = 'visually-hidden';
      $form['message']['#markup'] = $this->t('Finishing upload. Please wait...');
    }
    else {
      $form['actions']['submit']['#eb_widget_main_submit'] = FALSE;
      $form['actions']['submit']['#webdam_upload_submit'] = TRUE;
    }

    return $form;
  }

  /**
   * Build folder options array.
   *
   * @param \Bynder\webdam\Entity\Folder[] $folders
   *   An array containing folders.
   * @param array $folder_options
   *   The options, that we are building, are passed by reference.
   * @param int $level
   *   The nesting level, we are currently in.
   */
  protected function buildFolderOptions(array $folders, array &$folder_options, int $level = -1) {
    $level++;
    foreach ($folders as $folder) {

      // @todo There seem to be permissions, but I am not sure how do we set
      // them. Ask them.
      $option = str_repeat('-', $level) . " $folder->name";
      if (!in_array('upload', $folder->permissions->assets)) {
        $option .= ' (' . $this->t('protected') . ')';
      }
      $folder_options[$folder->id] = $option;

      if ((int) $folder->numchildren > 0) {
        $loaded_folder = $this->webdamConnector->getClient()
          ->getFolder($folder->id);
        $this->buildFolderOptions($loaded_folder->folders, $folder_options, $level);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function prepareEntities(array $form, FormStateInterface $form_state) {
    if ($entities = $form_state->get('uploaded_entities')) {
      return $entities;
    }

    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function submit(array &$element, array &$form, FormStateInterface $form_state) {
    if (!empty($form_state->getTriggeringElement()['#webdam_upload_submit'])) {
      /** @var \Drupal\media\MediaTypeInterface $type */
      $type = $this->entityTypeManager->getStorage('media_type')
        ->load($this->configuration['media_type']);

      if ($type && ($type->getSource()) instanceof Webdam) {
        $form_state->setRebuild();
        $batch = [
          'title' => $this->t('Uploading assets to Webdam'),
          'init_message' => $this->t('Initializing upload.'),
          'progress_message' => $this->t('Processing (@percentage)...'),
          'operations' => [],
          'finished' => [static::class, 'batchFinish'],
        ];
        foreach ((array) $form_state->getValue(['upload', 'uploaded_files'], []) as $file) {
          $batch['operations'][] = [
            [static::class, 'batchUploadFiles'],
            [
              $file,
              $form_state->getValue('folder'),
            ],
          ];
        }

        foreach ((array) $form_state->getValue(['upload', 'uploaded_files'], []) as $file) {
          $batch['operations'][] = [
            [static::class, 'batchCreateEntities'],
            [
              $file,
              $type->get('source_configuration')['source_field'],
              $type->id(),
            ],
          ];
        }

        // Batch redirect callback needs UUID so we save it into the session.
        $this->session->set('webdam_upload_batch_uuid', $form_state->get(['entity_browser', 'instance_uuid']));
        batch_set($batch);

        // Now that the batch is set manually set source URL which will ensure
        // that we persist all needed query arguments when redirected back to
        // the form.
        if (\Drupal::request()->query->count()) {
          $batch = &batch_get();
          $source_url = Url::fromRouteMatch(\Drupal::routeMatch());
          $source_url->setOption('query', \Drupal::request()->query->all());
          $batch['source_url'] = $source_url;
        }
      }
      else {
        if (!$type) {
          (new BundleNotExistException($this->configuration['media_type']))->logException()->displayMessage();
        }
        else {
          (new BundleNotWebdamException($type->label()))->logException()->displayMessage();
        }

      }
    }
    elseif (!empty($form_state->getTriggeringElement()['#eb_widget_main_submit'])) {
      try {
        $media = $this->prepareEntities($form, $form_state);
        array_walk($media, function (MediaInterface $item) {
          if (!$item->id()) {
            // Some race conditions might occur in some circumstances and could
            // try to save this entity twice.
            try {
              $item->save();
            }
            catch (EntityStorageException $e) {
            }
          }
        });
        $this->selectEntities($media, $form_state);
        $form_state->set('uploaded_assets', NULL);
        $this->clearFormValues($element, $form_state);
      }
      catch (WebdamException $e) {
        $e->displayMessage();
        return;
      }
    }
  }

  /**
   * Upload batch operation callback which uploads assets to Webdam.
   */
  public static function batchUploadFiles($file, $folder, &$context) {
    try {
      /** @var \Drupal\Core\File\FileSystemInterface $file_system */
      $file_system = \Drupal::service('file_system');
      /** @var \Bynder\webdam\Client $client */
      $client = \Drupal::service('webdam.connector')->getClient();

      $result = $client->uploadAsset(
        $file_system->realpath($file['path']),
        $file['filename'],
        $folder
      );

      $context['results'][$file['path']] = $result;
      $file_system->delete($file['path']);
      $context['message'] = t('Uploaded @file to Webdam.', ['@file' => $file['filename']]);
    }
    catch (\Exception $e) {
      // If fetching failed try few more times. If waiting doesn't help fail the
      // batch eventually.
      if (empty($context['sandbox']['fails'])) {
        $context['sandbox']['fails'] = 0;
      }
      $context['sandbox']['fails']++;
      $context['finished'] = 0;
      $context['message'] = t('Uploading @file to Webdam.', ['@file' => $file['filename']]);

      if ($context['sandbox']['fails'] >= static::FAIL_LIMIT) {
        throw $e;
      }
    }
  }

  /**
   * Upload batch operation callback which creates media entities.
   */
  public static function batchCreateEntities($file, $source_field, $bundle, &$context) {
    try {
      // Let's try to fetch the uploaded resource from the API as we will be
      // able to save it only if that succeeds.
      $uuid = $context['results'][$file['path']];
      // @todo We do not have any media info, because we cannot provide any.
      /** @var \Drupal\webdam\WebdamConnector $webdam_connector */
      $webdam_connector = \Drupal::service('webdam.connector');
      $media_info = $webdam_connector->getMetadataGraphQL($uuid);

      if (!$media_info) {
        throw new \Exception('Unable to JSON encode the returned API response for the media UUID ' . $uuid);
      }

      $entity = Media::create([
        'bundle' => $bundle,
        $source_field => $uuid,
        WebdamMetadataItem::METADATA_FIELD_NAME => $media_info,
      ]);
      unset($context['results'][$file['path']]);
      $context['results'][] = $entity;
      $context['message'] = t('Mapped @file locally.', ['@file' => $file['filename']]);
    }
    catch (\Exception $e) {
      // If fetching failed try few more times. If waiting doesn't help fail the
      // batch eventually.
      if (empty($context['sandbox']['fails'])) {
        $context['sandbox']['fails'] = 0;
      }
      $context['sandbox']['fails']++;
      $context['finished'] = 0;
      $context['message'] = t('Mapping @file locally.', ['@file' => $file['filename']]);
      sleep(3);

      if ($context['sandbox']['fails'] >= static::FAIL_LIMIT) {
        Error::logException(\Drupal::logger('webdam'), $e);
        (new UploadFailedException(t("There was an unexpected error after uploading the file to Webdam.")))->displayMessage();
        \Drupal::messenger()->addWarning(t('There was an unexpected error after uploading the file to Webdam. Please contact your site administrator for more info.'));
      }
    }
  }

  /**
   * Upload batch finish callback.
   *
   * Stores results (media entities) into the session for the form to be able to
   * pick them up.
   */
  public static function batchFinish($success, $results, $operations) {
    // Save results into the form state to make them available in the form.
    \Drupal::service('session')->set('webdam_upload_batch_result', $results);
  }

  /**
   * Clear values from upload form element.
   *
   * @param array $element
   *   Upload form element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state object.
   */
  protected function clearFormValues(array &$element, FormStateInterface $form_state) {
    $form_state->setValueForElement($element['upload']['uploaded_files'], '');
    NestedArray::setValue($form_state->getUserInput(), $element['upload']['uploaded_files']['#parents'], '');
    $form_state->set('uploaded_entities', NULL);
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form = parent::buildConfigurationForm($form, $form_state);

    foreach ($this->entityTypeManager->getStorage('media_type')->loadMultiple() as $type) {
      /** @var \Drupal\media\MediaTypeInterface $type */
      if ($type->getSource() instanceof Webdam) {
        $form['media_type']['#options'][$type->id()] = $type->label();
      }
    }

    if (empty($form['media_type']['#options'])) {
      $form['media_type']['#disabled'] = TRUE;
      $form['media_type']['#description'] = $this->t('You must @create_bundle before using this widget.', [
        '@create_bundle' => Link::createFromRoute($this->t('create a Webdam media type'), 'media.bundle_add')->toString(),
      ]);
    }

    $form['extensions'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Allowed file extensions'),
      '#desciption' => $this->t('A space separated list of file extensions'),
      '#default_value' => $this->configuration['extensions'],
    ];

    $form['dropzone_description'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Dropzone drag-n-drop zone text'),
      '#default_value' => $this->configuration['dropzone_description'],
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validate(array &$form, FormStateInterface $form_state) {
    try {
      parent::validate($form, $form_state);
    }
    catch (WebdamException $e) {
      $e->displayMessage();
      return;
    }
  }

}

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

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