Skip to content
Snippets Groups Projects

Issue #2899902: Send/Upload files?

8 unresolved threads
Files
10
<?php
namespace Drupal\webform_rest\Plugin\rest\resource;
use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\Environment;
use Drupal\Core\File\FileSystemInterface;
use Drupal\file\Entity\File;
use Drupal\file\Plugin\rest\resource\FileUploadResource;
use Drupal\rest\ModifiedResourceResponse;
use Drupal\webform\Entity\Webform;
use Drupal\webform\Entity\WebformSubmission;
use Drupal\webform\WebformSubmissionForm;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\HttpException;
/**
* Creates a resource for webform file uploads.
*
* @RestResource(
* id = "webform_rest_file_upload",
* label = @Translation("Webform File Upload"),
* serialization_class = "Drupal\file\Entity\File",
* uri_paths = {
* "create" = "/webform_rest/{webform_id}/upload/{field_name}"
* },
* config_dependencies = {
* "module" = {
* "file"
* }
* }
* )
*/
class WebformFileUploadResource extends FileUploadResource {
/**
* Creates a file from an endpoint.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
* @param string $webform_id
* The webform ID.
* @param string $field_name
* The field name.
* @param string $placeholder
* An unused placeholder to maintain compatibility with the parent method.
*
* @return \Drupal\rest\ResourceResponseInterface
* The HTTP response object.
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
* Throws HttpException in case of error.
*/
public function post(Request $request, $webform_id, $field_name, $placeholder = '') {
// Check for a valid webform.
$webform = Webform::load($webform_id);
if (!$webform) {
throw new BadRequestHttpException('Invalid webform_id value.');
}
// Check if the user has access to the webform.
if (!$webform->access('submission_create')) {
throw new AccessDeniedHttpException('You do not have access to this webform.');
}
// Check if the webform is open.
$is_open = WebformSubmissionForm::isOpen($webform);
if ($is_open === TRUE) {
$filename = $this->validateAndParseContentDispositionHeader($request);
$element = $webform->getElement($field_name);
if ($element === NULL) {
throw new BadRequestHttpException('Invalid webform field.');
}
$webform_submission = WebformSubmission::create(['webform_id' => $webform->id()]);
// Prepare upload location and validators for the element
$element_plugin = $this->getElementPlugin($element);
$element_plugin->prepare($element, $webform_submission);
$destination = $element['#upload_location'];
// Check the destination file path is writable.
if (!$this->fileSystem->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY)) {
throw new HttpException(500, 'Destination file path is not writable');
}
$validators = $this->getElementValidators($element);
$prepared_filename = $this->prepareFilename($filename, $validators);
// Create the file.
if (substr($destination, -1) === '/') {
$file_uri = "{$destination}{$prepared_filename}";
}
else {
$file_uri = "{$destination}/{$prepared_filename}";
}
$temp_file_path = $this->streamUploadData();
// This will take care of altering $file_uri if a file already exists.
$file_uri = $this->fileSystem->getDestinationFilename($file_uri, FileSystemInterface::EXISTS_RENAME);
// Lock based on the prepared file URI.
$lock_id = $this->generateLockIdFromFileUri($file_uri);
if (!$this->lock->acquire($lock_id)) {
throw new HttpException(503, sprintf('File "%s" is already locked for writing'), NULL, ['Retry-After' => 1]);
}
// Begin building file entity.
$file = File::create([]);
$file->setOwnerId($this->currentUser->id());
$file->setFilename($prepared_filename);
$file->setMimeType($this->mimeTypeGuesser->guessMimeType($prepared_filename));
// set temp path so any file validation implementers can check the file.
$file->setFileUri($temp_file_path);
// Set the size. This is done in File::preSave() but we validate the file
// before it is saved.
$file->setSize(@filesize($temp_file_path));
// Validate the file entity against entity-level validation and
// field-level validators.
$this->validate($file, $validators);
// Move the file to the correct location after validation. Use
// FILE_EXISTS_ERROR as the file location has already been determined
// above in file_unmanaged_prepare().
if (!$this->fileSystem->move($temp_file_path, $file_uri, FileSystemInterface::EXISTS_ERROR)) {
throw new HttpException(500, 'Temporary file could not be moved to file location');
}
// set real path after move.
$file->setFileUri($file_uri);
$file->save();
$this->lock->release($lock_id);
// 201 Created responses return the newly created entity in the response
// body. These responses are not cacheable, so we add no cacheability
// metadata here.
return new ModifiedResourceResponse($file, 201);
}
else {
throw new AccessDeniedHttpException('This webform is closed, or too many submissions have been made.');
}
}
/**
* Retrieves the upload validators for an element.
*
* This is copied from \Drupal\file\Plugin\Field\FieldType\FileItem as there
* is no entity instance available here that a FileItem would exist for.
*
* @param array $element
* The element for which to get validators.
*
* @return array
* An array suitable for passing to file_save_upload() or the file field
* element's '#upload_validators' property.
*
* @see \Drupal\file\Plugin\rest\resource\FileUploadResource::getUploadValidators()
*/
protected function getElementValidators(array $element) {
$validators = [
// Add in our check of the file name length.
'file_validate_name_length' => [],
];
// Cap the upload size according to the PHP limit.
if (version_compare(\Drupal::VERSION, '9.1', '<')) {
$max_filesize = Bytes::toInt(Environment::getUploadMaxSize());
}
else {
$max_filesize = Bytes::toNumber(Environment::getUploadMaxSize());
}
if (!empty($element["#max_filesize"])) {
if (version_compare(\Drupal::VERSION, '9.1', '<')) {
$max_elem_filesize = Bytes::toInt($element['#max_filesize'] * 1024 * 1024);
}
else {
$max_elem_filesize = Bytes::toNumber($element['#max_filesize'] * 1024 * 1024);
}
$max_filesize = min($max_filesize, $max_elem_filesize);
}
// There is always a file size limit due to the PHP server limit.
$validators['file_validate_size'] = [$max_filesize];
// Add the extension check if necessary.
if (!empty($element['#file_extensions'])) {
$validators['file_validate_extensions'] = [$element['#file_extensions']];
}
return $validators;
}
/**
* Loads the webform element plugin for the provided element.
*
* @param array $element
* The element for which to get the plugin.
*
* @return \Drupal\Core\Render\Element\ElementInterface
* The element plugin.
*/
protected function getElementPlugin(array $element) {
/** @var \Drupal\Core\Render\ElementInfoManager $plugin_manager */
$plugin_manager = \Drupal::service('plugin.manager.webform.element');
$plugin_definition = $plugin_manager->getDefinition($element['#type']);
$element_plugin = $plugin_manager->createInstance($element['#type'], $plugin_definition);
return $element_plugin;
}
}
Loading