You are here

class DropzoneJsUploadSave in DropzoneJS 8.2

Same name and namespace in other branches
  1. 8 src/DropzoneJsUploadSave.php \Drupal\dropzonejs\DropzoneJsUploadSave

A service that saves files uploaded by the dropzonejs element as files.

Most of this file mimics or directly copies what core does. For more information and comments see file_save_upload().

Hierarchy

Expanded class hierarchy of DropzoneJsUploadSave

1 string reference to 'DropzoneJsUploadSave'
dropzonejs.services.yml in ./dropzonejs.services.yml
dropzonejs.services.yml
1 service uses DropzoneJsUploadSave
dropzonejs.upload_save in ./dropzonejs.services.yml
Drupal\dropzonejs\DropzoneJsUploadSave

File

src/DropzoneJsUploadSave.php, line 25

Namespace

Drupal\dropzonejs
View source
class DropzoneJsUploadSave implements DropzoneJsUploadSaveInterface {
  use StringTranslationTrait;

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

  /**
   * Mime type guesser service.
   *
   * @var \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface
   */
  protected $mimeTypeGuesser;

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

  /**
   * The logger service.
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected $logger;

  /**
   * The renderer service.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected $renderer;

  /**
   * Config factory service.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

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

  /**
   * The messenger service.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * The stream wrapper manager.
   *
   * @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
   */
  protected $streamWrapperManager;

  /**
   * Construct the DropzoneUploadSave object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   Entity type manager service.
   * @param \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface $mimetype_guesser
   *   The mime type guesser service.
   * @param \Drupal\Core\File\FileSystemInterface $file_system
   *   The file system service.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   The logger factory service.
   * @param \Drupal\Core\Render\RendererInterface $renderer
   *   The renderer service.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Config factory service.
   * @param \Drupal\Core\Utility\Token $token
   *   The token service.
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   The messenger service.
   * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
   *   The stream wrapper manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, MimeTypeGuesserInterface $mimetype_guesser, FileSystemInterface $file_system, LoggerChannelFactoryInterface $logger_factory, RendererInterface $renderer, ConfigFactoryInterface $config_factory, Token $token, MessengerInterface $messenger, StreamWrapperManagerInterface $stream_wrapper_manager) {
    $this->entityTypeManager = $entity_type_manager;
    $this->mimeTypeGuesser = $mimetype_guesser;
    $this->fileSystem = $file_system;
    $this->logger = $logger_factory
      ->get('dropzonejs');
    $this->renderer = $renderer;
    $this->configFactory = $config_factory;
    $this->token = $token;
    $this->messenger = $messenger;
    $this->streamWrapperManager = $stream_wrapper_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function createFile($uri, $destination, $extensions, AccountProxyInterface $user, array $validators = []) {

    // Create the file entity.
    $uri = $this->streamWrapperManager
      ->normalizeUri($uri);
    $file_info = new \SplFileInfo($uri);

    /** @var \Drupal\file\FileInterface $file */
    $file = $this->entityTypeManager
      ->getStorage('file')
      ->create([
      'uid' => $user
        ->id(),
      'status' => 0,
      'filename' => $file_info
        ->getFilename(),
      'uri' => $uri,
      'filesize' => $file_info
        ->getSize(),
      'filemime' => $this->mimeTypeGuesser
        ->guess($uri),
    ]);

    // Replace tokens. As the tokens might contain HTML we convert it to plain
    // text.
    $destination = PlainTextOutput::renderFromHtml($this->token
      ->replace($destination));

    // Handle potentialy dangerous extensions.
    $renamed = $this
      ->renameExecutableExtensions($file);

    // The .txt extension may not be in the allowed list of extensions. We have
    // to add it here or else the file upload will fail.
    if ($renamed && !empty($extensions)) {
      $extensions .= ' txt';
      $this->messenger
        ->addMessage($this
        ->t('For security reasons, your upload has been renamed to %filename.', [
        '%filename' => $file
          ->getFilename(),
      ]));
    }

    // Validate the file.
    $errors = $this
      ->validateFile($file, $extensions, $validators);
    if (!empty($errors)) {
      $message = [
        'error' => [
          '#markup' => $this
            ->t('The specified file %name could not be uploaded.', [
            '%name' => $file
              ->getFilename(),
          ]),
        ],
        'item_list' => [
          '#theme' => 'item_list',
          '#items' => $errors,
        ],
      ];
      $this->messenger
        ->addError($this->renderer
        ->renderPlain($message));
      return FALSE;
    }

