From 706359a37799addfb2ed827bd581cf15af8a5bbf Mon Sep 17 00:00:00 2001
From: Bernd Oliver Suenderhauf <bos@suenderhauf.de>
Date: Mon, 25 Mar 2019 11:42:21 +0100
Subject: [PATCH] Issue #3039482 by Pancho: Use managed_file element for PDF
 template uploads

---
 src/Entity/FillPdfForm.php                  |  1 +
 src/Form/FillPdfFormForm.php                | 82 ++++++++++-----------
 src/Form/FillPdfFormUploadTrait.php         | 65 ----------------
 src/Form/FillPdfOverviewForm.php            | 80 ++++++++++----------
 src/Form/FillPdfSettingsForm.php            |  5 ++
 tests/src/Functional/TemplateUploadTest.php |  8 +-
 tests/src/Traits/TestFillPdfTrait.php       |  2 +-
 7 files changed, 94 insertions(+), 149 deletions(-)
 delete mode 100644 src/Form/FillPdfFormUploadTrait.php

diff --git a/src/Entity/FillPdfForm.php b/src/Entity/FillPdfForm.php
index de33f44..8db0819 100644
--- a/src/Entity/FillPdfForm.php
+++ b/src/Entity/FillPdfForm.php
@@ -64,6 +64,7 @@ class FillPdfForm extends ContentEntityBase implements FillPdfFormInterface {
 
     $fields['file'] = BaseFieldDefinition::create('file')
       ->setLabel(t('The associated managed file.'))
+      ->setSetting('file_extensions', 'pdf')
       ->setDescription(t('The associated managed file.'));
 
     $overview_url = Url::fromUri('base://admin/structure/fillpdf')->toString();
diff --git a/src/Form/FillPdfFormForm.php b/src/Form/FillPdfFormForm.php
index 611b84b..abeaee5 100644
--- a/src/Form/FillPdfFormForm.php
+++ b/src/Form/FillPdfFormForm.php
@@ -21,8 +21,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  */
 class FillPdfFormForm extends ContentEntityForm {
 
-  use FillPdfFormUploadTrait;
-
   /**
    * The FillPdf admin form helper.
    *
@@ -169,31 +167,45 @@ class FillPdfFormForm extends ContentEntityForm {
         '#title' => $this->t('Uploaded PDF'),
         '#description' => $file_entity->getFileUri(),
         '#weight' => $pdf_info_weight++,
-      ],
-      'upload_pdf' => [
-        '#type' => 'file',
+      ]
+    ];
+
+    $upload_location = FillPdf::buildFileUri($this->config('fillpdf.settings')->get('scheme'), 'fillpdf');
+    if (!file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY + FILE_MODIFY_PERMISSIONS)) {
+      $this->messenger()->addError($this->t('The %directory subdirectory does not exist or is not writable. Please check permissions.', [
+        '%directory' => 'fillpdf',
+      ]));
+    }
+    else {
+      $form['pdf_info']['upload_pdf'] = [
+        '#type' => 'managed_file',
         '#title' => $this->t('Update PDF template'),
-        '#attributes' => ['accept' => 'application/pdf'],
+        '#accept' => 'application/pdf',
+        '#upload_validators' => [
+          'file_validate_extensions' => ['pdf'],
+        ],
+        '#upload_location' => $upload_location,
         '#description' => $this->t('Update the PDF file used as template by this form.'),
         '#weight' => $pdf_info_weight++,
-      ],
-      'sample_populate' => [
-        '#type' => 'item',
-        '#title' => $this->t('Sample PDF'),
-        '#description' => $this->l($this->t('See which fields are which in this PDF.'),
-            $this->linkManipulator->generateLink([
-              'fid' => $fid,
-              'sample' => TRUE,
-            ])) . '<br />' .
-          $this->t('If you have set a custom path on this PDF, the sample will be saved there silently.'),
-        '#weight' => $pdf_info_weight++,
-      ],
-      'form_id' => [
-        '#type' => 'item',
-        '#title' => $this->t('Form info'),
-        '#description' => $this->t("Form ID: [@fid].  Populate this form with entity IDs, such as /fillpdf?fid=$fid&entity_type=node&entity_id=10<br/>", ['@fid' => $fid]),
-        '#weight' => $pdf_info_weight,
-      ],
+      ];
+    }
+
+    $form['pdf_info']['sample_populate'] = [
+      '#type' => 'item',
+      '#title' => $this->t('Sample PDF'),
+      '#description' => $this->l($this->t('See which fields are which in this PDF.'),
+          $this->linkManipulator->generateLink([
+            'fid' => $fid,
+            'sample' => TRUE,
+          ])) . '<br />' .
+        $this->t('If you have set a custom path on this PDF, the sample will be saved there silently.'),
+      '#weight' => $pdf_info_weight++,
+    ];
+    $form['pdf_info']['form_id'] = [
+      '#type' => 'item',
+      '#title' => $this->t('Form info'),
+      '#description' => $this->t("Form ID: [@fid].  Populate this form with entity IDs, such as /fillpdf?fid=$fid&entity_type=node&entity_id=10<br/>", ['@fid' => $fid]),
+      '#weight' => $pdf_info_weight,
     ];
 
     if (!empty($entity->get('default_entity_id')->first()->value)) {
@@ -253,18 +265,6 @@ class FillPdfFormForm extends ContentEntityForm {
     return $form;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    parent::validateForm($form, $form_state);
-
-    $files = $this->getRequest()->files->get('files');
-    if (isset($files['upload_pdf'])) {
-      $this->validatePdfUpload($form, $form_state, $files['upload_pdf']);
-    }
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -272,13 +272,13 @@ class FillPdfFormForm extends ContentEntityForm {
     /** @var \Drupal\fillpdf\FillPdfFormInterface $entity */
     $entity = $this->getEntity();
 
-    /** @var \Drupal\file\FileInterface $file */
-    $file = $form_state->getValue('upload_pdf');
-
     $message = [];
     $message[] = $this->t('FillPDF Form %link has been updated.', ['%link' => $entity->toLink()->toString()]);
 
-    if ($file) {
+    if ($form_state->getValue('upload_pdf')) {
+      /** @var \Drupal\file\FileInterface $new_file */
+      $new_file = File::load($form_state->getValue('upload_pdf')['0']);
+
       $existing_fields = $this->entityHelper->getFormFields($entity);
 
       // Delete existing fields.
@@ -287,7 +287,7 @@ class FillPdfFormForm extends ContentEntityForm {
         $existing_field->delete();
       }
 
-      $added = $this->inputHelper->attachPdfToForm($file, $entity);
+      $added = $this->inputHelper->attachPdfToForm($new_file, $entity);
 
       $form_fields = $added['fields'];
 
diff --git a/src/Form/FillPdfFormUploadTrait.php b/src/Form/FillPdfFormUploadTrait.php
deleted file mode 100644
index b5d03fb..0000000
--- a/src/Form/FillPdfFormUploadTrait.php
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-
-namespace Drupal\fillpdf\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\file\Entity\File;
-use Drupal\fillpdf\Component\Utility\FillPdf;
-use Symfony\Component\HttpFoundation\File\UploadedFile;
-
-/**
- * Class FillPdfFormUploadTrait
- * @package Drupal\fillpdf\Form
- */
-trait FillPdfFormUploadTrait {
-
-  protected function validatePdfUpload(array &$form, FormStateInterface &$form_state, UploadedFile $file_upload) {
-    /**
-     * @var $file_upload \Symfony\Component\HttpFoundation\File\UploadedFile
-     */
-    if ($file_upload && $file_upload->isValid()) {
-      // Move it to somewhere we know.
-      $uploaded_filename = $file_upload->getClientOriginalName();
-
-      // Ensure the destination is unique; we deliberately use managed files,
-      // but they are keyed on file URI, so we can't save the same one twice.
-      $scheme = $this->config('fillpdf.settings')->get('scheme');
-      $destination = file_destination(FillPdf::buildFileUri($scheme, 'fillpdf/' . $uploaded_filename), FILE_EXISTS_RENAME);
-
-      // Ensure our directory exists.
-      $fillpdf_directory = FillPdf::buildFileUri($scheme, 'fillpdf');
-      $directory_exists = file_prepare_directory($fillpdf_directory, FILE_CREATE_DIRECTORY + FILE_MODIFY_PERMISSIONS);
-
-      if ($directory_exists) {
-        $file_moved = $this->fileSystem->moveUploadedFile($file_upload->getRealPath(), $destination);
-
-        if ($file_moved) {
-          // Create a File object from the uploaded file.
-          $new_file = File::create([
-            'uri' => $destination,
-            'uid' => $this->currentUser()->id(),
-          ]);
-
-          $errors = file_validate_extensions($new_file, 'pdf');
-
-          if (count($errors)) {
-            $form_state->setErrorByName('upload_pdf', $this->t('Only PDF files are supported, and they must end in .pdf.'));
-          }
-          else {
-            $form_state->setValue('upload_pdf', $new_file);
-          }
-        }
-        else {
-          $form_state->setErrorByName('upload_pdf', $this->t("Could not move your uploaded file from PHP's temporary location to Drupal file storage."));
-        }
-      }
-      else {
-        $form_state->setErrorByName('upload_pdf', $this->t('Could not automatically create the <em>fillpdf</em> subdirectory. Please create this manually before uploading your PDF form.'));
-      }
-    }
-    else {
-      $form_state->setErrorByName('upload_pdf', $this->t('Your PDF could not be uploaded. Did you select one?'));
-    }
-  }
-
-}
diff --git a/src/Form/FillPdfOverviewForm.php b/src/Form/FillPdfOverviewForm.php
index f9aeaa6..d5b44e3 100644
--- a/src/Form/FillPdfOverviewForm.php
+++ b/src/Form/FillPdfOverviewForm.php
@@ -7,16 +7,15 @@ use Drupal\Core\Entity\Query\QueryFactory;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\File\FileSystemInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Url;
+use Drupal\file\Entity\File;
 use Drupal\fillpdf\FillPdfBackendManager;
 use Drupal\fillpdf\InputHelperInterface;
+use Drupal\fillpdf\Component\Utility\FillPdf;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\File\UploadedFile;
 
 class FillPdfOverviewForm extends FillPdfAdminFormBase {
-  use FillPdfFormUploadTrait;
 
   /**
    * The backend manager (finds the filling plugin the user selected).
@@ -84,45 +83,49 @@ class FillPdfOverviewForm extends FillPdfAdminFormBase {
 
     $config = $this->config('fillpdf.settings');
     // Only show PDF upload form if fillpdf is configured.
-    if ($config->get('backend')) {
-      // If using FillPDF Service, ensure XML-RPC module is present.
-      if ($config->get('backend') !== 'fillpdf_service' || $this->moduleHandler->moduleExists('xmlrpc')) {
-        $form['upload_pdf'] = [
-          '#type' => 'file',
-          '#title' => $this->t('Upload PDF template'),
-          '#attributes' => ['accept' => 'application/pdf'],
-          '#description' => $this->t('Upload a fillable PDF file to create a new form.'),
-        ];
-
-        $form['submit'] = [
-          '#type' => 'submit',
-          '#value' => $this->t('Upload'),
-          '#weight' => 15,
-        ];
-      }
-      else {
-        drupal_set_message($this->t('You must install the <a href=":xmlrpc">contributed XML-RPC module</a> in order to use FillPDF Service as your PDF-filling method.', [
-          '@xmlrpc' => Url::fromUri('https://drupal.org/project/xmlrpc')
-            ->toString(),
-        ]), 'error');
-      }
-    }
-    else {
+    if (!$config->get('backend')) {
       $form['message'] = [
         '#markup' => '<p>' . $this->t('Before you can upload PDF files, you must @link.', ['@link' => new FormattableMarkup($this->l($this->t('configure FillPDF'), Url::fromRoute('fillpdf.settings')), [])]) . '</p>',
       ];
-      drupal_set_message($this->t('FillPDF is not configured.'), 'error');
+      $this->messenger()->addError($this->t('FillPDF is not configured.'));
+      return $form;
     }
-    return $form;
-  }
 
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    $files = $this->getRequest()->files->get('files');
+    // If using FillPDF Service, ensure XML-RPC module is present.
+    if ($config->get('backend') === 'fillpdf_service' && !$this->moduleHandler->moduleExists('xmlrpc')) {
+      $this->messenger()->addError($this->t('You must install the <a href=":xmlrpc">contributed XML-RPC module</a> in order to use FillPDF Service as your PDF-filling method.', [
+        '@xmlrpc' => Url::fromUri('https://drupal.org/project/xmlrpc')->toString(),
+      ]));
+      return $form;
+    }
 
-    $file_upload = !empty($files) && array_key_exists('upload_pdf', $files) ? $files['upload_pdf'] : NULL;
-    if ($file_upload) {
-      $this->validatePdfUpload($form, $form_state, $file_upload);
+    $upload_location = FillPdf::buildFileUri($this->config('fillpdf.settings')->get('scheme'), 'fillpdf');
+    if (!file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY + FILE_MODIFY_PERMISSIONS)) {
+      $this->messenger()->addError($this->t('The %directory subdirectory does not exist or is not writable. Please check permissions.', [
+        '%directory' => 'fillpdf',
+      ]));
     }
+    else {
+      $form['upload_pdf'] = [
+        '#type' => 'managed_file',
+        '#title' => $this->t('Upload PDF template'),
+        '#accept' => 'application/pdf',
+        '#upload_validators' => [
+          'file_validate_extensions' => ['pdf'],
+        ],
+        '#upload_location' => $upload_location,
+        '#description' => $this->t('Upload a fillable PDF file to create a new form.'),
+      ];
+
+      $form['submit'] = [
+        '#type' => 'submit',
+        '#value' => $this->t('Create'),
+        '#weight' => 15,
+      ];
+    }
+
+    return $form;
+
   }
 
   /**
@@ -134,11 +137,12 @@ class FillPdfOverviewForm extends FillPdfAdminFormBase {
    *   The current state of the form.
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    /** @var \Drupal\file\FileInterface $file */
-    $file = $form_state->getValue('upload_pdf');
-    if ($file) {
+    if ($form_state->getValue('upload_pdf')) {
+      /** @var \Drupal\file\FileInterface $file */
+      $file = File::load($form_state->getValue('upload_pdf')['0']);
       $added = $this->inputHelper->attachPdfToForm($file);
 
+      /** @var \Drupal\fillpdf\Entity\FillPdfForm $fillpdf_form */
       $fillpdf_form = $added['form'];
       $fid = $fillpdf_form->id();
 
diff --git a/src/Form/FillPdfSettingsForm.php b/src/Form/FillPdfSettingsForm.php
index 2d8044f..02f0f0b 100644
--- a/src/Form/FillPdfSettingsForm.php
+++ b/src/Form/FillPdfSettingsForm.php
@@ -206,6 +206,11 @@ class FillPdfSettingsForm extends ConfigFormBase {
         }
         break;
     }
+
+    $directory = FillPdf::buildFileUri($form_state->getValue('scheme'), 'fillpdf');
+    if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY + FILE_MODIFY_PERMISSIONS)) {
+      $form_state->setErrorByName('upload_pdf', $this->t('Could not automatically create the <em>fillpdf</em> subdirectory. Please create this manually before uploading your PDF form.'));
+    }
   }
 
   /**
diff --git a/tests/src/Functional/TemplateUploadTest.php b/tests/src/Functional/TemplateUploadTest.php
index b3ece30..e6695c5 100644
--- a/tests/src/Functional/TemplateUploadTest.php
+++ b/tests/src/Functional/TemplateUploadTest.php
@@ -39,7 +39,7 @@ class TemplateUploadTest extends BrowserTestBase {
     $this->drupalGet('admin/structure/fillpdf');
 
     // Check if the 'accept' attribute is correctly set.
-    $this->assertSession()->elementAttributeContains('css', '#edit-upload-pdf', 'accept', 'application/pdf');
+    $this->assertSession()->elementAttributeContains('css', 'input#edit-upload-pdf-upload', 'accept', 'application/pdf');
 
     // Without a PDF file being supplied, no FillPdfForm should be created.
     $this->uploadTestPdf(NULL);
@@ -51,7 +51,7 @@ class TemplateUploadTest extends BrowserTestBase {
     $this->drupalPostForm(NULL, ['files[upload_pdf]' => $file->uri], 'Upload');
     $this->assertSession()->statusCodeEquals(200);
     $this->assertSession()->pageTextNotContains('New FillPDF form has been created.');
-    $this->assertSession()->pageTextContains('Only PDF files are supported, and they must end in .pdf.');
+    $this->assertSession()->pageTextContains('Only files with the following extensions are allowed: pdf.');
 
     // With a PDF file being supplied, a new FillPdfForm should be created.
     $this->uploadTestPdf('fillpdf_test_v3.pdf');
@@ -71,14 +71,14 @@ class TemplateUploadTest extends BrowserTestBase {
     $this->assertSession()->statusCodeEquals(200);
 
     // Check if the 'accept' attribute is correctly set.
-    $this->assertSession()->elementAttributeContains('css', '#edit-upload-pdf', 'accept', 'application/pdf');
+    $this->assertSession()->elementAttributeContains('css', 'input#edit-upload-pdf-upload', 'accept', 'application/pdf');
 
     // When trying to upload a .txt file, validation should set an error.
     $files = $this->getTestFiles('text');
     $file = reset($files);
     $this->drupalPostForm(NULL, ['files[upload_pdf]' => $file->uri], 'Save');
     $this->assertSession()->statusCodeEquals(200);
-    $this->assertSession()->pageTextContains('Only PDF files are supported, and they must end in .pdf.');
+    $this->assertSession()->pageTextContains('Only files with the following extensions are allowed: pdf.');
   }
 
   /**
diff --git a/tests/src/Traits/TestFillPdfTrait.php b/tests/src/Traits/TestFillPdfTrait.php
index 4fa2e60..c2356e7 100644
--- a/tests/src/Traits/TestFillPdfTrait.php
+++ b/tests/src/Traits/TestFillPdfTrait.php
@@ -83,7 +83,7 @@ trait TestFillPdfTrait {
     $edit = [
       'files[upload_pdf]' => isset($path) ? $path : NULL,
     ];
-    $this->drupalPostForm('admin/structure/fillpdf', $edit, 'Upload');
+    $this->drupalPostForm('admin/structure/fillpdf', $edit, 'Create');
     $this->assertSession()->statusCodeEquals(200);
   }
 
-- 
GitLab