file_entity-8.x-2.x-dev/src/Form/FileAddForm.php
src/Form/FileAddForm.php
<?php
namespace Drupal\file_entity\Form;
use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\field\FieldConfigInterface;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\file_entity\Entity\FileType;
use Drupal\file_entity\UploadValidatorsTrait;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form controller for file type forms.
*/
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 ($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 = \Drupal::service('file.repository')->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->setPermanent();
// 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();
}
}
}
