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