You are here

class FileAddForm in File Entity (fieldable files) 8.2

Form controller for file type forms.

Hierarchy

Expanded class hierarchy of FileAddForm

1 string reference to 'FileAddForm'
file_entity.routing.yml in ./file_entity.routing.yml
file_entity.routing.yml

File

src/Form/FileAddForm.php, line 25

Namespace

Drupal\file_entity\Form
View source
class FileAddForm extends FormBase {
  use UploadValidatorsTrait;

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

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

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

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

  /**
   * {@inheritdoc}
   */
  public function __construct(RendererInterface $renderer, EntityTypeManagerInterface $entity_type_manager, FileSystemInterface $file_system, MessengerInterface $messenger) {
    $this->renderer = $renderer;
    $this->entityTypeManager = $entity_type_manager;
    $this->fileSystem = $file_system;
    $this->messenger = $messenger;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static($container
      ->get('renderer'), $container
      ->get('entity_type.manager'), $container
      ->get('file_system'), $container
      ->get('messenger'));
  }

  /**
   * Returns a unique string identifying the form.
   *
   * @return string
   *   The unique string identifying the form.
   */
  public function getFormId() {
    return 'file_add';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, array $options = array()) {
    $step = in_array($form_state
      ->get('step'), array(
      1,
      2,
      3,
      4,
    )) ? $form_state
      ->get('step') : 1;
    $form_state
      ->set('step', $step);
    $form_state
      ->set('options', $options);
    switch ($step) {
      case 1:
        return $this
          ->stepUpload($form, $form_state, $options);
      case 2:
        return $this
          ->stepFileType($form, $form_state);
      case 3:
        return $this
          ->stepScheme($form, $form_state);
      case 4:
        return $this
          ->stepFields($form, $form_state);
    }
    return FALSE;
  }

  /**
   * Step 1
   * Generate form fields for the first step in the add file wizard.
   *
   * @param array $form
   *   Holds form data
   * @param FormStateInterface $form_state
   *   Holds form state data
   * @return array
   *   Returns form data
   */
  function stepUpload(array $form, FormStateInterface $form_state) {
    $options = [
      'file_extensions' => \Drupal::config('file_entity.settings')
        ->get('default_allowed_extensions'),
    ];
    $options = $form_state
      ->get('options') ? $form_state
      ->get('options') : $options;
    $validators = $this
      ->getUploadValidators($options);
    $form['upload'] = array(
      '#type' => 'managed_file',
      '#title' => t('Upload a new file'),
      '#upload_location' => $this
        ->getUploadDestinationUri($form_state
        ->get('options')),
      '#upload_validators' => $validators,
      '#progress_indicator' => 'bar',
      '#required' => TRUE,
      '#default_value' => $form_state
        ->has('file') ? array(
        $form_state
          ->get('file')
          ->id(),
      ) : NULL,
    );
    $file_upload_help = array(
      '#theme' => 'file_upload_help',
      '#upload_validators' => $form['upload']['#upload_validators'],
    );
    $form['upload']['#description'] = $this->renderer
      ->render($file_upload_help);
    $form['actions'] = array(
      '#type' => 'actions',
    );
    $form['actions']['next'] = array(
      '#type' => 'submit',
      '#button_type' => 'primary',
      '#value' => t('Next'),
    );
    return $form;
  }

  /**
   * Determines the upload location for the file add upload form.
   *
   * @param array $params
   *   An array of parameters from the media browser.
   * @param array $data
   *   (optional) An array of token objects to pass to token_replace().
   *
   * @return string
   *   A file directory URI with tokens replaced.
   *
   * @see token_replace()
   */
  function getUploadDestinationUri(array $params, array $data = array()) {
    $params += array(
      'uri_scheme' => $this
        ->config('system.file')
        ->get('default_scheme'),
      'file_directory' => '',
    );
    $destination = trim($params['file_directory'], '/');

    // Replace tokens.
    $destination = \Drupal::token()
      ->replace($destination, $data);
    return $params['uri_scheme'] . '://' . $destination;
  }

  /**
   * Form Step 2
   * Select file types.
   *
   * Skipped if there is only one file type known for the uploaded file.
   *
   * @param $form
   * @param $form_state
   */
  function stepFileType(array $form, FormStateInterface $form_state) {
    $file = $form_state
      ->get('file');
    $form['type'] = array(
      '#type' => 'radios',
      '#title' => t('File type'),
      '#options' => $this
        ->getCandidateFileTypes($file),
      '#default_value' => $form_state
        ->get('type'),
      '#required' => TRUE,
    );
    $form['actions'] = array(
      '#type' => 'actions',
    );
    $form['actions']['previous'] = array(
      '#type' => 'submit',
      '#value' => t('Previous'),
      '#limit_validation_errors' => array(),
    );
    $form['actions']['next'] = array(
      '#type' => 'submit',
      '#button_type' => 'primary',
      '#value' => t('Next'),
    );
    return $form;
  }

