diff --git a/fillpdf.info.yml b/fillpdf.info.yml index 0f7a11e8252e5da8540ea64af543eeca4c64459a..e6db379fb4b1dedf360fe306c8cde7f3de3eb145 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 845c28caf3594415d6436fef96381f4d127b7341..ae8508c6ab3b961b41ad868079de23865d6dc649 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 53cbfa686a1bad25e291e181c4a5e642c7bc0bb0..98d45878e0277f40926eddb70fd055dfeba692d5 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 6961fbd3a4f4fb5e26b4b2e3572fd2e2120b3367..5fadcfeb9b0f760f002959d4b8fb88ba80bafb81 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 37cb22babb098a2b1d847b75ca8ea9f10897eff8..10f2e121258ca721b897e395461bf2c2bd12f3f8 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 b7760e34f9fa52465cfd26a60320b127c0e1fd98..51637e397f6a7bccfdc691128f6850ba33b3d54a 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 0000000000000000000000000000000000000000..fade90b0ba3ecb0fe0fdb60d0cdc0be3450d3010 --- /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 0000000000000000000000000000000000000000..2f465b6bca82aa8a71ac1eb755faf237724895b0 --- /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 0000000000000000000000000000000000000000..1379aca7c556b78650f81f90d496388b5120a849 --- /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 8967cd15a94b1e2ad4826144b2359c3da15833f9..2031f423b3b9c2d537458dd1b62ce1771a5987bd 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 0000000000000000000000000000000000000000..2709341ec295d765124f64ebe910aa74a44d1f6f --- /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()]); + } + +}