Skip to content

Commit

Permalink
feat(drupal): Implement Drupal 11 readiness. (#1407)
Browse files Browse the repository at this point in the history
  • Loading branch information
apathak18 authored Aug 2, 2024
1 parent e0dacc2 commit b677475
Show file tree
Hide file tree
Showing 26 changed files with 129 additions and 119 deletions.
20 changes: 9 additions & 11 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,24 @@ on:
jobs:
drupal:
name: Drupal ${{ matrix.drupal-core }} (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
# We cannot use ubuntu-latest right now as it still points to 22.04 and we need a newer database driver.
runs-on: ubuntu-24.04
env:
extensions: mbstring, xml, pdo_sqlite, gd, opcache
strategy:
fail-fast: false
matrix:
php-versions: ['8.1', '8.2']
php-versions: ['8.1', '8.2', '8.3']
drupal-core: ['10.3.x']
phpstan: ['0']
include:
# Extra run to test older supported Drupal 10.1.x.
- php-versions: '8.1'
drupal-core: '10.1.x'
phpstan: '0'
# Extra run to test older supported Drupal 10.2.x.
- php-versions: '8.1'
drupal-core: '10.2.x'
phpstan: '0'
# We only need to run PHPStan once on the latest PHP version.
- php-versions: '8.3'
drupal-core: '10.3.x'
drupal-core: '11.0.x'
phpstan: '1'
steps:
- name: Checkout Drupal core
Expand Down Expand Up @@ -86,12 +83,13 @@ jobs:
composer --no-interaction run-script drupal-phpunit-upgrade
composer config --no-plugins allow-plugins.phpstan/extension-installer true
# Revisit - check for latest release of dependent modules.
- name: Install GraphQL dependencies
run: |
composer --no-interaction --no-progress require \
webonyx/graphql-php:^14.8 \
drupal/typed_data:^1.0 \
drupal/redirect:^1.0
drupal/typed_data:^2.0 \
drupal/redirect:dev-1.x
- name: Run PHPUnit
run: |
Expand All @@ -109,8 +107,8 @@ jobs:
mglaman/phpstan-drupal:^1.1.2 \
phpstan/phpstan-deprecation-rules:^1.0.0 \
jangregor/phpstan-prophecy:^1.0.0 \
phpstan/phpstan-phpunit:^1.0.0 \
phpstan/extension-installer:^1.0
phpstan/phpstan-phpunit:^1.4 \
phpstan/extension-installer:^1.4
composer --no-interaction --no-progress --with-all-dependencies upgrade drupal/coder:8.3.24
- name: Run PHPStan
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"homepage": "http://drupal.org/project/graphql",
"license": "GPL-2.0+",
"require": {
"php": ">=7.3",
"drupal/typed_data": "^1.0 || ^2.0",
"php": ">=8.1",
"webonyx/graphql-php": "^14.8.0"
},
"minimum-stability": "dev"
Expand Down
2 changes: 1 addition & 1 deletion examples/graphql_composable/graphql_composable.info.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ package: GraphQL
dependencies:
- graphql:graphql
- node:node
core_version_requirement: ^10.1
core_version_requirement: ^10.2 || ^11
2 changes: 1 addition & 1 deletion examples/graphql_example/graphql_examples.info.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ package: GraphQL
dependencies:
- graphql:graphql
- node:node
core_version_requirement: ^10.1
core_version_requirement: ^10.2 || ^11
2 changes: 1 addition & 1 deletion graphql.info.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ type: module
description: 'Base module for integrating GraphQL with Drupal.'
package: GraphQL
configure: graphql.config_page
core_version_requirement: ^10.1
core_version_requirement: ^10.2 || ^11
dependencies:
- typed_data:typed_data
1 change: 1 addition & 0 deletions graphql.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ services:
- '@renderer'
- '@event_dispatcher'
- '@image.factory'
- '@file.validator'

plugin.manager.graphql.persisted_query:
class: Drupal\graphql\Plugin\PersistedQueryPluginManager
Expand Down
2 changes: 1 addition & 1 deletion src/EventSubscriber/ApqSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public function onBeforeOperation(OperationEvent $event): void {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
public static function getSubscribedEvents(): array {
return [
OperationEvent::GRAPHQL_OPERATION_BEFORE => 'onBeforeOperation',
];
Expand Down
2 changes: 1 addition & 1 deletion src/EventSubscriber/OperationSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public function onAfterOperation(OperationEvent $event): void {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
public static function getSubscribedEvents(): array {
return [
OperationEvent::GRAPHQL_OPERATION_BEFORE => 'onBeforeOperation',
OperationEvent::GRAPHQL_OPERATION_AFTER => 'onAfterOperation',
Expand Down
2 changes: 1 addition & 1 deletion src/EventSubscriber/SubrequestSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function onKernelRequestFinished(FinishRequestEvent $event): void {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
public static function getSubscribedEvents(): array {
return [
KernelEvents::REQUEST => 'onKernelRequest',
KernelEvents::FINISH_REQUEST => 'onKernelRequestFinished',
Expand Down
79 changes: 44 additions & 35 deletions src/GraphQL/Utility/FileUpload.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\ByteSizeMarkup;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Utility\Token;
use Drupal\file\FileInterface;
use Drupal\file\Validation\FileValidatorInterface;
use Drupal\graphql\GraphQL\Response\FileUploadResponse;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
Expand Down Expand Up @@ -111,6 +113,13 @@ class FileUpload {
*/
protected $imageFactory;

/**
* The file validator service.
*
* @var \Drupal\file\Validation\FileValidatorInterface
*/
protected FileValidatorInterface $fileValidator;

/**
* Constructor.
*/
Expand All @@ -126,6 +135,7 @@ public function __construct(
RendererInterface $renderer,
EventDispatcherInterface $eventDispatcher,
ImageFactory $image_factory,
FileValidatorInterface $file_validator,
) {
/** @var \Drupal\file\FileStorageInterface $file_storage */
$file_storage = $entityTypeManager->getStorage('file');
Expand All @@ -140,6 +150,7 @@ public function __construct(
$this->renderer = $renderer;
$this->eventDispatcher = $eventDispatcher;
$this->imageFactory = $image_factory;
$this->fileValidator = $file_validator;
}

/**
Expand Down Expand Up @@ -193,10 +204,7 @@ public function saveFileUpload(UploadedFile $uploaded_file, array $settings): Fi
switch ($uploaded_file->getError()) {
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
// @todo Drupal 10.1 compatibility, needs to be converted to
// ByteSizeMarkup later.
// @phpstan-ignore-next-line
$maxUploadSize = format_size($this->getMaxUploadSize($settings));
$maxUploadSize = ByteSizeMarkup::create($this->getMaxUploadSize($settings));
$response->addViolation($this->t('The file @file could not be saved because it exceeds @maxsize, the maximum allowed size for uploads.', [
'@file' => $uploaded_file->getClientOriginalName(),
'@maxsize' => $maxUploadSize,
Expand Down Expand Up @@ -248,8 +256,8 @@ public function saveFileUpload(UploadedFile $uploaded_file, array $settings): Fi

$temp_file_path = $uploaded_file->getRealPath();

// Drupal 10.2 compatibility: use the deprecated constant for now.
// @phpstan-ignore-next-line
// Drupal 10.3 compatibility: use the deprecated constant for now.
// @phpstan-ignore-next-line as it is deprecated in D12.
$file_uri = $this->fileSystem->getDestinationFilename($file_uri, FileSystemInterface::EXISTS_RENAME);

// Lock based on the prepared file URI.
Expand All @@ -272,11 +280,17 @@ public function saveFileUpload(UploadedFile $uploaded_file, array $settings): Fi
// before it is saved.
$file->setSize(@filesize($temp_file_path));

// Validate against file_validate() first with the temporary path.
// @todo Drupal 10.1 compatibility, needs to be converted to file validate
// service later.
// @phpstan-ignore-next-line
$errors = file_validate($file, $validators);
// Validate against fileValidator first with the temporary path.
/** @var \Symfony\Component\Validator\ConstraintViolationListInterface $file_validate_errors */
$file_validate_errors = $this->fileValidator->validate($file, $validators);
$errors = [];
if (count($file_validate_errors) > 0) {
foreach ($file_validate_errors as $violation) {
$errors[] = $violation->getMessage();
}
}

// Validate Image resolution.
$maxResolution = $settings['max_resolution'] ?? 0;
$minResolution = $settings['min_resolution'] ?? 0;
if (!empty($maxResolution) || !empty($minResolution)) {
Expand All @@ -287,14 +301,13 @@ public function saveFileUpload(UploadedFile $uploaded_file, array $settings): Fi
$response->addViolations($errors);
return $response;
}

$file->setFileUri($file_uri);
// Move the file to the correct location after validation. Use
// FileSystemInterface::EXISTS_ERROR as the file location has already been
// determined above in FileSystem::getDestinationFilename().
try {
// Drupal 10.2 compatibility: use the deprecated constant for now.
// @phpstan-ignore-next-line
// Drupal 10.3 compatibility: use the deprecated constant for now.
// @phpstan-ignore-next-line as it is deprecated in D12.
$this->fileSystem->move($temp_file_path, $file_uri, FileSystemInterface::EXISTS_ERROR);
}
catch (FileException $e) {
Expand All @@ -315,7 +328,6 @@ public function saveFileUpload(UploadedFile $uploaded_file, array $settings): Fi
}

$file->save();

$response->setFileEntity($file);
return $response;
}
Expand Down Expand Up @@ -487,12 +499,12 @@ protected function validateFileImageResolution(FileInterface $file, $maximum_dim
protected function prepareFilename(string $filename, array &$validators): string {
// Don't rename if 'allow_insecure_uploads' evaluates to TRUE.
if (!$this->systemFileConfig->get('allow_insecure_uploads')) {
if (!empty($validators['file_validate_extensions'][0])) {
// If there is a file_validate_extensions validator and a list of
// valid extensions, munge the filename to protect against possible
// malicious extension hiding within an unknown file type. For example,
// "filename.html.foo".
$event = new FileUploadSanitizeNameEvent($filename, $validators['file_validate_extensions'][0]);
if (!empty($validators['FileExtension']['extensions'])) {
// If there is a fileValidator service to validate FileExtension and
// a list of valid extensions, munge the filename to protect against
// possible malicious extension hiding within an unknown file type.
// For example, "filename.html.foo".
$event = new FileUploadSanitizeNameEvent($filename, $validators['FileExtension']['extensions']);
$this->eventDispatcher->dispatch($event);
$filename = $event->getFilename();
}
Expand All @@ -502,33 +514,30 @@ protected function prepareFilename(string $filename, array &$validators): string
// and filename._php.txt, respectively).
if (preg_match(FileSystemInterface::INSECURE_EXTENSION_REGEX, $filename)) {
// If the file will be rejected anyway due to a disallowed extension, it
// should not be renamed; rather, we'll let file_validate_extensions()
// reject it below.
// should not be renamed; rather, we'll let fileValidator service
// to validate FileExtension reject it below.
$passes_validation = FALSE;
if (!empty($validators['file_validate_extensions'][0])) {
if (!empty($validators['FileExtension']['extensions'])) {
/** @var \Drupal\file\FileInterface $file */
$file = $this->fileStorage->create([]);
$file->setFilename($filename);
// @todo Drupal 10.1 compatibility, needs to be converted to file
// validator service later.
// @phpstan-ignore-next-line
$passes_validation = empty(file_validate_extensions($file, $validators['file_validate_extensions'][0]));
$passes_validation = count($this->fileValidator->validate($file, $validators['FileExtension']['extensions']));
}
if (empty($validators['file_validate_extensions'][0]) || $passes_validation) {
if (empty($validators['FileExtension']['extensions']) || ($passes_validation > 0)) {
if ((substr($filename, -4) != '.txt')) {
// The destination filename will also later be used to create the
// URI.
$filename .= '.txt';
}

$event = new FileUploadSanitizeNameEvent($filename, $validators['file_validate_extensions'][0] ?? '');
$event = new FileUploadSanitizeNameEvent($filename, $validators['FileExtension']['extensions'] ?? '');
$this->eventDispatcher->dispatch($event);
$filename = $event->getFilename();

// The .txt extension may not be in the allowed list of extensions. We
// have to add it here or else the file upload will fail.
if (!empty($validators['file_validate_extensions'][0])) {
$validators['file_validate_extensions'][0] .= ' txt';
if (!empty($validators['FileExtension']['extensions'])) {
$validators['FileExtension']['extensions'] .= ' txt';
}
}
}
Expand Down Expand Up @@ -579,7 +588,7 @@ protected function getUploadLocation(array $settings): string {
protected function getUploadValidators(array $settings): array {
$validators = [
// Add in our check of the file name length.
'file_validate_name_length' => [],
'FileNameLength' => [],
];

// Cap the upload size according to the PHP limit.
Expand All @@ -589,11 +598,11 @@ protected function getUploadValidators(array $settings): array {
}

// There is always a file size limit due to the PHP server limit.
$validators['file_validate_size'] = [$max_filesize];
$validators['FileSizeLimit'] = ['fileLimit' => $max_filesize];

// Add the extension check if necessary.
if (!empty($settings['file_extensions'])) {
$validators['file_validate_extensions'] = [$settings['file_extensions']];
$validators['FileExtension'] = ['extensions' => $settings['file_extensions']];
}

return $validators;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ public function resolve(FileInterface $entity = NULL, $style, RefinableCacheable
$access = $entity->access('view', NULL, TRUE);
$metadata->addCacheableDependency($access);
if ($access->isAllowed() && $image_style = ImageStyle::load($style)) {

// @phpstan-ignore-next-line
$width = $entity->width;
// @phpstan-ignore-next-line
$height = $entity->height;

// @phpstan-ignore-next-line
if (empty($width) || empty($height)) {
if ($width == NULL || $height == NULL) {
/** @var \Drupal\Core\Image\ImageInterface $image */
$image = \Drupal::service('image.factory')->get($entity->getFileUri());
if ($image->isValid()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type: module
name: GraphQL File Validate Test
description: Tests hook_file_validate() on uploads.
description: Tests file validate on uploads.
package: Testing
hidden: TRUE
17 changes: 0 additions & 17 deletions tests/modules/graphql_file_validate/graphql_file_validate.module

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
services:
graphql_file_validate_test_subscriber:
class: Drupal\graphql_file_validate\EventSubscriber\GraphqlFileValidationTestSubscriber
tags:
- { name: event_subscriber }
Loading

0 comments on commit b677475

Please sign in to comment.