Initial Drupal 11 with DDEV setup

This commit is contained in:
gluebox
2025-10-08 11:39:17 -04:00
commit 89ef74b305
25344 changed files with 2599172 additions and 0 deletions

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Drupal\file\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* A constraint for UploadedFile objects.
*/
class UploadedFileConstraint extends Constraint {
/**
* The upload max size. Defaults to checking the environment.
*
* @var int|null
*/
public ?int $maxSize;
/**
* The upload ini size error message.
*
* @var string
*/
public string $uploadIniSizeErrorMessage = 'The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.';
/**
* The upload form size error message.
*
* @var string
*/
public string $uploadFormSizeErrorMessage = 'The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.';
/**
* The upload partial error message.
*
* @var string
*/
public string $uploadPartialErrorMessage = 'The file %file could not be saved because the upload did not complete.';
/**
* The upload no file error message.
*
* @var string
*/
public string $uploadNoFileErrorMessage = 'The file %file could not be saved because the upload did not complete.';
/**
* The generic file upload error message.
*
* @var string
*/
public string $uploadErrorMessage = 'The file %file could not be saved. An unknown error has occurred.';
}

View File

@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace Drupal\file\Validation\Constraint;
use Drupal\Component\Utility\Environment;
use Drupal\Core\StringTranslation\ByteSizeMarkup;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Constraint validator for uploaded files.
*
* Use FileValidatorInterface for validating file entities.
*
* @see \Drupal\Core\Validation\FileValidatorInterface
*/
class UploadedFileConstraintValidator extends ConstraintValidator {
/**
* {@inheritdoc}
*/
public function validate(mixed $value, Constraint $constraint): void {
if (!$constraint instanceof UploadedFileConstraint) {
throw new UnexpectedTypeException($constraint, UploadedFileConstraint::class);
}
if (!$value instanceof UploadedFile) {
throw new UnexpectedTypeException($value, UploadedFile::class);
}
if ($value->isValid()) {
return;
}
$maxSize = $constraint->maxSize ?? Environment::getUploadMaxSize();
match ($value->getError()) {
\UPLOAD_ERR_INI_SIZE => $this->context->buildViolation($constraint->uploadIniSizeErrorMessage, [
'%file' => $value->getClientOriginalName(),
'%maxsize' => ByteSizeMarkup::create($maxSize),
])->setCode((string) \UPLOAD_ERR_INI_SIZE)
->addViolation(),
\UPLOAD_ERR_FORM_SIZE => $this->context->buildViolation($constraint->uploadFormSizeErrorMessage, [
'%file' => $value->getClientOriginalName(),
'%maxsize' => ByteSizeMarkup::create($maxSize),
])->setCode((string) \UPLOAD_ERR_FORM_SIZE)
->addViolation(),
\UPLOAD_ERR_PARTIAL => $this->context->buildViolation($constraint->uploadPartialErrorMessage, [
'%file' => $value->getClientOriginalName(),
])->setCode((string) \UPLOAD_ERR_PARTIAL)
->addViolation(),
\UPLOAD_ERR_NO_FILE => $this->context->buildViolation($constraint->uploadNoFileErrorMessage, [
'%file' => $value->getClientOriginalName(),
])->setCode((string) \UPLOAD_ERR_NO_FILE)
->addViolation(),
default => $this->context->buildViolation($constraint->uploadErrorMessage, [
'%file' => $value->getClientOriginalName(),
])->setCode((string) $value->getError())
->addViolation()
};
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Drupal\file\Validation;
use Drupal\file\FileInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Symfony\Contracts\EventDispatcher\Event;
/**
* Event for file validations.
*/
class FileValidationEvent extends Event {
/**
* Creates a new FileValidationEvent.
*
* @param \Drupal\file\FileInterface $file
* The file.
* @param \Symfony\Component\Validator\ConstraintViolationListInterface $violations
* The violations.
*/
public function __construct(
public readonly FileInterface $file,
public readonly ConstraintViolationListInterface $violations,
) {}
}

View File

@ -0,0 +1,63 @@
<?php
namespace Drupal\file\Validation;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Validation\ConstraintManager;
use Drupal\file\FileInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
/**
* Provides a class for file validation.
*/
class FileValidator implements FileValidatorInterface {
/**
* Creates a new FileValidator.
*
* @param \Symfony\Component\Validator\Validator\ValidatorInterface $validator
* The validator.
* @param \Drupal\Core\Validation\ConstraintManager $constraintManager
* The constraint factory.
* @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $eventDispatcher
* The event dispatcher.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
* The module handler.
*/
public function __construct(
protected ValidatorInterface $validator,
protected ConstraintManager $constraintManager,
protected EventDispatcherInterface $eventDispatcher,
protected ModuleHandlerInterface $moduleHandler,
) {}
/**
* {@inheritdoc}
*/
public function validate(FileInterface $file, array $validators): ConstraintViolationListInterface {
$constraints = [];
foreach ($validators as $validator => $options) {
// Create the constraint.
// Options are an associative array of constraint properties and values.
$constraints[] = $this->constraintManager->create($validator, $options);
}
// Get the typed data.
$fileTypedData = $file->getTypedData();
$violations = $this->validator->validate($fileTypedData, $constraints);
$this->eventDispatcher->dispatch(new FileValidationEvent($file, $violations));
// Always check the insecure upload constraint.
if (count($violations) === 0) {
$insecureUploadConstraint = $this->constraintManager->create('FileExtensionSecure', []);
$violations = $this->validator->validate($fileTypedData, $insecureUploadConstraint);
}
return $violations;
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Drupal\file\Validation;
use Drupal\file\FileInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;
/**
* Provides a file validator that supports a list of validations.
*/
interface FileValidatorInterface {
/**
* Validates a File with a list of validators.
*
* @param \Drupal\file\FileInterface $file
* The file to validate.
* @param array $validators
* An associative array of validators with:
* - key: the plugin ID of the file validation constraint.
* - value: an associative array of options to pass to the constraint.
*
* @return \Symfony\Component\Validator\ConstraintViolationListInterface
* The violations list.
*/
public function validate(FileInterface $file, array $validators): ConstraintViolationListInterface;
}

View File

@ -0,0 +1,51 @@
<?php
namespace Drupal\file\Validation;
use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\Environment;
/**
* Provides a trait to create validators from settings.
*/
trait FileValidatorSettingsTrait {
/**
* Gets the upload validators for the specified settings.
*
* @param array $settings
* An associative array of settings. The following keys are supported:
* - max_filesize: The maximum file size in bytes. Defaults to the PHP max
* upload size.
* - file_extensions: A space-separated list of allowed file extensions.
*
* @return array
* An array suitable for passing to file_save_upload() or the file field
* element's '#upload_validators' property.
*/
public function getFileUploadValidators(array $settings): array {
$validators = [
// Add in our check of the file name length.
'FileNameLength' => [],
];
// Cap the upload size according to the PHP limit.
$maxFilesize = Bytes::toNumber(Environment::getUploadMaxSize());
if (!empty($settings['max_filesize'])) {
$maxFilesize = min($maxFilesize, Bytes::toNumber($settings['max_filesize']));
}
// There is always a file size limit due to the PHP server limit.
$validators['FileSizeLimit'] = ['fileLimit' => $maxFilesize];
// Add the extension check if necessary.
if (!empty($settings['file_extensions'])) {
$validators['FileExtension'] = [
'extensions' => $settings['file_extensions'],
];
}
return $validators;
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Drupal\file\Validation;
use Drupal\Core\DependencyInjection\ClassResolverInterface;
use Drupal\Core\TypedData\TypedDataManagerInterface;
use Drupal\Core\Validation\ExecutionContextFactory;
use Drupal\Core\TypedData\Validation\RecursiveValidator;
use Drupal\Core\Validation\ConstraintValidatorFactory;
use Drupal\Core\Validation\DrupalTranslator;
/**
* Factory for creating a new RecursiveValidator.
*/
class RecursiveValidatorFactory {
/**
* Constructs a new RecursiveValidatorFactory.
*
* @param \Drupal\Core\DependencyInjection\ClassResolverInterface $classResolver
* The class resolver.
* @param \Drupal\Core\TypedData\TypedDataManagerInterface $typedDataManager
* The typed data manager.
*/
public function __construct(
protected ClassResolverInterface $classResolver,
protected TypedDataManagerInterface $typedDataManager,
) {}
/**
* Creates a new RecursiveValidator.
*
* @return \Drupal\Core\TypedData\Validation\RecursiveValidator
* The validator.
*/
public function createValidator(): RecursiveValidator {
return new RecursiveValidator(
new ExecutionContextFactory(new DrupalTranslator()),
new ConstraintValidatorFactory($this->classResolver),
$this->typedDataManager,
);
}
}