From 82c59f849986d71456b966890376bc798a0449b9 Mon Sep 17 00:00:00 2001
From: Lee Rowlands <lee.rowlands@previousnext.com.au>
Date: Mon, 13 Nov 2023 14:35:58 +1000
Subject: [PATCH] Issue #3389447 by kim.pepper, smustgrave, quietone: Provide a
 trait to create file upload validators from file field settings

---
 .../src/Plugin/Field/FieldType/FileItem.php   | 22 ++------
 .../rest/resource/FileUploadResource.php      | 48 ++---------------
 .../Validation/FileValidatorSettingsTrait.php | 51 +++++++++++++++++++
 .../TemporaryJsonapiFileFieldUploader.php     | 46 ++---------------
 4 files changed, 62 insertions(+), 105 deletions(-)
 create mode 100644 core/modules/file/src/Validation/FileValidatorSettingsTrait.php

diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
index d61822ceb04d..d9fe46ec600c 100644
--- a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
@@ -15,6 +15,7 @@
 use Drupal\Core\StringTranslation\ByteSizeMarkup;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
+use Drupal\file\Validation\FileValidatorSettingsTrait;
 
 /**
  * Plugin implementation of the 'file' field type.
@@ -35,6 +36,8 @@
  */
 class FileItem extends EntityReferenceItem {
 
+  use FileValidatorSettingsTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -327,24 +330,7 @@ protected static function doGetUploadLocation(array $settings, $data = []) {
    *   element's '#upload_validators' property.
    */
   public function getUploadValidators() {
-    $validators = [];
-    $settings = $this->getSettings();
-
-    // Cap the upload size according to the PHP limit.
-    $max_filesize = Bytes::toNumber(Environment::getUploadMaxSize());
-    if (!empty($settings['max_filesize'])) {
-      $max_filesize = min($max_filesize, Bytes::toNumber($settings['max_filesize']));
-    }
-
-    // There is always a file size limit due to the PHP server limit.
-    $validators['FileSizeLimit'] = ['fileLimit' => $max_filesize];
-
-    // Add the extension check if necessary.
-    if (!empty($settings['file_extensions'])) {
-      $validators['FileExtension'] = ['extensions' => $settings['file_extensions']];
-    }
-
-    return $validators;
+    return $this->getFileUploadValidators($this->getSettings());
   }
 
   /**
diff --git a/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php
index 8abc3cc29f13..607f258c509f 100644
--- a/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php
+++ b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php
@@ -3,13 +3,10 @@
 namespace Drupal\file\Plugin\rest\resource;
 
 use Drupal\Component\Render\PlainTextOutput;
-use Drupal\Component\Utility\Bytes;
 use Drupal\Component\Utility\Crypt;
-use Drupal\Component\Utility\Environment;
 use Drupal\Core\Config\Config;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\File\Event\FileUploadSanitizeNameEvent;
 use Drupal\Core\File\Exception\FileException;
 use Drupal\Core\File\FileSystemInterface;
@@ -20,6 +17,7 @@
 use Drupal\file\Upload\ContentDispositionFilenameParser;
 use Drupal\file\Upload\InputStreamFileWriterInterface;
 use Drupal\file\Validation\FileValidatorInterface;
+use Drupal\file\Validation\FileValidatorSettingsTrait;
 use Drupal\rest\ModifiedResourceResponse;
 use Drupal\rest\Plugin\ResourceBase;
 use Drupal\rest\Plugin\rest\resource\EntityResourceValidationTrait;
@@ -59,6 +57,7 @@
  */
 class FileUploadResource extends ResourceBase {
 
+  use FileValidatorSettingsTrait;
   use EntityResourceValidationTrait {
     validate as resourceValidate;
   }
@@ -282,7 +281,7 @@ public function post(Request $request, $entity_type_id, $bundle, $field_name) {
       throw new HttpException(500, 'Destination file path is not writable');
     }
 
-    $validators = $this->getUploadValidators($field_definition);
+    $validators = $this->getFileUploadValidators($field_definition->getSettings());
 
     $prepared_filename = $this->prepareFilename($filename, $validators);
 
@@ -488,47 +487,6 @@ protected function getUploadLocation(array $settings) {
     return $settings['uri_scheme'] . '://' . $destination;
   }
 
-  /**
-   * Retrieves the upload validators for a field definition.
-   *
-   * 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 \Drupal\Core\Field\FieldDefinitionInterface $field_definition
-   *   The field definition 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.
-   */
-  protected function getUploadValidators(FieldDefinitionInterface $field_definition) {
-    $validators = [
-      // Add in our check of the file name length.
-      'FileNameLength' => [],
-    ];
-    $settings = $field_definition->getSettings();
-
-    // Cap the upload size according to the PHP limit.
-    $max_filesize = Bytes::toNumber(Environment::getUploadMaxSize());
-    if (!empty($settings['max_filesize'])) {
-      $max_filesize = min($max_filesize, Bytes::toNumber($settings['max_filesize']));
-    }
-
-    // There is always a file size limit due to the PHP server limit.
-    $validators['FileSizeLimit'] = [
-      'fileLimit' => $max_filesize,
-    ];
-
-    // Add the extension check if necessary.
-    if (!empty($settings['file_extensions'])) {
-      $validators['FileExtension'] = [
-        'extensions' => $settings['file_extensions'],
-      ];
-    }
-
-    return $validators;
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/file/src/Validation/FileValidatorSettingsTrait.php b/core/modules/file/src/Validation/FileValidatorSettingsTrait.php
new file mode 100644
index 000000000000..26e36498b37c
--- /dev/null
+++ b/core/modules/file/src/Validation/FileValidatorSettingsTrait.php
@@ -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;
+  }
+
+}
diff --git a/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php b/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php
index b897ed5dfb2b..0e469ff18f44 100644
--- a/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php
+++ b/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php
@@ -3,9 +3,7 @@
 namespace Drupal\jsonapi\Controller;
 
 use Drupal\Component\Render\PlainTextOutput;
-use Drupal\Component\Utility\Bytes;
 use Drupal\Component\Utility\Crypt;
-use Drupal\Component\Utility\Environment;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
@@ -22,6 +20,7 @@
 use Drupal\file\Upload\ContentDispositionFilenameParser;
 use Drupal\file\Upload\InputStreamFileWriterInterface;
 use Drupal\file\Validation\FileValidatorInterface;
+use Drupal\file\Validation\FileValidatorSettingsTrait;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\HttpFoundation\File\Exception\CannotWriteFileException;
 use Symfony\Component\HttpFoundation\File\Exception\NoFileException;
@@ -45,6 +44,8 @@
  */
 class TemporaryJsonapiFileFieldUploader {
 
+  use FileValidatorSettingsTrait;
+
   /**
    * The regex used to extract the filename from the content disposition header.
    *
@@ -200,7 +201,7 @@ public function handleFileUploadForField(FieldDefinitionInterface $field_definit
       throw new HttpException(500, 'Destination file path is not writable');
     }
 
-    $validators = $this->getUploadValidators($field_definition);
+    $validators = $this->getFileUploadValidators($field_definition->getSettings());
 
     $prepared_filename = $this->prepareFilename($filename, $validators);
 
@@ -423,45 +424,6 @@ protected function getUploadLocation(array $settings) {
     return $settings['uri_scheme'] . '://' . $destination;
   }
 
-  /**
-   * Retrieves the upload validators for a field definition.
-   *
-   * 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 \Drupal\Core\Field\FieldDefinitionInterface $field_definition
-   *   The field definition 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.
-   */
-  protected function getUploadValidators(FieldDefinitionInterface $field_definition) {
-    $validators = [
-      // Add in our check of the file name length.
-      'FileNameLength' => [],
-    ];
-    $settings = $field_definition->getSettings();
-
-    // Cap the upload size according to the PHP limit.
-    $max_filesize = Bytes::toNumber(Environment::getUploadMaxSize());
-    if (!empty($settings['max_filesize'])) {
-      $max_filesize = min($max_filesize, Bytes::toNumber($settings['max_filesize']));
-    }
-
-    // There is always a file size limit due to the PHP server limit.
-    $validators['FileSizeLimit'] = ['fileLimit' => $max_filesize];
-
-    // Add the extension check if necessary.
-    if (!empty($settings['file_extensions'])) {
-      $validators['FileExtension'] = [
-        'extensions' => $settings['file_extensions'],
-      ];
-    }
-
-    return $validators;
-  }
-
   /**
    * Generates a lock ID based on the file URI.
    *
-- 
GitLab