    // Prepare destination.
    if (!$this
      ->prepareDestination($file, $destination)) {
      $this->messenger
        ->addError($this
        ->t('The file could not be uploaded because the destination %destination is invalid.', [
        '%destination' => $destination,
      ]));
      return FALSE;
    }

    // Move uploaded files from PHP's upload_tmp_dir to destination.
    $move_result = $this->fileSystem
      ->move($uri, $file
      ->getFileUri());
    if (!$move_result) {
      $this->messenger
        ->addError($this
        ->t('File upload error. Could not move uploaded file.'));
      $this->logger
        ->notice('Upload error. Could not move uploaded file %file to destination %destination.', [
        '%file' => $file
          ->getFilename(),
        '%destination' => $file
          ->getFileUri(),
      ]);
      return FALSE;
    }

    // Set the permissions on the new file.
    $this->fileSystem
      ->chmod($file
      ->getFileUri());
    return $file;
  }

  /**
   * {@inheritdoc}
   */
  public function validateFile(FileInterface $file, $extensions, array $additional_validators = []) {
    $validators = $additional_validators;
    if (!empty($extensions)) {
      $validators['file_validate_extensions'] = [
        $extensions,
      ];
    }
    $validators['file_validate_name_length'] = [];

    // Call the validation functions specified by this function's caller.
    return file_validate($file, $validators);
  }

  /**
   * Rename potentially executable files.
   *
   * @param \Drupal\file\FileInterface $file
   *   The file entity object.
   *
   * @return bool
   *   Whether the file was renamed or not.
   */
  protected function renameExecutableExtensions(FileInterface $file) {
    if (!$this->configFactory
      ->get('system.file')
      ->get('allow_insecure_uploads') && preg_match('/\\.(php|pl|py|cgi|asp|js)(\\.|$)/i', $file
      ->getFilename()) && substr($file
      ->getFilename(), -4) != '.txt') {
      $file
        ->setMimeType('text/plain');

      // The destination filename will also later be used to create the URI.
      $file
        ->setFilename($file
        ->getFilename() . '.txt');
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Validate and set destination the destination URI.
   *
   * @param \Drupal\file\FileInterface $file
   *   The file entity object.
   * @param string $destination
   *   A string containing the URI that the file should be copied to. This must
   *   be a stream wrapper URI.
   *
   * @return bool
   *   True if the destination was sucesfully validated and set, otherwise
   *   false.
   */
  protected function prepareDestination(FileInterface $file, $destination) {

    // Assert that the destination contains a valid stream.
    $destination_scheme = $this->streamWrapperManager::getScheme($destination);
    if (!$this->streamWrapperManager
      ->isValidScheme($destination_scheme)) {
      return FALSE;
    }

    // Prepare the destination dir.
    if (!file_exists($destination)) {
      $this->fileSystem
        ->mkdir($destination, NULL, TRUE);
    }

    // A file URI may already have a trailing slash or look like "public://".
    if (substr($destination, -1) != '/') {
      $destination .= '/';
    }
    $destination = $this->fileSystem
      ->getDestinationFilename($destination . $file
      ->getFilename(), FileSystemInterface::EXISTS_RENAME);
    $file
      ->setFileUri($destination);
    return TRUE;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DropzoneJsUploadSave::$configFactory protected property Config factory service.
DropzoneJsUploadSave::$entityTypeManager protected property Entity manager service.
DropzoneJsUploadSave::$fileSystem protected property The file system service.
DropzoneJsUploadSave::$logger protected property The logger service.
DropzoneJsUploadSave::$messenger protected property The messenger service.
DropzoneJsUploadSave::$mimeTypeGuesser protected property Mime type guesser service.
DropzoneJsUploadSave::$renderer protected property The renderer service.
DropzoneJsUploadSave::$streamWrapperManager protected property The stream wrapper manager.
DropzoneJsUploadSave::$token protected property The token service.
DropzoneJsUploadSave::createFile public function Creates a file entity form an uploaded file. Overrides DropzoneJsUploadSaveInterface::createFile
DropzoneJsUploadSave::prepareDestination protected function Validate and set destination the destination URI.
DropzoneJsUploadSave::renameExecutableExtensions protected function Rename potentially executable files.
DropzoneJsUploadSave::validateFile public function Validate the uploaded file. Overrides DropzoneJsUploadSaveInterface::validateFile
DropzoneJsUploadSave::__construct public function Construct the DropzoneUploadSave object.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.