View source
<?php
namespace Drupal\jsonapi\Controller;
use Drupal\Component\Render\PlainTextOutput;
use Drupal\Core\Access\AccessResultReasonInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityConstraintViolationListInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\jsonapi\Entity\EntityValidationTrait;
use Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel;
use Drupal\jsonapi\JsonApiResource\Link;
use Drupal\jsonapi\JsonApiResource\LinkCollection;
use Drupal\jsonapi\JsonApiResource\NullIncludedData;
use Drupal\jsonapi\JsonApiResource\ResourceObject;
use Drupal\jsonapi\JsonApiResource\ResourceObjectData;
use Drupal\jsonapi\ResourceResponse;
use Drupal\jsonapi\ResourceType\ResourceType;
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;
class FileUpload {
use EntityValidationTrait;
protected $currentUser;
protected $fieldManager;
protected $fileUploader;
protected $httpKernel;
public function __construct(AccountInterface $current_user, EntityFieldManagerInterface $field_manager, TemporaryJsonapiFileFieldUploader $file_uploader, HttpKernelInterface $http_kernel) {
$this->currentUser = $current_user;
$this->fieldManager = $field_manager;
$this->fileUploader = $file_uploader;
$this->httpKernel = $http_kernel;
}
public function handleFileUploadForExistingResource(Request $request, ResourceType $resource_type, $file_field_name, FieldableEntityInterface $entity) {
$field_definition = $this
->validateAndLoadFieldDefinition($resource_type
->getEntityTypeId(), $resource_type
->getBundle(), $file_field_name);
static::ensureFileUploadAccess($this->currentUser, $field_definition, $entity);
$filename = $this->fileUploader
->validateAndParseContentDispositionHeader($request);
$file = $this->fileUploader
->handleFileUploadForField($field_definition, $filename, $this->currentUser);
if ($file instanceof EntityConstraintViolationListInterface) {
$violations = $file;
$message = "Unprocessable Entity: file validation failed.\n";
$message .= implode("\n", array_map(function (ConstraintViolationInterface $violation) {
return PlainTextOutput::renderFromHtml($violation
->getMessage());
}, (array) $violations
->getIterator()));
throw new UnprocessableEntityHttpException($message);
}
if ($field_definition
->getFieldStorageDefinition()
->getCardinality() === 1) {
$entity->{$file_field_name} = $file;
}
else {
$entity
->get($file_field_name)
->appendItem($file);
}
static::validate($entity, [
$file_field_name,
]);
$entity
->save();
$route_parameters = [
'entity' => $entity
->uuid(),
];
$route_name = sprintf('jsonapi.%s.%s.related', $resource_type
->getTypeName(), $file_field_name);
$related_url = Url::fromRoute($route_name, $route_parameters)
->toString(TRUE);
$request = Request::create($related_url
->getGeneratedUrl(), 'GET', [], $request->cookies
->all(), [], $request->server
->all());
return $this->httpKernel
->handle($request, HttpKernelInterface::SUB_REQUEST);
}
public function handleFileUploadForNewResource(Request $request, ResourceType $resource_type, $file_field_name) {
$field_definition = $this
->validateAndLoadFieldDefinition($resource_type
->getEntityTypeId(), $resource_type
->getBundle(), $file_field_name);
static::ensureFileUploadAccess($this->currentUser, $field_definition);
$filename = $this->fileUploader
->validateAndParseContentDispositionHeader($request);
$file = $this->fileUploader
->handleFileUploadForField($field_definition, $filename, $this->currentUser);
if ($file instanceof EntityConstraintViolationListInterface) {
$violations = $file;
$message = "Unprocessable Entity: file validation failed.\n";
$message .= implode("\n", array_map(function (ConstraintViolationInterface $violation) {
return PlainTextOutput::renderFromHtml($violation
->getMessage());
}, iterator_to_array($violations)));
throw new UnprocessableEntityHttpException($message);
}
$self_link = new Link(new CacheableMetadata(), Url::fromRoute('jsonapi.file--file.individual', [
'entity' => $file
->uuid(),
]), [
'self',
]);
$links = new LinkCollection([
'self' => $self_link,
]);
$relatable_resource_types = $resource_type
->getRelatableResourceTypesByField($file_field_name);
$file_resource_type = reset($relatable_resource_types);
$resource_object = ResourceObject::createFromEntity($file_resource_type, $file);
return new ResourceResponse(new JsonApiDocumentTopLevel(new ResourceObjectData([
$resource_object,
], 1), new NullIncludedData(), $links), 201, []);
}
protected static function ensureFileUploadAccess(AccountInterface $account, FieldDefinitionInterface $field_definition, FieldableEntityInterface $entity = NULL) {
$access_result = $entity ? TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($account, $field_definition, $entity) : TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($account, $field_definition);
if (!$access_result
->isAllowed()) {
$reason = 'The current user is not permitted to upload a file for this field.';
if ($access_result instanceof AccessResultReasonInterface) {
$reason .= ' ' . $access_result
->getReason();
}
throw new AccessDeniedHttpException($reason);
}
}
protected function validateAndLoadFieldDefinition($entity_type_id, $bundle, $field_name) {
$field_definitions = $this->fieldManager
->getFieldDefinitions($entity_type_id, $bundle);
if (!isset($field_definitions[$field_name])) {
throw new NotFoundHttpException(sprintf('Field "%s" does not exist.', $field_name));
}
$field_definition = $field_definitions[$field_name];
if ($field_definition
->getSetting('target_type') !== 'file') {
throw new AccessDeniedException(sprintf('"%s" is not a file field', $field_name));
}
return $field_definition;
}
}