From 469a507a7323d6eec136f5f6a6e0b81b0f2d506a Mon Sep 17 00:00:00 2001 From: Janne Suominen Date: Tue, 19 Nov 2024 09:52:41 +0200 Subject: [PATCH] fix: UHF-10903: Update application copy modal to use new dialog (#1563) * UHF-10903: New dialog form * UHF-10903: Refactor application copy to custom controller. * UHF-10903: Add custom selector option for dialogs. * UHF-10903: Update tests for new copy functionality. * UHF-10903: Rename method + 1 error * UHF-10903: Fix typo * UHF-10903: Refactor createDialog to support object argument. --- e2e/utils/copying_helpers.ts | 25 +- .../grants_handler.libraries.yml | 8 + .../grants_handler/grants_handler.module | 42 +-- .../grants_handler/grants_handler.routing.yml | 24 +- .../js/application-copy-dialog.js | 36 +++ .../custom/grants_handler/js/grants-dialog.js | 77 +++-- .../grants_handler/js/webform.form.unsaved.js | 34 +- .../grants_handler/src/ApplicationHelpers.php | 4 + .../src/Controller/ApplicationController.php | 3 +- .../CopyApplicationAjaxController.php | 199 ++++++++++++ .../src/Form/CopyApplicationForm.php | 146 --------- .../src/Form/CopyApplicationModalForm.php | 300 ------------------ .../grants_profile/js/profile_dialog.js | 36 +-- .../application-copy-dialog-content.html.twig | 2 + .../webform-submission-information.html.twig | 70 ++-- 15 files changed, 428 insertions(+), 578 deletions(-) create mode 100644 public/modules/custom/grants_handler/js/application-copy-dialog.js create mode 100644 public/modules/custom/grants_handler/src/Controller/CopyApplicationAjaxController.php delete mode 100644 public/modules/custom/grants_handler/src/Form/CopyApplicationForm.php delete mode 100644 public/modules/custom/grants_handler/src/Form/CopyApplicationModalForm.php create mode 100644 public/themes/custom/hdbt_subtheme/templates/webform/application-copy-dialog-content.html.twig diff --git a/e2e/utils/copying_helpers.ts b/e2e/utils/copying_helpers.ts index fa20439edd..2a293a4c0b 100644 --- a/e2e/utils/copying_helpers.ts +++ b/e2e/utils/copying_helpers.ts @@ -117,13 +117,13 @@ const makeApplicationCopy = async ( logger(`Navigated to: ${viewPageURL}.`); // Make sure we get there. - const applicationIdContainer = await page.locator('.webform-submission__application_id'); + const applicationIdContainer = page.locator('.webform-submission__application_id'); const applicationIdContainerText = await applicationIdContainer.textContent(); expect(applicationIdContainerText).toContain(originalApplicationId); // Make sure the application can be copied. If not, skip this test. - const isCopyApplicationButtonVisible = await page.locator('#copy-application-modal-form-link').isVisible(); - if (!isCopyApplicationButtonVisible) { + const copyApplicationDialogButton = page.locator('#copy-application-button'); + if (!await copyApplicationDialogButton.isVisible()) { logger(`Copying disabled for application: ${originalApplicationId}. Skipping test.`); test.skip(true, 'Skip copy test'); } @@ -131,8 +131,23 @@ const makeApplicationCopy = async ( // Copy the original application. logger(`Copying application: ${originalApplicationId}...`); - await page.locator('#copy-application-modal-form-link').click(); - await page.locator('#copy-application-modal-form-submit').click(); + // click the copy button + await copyApplicationDialogButton.click(); + + const dialogSelector = '.dialog.application-copy-dialog'; + const actionButtonSelector = '#helfi-dialog__action-button'; + + // Wait for the dialog to appear + await page.waitForSelector(dialogSelector); + + // Check if the dialog is visible + const isDialogVisible = await page.locator(dialogSelector).isVisible(); + + // Assert that the dialog is visible + expect(isDialogVisible).toBe(true); + + // Click the action button within the specific dialog + await page.locator(`${dialogSelector} ${actionButtonSelector}`).click(); // Wait for a redirect to the new application and store the new application ID and submission URL. await logCurrentUrl(page); diff --git a/public/modules/custom/grants_handler/grants_handler.libraries.yml b/public/modules/custom/grants_handler/grants_handler.libraries.yml index 9ba38aff5b..029766017b 100644 --- a/public/modules/custom/grants_handler/grants_handler.libraries.yml +++ b/public/modules/custom/grants_handler/grants_handler.libraries.yml @@ -61,3 +61,11 @@ grants-dialog: js/grants-dialog.js: { } dependencies: - core/drupal + +application-copy-dialog: + js: + js/application-copy-dialog.js: {} + dependencies: + - core/jquery + - core/drupal + - core/drupalSettings diff --git a/public/modules/custom/grants_handler/grants_handler.module b/public/modules/custom/grants_handler/grants_handler.module index d8680f8d66..df4f2a8c13 100644 --- a/public/modules/custom/grants_handler/grants_handler.module +++ b/public/modules/custom/grants_handler/grants_handler.module @@ -21,7 +21,6 @@ use Drupal\Core\Url; use Drupal\grants_attachments\AttachmentHandlerHelper; use Drupal\grants_handler\ApplicationHelpers; use Drupal\grants_handler\Event\UserLogoutEvent; -use Drupal\grants_handler\Form\CopyApplicationModalForm; use Drupal\grants_handler\GrantsErrorStorage; use Drupal\grants_handler\Helpers; use Drupal\grants_handler\Plugin\WebformElement\CompensationsComposite; @@ -1771,6 +1770,8 @@ function grants_handler_preprocess_webform_submission_information(array &$variab $webform = $submission->getWebForm(); $thirdPartySettings = $webform->getThirdPartySettings('grants_metadata'); + $applicationNumber = $submissionData['application_number'] ?? ApplicationHelpers::createApplicationNumber($submission); + $printApplicationUrl = Url::fromRoute( 'grants_webform_print.submission_print', [ @@ -1836,38 +1837,36 @@ function grants_handler_preprocess_webform_submission_information(array &$variab !$isApplicationArchived && $isApplicationOpen ) { + $copyApplicationUrl = Url::fromRoute( - 'grants_handler.copy_application_modal', + 'grants_handler.copy_application', [ 'submission_id' => $submissionData['application_number'], - 'nojs' => 'ajax', - ], - [ - 'attributes' => [ - 'class' => ['use-ajax', 'hds-button', 'hds-button--supplementary'], - 'data-dialog-type' => 'modal', - 'data-dialog-options' => json_encode(CopyApplicationModalForm::getDataDialogOptions()), - // Add this id so that we can test this form. - 'id' => 'copy-application-modal-form-link', - ], ] ); - $copyApplicationLinkText = [ - '#theme' => 'edit-label-with-icon', - '#icon' => 'copy', - '#text_label' => t('Copy application', [], $tOpts), + + // Attach the JavaScript file. + $variables['#attached']['library'][] = 'grants_handler/grants-dialog'; + $variables['#attached']['library'][] = 'grants_handler/application-copy-dialog'; + + // Render a Twig template. + $html_content = \Drupal::service('twig')->render('themes/custom/hdbt_subtheme/templates/webform/application-copy-dialog-content.html.twig', [ + 'applicationNumber' => $applicationNumber, + 'webformTitle' => $webform->label(), + ]); + + // Define the settings you want to pass. + $variables['#attached']['drupalSettings']['grants_handler'] = [ + 'copyUrl' => $copyApplicationUrl->toString(), + 'htmlContent' => $html_content, ]; - $copyApplicationLink = Link::fromTextAndUrl($copyApplicationLinkText, $copyApplicationUrl); - $variables['copyApplicationLink'] = $copyApplicationLink; + $variables['copyText'] = t('Copy application', [], $tOpts); } $variables['formTitle'] = $webform->label(); $variables['printLink'] = ''; $variables['copyLink'] = ''; - $submissionData = $submission->getData(); - $applicationNumber = $submissionData['application_number'] ?? ApplicationHelpers::createApplicationNumber($submission); - $variables['applicationNumber'] = $applicationNumber; $variables['isEditable'] = $applicationStatusService->isSubmissionEditable($submission); $variables['isEditPage'] = 'grants_handler.edit_application' === \Drupal::routeMatch() @@ -1901,6 +1900,7 @@ function grants_handler_preprocess_webform_submission_information(array &$variab '#theme' => 'webform_submission_attachment_list', '#submission' => $submission, ]; + } /** diff --git a/public/modules/custom/grants_handler/grants_handler.routing.yml b/public/modules/custom/grants_handler/grants_handler.routing.yml index 8373248687..b183f94e71 100644 --- a/public/modules/custom/grants_handler/grants_handler.routing.yml +++ b/public/modules/custom/grants_handler/grants_handler.routing.yml @@ -22,6 +22,14 @@ grants_handler.view_application: requirements: _custom_access: '\Drupal\grants_handler\Controller\ApplicationController::accessByApplicationNumber' +grants_handler.copy_application: + path: '/hakemus/{submission_id}/kopioi' + defaults: + _title_callback: '\Drupal\grants_handler\Controller\ApplicationController::getTitle' + _controller: '\Drupal\grants_handler\Controller\CopyApplicationAjaxController' + requirements: + _custom_access: '\Drupal\grants_handler\Controller\ApplicationController::accessByApplicationNumber' + grants_handler.edit_application: path: '/hakemus/{webform}/{webform_submission}/muokkaa' defaults: @@ -45,22 +53,6 @@ grants_handler.message_read: requirements: _custom_access: '\Drupal\grants_handler\Controller\ApplicationController::accessByApplicationNumber' -grants_handler.copy_application: - path: '/hakemus/{submission_id}/kopioi' - defaults: - _title: 'Copy application' - _form: 'Drupal\grants_handler\Form\CopyApplicationForm' - requirements: - _custom_access: '\Drupal\grants_handler\Controller\ApplicationController::accessByApplicationNumber' - -grants_handler.copy_application_modal: - path: '/hakemus/{submission_id}/kopioi/{nojs}' - defaults: - _title: 'You are copying an application' - _form: 'Drupal\grants_handler\Form\CopyApplicationModalForm' - requirements: - _custom_access: '\Drupal\grants_handler\Controller\ApplicationController::accessByApplicationNumber' - grants_handler.atv_print_view: path: '/hakemus/{submission_id}/tulosta' defaults: diff --git a/public/modules/custom/grants_handler/js/application-copy-dialog.js b/public/modules/custom/grants_handler/js/application-copy-dialog.js new file mode 100644 index 0000000000..ae1477e94e --- /dev/null +++ b/public/modules/custom/grants_handler/js/application-copy-dialog.js @@ -0,0 +1,36 @@ +(function (Drupal) { + Drupal.behaviors.copyApplicationModalForm = { + attach: function (context, settings) { + const triggerButton = document.getElementById('copy-application-button'); + + triggerButton.addEventListener('click', function (event) { + + event.preventDefault(); + + const htmlContent = drupalSettings.grants_handler.htmlContent; + const copyUrl = drupalSettings.grants_handler.copyUrl; + + Drupal.dialogFunctions.createDialog({ + dialogContent: htmlContent, + actionButtonText: Drupal.t('Copy application', [], { context: 'grants_handler' }), + backButtonText: Drupal.t('Close', [], { context: 'grants_handler' }), + closeButtonText: Drupal.t('Close', [], { context: 'grants_handler' }), + actionButtonCallback: () => { + // Redirect to a new URL + window.location.href = copyUrl; + /* + We probably should handle the whole copy process here, but for + now we just redirect to the copy URL. + + Much better UX would be to show spinner here while copying + application and then redirect when it's ready + + */ + }, + dialogTitle: Drupal.t('Copy application', [], { context: 'grants_handler' }), + customSelector: 'application-copy-dialog' + }) + }); + }, + }; +})(Drupal); diff --git a/public/modules/custom/grants_handler/js/grants-dialog.js b/public/modules/custom/grants_handler/js/grants-dialog.js index 925eead272..a1f651173d 100644 --- a/public/modules/custom/grants_handler/js/grants-dialog.js +++ b/public/modules/custom/grants_handler/js/grants-dialog.js @@ -12,31 +12,51 @@ * closes the dialog. * @param {Function} actionButtonCallback - The function to execute when * the "action" button is clicked. + * @param dialogTitle + * If we want to override the default title + * @param {Function} closeButtonCallback + * The function to execute when the "close" button is clicked. + * @param {Function} backButtonCallback + * The function to execute when the "back" button is clicked. + * @param escapeButtonCallback + * The function to execute when the "escape" button is clicked. + * @param customSelector + * If we want to add a custom class to the dialog container */ - createDialog: (dialogContent, actionButtonText, backButtonText, closeButtonText, actionButtonCallback = null) => { - const dialogTitle = Drupal.t('Attention', {}, { context: 'grants_handler' }); + createDialog: ({ + dialogContent, + actionButtonText, + backButtonText, + closeButtonText, + dialogTitle = Drupal.t('Attention', {}, { context: 'grants_handler' }), + actionButtonCallback = null, + closeButtonCallback = null, + backButtonCallback = null, + escapeButtonCallback = null, + customSelector = '', + }) => { const actionButtonHTML = actionButtonText && ``; const backButtonHTML = backButtonText && ``; const closeButtonHTML = closeButtonText && ``; const dialogHTML = ` -
-
- -
- ${closeButtonHTML} -

${dialogTitle}

-
-
- ${dialogContent} -
-
- ${actionButtonHTML} - ${backButtonHTML} -
-
+
+
+ +
+ ${closeButtonHTML} +

${dialogTitle}

- `; +
+ ${dialogContent} +
+
+ ${actionButtonHTML} + ${backButtonHTML} +
+
+
+ `; // TODO: Surveys use very similar javascript dialog implementation. // This and the survey implementation could possibly be merged with some @@ -54,7 +74,7 @@ const closeButton = document.getElementById('helfi-dialog__close-button'); const dialog = document.getElementById('helfi-dialog__container'); const dialogFocusTrap = window.focusTrap.createFocusTrap('#helfi-dialog__container', { - initialFocus: () => '#helfi-dialog__title' + initialFocus: () => '#helfi-dialog__title', }); // Activate the focus trap so that the user needs to react to the dialog. @@ -68,18 +88,30 @@ // Add click event listener to back button backButton.addEventListener('click', () => { dialogFocusTrap.deactivate(); + // If we have a callback, execute it. + if (backButtonCallback) { + backButtonCallback(); + } Drupal.dialogFunctions.removeDialog(dialog); }); // Add click event listener to close button closeButton.addEventListener('click', () => { dialogFocusTrap.deactivate(); + // If we have a callback, execute it. + if (closeButtonCallback) { + closeButtonCallback(); + } Drupal.dialogFunctions.removeDialog(dialog); }); // Add event listener to ESC button to remove the dialog document.body.addEventListener('keydown', function (event) { if (event.key === 'Escape') { + // If we have a escapeButtonCallback, execute it also when pressing escape. + if (escapeButtonCallback) { + escapeButtonCallback(); + } Drupal.dialogFunctions.removeDialog(dialog); } }); @@ -90,8 +122,7 @@ document.body.style.paddingRight = `${ window.innerWidth - document.documentElement.clientWidth }px`; - } - else { + } else { document.body.style.removeProperty('padding-right'); } }, @@ -105,6 +136,6 @@ dialog.remove(); Drupal.dialogFunctions.toggleNoScroll(false); Drupal.dialogFunctions.setBodyPaddingRight(false); - } - } + }, + }; })(Drupal); diff --git a/public/modules/custom/grants_handler/js/webform.form.unsaved.js b/public/modules/custom/grants_handler/js/webform.form.unsaved.js index 99e66c6b8d..4f96d10cd1 100644 --- a/public/modules/custom/grants_handler/js/webform.form.unsaved.js +++ b/public/modules/custom/grants_handler/js/webform.form.unsaved.js @@ -108,7 +108,7 @@ }); }); } - } + }, }; $('a').on('click', function (event) { @@ -116,18 +116,18 @@ if (unsaved && !containingElement.contains(event.target) && !event.target.getAttribute('href').startsWith('#')) { event.preventDefault(); - return Drupal.dialogFunctions.createDialog( - Drupal.t('You have unsaved changes. Are you sure you want to leave?', {}, { context: 'grants_handler' }), - Drupal.t('Leave the application', {}, { context: 'grants_handler' }), - Drupal.t('Back to application', {}, { context: 'grants_handler' }), - Drupal.t('Close', {}, { context: 'grants_handler' }), - () => { + return Drupal.dialogFunctions.createDialog({ + dialogContent: Drupal.t('You have unsaved changes. Are you sure you want to leave?', {}, { context: 'grants_handler' }), + actionButtonText: Drupal.t('Leave the application', {}, { context: 'grants_handler' }), + backButtonText: Drupal.t('Back to application', {}, { context: 'grants_handler' }), + closeButtonText: Drupal.t('Close', {}, { context: 'grants_handler' }), + actionButtonCallback: () => { unsaved = false; const dialog = document.getElementById('helfi-dialog__container'); Drupal.dialogFunctions.removeDialog(dialog); window.top.location.href = event.currentTarget.href; - } - ); + }, + }); } }); @@ -147,18 +147,18 @@ if (unsaved && (e.which === 116 || (e.which === 82 && (e.ctrlKey || e.metaKey)))) { e.preventDefault(); // Prevent F5 and Ctrl+R / Cmd+R refresh - return Drupal.dialogFunctions.createDialog( - Drupal.t('You have unsaved changes. Are you sure you want to refresh?', {}, { context: 'grants_handler' }), - Drupal.t('Refresh the page', {}, { context: 'grants_handler' }), - Drupal.t('Back to application', {}, { context: 'grants_handler' }), - Drupal.t('Close', {}, { context: 'grants_handler' }), - () => { + return Drupal.dialogFunctions.createDialog({ + dialogContent: Drupal.t('You have unsaved changes. Are you sure you want to refresh?', {}, { context: 'grants_handler' }), + actionButtonText: Drupal.t('Refresh the page', {}, { context: 'grants_handler' }), + backButtonText: Drupal.t('Back to application', {}, { context: 'grants_handler' }), + closeButtonText: Drupal.t('Close', {}, { context: 'grants_handler' }), + actionButtonCallback: () => { unsaved = false; const dialog = document.getElementById('helfi-dialog__container'); Drupal.dialogFunctions.removeDialog(dialog); location.reload(); - } - ); + }, + }); } }); diff --git a/public/modules/custom/grants_handler/src/ApplicationHelpers.php b/public/modules/custom/grants_handler/src/ApplicationHelpers.php index 110f4c5a6e..bacfcf57ee 100644 --- a/public/modules/custom/grants_handler/src/ApplicationHelpers.php +++ b/public/modules/custom/grants_handler/src/ApplicationHelpers.php @@ -411,6 +411,10 @@ public static function updateFieldOptions(array &$form, array $newOptions, array $currentField = &$currentField['#element'][$fieldName]; } else { + // If we don't have current field array, we can't update the options. + if (!is_array($currentField)) { + return; + } // If the field is not found, continue searching recursively. foreach ($currentField as &$subField) { if (is_array($subField)) { diff --git a/public/modules/custom/grants_handler/src/Controller/ApplicationController.php b/public/modules/custom/grants_handler/src/Controller/ApplicationController.php index 272cb2d1ff..ffd9e645e7 100644 --- a/public/modules/custom/grants_handler/src/Controller/ApplicationController.php +++ b/public/modules/custom/grants_handler/src/Controller/ApplicationController.php @@ -114,7 +114,8 @@ public function access(AccountInterface $account, string $webform, string $webfo * @return \Drupal\Core\Access\AccessResultInterface * The access result. * - * @throws \GuzzleHttp\Exception\GuzzleException + * @throws \Drupal\grants_mandate\CompanySelectException + * @throws \Drupal\grants_profile\GrantsProfileException */ public function accessByApplicationNumber(AccountInterface $account, string $submission_id): AccessResultInterface { try { diff --git a/public/modules/custom/grants_handler/src/Controller/CopyApplicationAjaxController.php b/public/modules/custom/grants_handler/src/Controller/CopyApplicationAjaxController.php new file mode 100644 index 0000000000..0830d35494 --- /dev/null +++ b/public/modules/custom/grants_handler/src/Controller/CopyApplicationAjaxController.php @@ -0,0 +1,199 @@ +get('grants_handler.application_getter_service'), + $container->get('grants_handler.application_init_service'), + $container->get('grants_events.events_service'), + $container->get('grants_handler.application_status_service'), + ); + } + + /** + * Builds the response. + * + * @param string $submission_id + * The submission ID. + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + * The redirect response. + */ + public function __invoke(string $submission_id): RedirectResponse { + $oldApplicationUrl = $this->getOldApplicationUrl($submission_id); + + try { + $webform_submission = $this->getWebformSubmission($submission_id); + $webform = $webform_submission->getWebForm(); + $this->makeSureCopyingIsEnabled($webform); + + $newSubmission = $this->initNewApplication($webform->id(), $webform_submission->getData()); + $this->logEvent($newSubmission, $webform_submission->getData()); + + return $this->getNewApplicationRedirect($webform, $newSubmission); + } + catch (\Throwable $e) { + $this->messenger()->addError('Application copying failed.'); + $this->getLogger('grants_handler') + ->error('Application copying failed: @message', ['@message' => $e->getMessage()]); + return new RedirectResponse($oldApplicationUrl->toString()); + } + } + + /** + * Gets the URL of the old application. + * + * @param string $submission_id + * The submission ID. + * + * @return \Drupal\Core\Url + * The URL of the old application. + */ + private function getOldApplicationUrl(string $submission_id): Url { + return Url::fromRoute('grants_handler.view_application', ['submission_id' => $submission_id]); + } + + /** + * Gets the webform submission object. + * + * @param string $submission_id + * The submission ID. + * + * @return \Drupal\webform\Entity\WebformSubmission + * The webform submission object. + * + * @throws \RuntimeException + * If the webform submission cannot be retrieved. + */ + private function getWebformSubmission(string $submission_id): WebformSubmission { + try { + return $this->applicationGetterService->submissionObjectFromApplicationNumber($submission_id); + } + catch (EntityStorageException | CompanySelectException $e) { + throw new \RuntimeException('Failed to get webform submission.'); + } + } + + /** + * Validates the application. + * + * @param \Drupal\webform\WebformInterface $webform + * The webform object. + * + * @throws \RuntimeException + * If the application cannot be copied. + */ + private function makeSureCopyingIsEnabled(WebformInterface $webform): void { + $thirdPartySettings = $webform->getThirdPartySettings('grants_metadata'); + $isApplicationArchived = $thirdPartySettings["status"] === 'archived' ?? TRUE; + $isApplicationOpen = $this->applicationStatusService->isApplicationOpen($webform); + + if ($thirdPartySettings["disableCopying"] === 1 || $isApplicationArchived || !$isApplicationOpen) { + throw new \RuntimeException('Application copying disabled.'); + } + } + + /** + * Initializes a new application with copied data. + * + * @param string $webform_id + * The webform ID. + * @param array $data + * The data to initialize the new application with. + * + * @return \Drupal\webform\Entity\WebformSubmission + * The new webform submission object. + * + * @throws \RuntimeException + * If the new application cannot be initialized. + */ + private function initNewApplication(string $webform_id, array $data): WebformSubmission { + try { + return $this->applicationInitService->initApplication($webform_id, $data); + } + catch (\Throwable $e) { + throw new \RuntimeException('Failed to initialize new application.'); + } + } + + /** + * Logs the event of copying the application. + * + * @param \Drupal\webform\Entity\WebformSubmission $newSubmission + * The new webform submission object. + * @param array $oldData + * The data of the old application. + * + * @throws \Drupal\grants_events\EventException + */ + private function logEvent(WebformSubmission $newSubmission, array $oldData): void { + $newData = $newSubmission->getData(); + $this->messenger() + ->addStatus($this->t('Grant application copied, new id: @number', ['@number' => $newData['application_number']], ['context' => 'grants_handler'])); + $this->eventsService->logEvent($newData['application_number'], 'HANDLER_APP_COPIED', $this->t('Application copied from application id: @id', ['@id' => $oldData['application_number']], ['context' => 'grants_handler']) + ->render(), $newData['application_number']); + } + + /** + * Gets the redirect response for the new application. + * + * @param \Drupal\webform\WebformInterface $webform + * The webform object. + * @param \Drupal\webform\Entity\WebformSubmission $newSubmission + * The new webform submission object. + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + * The redirect response. + */ + private function getNewApplicationRedirect(WebformInterface $webform, WebformSubmission $newSubmission): RedirectResponse { + $newApplicationUrl = Url::fromRoute('grants_handler.edit_application', [ + 'webform' => $webform->id(), + 'webform_submission' => $newSubmission->id(), + ]); + return new RedirectResponse($newApplicationUrl->toString()); + } + +} diff --git a/public/modules/custom/grants_handler/src/Form/CopyApplicationForm.php b/public/modules/custom/grants_handler/src/Form/CopyApplicationForm.php deleted file mode 100644 index a40dcec15e..0000000000 --- a/public/modules/custom/grants_handler/src/Form/CopyApplicationForm.php +++ /dev/null @@ -1,146 +0,0 @@ -setDebug(NULL); - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container): MessageForm|static { - return new static( - $container->get('grants_handler.application_init_service'), - $container->get('grants_handler.application_getter_service') - ); - } - - /** - * {@inheritdoc} - */ - public function getFormId(): string { - return 'grants_handler_copy_application'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state, string $submission_id = ''): array { - $tOpts = ['context' => 'grants_handler']; - - try { - $webform_submission = $this->applicationGetterService->submissionObjectFromApplicationNumber($submission_id); - - if ($webform_submission != NULL) { - $form_state->setStorage(['submission' => $webform_submission]); - } - } - catch (\Exception | GuzzleException $e) { - $this->logger(self::LOGGER_CHANNEL)->error('Failed to load submission: @error', ['@error' => $e->getMessage()]); - } - $form['copyFrom'] = [ - '#type' => 'markup', - '#markup' => 'Tähän vois sitte laittaa hakemuksen perussettejä, tai vaikka koko hakemus näytille.', - ]; - - $form['actions'] = [ - '#type' => 'actions', - ]; - $form['actions']['submit'] = [ - '#type' => 'submit', - '#value' => $this->t('Copy application', [], $tOpts), - ]; - - return $form; - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, FormStateInterface $form_state) {} - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state): void { - $storage = $form_state->getStorage(); - /** @var \Drupal\webform\Entity\WebformSubmission $webform_submission */ - $webform_submission = $storage['submission']; - $webform = $webform_submission->getWebForm(); - - // Init new application with copied data. - try { - $newSubmission = $this->applicationInitService->initApplication($webform->id(), $webform_submission->getData()); - } - catch (EntityStorageException | - GrantsProfileException | - AtvDocumentNotFoundException | - AtvFailedToConnectException | - ProfileDataException | - TokenExpiredException | - GuzzleException $e) { - $newSubmission = FALSE; - } - - if ($newSubmission) { - - $newData = $newSubmission->getData(); - - $this->messenger() - ->addStatus( - $this->t( - 'Grant application copied(@number)', - [ - '@number' => $newData['application_number'], - ] - ) - ); - - $form_state->setRedirect( - 'grants_handler.completion', - ['submission_id' => $newData['application_number']], - [ - 'attributes' => [ - 'data-drupal-selector' => 'application-saved-successfully-link', - ], - ] - ); - } - else { - $this->messenger()->addError('Grant application copy failed.'); - } - } - -} diff --git a/public/modules/custom/grants_handler/src/Form/CopyApplicationModalForm.php b/public/modules/custom/grants_handler/src/Form/CopyApplicationModalForm.php deleted file mode 100644 index 7e2c463eb2..0000000000 --- a/public/modules/custom/grants_handler/src/Form/CopyApplicationModalForm.php +++ /dev/null @@ -1,300 +0,0 @@ -setDebug(NULL); - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container): CopyApplicationModalForm|static { - - // Create a new form object and inject its services. - $form = new static( - $container->get('renderer'), - $container->get('grants_events.events_service'), - $container->get('grants_handler.application_status_service'), - $container->get('grants_handler.application_init_service'), - $container->get('grants_handler.application_getter_service') - ); - $form->setRequestStack($container->get('request_stack')); - $form->setStringTranslation($container->get('string_translation')); - $form->setMessenger($container->get('messenger')); - - return $form; - - } - - /** - * {@inheritdoc} - */ - public function getFormId(): string { - return 'grants_profile_copy_application_modal_form'; - } - - /** - * Helper method so we can have consistent dialog options. - * - * @return string[] - * An array of jQuery UI elements to pass on to our dialog form. - */ - public static function getDataDialogOptions(): array { - return [ - 'width' => '33%', - 'closeText' => t('Close'), - ]; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state, string $submission_id = '', string $nojs = ''): array { - $tOpts = ['context' => 'grants_handler']; - - // Add the core AJAX library. - $form['#attached']['library'][] = 'core/drupal.ajax'; - $form['#theme'] = 'application_copy_modal_form'; - - try { - $webform_submission = $this->applicationGetterService->submissionObjectFromApplicationNumber($submission_id); - - if ($webform_submission != NULL) { - // Set webform submission template. - $build = [ - '#theme' => 'submission_for_modal_form', - '#submission' => $webform_submission, - '#submission_id' => $submission_id, - ]; - - $form_state->setStorage(['submission' => $webform_submission]); - $form['modal_markup'] = [ - '#markup' => $this->renderer->render($build), - ]; - } - - } - catch (\Exception | GuzzleException $e) { - $this->logger('copy_application_modal_form')->error('Error: %error', [ - '%error' => $e->getMessage(), - ]); - } - - // Add a link to show this form in a modal dialog if we're not already in - // one. - if ($nojs == 'nojs') { - $form['use_ajax_container'] = [ - '#type' => 'details', - '#open' => TRUE, - ]; - $form['use_ajax_container']['use_ajax'] = [ - '#type' => 'link', - '#title' => $this->t('See this form as a modal.', [], $tOpts), - '#url' => Url::fromRoute('grants_handler.copy_application_modal', ['nojs' => 'ajax']), - '#attributes' => [ - 'class' => ['use-ajax'], - 'data-dialog-type' => 'modal', - 'data-dialog-options' => json_encode(static::getDataDialogOptions()), - // Add this id so that we can test this form. - 'id' => 'copy-application-modal-form-link', - ], - ]; - } - - // This element is responsible for displaying form errors in the AJAX - // dialog. - if ($nojs == 'ajax') { - $form['status_messages'] = [ - '#type' => 'status_messages', - '#weight' => -999, - ]; - } - - // Add a submit button that handles the submission of the form. - $form['actions']['submit'] = [ - '#type' => 'submit', - '#value' => $this->t('Use application as base', [], $tOpts), - '#ajax' => [ - 'callback' => '::ajaxSubmitForm', - 'event' => 'click', - ], - '#attributes' => [ - 'id' => 'copy-application-modal-form-submit', - ], - ]; - - // Set the form to not use AJAX if we're on a nojs path. When this form is - // within the modal dialog, Drupal will make sure we're using an AJAX path - // instead of a nojs one. - if ($nojs == 'nojs') { - unset($form['actions']['submit']['#ajax']); - } - - return $form; - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, FormStateInterface $form_state): void { - $storage = $form_state->getStorage(); - /** @var \Drupal\webform\Entity\WebformSubmission $webform_submission */ - $webform_submission = $storage['submission']; - $webform = $webform_submission->getWebForm(); - - $isApplicationOpen = $this->applicationStatusService->isApplicationOpen($webform); - $thirdPartySettings = $webform->getThirdPartySettings('grants_metadata'); - - // If copying is disabled in 3rd party settings, do not allow forward. - if ($thirdPartySettings["disableCopying"] == 1 || - $thirdPartySettings["status"] === 'archived' || - !$isApplicationOpen - ) { - $form_state->setErrorByName('modal_markup', 'Copying is disabled for this form.'); - } - - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state): void { - $storage = $form_state->getStorage(); - /** @var \Drupal\webform\Entity\WebformSubmission $webform_submission */ - $webform_submission = $storage['submission']; - $oldData = $webform_submission->getData(); - $webform = $webform_submission->getWebForm(); - - // Init new application with copied data. - try { - $newSubmission = $this->applicationInitService->initApplication($webform->id(), $webform_submission->getData()); - $newData = $newSubmission->getData(); - } - catch (\Exception $e) { - $newSubmission = FALSE; - $newData = []; - } - - if ($newSubmission) { - $this->messenger() - ->addStatus( - $this->t( - 'Grant application copied, new id: @number', - [ - '@number' => $newData['application_number'], - ], - ['context' => 'grants_handler'] - ) - ); - - $storage['newSubmission'] = $newSubmission; - $form_state->setStorage($storage); - - $this->eventsService->logEvent( - $newData['application_number'], - 'HANDLER_APP_COPIED', - $this->t('Application copied from application id: @id', ['@id' => $oldData['application_number']], ['context' => 'grants_handler']), - $newData['application_number'] - ); - - $form_state->setRedirect( - 'grants_handler.edit_application', - [ - 'webform_submission' => $newSubmission->id(), - 'webform' => $webform->id(), - ] - ); - } - else { - $this->messenger()->addError('Grant application copy failed'); - } - } - - /** - * Implements the submit handler for the modal dialog AJAX call. - * - * @param array $form - * Render array representing from. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * Current form state. - * - * @return \Drupal\Core\Ajax\AjaxResponse - * Array of AJAX commands to execute on submit of the modal form. - */ - public function ajaxSubmitForm(array &$form, FormStateInterface $form_state) { - $tOpts = ['context' => 'grants_handler']; - - // We begin building a new ajax reponse. - $response = new AjaxResponse(); - - // If the user submitted the form and there are errors, show them the - // input dialog again with error messages. Since the title element is - // required, the empty string wont't validate and there will be an error. - if ($form_state->getErrors()) { - // If there are errors, we can show the form again with the errors in - // the status_messages section. - $form['status_messages'] = [ - '#type' => 'status_messages', - '#weight' => -10, - ]; - $response->addCommand(new OpenModalDialogCommand($this->t('Errors', [], $tOpts), $form, static::getDataDialogOptions())); - } - else { - // No errors, we load things from form state. - $storage = $form_state->getStorage(); - /** @var \Drupal\webform\Entity\WebformSubmission $webform_submission */ - $webform_submission = $storage['newSubmission']; - $webform = $webform_submission->getWebForm(); - - // Create url redirect for this new submission. - $url = Url::fromRoute('grants_handler.edit_application', - [ - 'webform_submission' => $webform_submission->id(), - 'webform' => $webform->id(), - ]); - $response->addCommand(new CloseModalDialogCommand()); - $command = new RedirectCommand($url->toString()); - $response->addCommand($command); - } - - // Finally return our response. - return $response; - } - -} diff --git a/public/modules/custom/grants_profile/js/profile_dialog.js b/public/modules/custom/grants_profile/js/profile_dialog.js index 1ea699707b..67aeeef575 100644 --- a/public/modules/custom/grants_profile/js/profile_dialog.js +++ b/public/modules/custom/grants_profile/js/profile_dialog.js @@ -29,28 +29,28 @@ let containingElement = document.querySelector('form'); if ((unset_name) && !containingElement.contains(event.target)) { event.preventDefault(); - return Drupal.dialogFunctions.createDialog( - Drupal.t('You need to have a name for your unregistered community or group. Please add a name and save or cancel them.', {}, { context: 'grants_profile' }), - '', - Drupal.t('Back to profile', {}, { context: 'grants_profile' }), - Drupal.t('Close', {}, { context: 'grants_profile' }), - ); + return Drupal.dialogFunctions.createDialog({ + dialogContent: Drupal.t('You need to have a name for your unregistered community or group. Please add a name and save or cancel them.', {}, { context: 'grants_profile' }), + actionButtonText: '', + backButtonText: Drupal.t('Back to profile', {}, { context: 'grants_profile' }), + closeButtonText: Drupal.t('Close', {}, { context: 'grants_profile' }), + }); } else if ((current_name !== initial_name) && !containingElement.contains(event.target)) { event.preventDefault(); - return Drupal.dialogFunctions.createDialog( - Drupal.t('You have unsaved changes in your profile. Please save or cancel them.', {}, { context: 'grants_profile' }), - '', - Drupal.t('Back to profile', {}, { context: 'grants_profile' }), - Drupal.t('Close', {}, { context: 'grants_profile' }), - ); + return Drupal.dialogFunctions.createDialog({ + dialogContent: Drupal.t('You have unsaved changes in your profile. Please save or cancel them.', {}, { context: 'grants_profile' }), + actionButtonText: '', + backButtonText: Drupal.t('Back to profile', {}, { context: 'grants_profile' }), + closeButtonText: Drupal.t('Close', {}, { context: 'grants_profile' }), + }); } else if (($('[data-drupal-selector="edit-isnewprofile"]').val() === 'initialSave') && !containingElement.contains(event.target)) { event.preventDefault(); - return Drupal.dialogFunctions.createDialog( - Drupal.t('You have not saved your profile. Please save your profile before leaving the form.', {}, { context: 'grants_profile' }), - '', - Drupal.t('Back to profile', {}, { context: 'grants_profile' }), - Drupal.t('Close', {}, { context: 'grants_profile' }), - ); + return Drupal.dialogFunctions.createDialog({ + dialogContent: Drupal.t('You have not saved your profile. Please save your profile before leaving the form.', {}, { context: 'grants_profile' }), + actionButtonText: '', + backButtonText: Drupal.t('Back to profile', {}, { context: 'grants_profile' }), + closeButtonText: Drupal.t('Close', {}, { context: 'grants_profile' }), + }); } is_element_click = false; diff --git a/public/themes/custom/hdbt_subtheme/templates/webform/application-copy-dialog-content.html.twig b/public/themes/custom/hdbt_subtheme/templates/webform/application-copy-dialog-content.html.twig new file mode 100644 index 0000000000..11fdb8af1c --- /dev/null +++ b/public/themes/custom/hdbt_subtheme/templates/webform/application-copy-dialog-content.html.twig @@ -0,0 +1,2 @@ +

Kopioitava hakemus: {{ webformTitle }}

+

Hakemustunnus: {{ applicationNumber }}

diff --git a/public/themes/custom/hdbt_subtheme/templates/webform/webform-submission-information.html.twig b/public/themes/custom/hdbt_subtheme/templates/webform/webform-submission-information.html.twig index 58cc39d906..daa38f316b 100644 --- a/public/themes/custom/hdbt_subtheme/templates/webform/webform-submission-information.html.twig +++ b/public/themes/custom/hdbt_subtheme/templates/webform/webform-submission-information.html.twig @@ -33,43 +33,51 @@

{{ 'Application info'|t }}

{{ 'Summary about your application and processing details.'|t }}

-
-

- {{ webform.text }} -

- +
+

+ {{ webform.text }} +

+ -
- -
-
{{ "Application number"|t }}
- {{ applicationNumber }} -
{{ 'Sent date'|t }}
- {{ applicationSent }} -
{{ 'Handler information'|t }}
- {{ handler }} -
-
-
{{ "Application statuses"|t }}
- {{ history }} +
+
+
+
{{ "Application number"|t }}
+ {{ applicationNumber }} +
{{ 'Sent date'|t }}
+ {{ applicationSent }} +
{{ 'Handler information'|t }}
+ {{ handler }} +
+
+
{{ "Application statuses"|t }}
+ {{ history }} -
-
-
{{ "Application attachments"|t }}
+
+
+
{{ "Application attachments"|t }}
- {{ attachments }} + {{ attachments }} -
+
+
+ {% if isEditable and not isEditPage %} +
+ {{ editApplicationLink }}
- {% if isEditable and not isEditPage %} -
- {{ editApplicationLink }} -
- {% endif %} + {% endif %}