filefield_sources_jsonapi-8.x-1.0-beta9/src/Plugin/FilefieldSource/RemoteJSONAPI.php

src/Plugin/FilefieldSource/RemoteJSONAPI.php
<?php

namespace Drupal\filefield_sources_jsonapi\Plugin\FilefieldSource;

use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\filefield_sources\Plugin\FilefieldSource\Remote;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\File\FileSystem;
use Drupal\field\Entity\FieldConfig;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Site\Settings;
use Drupal\Core\Url;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Field\WidgetInterface;
use Drupal\filefield_sources_jsonapi\Entity\FileFieldSourcesJSONAPI;
use Drupal\image\Entity\ImageStyle;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\Html;
use Drupal\Core\File\FileSystemInterface;
use GuzzleHttp\Exception\RequestException;

/**
 * FileField source plugin to allow getting a file from JSON Rest API.
 *
 * @FilefieldSource(
 *   id = "remote_jsonapi",
 *   name = @Translation("JSON API remote URL"),
 *   label = @Translation("JSON API Library"),
 *   description = @Translation("Download (or store as remote url) file from JSON Rest API.")
 * )
 */
class RemoteJSONAPI extends Remote {

  const REMOTE_JSONAPI_LISTER_MODAL_WIDTH = 1000;

  /**
   * {@inheritdoc}
   *
   * Based on Remote->value().
   */
  public static function value(array &$element, &$input, FormStateInterface $form_state) {
    if (isset($input['filefield_remote_jsonapi']['url']) && strlen($input['filefield_remote_jsonapi']['url']) > 0 && UrlHelper::isValid($input['filefield_remote_jsonapi']['url']) && $input['filefield_remote_jsonapi']['url'] != FILEFIELD_SOURCE_REMOTE_HINT_TEXT) {
      $source = $input['filefield_remote_jsonapi']['source'];
      $config = FileFieldSourcesJSONAPI::load($source);

      if (!$config->getBasicAuthentication() && $config->getRemoteStream()) {
        $uri = $input['filefield_remote_jsonapi']['url'];
        // Copy of Drupal\remote_stream_wrapper\Plugin\FilefieldSource\RemoteFileUrl::value()
        $scheme = StreamWrapperManager::getScheme($uri);

        if (!UrlHelper::isValid($uri, TRUE)) {
          $form_state->setError($element, t('Invalid Remote File URL.'));
          return;
        }

        if (!\Drupal::service('stream_wrapper_manager')->isValidScheme($scheme)) {
          // Check that the scheme is supported.
          $form_state->setError($element, t('Remote file URL with the %scheme scheme are not supported.', ['%scheme' => $scheme]));
          return;
        }

        // Check that the file url exists.
        try {
          $response = \Drupal::httpClient()->get($uri, ['headers' => ['Accept' => 'text/plain']]);
          $data = (string) $response->getBody();
          if (empty($data)) {
            return;
          }
        }
        catch (RequestException $e) {
          $form_state->setError($element, t(
            'Unable to fetch remote file URL: %url (error code: @code).',
            ['%url' => $uri, '@code' => $e->getCode()])
          );
          return;
        }

        try {
          $files = \Drupal::entityTypeManager()
            ->getStorage('file')
            ->loadByProperties(['uri' => $uri]);
          $file = reset($files);

          if (!$file) {
            /** @var \Drupal\file\FileInterface $file */
            $file = \Drupal::entityTypeManager()
              ->getStorage('file')->create([
                'uri' => $uri,
                'uid' => \Drupal::currentUser()->id(),
              ]);

            // Validate file.
            $errors = file_validate($file, $element['#upload_validators']);
            if (!empty($errors)) {
              $message = [
                'error' => [
                  '#markup' => t('The specified file %name could not be uploaded.', ['%name' => $file->getFilename()]),
                ],
                'item_list' => [
                  '#theme' => 'item_list',
                  '#items' => $errors,
                ],
              ];
              $form_state->setError($element, $message);
              return;
            }

            $file->save();
          }
        }
        catch (\Exception $e) {
          $form_state->setError($element, $e->getMessage());
          return;
        }

        if (empty($file->id())) {
          $form_state->setError($element, t('Unable to add file from URL %uri.', ['%uri' => $uri]));
          return;
        }

        if ($file) {
          if (!in_array($file->id(), $input['fids'])) {
            $input['fids'][] = $file->id();
          }
        }
      }
      else {
        /** @var \Drupal\Core\File\FileSystemInterface $fileSystem */
        $fileSystem = \Drupal::service('file_system');

        /** @var \Drupal\field\FieldConfigInterface $field */
        $field = FieldConfig::loadByName($element['#entity_type'], $element['#bundle'], $element['#field_name']);
        $url = $input['filefield_remote_jsonapi']['url'];

        // Check that the destination is writable.
        $temporary_directory = 'temporary://';
        if (!$fileSystem->prepareDirectory($temporary_directory, FileSystemInterface::MODIFY_PERMISSIONS)) {
          \Drupal::logger('filefield_sources')->log(E_NOTICE, 'The directory %directory is not writable, because it does not have the correct permissions set.', ['%directory' => $fileSystem->realpath($temporary_directory)]);
          \Drupal::messenger()->addError(t('The file could not be transferred because the temporary directory is not writable.'));
          return;
        }

        // Check that the destination is writable.
        $directory = $element['#upload_location'];
        $mode = Settings::get('file_chmod_directory', FileSystem::CHMOD_DIRECTORY);

        // This first chmod check is for other systems such as S3, which don't
        // work with file_prepare_directory().
        if (!$fileSystem->chmod($directory, $mode) && !$fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY)) {
          \Drupal::logger('filefield_sources')->log(E_NOTICE,
            'File %file could not be copied, because the destination directory %destination is not configured correctly.',
            [
              '%file' => $url,
              '%destination' => $fileSystem->realpath($directory),
            ]
          );
          \Drupal::messenger()->addError(t('The specified file %file could not be copied, because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', ['%file' => $url]));
          return;
        }

        // Check the basicAuthentication config value and if it is checked,
        // we get the file with basic authentication.
        $basic_auth = $config->getBasicAuthentication();
        if ($basic_auth) {
          $myConfig = \Drupal::config('filefield_sources_jsonapi');
          $username = $myConfig->get('username');
          $password = $myConfig->get('password');
        }

        // @todo refactor the three curl function call.
        // Check the headers to make sure it exists and is within the allowed
        // size.
        $client = \Drupal::httpClient();
        $authorization_options = [];
        if ($basic_auth) {
          $authorization_options = [
            'auth' => [
              $username,
              $password,
            ],
          ];
        }
        try {
          $request = $client->get($url, $authorization_options);
          $file_contents = $request->getBody()->getContents();
        }
        catch (RequestException $e) {
          // An error happened.
          \Drupal::logger('filefield_sources')
            ->log(E_ERROR, 'url %url could not be fetched', ['%url' => $url]);
          switch ($e->getCode()) {
            case 403:
              $form_state->setError($element, t('The remote file could not be transferred because access to the file was denied.'));
              break;

            case 404:
              $form_state->setError($element, t('The remote file could not be transferred because it was not found.'));
              break;

            default:
              $form_state->setError($element, t('The remote file could not be transferred due to an HTTP error (@code).', ['@code' => $e->getCode()]));
          }
          return;
        }

        $url_info = parse_url($url);

        // Determine the proper filename by reading the filename given in the
        // Content-Disposition header. If the server fails to send this header,
        // fall back on the basename of the URL.
        //
        // We prefer to use the Content-Disposition header, because we can then
        // use URLs like http://example.com/get_file/23 which would otherwise be
        // rejected because the URL basename lacks an extension.
        $filename = static::filename();
        if (empty($filename)) {
          $filename = rawurldecode(basename($url_info['path']));
        }

        $pathinfo = pathinfo($filename);

        // Create the file extension from the MIME header if all else has
        // failed.
        if (empty($pathinfo['extension']) && $extension = static::mimeExtension()) {
          $filename = $filename . '.' . $extension;
          $pathinfo = pathinfo($filename);
        }

        $filename = filefield_sources_clean_filename($filename, $field->getSetting('file_extensions'));
        $filepath = $fileSystem->createFilename($filename, $temporary_directory);

        if (empty($pathinfo['extension'])) {
          $form_state->setError($element, t('The remote URL must be a file and have an extension.'));
          return;
        }

        // Perform basic extension check on the file before trying to transfer.
        $extensions = $field->getSetting('file_extensions');
        $regex = '/\.(' . preg_replace('/[ +]/', '|', preg_quote($extensions)) . ')$/i';
        if (!empty($extensions) && !preg_match($regex, $filename)) {
          $form_state->setError($element, t('Only files with the following extensions are allowed: %files-allowed.', ['%files-allowed' => $extensions]));
          return;
        }

        // Set progress bar information.
        $options = [
          'key' => $element['#entity_type'] . '_' . $element['#bundle'] . '_' . $element['#field_name'] . '_' . $element['#delta'],
          'filepath' => $filepath,
        ];
        static::setTransferOptions($options);

        $transfer_success = FALSE;
        // If we've already downloaded the entire file because the
        // header-retrieval failed, just ave the contents we have.
        if ($fp = @fopen($filepath, 'w')) {
          fwrite($fp, $file_contents);
          fclose($fp);
          $transfer_success = TRUE;
        }
        $file_size = $request->getHeaderLine('Content-Length');

        // Transform image before saving it.
        $image_factory = \Drupal::service('image.factory');
        $image = $image_factory->get($filepath);
        $image_style = $element['#filefield_sources_settings']['source_remote_jsonapi']['image_style'] ?? NULL;
        if ($image->isValid() && $image_style) {
          $style = ImageStyle::load($image_style);
          if ($style->createDerivative($filepath, $filepath)) {
            $file_size = filesize($filepath);
          }
        }

        // Check file size based off of header information.
        if (!empty($element['#upload_validators']['file_validate_size'][0])) {
          $max_size = $element['#upload_validators']['file_validate_size'][0];
          if ($file_size > $max_size) {
            $form_state->setError($element, t(
              'The remote file is %filesize exceeding the maximum file size of %maxsize.',
              [
                '%filesize' => format_size($file_size),
                '%maxsize' => format_size($max_size),
              ]
            ));
            return;
          }
        }

        if ($transfer_success && $file = filefield_sources_save_file($filepath, $element['#upload_validators'], $element['#upload_location'])) {
          if (!in_array($file->id(), $input['fids'])) {
            $input['fids'][] = $file->id();
          }
        }

        // Delete the temporary file.
        @unlink($filepath);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function process(array &$element, FormStateInterface $form_state, array &$complete_form) {
    if (!isset($element['#filefield_sources_settings']['source_remote_jsonapi']['sources'])) {
      return $element;
    }
    $routing_params = [
      'entity_type' => $element['#entity_type'],
      'bundle' => $element['#bundle'],
      'form_mode' => 'default',
      'field_name' => $element['#field_name'],
      'wrapper' => Html::getClass(implode('-', $element['#field_parents'])),
    ];
    $element['filefield_remote_jsonapi'] = [
      '#weight' => 100.5,
      '#theme' => 'filefield_sources_element',
      '#source_id' => 'remote_jsonapi',
      // Required for proper theming.
      '#filefield_source' => TRUE,
      '#filefield_sources_hint_text' => FILEFIELD_SOURCE_REMOTE_HINT_TEXT,
      '#filefield_sources_remote_jsonapi_routing_params' => $routing_params,
      '#filefield_sources_remote_jsonapi_settings' => $element['#filefield_sources_settings']['source_remote_jsonapi'],
    ];

    $element['filefield_remote_jsonapi']['url'] = [
      '#type' => 'textfield',
      '#description' => filefield_sources_element_validation_help($element['#upload_validators']),
      '#maxlength' => NULL,
      '#attributes' => ['class' => ['visually-hidden']],
    ];
    if (isset($element['#alt_field']) && $element['#alt_field']) {
      $element['filefield_remote_jsonapi']['alt'] = [
        '#type' => 'hidden',
        '#value' => '',
      ];
    }
    if (isset($element['#title_field']) && $element['#title_field']) {
      $element['filefield_remote_jsonapi']['title'] = [
        '#type' => 'hidden',
        '#value' => '',
      ];
    }
    if (isset($element['#description_field']) && $element['#description_field']) {
      $element['filefield_remote_jsonapi']['description'] = [
        '#type' => 'hidden',
        '#value' => '',
      ];
    }
    $element['filefield_remote_jsonapi']['source'] = [
      '#type' => 'hidden',
      '#value' => '',
    ];

    $class = '\Drupal\file\Element\ManagedFile';
    $ajax_settings = [
      'callback' => [$class, 'uploadAjaxCallback'],
      'options' => [
        'query' => [
          'element_parents' => implode('/', $element['#array_parents']),
        ],
      ],
      'wrapper' => $element['upload_button']['#ajax']['wrapper'],
      'effect' => 'fade',
      'progress' => [
        'type' => 'bar',
        'path' => 'file/remote/progress/' . $element['#entity_type'] . '/' . $element['#bundle'] . '/' . $element['#field_name'] . '/' . $element['#delta'],
        'message' => t('Starting transfer...'),
      ],
    ];

    $element['filefield_remote_jsonapi']['transfer'] = [
      '#name' => implode('_', $element['#parents']) . '_transfer',
      '#type' => 'submit',
      '#value' => t('Transfer'),
      '#validate' => [],
      '#submit' => ['filefield_sources_field_submit'],
      '#limit_validation_errors' => [$element['#parents']],
      '#ajax' => $ajax_settings,
      '#attributes' => ['class' => ['visually-hidden']],
    ];

    return $element;
  }

  /**
   * Theme the output of the remote element.
   *
   * @todo Refactor.
   */
  public static function element($variables) {
    $element = $variables['element'];
    $element['url']['#field_suffix'] = \Drupal::service('renderer')
      ->render($element['transfer']);

    $width = $element['#filefield_sources_remote_jsonapi_settings']['modal_width'] ?? self::REMOTE_JSONAPI_LISTER_MODAL_WIDTH;

    $button = [
      '#type' => 'link',
      '#title' => t('Open JSON API browser'),
      '#url' => Url::fromRoute(
        'filefield_sources_jsonapi.modal_browser_form',
        $element['#filefield_sources_remote_jsonapi_routing_params'],
        [
          'attributes' => [
            'class' => ['use-ajax'],
            'data-dialog-type' => 'modal',
            'data-dialog-options' => Json::encode([
              'width' => $width,
            ]),
          ],
        ]
      ),
      '#attributes' => ['class' => ['button']],
    ];

    $rendered_button = \Drupal::service('renderer')->render($button);

    $content = \Drupal::service('renderer')->render($element['url']) . \Drupal::service('renderer')->render($element['alt']) . \Drupal::service('renderer')->render($element['title']) . \Drupal::service('renderer')->render($element['description']) . \Drupal::service('renderer')->render($element['source']) . $rendered_button;

    return '<div class="filefield-source filefield-source-remote_jsonapi clear-block">' . $content . '</div>';
  }

  /**
   * Implements hook_filefield_source_settings().
   */
  public static function settings(WidgetInterface $plugin) {
    $settings = $plugin->getThirdPartySetting('filefield_sources', 'filefield_sources');

    $return['source_remote_jsonapi'] = [
      '#title' => t('JSON Api settings'),
      '#type' => 'details',
      '#description' => t('Enable JSON API browser'),
      '#weight' => 10,
    ];
    $return['source_remote_jsonapi']['sources'] = [
      '#type' => 'checkboxes',
      '#options' => FileFieldSourcesJSONAPI::getSettingsOptionList(),
      '#title' => t('JSON API settings'),
      '#description' => t('Defined JSON API settings at <a href=":url">manage JSON API sources</a> page.', [':url' => Url::fromRoute('entity.filefield_sources_jsonapi.collection')->toString()]),
      '#default_value' => $settings['source_remote_jsonapi']['sources'] ?: NULL,
      '#element_validate' => [
        [get_called_class(), 'jsonApiSourceValidateRequired'],
      ],
    ];
    $return['source_remote_jsonapi']['image_style'] = [
      '#type' => 'select',
      '#title' => t('Image style'),
      '#options' => image_style_options(),
      '#description' => t('Transform image file before save.'),
      '#default_value' => $settings['source_remote_jsonapi']['image_style'] ?: NULL,
    ];
    $return['source_remote_jsonapi']['modal_width'] = [
      '#type' => 'number',
      '#min' => 400,
      '#title' => t('Modal window width'),
      '#description' => t('Modal window initial width.'),
      '#default_value' => $settings['source_remote_jsonapi']['modal_width'] ?: self::REMOTE_JSONAPI_LISTER_MODAL_WIDTH,
    ];

    return $return;
  }

  /**
   * Custom validation for JSON API source.
   */
  public static function jsonApiSourceValidateRequired($element, FormStateInterface $form_state) {
    // Go 2 levels up.
    $parents = array_slice($element['#parents'], 0, count($element['#parents']) - 2, TRUE);
    $input = NestedArray::getValue($form_state->getValues(), $parents);
    if ($input['sources']['remote_jsonapi'] && empty(array_filter($input['source_remote_jsonapi']['sources']))) {
      $form_state->setError($element, 'JSON API settings are required.');
    }
  }

}

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

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