  /**
   * Get the candidate filetypes for a given file.
   *
   * Only filetypes for which the user has access to create entities are returned.
   *
   * @param \Drupal\file\FileInterface $file
   *   An upload file from form_state.
   *
   * @return array
   *   An array of file type bundles that support the file's mime type.
   */
  function getCandidateFileTypes(FileInterface $file) {
    $types = \Drupal::moduleHandler()
      ->invokeAll('file_type', array(
      $file,
    ));
    \Drupal::moduleHandler()
      ->alter('file_type', $types, $file);
    $candidates = array();
    foreach ($types as $type) {
      if ($has_access = $this->entityTypeManager
        ->getAccessControlHandler('file')
        ->createAccess($type)) {
        $candidates[$type] = FileType::load($type)
          ->label();
      }
    }
    return $candidates;
  }

  /**
   * Form Step 3
   *
   * @param $form
   * @param $form_state
   * @return mixed
   */
  function stepScheme(array $form, FormStateInterface $form_state) {
    $options = array();
    foreach (\Drupal::service('stream_wrapper_manager')
      ->getDescriptions(StreamWrapperInterface::WRITE_VISIBLE) as $scheme => $description) {
      $options[$scheme] = Html::escape($description);
    }
    $form['scheme'] = array(
      '#type' => 'radios',
      '#title' => t('Destination'),
      '#options' => $options,
      '#default_value' => $form_state
        ->get('scheme') ?: $this
        ->config('system.file')
        ->get('default_scheme'),
      '#required' => TRUE,
    );
    $form['actions'] = array(
      '#type' => 'actions',
    );
    $form['actions']['previous'] = array(
      '#type' => 'submit',
      '#value' => t('Previous'),
      '#limit_validation_errors' => array(),
    );
    $form['actions']['next'] = array(
      '#type' => 'submit',
      '#button_type' => 'primary',
      '#value' => t('Next'),
    );
    return $form;
  }

