From 0443439e9154feaef6f32e2078845684858190da Mon Sep 17 00:00:00 2001 From: Kevin Kaland <kevin@wizonesolutions.com> Date: Wed, 18 Nov 2015 21:54:52 +0100 Subject: [PATCH] Issue #2359213: Port import/export form. --- fillpdf.info.yml | 1 + fillpdf.install | 7 ++ fillpdf.routing.yml | 20 ++++ fillpdf.services.yml | 3 + src/Controller/HandlePdfController.php | 18 ++- src/Entity/FillPdfForm.php | 2 + src/EntityHelper.php | 40 +++++++ src/EntityHelperInterface.php | 18 +++ src/Form/FillPdfFormExportForm.php | 68 +++++++++++ src/Form/FillPdfFormForm.php | 6 +- src/Form/FillPdfFormImportForm.php | 160 +++++++++++++++++++++++++ 11 files changed, 331 insertions(+), 12 deletions(-) create mode 100644 src/EntityHelper.php create mode 100644 src/EntityHelperInterface.php create mode 100644 src/Form/FillPdfFormExportForm.php create mode 100644 src/Form/FillPdfFormImportForm.php diff --git a/fillpdf.info.yml b/fillpdf.info.yml index 0f7a11e..e6db379 100644 --- a/fillpdf.info.yml +++ b/fillpdf.info.yml @@ -9,3 +9,4 @@ dependencies: - link - token - views + - serialization diff --git a/fillpdf.install b/fillpdf.install index 845c28c..ae8508c 100644 --- a/fillpdf.install +++ b/fillpdf.install @@ -101,3 +101,10 @@ function fillpdf_update_8104() { ->setDescription(FillPdfAdminFormHelper::getReplacementsDescription()); $edum->installFieldStorageDefinition('replacements', 'fillpdf_form', 'fillpdf_form', $replacements); } + +/** + * Enable Serialization module. + */ +function fillpdf_update_8105() { + \Drupal::getContainer()->get('module_installer')->install(['serialization']); +} diff --git a/fillpdf.routing.yml b/fillpdf.routing.yml index 53cbfa6..98d4587 100644 --- a/fillpdf.routing.yml +++ b/fillpdf.routing.yml @@ -46,6 +46,26 @@ entity.fillpdf_form.delete_form: options: _admin_route: TRUE +entity.fillpdf_form.export_form: + path: '/admin/structure/fillpdf/{fillpdf_form}/export' + defaults: + _entity_form: fillpdf_form.export + _title: 'Export FillPDF form configuration and field mappings' + requirements: + _entity_access: fillpdf_form.view + options: + _admin_route: TRUE + +entity.fillpdf_form.import_form: + path: '/admin/structure/fillpdf/{fillpdf_form}/import' + defaults: + _entity_form: fillpdf_form.import + _title: 'Import FillPDF form configuration and field mappings' + requirements: + _entity_access: fillpdf_form.view + options: + _admin_route: TRUE + entity.fillpdf_form_field.edit_form: path: '/admin/structure/fillpdf/{fillpdf_form}/{fillpdf_form_field}' defaults: diff --git a/fillpdf.services.yml b/fillpdf.services.yml index 6961fbd..5fadcfe 100644 --- a/fillpdf.services.yml +++ b/fillpdf.services.yml @@ -37,3 +37,6 @@ services: class: Drupal\fillpdf\TokenResolver arguments: ["@token"] + fillpdf.entity_helper: + class: Drupal\fillpdf\EntityHelper + arguments: ["@entity.query"] diff --git a/src/Controller/HandlePdfController.php b/src/Controller/HandlePdfController.php index 37cb22b..10f2e12 100644 --- a/src/Controller/HandlePdfController.php +++ b/src/Controller/HandlePdfController.php @@ -8,11 +8,9 @@ namespace Drupal\fillpdf\Controller; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Entity\Query\QueryFactory; -use Drupal\Core\Utility\Token; use Drupal\fillpdf\Component\Helper\FillPdfMappingHelper; use Drupal\fillpdf\Entity\FillPdfForm; -use Drupal\fillpdf\Entity\FillPdfFormField; +use Drupal\fillpdf\EntityHelper; use Drupal\fillpdf\FillPdfBackendManager; use Drupal\fillpdf\FillPdfBackendPluginInterface; use Drupal\fillpdf\FillPdfContextManagerInterface; @@ -37,8 +35,8 @@ class HandlePdfController extends ControllerBase { /** @var FillPdfBackendManager $backendManager */ protected $backendManager; - /** @var QueryFactory $entityQuery */ - protected $entityQuery; + /** @var EntityHelper */ + protected $entityHelper; /** @var FillPdfContextManagerInterface $contextManager */ protected $contextManager; @@ -46,14 +44,14 @@ class HandlePdfController extends ControllerBase { /** @var TokenResolverInterface */ protected $tokenResolver; - public function __construct(FillPdfLinkManipulatorInterface $link_manipulator, FillPdfContextManagerInterface $context_manager, TokenResolverInterface $token_resolver, RequestStack $request_stack, FillPdfBackendManager $backend_manager, FillPdfActionPluginManager $action_manager, QueryFactory $entity_query) { + public function __construct(FillPdfLinkManipulatorInterface $link_manipulator, FillPdfContextManagerInterface $context_manager, EntityHelper $entity_helper, TokenResolverInterface $token_resolver, RequestStack $request_stack, FillPdfBackendManager $backend_manager, FillPdfActionPluginManager $action_manager) { $this->linkManipulator = $link_manipulator; $this->contextManager = $context_manager; $this->tokenResolver = $token_resolver; $this->requestStack = $request_stack; $this->backendManager = $backend_manager; $this->actionManager = $action_manager; - $this->entityQuery = $entity_query; + $this->entityHelper = $entity_helper; } /** @@ -63,6 +61,7 @@ class HandlePdfController extends ControllerBase { return new static( $container->get('fillpdf.link_manipulator'), $container->get('fillpdf.context_manager'), + $container->get('fillpdf.entity_helper'), $container->get('fillpdf.token_resolver'), $container->get('request_stack'), $container->get('plugin.manager.fillpdf_backend'), @@ -90,10 +89,7 @@ class HandlePdfController extends ControllerBase { return new RedirectResponse('/'); } - $fields = FillPdfFormField::loadMultiple( - $this->entityQuery->get('fillpdf_form_field') - ->condition('fillpdf_form', $fillpdf_form->id()) - ->execute()); + $fields = $this->entityHelper->getFormFields($fillpdf_form); // Populate entities array based on what user passed in $entities = $this->contextManager->loadEntities($context); diff --git a/src/Entity/FillPdfForm.php b/src/Entity/FillPdfForm.php index b7760e3..51637e3 100644 --- a/src/Entity/FillPdfForm.php +++ b/src/Entity/FillPdfForm.php @@ -26,6 +26,8 @@ use Drupal\fillpdf\Service\FillPdfAdminFormHelper; * "form" = { * "edit" = "Drupal\fillpdf\Form\FillPdfFormForm", * "delete" = "Drupal\fillpdf\Form\FillPdfFormDeleteForm", + * "export" = "Drupal\fillpdf\Form\FillPdfFormExportForm", + * "import" = "Drupal\fillpdf\Form\FillPdfFormImportForm", * }, * "access" = "Drupal\fillpdf\FillPdfFormAccessControlHandler", * }, diff --git a/src/EntityHelper.php b/src/EntityHelper.php new file mode 100644 index 0000000..fade90b --- /dev/null +++ b/src/EntityHelper.php @@ -0,0 +1,40 @@ +<?php + +/** + * @file + * Contains \Drupal\fillpdf\EntityHelper. + */ + +namespace Drupal\fillpdf; + +use Drupal\Core\Entity\Query\QueryFactory; +use Drupal\fillpdf\Entity\FillPdfFormField; + +/** + * Class EntityHelper. + * + * @package Drupal\fillpdf + */ +class EntityHelper implements EntityHelperInterface { + + /** + * Drupal\Core\Entity\Query\QueryFactory definition. + * + * @var \Drupal\Core\Entity\Query\QueryFactory + */ + protected $entityQuery; + /** + * Constructor. + */ + public function __construct(QueryFactory $entity_query) { + $this->entityQuery = $entity_query; + } + + public function getFormFields(FillPdfFormInterface $fillpdf_form) { + return FillPdfFormField::loadMultiple( + $this->entityQuery->get('fillpdf_form_field') + ->condition('fillpdf_form', $fillpdf_form->id()) + ->execute()); + } + +} diff --git a/src/EntityHelperInterface.php b/src/EntityHelperInterface.php new file mode 100644 index 0000000..2f465b6 --- /dev/null +++ b/src/EntityHelperInterface.php @@ -0,0 +1,18 @@ +<?php + +/** + * @file + * Contains \Drupal\fillpdf\EntityHelperInterface. + */ + +namespace Drupal\fillpdf; + +/** + * Interface EntityHelperInterface. + * + * @package Drupal\fillpdf + */ +interface EntityHelperInterface { + + +} diff --git a/src/Form/FillPdfFormExportForm.php b/src/Form/FillPdfFormExportForm.php new file mode 100644 index 0000000..1379aca --- /dev/null +++ b/src/Form/FillPdfFormExportForm.php @@ -0,0 +1,68 @@ +<?php +/** + * @file + * Contains \Drupal\fillpdf\Form\FillPdfFormExportForm. + */ + +namespace Drupal\fillpdf\Form; + +use Drupal\Core\Entity\EntityForm; +use Drupal\Core\Form\FormStateInterface; +use Drupal\fillpdf\EntityHelper; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Serializer\Serializer; + +class FillPdfFormExportForm extends EntityForm { + + /** @var \Symfony\Component\Serializer\Serializer */ + protected $serializer; + + /** @var \Drupal\fillpdf\EntityHelper */ + protected $entityHelper; + + public function __construct(Serializer $serializer, EntityHelper $entity_helper) { + $this->serializer = $serializer; + $this->entityHelper = $entity_helper; + } + + public static function create(ContainerInterface $container) { + return new static($container->get('serializer'), $container->get('fillpdf.entity_helper')); + } + + public function form(array $form, FormStateInterface $form_state) { + parent::form($form, $form_state); + + $entity = $this->getEntity(); + + $fields = $this->entityHelper->getFormFields($entity); + + $form_config = array( + 'form' => $this->serializer->normalize($entity), + 'fields' => $this->serializer->normalize($fields), + ); + + $code = $this->serializer->serialize($form_config, 'json'); + + $form = array(); + $form['export'] = array( + '#type' => 'textarea', + '#title' => t('FillPDF form configuration and mappings'), + '#default_value' => $code, + '#rows' => 30, + '#description' => t('Copy this code and then on the site you want to import to, go to the Edit page for the FillPDF form for which you want to import these mappings, and paste it in there.'), + '#attributes' => array( + 'style' => 'width: 97%;', + ), + ); + + return $form; + } + + public function buildForm(array $form, FormStateInterface $form_state) { + $form = parent::buildForm($form, $form_state); + unset($form['actions']); + $form['#after_build'] = []; + return $form; + } + +} diff --git a/src/Form/FillPdfFormForm.php b/src/Form/FillPdfFormForm.php index 8967cd1..2031f42 100644 --- a/src/Form/FillPdfFormForm.php +++ b/src/Form/FillPdfFormForm.php @@ -6,6 +6,7 @@ namespace Drupal\fillpdf\Form; use Drupal\Core\Entity\ContentEntityForm; +use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\file\Entity\File; use Drupal\file\FileInterface; @@ -20,14 +21,17 @@ class FillPdfFormForm extends ContentEntityForm { protected $adminFormHelper; protected $linkManipulator; - public function __construct(FillPdfAdminFormHelperInterface $admin_form_helper, + public function __construct(EntityManagerInterface $entity_manager, FillPdfAdminFormHelperInterface $admin_form_helper, FillPdfLinkManipulatorInterface $link_manipulator) { + $this->entityManager = $entity_manager; + parent::__construct($this->entityManager); $this->adminFormHelper = $admin_form_helper; $this->linkManipulator = $link_manipulator; } public static function create(ContainerInterface $container) { return new static( + $container->get('entity.manager'), $container->get('fillpdf.admin_form_helper'), $container->get('fillpdf.link_manipulator') ); diff --git a/src/Form/FillPdfFormImportForm.php b/src/Form/FillPdfFormImportForm.php new file mode 100644 index 0000000..2709341 --- /dev/null +++ b/src/Form/FillPdfFormImportForm.php @@ -0,0 +1,160 @@ +<?php +/** + * @file + * Contains \Drupal\fillpdf\Form\FillPdfFormImportForm. + */ + +namespace Drupal\fillpdf\Form; + +use Drupal\Core\Entity\EntityForm; +use Drupal\Core\Form\FormStateInterface; +use Drupal\fillpdf\EntityHelper; +use Drupal\fillpdf\FillPdfFormFieldInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Serializer\SerializerInterface; + +class FillPdfFormImportForm extends EntityForm { + + /** @var \Drupal\serialization\Normalizer\ContentEntityNormalizer */ + protected $serializer; + + /** @var \Drupal\fillpdf\EntityHelper */ + protected $entityHelper; + + public function __construct(SerializerInterface $serializer, EntityHelper $entity_helper) { + $this->serializer = $serializer; + $this->entityHelper = $entity_helper; + } + + public static function create(ContainerInterface $container) { + return new static($container->get('serializer'), $container->get('fillpdf.entity_helper')); + } + + public function form(array $form, FormStateInterface $form_state) { + $form = parent::form($form, $form_state); + + $entity = $this->getEntity(); + + $form['paste'] = [ + '#type' => 'details', + '#title' => t('Paste code'), + '#open' => TRUE, + ]; + $form['paste']['code'] = [ + '#type' => 'textarea', + '#default_value' => '', + '#rows' => 30, + '#description' => $this->t('Cut and paste the results of a <em>FillPDF configuration and mappings export</em> here.'), + ]; + $form['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Import'), + ]; + + return $form; + } + + public function buildForm(array $form, FormStateInterface $form_state) { + $form = parent::buildForm($form, $form_state); + unset($form['actions']); + $form['#after_build'] = []; + return $form; + } + + public function validateForm(array &$form, FormStateInterface $form_state) { + parent::validateForm($form, $form_state); + + $code = $form_state->getValue('code'); + $mappings_raw = json_decode($code, TRUE); + $fillpdf_form = $this->serializer->denormalize($mappings_raw['form'], 'Drupal\fillpdf\Entity\FillPdfForm'); + + // Denormalization is a pain; we had to iterate over the fields to actually + // recompose the $fields array. + $field_json = $mappings_raw['fields']; + $fields = []; + foreach ($field_json as $id => $normalized_field) { + $field = $this->serializer->denormalize($normalized_field, 'Drupal\fillpdf\Entity\FillPdfFormField'); + $fields[$field->pdf_key->value] = $field; + } + if (!is_object($fillpdf_form) || !count($fields)) { + $form_state->setErrorByName('code', $this->t('There was a problem processing your FillPDF form code. Please do a fresh export from the source and try pasting it again.')); + } + else { + $form_state->setValue('mappings', [ + 'form' => $fillpdf_form, + 'fields' => $fields, + ]); + } + } + + public function submitForm(array &$form, FormStateInterface $form_state) { + $form_state->cleanValues(); + + /** @var \Drupal\fillpdf\FillPdfFormInterface $fillpdf_form */ + $fillpdf_form = $this->getEntity(); + + $mappings = $form_state->getValue('mappings'); + + /** @var \Drupal\fillpdf\FillPdfFormInterface $imported_form */ + $imported_form = $mappings['form']; + + /** @var array $imported_fields */ + $imported_fields = $mappings['fields']; + + // Key the existing FillPDF fields on PDF keys. + $existing_fields = $this->entityHelper->getFormFields($fillpdf_form); + $existing_fields_by_key = []; + foreach ($existing_fields as $existing_field) { + $existing_fields_by_key[$existing_field->pdf_key->value] = $existing_field; + } + + // Iterate over FillPdfForm fields and copy them, EXCEPT for IDs and references. + $fillpdf_form_type = $this->entityTypeManager->getDefinition('fillpdf_form'); + $form_fields_to_ignore = array_filter(array_values($fillpdf_form_type->getKeys())); + $form_fields_to_ignore[] = 'file'; + foreach ($imported_form->getFields() as $name => $data) { + if (!in_array($name, $form_fields_to_ignore, TRUE)) { + $fillpdf_form->{$name} = $data; + } + } + $fillpdf_form->save(); + + drupal_set_message($this->t('Successfully imported FillPDF form configuration.')); + + // Iterate over each FillPdfFormField and override matching PDF keys + // (if any current fields have them). + $fillpdf_field_type = $this->entityTypeManager->getDefinition('fillpdf_form_field'); + $field_fields_to_ignore = array_filter(array_values($fillpdf_field_type->getKeys())); + $field_fields_to_ignore[] = 'fillpdf_form'; + + $existing_field_pdf_keys = array_keys($existing_fields_by_key); + /** + * @var string $pdf_key + * @var FillPdfFormFieldInterface $imported_field + */ + foreach ($imported_fields as $pdf_key => $imported_field) { + // If the imported field's PDF key matching the PDF key of the + // existing field, then copy the constituent entity fields. + // I know: they're both called fields. It's confusing as hell. + // I am sorry. + if (in_array($pdf_key, $existing_field_pdf_keys, TRUE)) { + /** @var FillPdfFormFieldInterface $existing_field_by_key */ + $existing_field_by_key = $existing_fields_by_key[$pdf_key]; + foreach ($imported_field->getFields() as $imported_field_name => $imported_field_data) { + if (!in_array($imported_field_name, $field_fields_to_ignore, TRUE)) { + $existing_field_by_key->{$imported_field_name} = $imported_field_data; + } + } + $existing_field_by_key->save(); + } + else { + drupal_set_message($this->t('Your code contained field mappings for the PDF field key <em>@pdf_key</em>, but it does not exist on this form. Therefore, it was ignored.', ['@pdf_key' => $pdf_key]), 'warning'); + } + } + + drupal_set_message($this->t('Successfully imported matching PDF field keys. If any field mappings failed to import, they are listed above.')); + + $form_state->setRedirect('entity.fillpdf_form.edit_form', ['fillpdf_form' => $fillpdf_form->id()]); + } + +} -- GitLab