  /**
   * Step 4
   *
   * @param $form
   * @param $form_state
   */
  function stepFields(array $form, FormStateInterface $form_state) {

    // Load the file and overwrite the filetype set on the previous screen.

    /** @var \Drupal\file\FileInterface$file*/
    $file = $form_state
      ->get('file');
    $form_state
      ->set('form_display', EntityFormDisplay::collectRenderDisplay($file, 'default'));
    $form_state
      ->get('form_display')
      ->buildForm($file, $form, $form_state);
    $form['actions'] = array(
      '#type' => 'actions',
    );
    $form['actions']['previous'] = array(
      '#type' => 'submit',
      '#value' => t('Previous'),
      '#limit_validation_errors' => array(),
    );
    $form['actions']['submit'] = array(
      '#type' => 'submit',
      '#button_type' => 'primary',
      '#value' => t('Save'),
    );
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    if ($form_state
      ->get('step') == 4) {

      /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
      $form_display = $form_state
        ->get('form_display');
      $form_display
        ->extractFormValues($form_state
        ->get('file'), $form, $form_state);
      $form_display
        ->validateFormValues($form_state
        ->get('file'), $form, $form_state);
    }
    parent::validateForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {

    // This var is set to TRUE when we are ready to save the file.
    $save = FALSE;
    $trigger = $form_state
      ->getTriggeringElement()['#id'];
    $current_step = $form_state
      ->get('step');

    // Store select values in $form_state.
    foreach (array(
      'type',
      'scheme',
    ) as $key) {
      if ($form_state
        ->hasValue($key)) {
        $form_state
          ->set($key, $form_state
          ->getValue($key));
      }
    }
    $steps_to_check = array(
      2,
      3,
    );
    if ($trigger == 'edit-previous') {

      // If the previous button was hit,
      // the step checking order should be reversed 3, 2.
      $steps_to_check = array_reverse($steps_to_check);
    }

    /* @var \Drupal\file\FileInterface $file */
    if ($form_state
      ->has('file')) {
      $file = $form_state
        ->get('file');
    }
    else {
      $file = File::load($form_state
        ->getValue(array(
        'upload',
        0,
      )));
      $form_state
        ->set('file', $file);
    }
    foreach ($steps_to_check as $step) {

      // Check if we can skip step 2 and 3.
      if ($current_step == $step - 1 && $trigger == 'edit-next' || $current_step == $step + 1 && $trigger == 'edit-previous') {
        if ($step == 2) {

          // Check if we can skip step 2.
          $candidates = $this
            ->getCandidateFileTypes($file);
          if (count($candidates) == 1) {
            $candidates_keys = array_keys($candidates);

            // There is only one possible filetype for this file.
            // Skip the second page.
            $current_step += $trigger == 'edit-previous' ? -1 : 1;
            $form_state
              ->set('type', reset($candidates_keys));
          }
          elseif (\Drupal::config('file_entity.settings')
            ->get('wizard_skip_file_type')) {

            // Do not assign the file a file type.
            $current_step += $trigger == 'edit-previous' ? -1 : 1;
            $form_state
              ->set('type', FILE_TYPE_NONE);
          }
        }
        else {

          // Check if we can skip step 3.
          $schemes = \Drupal::service('stream_wrapper_manager')
            ->getWrappers(StreamWrapperInterface::WRITE_VISIBLE);
          if (!$file
            ->isWritable()) {

            // The file is read-only (remote) and must use its provided scheme.
            $current_step += $trigger == 'edit-previous' ? -1 : 1;
            $form_state
              ->set('scheme', StreamWrapperManager::getScheme($file
              ->getFileUri()));
          }
          elseif (count($schemes) == 1) {

            // There is only one possible stream wrapper for this file.
            // Skip the third page.
            $current_step += $trigger == 'edit-previous' ? -1 : 1;
            $form_state
              ->set('scheme', key($schemes));
          }
          elseif (\Drupal::config('file_entity.settings')
            ->get('wizard_skip_scheme')) {

            // Assign the file the default scheme.
            $current_step += $trigger == 'edit-previous' ? -1 : 1;
            $form_state
              ->set('scheme', $this
              ->config('system.file')
              ->get('default_scheme'));
          }
        }
      }
    }

    // We have the filetype, check if we can skip step 4.
    if ($current_step == 3 && $trigger == 'edit-next') {
      $file
        ->updateBundle($form_state
        ->get('type'));
      $save = TRUE;
      foreach ($file
        ->getFieldDefinitions() as $field_definition) {
        if ($field_definition instanceof FieldConfigInterface) {

          // This filetype does have configurable fields, do not save as we
          // do step 4 first.
          $save = FALSE;
          break;
        }
      }
      if ($this
        ->config('file_entity.settings')
        ->get('wizard_skip_fields', FALSE)) {

        // Save the file with blanks fields.
        $save = TRUE;
      }
    }

    // Form id's can vary depending on how many other forms are displayed, so we
    // need to do string comparissons. e.g edit-submit--2.
    if (strpos($trigger, 'edit-next') !== FALSE) {
      $current_step++;
    }
    elseif (strpos($trigger, 'edit-previous') !== FALSE) {
      $current_step--;
    }
    elseif (strpos($trigger, 'edit-submit') !== FALSE) {
      $save = TRUE;
    }
    $form_state
      ->set('step', $current_step);
    if ($save) {
      if (StreamWrapperManager::getScheme($file
        ->getFileUri()) != $form_state
        ->get('scheme')) {

        // @TODO: Users should not be allowed to create private files without permission ('view private files')
        if ($moved_file = file_move($file, $form_state
          ->get('scheme') . '://' . StreamWrapperManager::getTarget($file
          ->getFileUri()), FileSystemInterface::EXISTS_RENAME)) {

          // Only re-assign the file object if file_move() did not fail.
          $moved_file
            ->setFilename($file
            ->getFilename());
          $file = $moved_file;
        }
      }
      $file->display = TRUE;

      // Change the file from temporary to permanent.
      $file->status = FILE_STATUS_PERMANENT;

      // Save entity
      $file
        ->save();
      $form_state
        ->set('file', $file);
      $this->messenger
        ->addMessage(t('@type %name was uploaded.', array(
        '@type' => $file->type->entity
          ->label(),
        '%name' => $file
          ->getFilename(),
      )));

      // Figure out destination.
      if (\Drupal::currentUser()
        ->hasPermission('administer files')) {
        $form_state
          ->setRedirect('entity.file.collection');
      }
      else {
        $form_state
          ->setRedirectUrl($file
          ->toUrl());
      }
    }
    else {
      $form_state
        ->setRebuild();
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
FileAddForm::$entityTypeManager protected property The entity type manager.
FileAddForm::$fileSystem protected property The file system service.
FileAddForm::$messenger protected property The messenger. Overrides MessengerTrait::$messenger
FileAddForm::$renderer protected property The renderer.
FileAddForm::buildForm public function Form constructor. Overrides FormInterface::buildForm
FileAddForm::create public static function Instantiates a new instance of this class. Overrides FormBase::create
FileAddForm::getCandidateFileTypes function Get the candidate filetypes for a given file.
FileAddForm::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId
FileAddForm::getUploadDestinationUri function Determines the upload location for the file add upload form.
FileAddForm::stepFields function Step 4
FileAddForm::stepFileType function Form Step 2 Select file types.
FileAddForm::stepScheme function Form Step 3
FileAddForm::stepUpload function Step 1 Generate form fields for the first step in the add file wizard.
FileAddForm::submitForm public function Form submission handler. Overrides FormInterface::submitForm
FileAddForm::validateForm public function Form validation handler. Overrides FormBase::validateForm
FileAddForm::__construct public function
FormBase::$configFactory protected property The config factory. 1
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::config protected function Retrieves a configuration object.
FormBase::configFactory protected function Gets the config factory for this form. 1
FormBase::container private function Returns the service container.
FormBase::currentUser protected function Gets the current user.
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
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.
UploadValidatorsTrait::getUploadValidators public function Retrieves the upload validators for a file or archive.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.