From 961966ce88e2e707d6ae92910b5f3fc2d4db6e36 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Thu, 14 Sep 2023 14:35:26 +0200 Subject: [PATCH 01/15] Handled anonymous users in notifications and flow tasks --- CHANGELOG.md | 2 + composer.json | 3 +- modules/os2forms_forloeb/CHANGELOG.md | 12 +- modules/os2forms_forloeb/README.md | 52 +- .../os2forms_forloeb.info.yml | 6 +- .../os2forms_forloeb.links.menu.yml | 5 + .../os2forms_forloeb/os2forms_forloeb.module | 120 ++-- .../os2forms_forloeb.routing.yml | 49 +- .../os2forms_forloeb.services.yml | 21 +- .../ForloebTaskConsoleController.php | 177 ----- .../MaestroNotificationController.php | 135 ++++ .../src/Exception/RuntimeException.php | 10 + .../src/Form/SettingsForm.php | 151 +++++ .../os2forms_forloeb/src/MaestroHelper.php | 627 ++++++++++++++++++ .../JobType/SendMeastroNotification.php | 55 ++ .../EngineTasks/MaestroWebformInheritTask.php | 232 +++---- .../MaestroNotificationHandler.php | 290 ++++++++ ...-notification-message-email-html.html.twig | 35 + ...eb-notification-message-pdf-html.html.twig | 63 ++ ...rms-forloeb-notification-preview.html.twig | 87 +++ 20 files changed, 1730 insertions(+), 402 deletions(-) create mode 100644 modules/os2forms_forloeb/os2forms_forloeb.links.menu.yml delete mode 100644 modules/os2forms_forloeb/src/Controller/ForloebTaskConsoleController.php create mode 100644 modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php create mode 100644 modules/os2forms_forloeb/src/Exception/RuntimeException.php create mode 100644 modules/os2forms_forloeb/src/Form/SettingsForm.php create mode 100644 modules/os2forms_forloeb/src/MaestroHelper.php create mode 100644 modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php create mode 100644 modules/os2forms_forloeb/src/Plugin/WebformHandler/MaestroNotificationHandler.php create mode 100644 modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-email-html.html.twig create mode 100644 modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-pdf-html.html.twig create mode 100644 modules/os2forms_forloeb/templates/os2forms-forloeb-notification-preview.html.twig diff --git a/CHANGELOG.md b/CHANGELOG.md index a367903..5824f9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ before starting to add changes. Use example [placed in the end of the page](#exa - [OS-58] New company address fields - Custom permissions by term field - Removing dependency to config_entity_revisions, webform_revisions, coc_forms_auto_export +- [PR-56](https://github.com/OS2Forms/os2forms/pull/56) + Handled anonymous users in notifications and flow tasks ## [3.10.0] 2023-08-23 diff --git a/composer.json b/composer.json index cbb7125..cc8c44d 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "drupal/libraries": "^3.0@beta", "drupal/linkit": "^5.0", "drupal/logging_alerts": "^2.0", - "drupal/maestro": "^3.0", + "drupal/maestro": "^3.1", "drupal/mailsystem": "^4.1", "drupal/masquerade": "^2.0@RC", "drupal/pathauto": "^1.5", @@ -67,6 +67,7 @@ "drupal/webform_validation": "^2.0", "drupal/webform_views": "^5.0@alpha", "drupal/workflow_participants": "^2.4", + "os2forms/os2forms_digital_post": "^3.0", "os2web/os2web_datalookup": "^1.0", "os2web/os2web_nemlogin": "^1.0", "phpoffice/phpword": "^0.18.2", diff --git a/modules/os2forms_forloeb/CHANGELOG.md b/modules/os2forms_forloeb/CHANGELOG.md index f985467..0ab070a 100644 --- a/modules/os2forms_forloeb/CHANGELOG.md +++ b/modules/os2forms_forloeb/CHANGELOG.md @@ -4,15 +4,19 @@ All notable changes to this project should be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -See ["how do I make a good changelog record?"](https://keepachangelog.com/en/1.0.0/#how) +See ["how do I make a good changelog record?"](https://keepachangelog.com/en/1.0.0/#how) before starting to add changes. ## [Unreleased] +- Implemented `hook_maestro_zero_user_notification` and added *Maestro + notification* handler for sending notifications via email or digital post. +- Cleaned up prefilling of forms in `MaestroWebformInheritTask`. + ## 2.5.2 - 27.03.2023 ### Updated -- Bumped drupal/ultimate_cron version fixing [Deprecated function: Implicit conversion from float-string](https://www.drupal.org/project/ultimate_cron/issues/3256142). +- Bumped drupal/ultimate_cron version fixing [Deprecated function: Implicit conversion from float-string](https://www.drupal.org/project/ultimate_cron/issues/3256142). ## 2.5.1 - 10.03.2023 - Added github action for checking changelog changes when creating pull requests @@ -24,11 +28,11 @@ before starting to add changes. ## 2.5.0 - 11.10.2022 -### Added +### Added - retry task controller action - Added support for inheriting values without creating a submission -## 2.4.0 +## 2.4.0 ### Added - Github CI action for checking Drupal Coding standards with PHP Code Sniffer diff --git a/modules/os2forms_forloeb/README.md b/modules/os2forms_forloeb/README.md index 8ebcf2c..e1e0ec3 100644 --- a/modules/os2forms_forloeb/README.md +++ b/modules/os2forms_forloeb/README.md @@ -1,10 +1,56 @@ # OS2forms 2.1 med Forløb [![Build Status](https://app.travis-ci.com/OS2Forms/os2forms_forloeb.svg?branch=develop)](https://app.travis-ci.com/OS2Forms/os2forms_forloeb) + Adds a Maestro workflow engine and advanced workflow functionality to OS2forms. ## Installing OS2forms 2.1 med Forløb + This module requires the codebase from the [OS2forms core project](https://github.com/OS2Forms/os2forms8) installed per the documentation and by selecting the os2forms_forloeb_profile at installation. After succesful installation you should have the OS2forms med Forløb Module available for install via gui. You can also install the module by using Drush: - ``` - ./vendor/bin/drush en os2forms_forloeb - ``` + +``` +./vendor/bin/drush pm:enable os2forms_forloeb +``` + +------------------------------------------------------------------------------- + +## Maestro notifications + +Maestro 3.1 adds a `hook_webform_submission_form_alter` hook which we utilize to +send assignment, reminder and escalation notifications by adding a *Maestro +notification* handler to a form that spawns a Maestro workflow or assigns a +task. If the notification recipient is identified by an an email address, the +notification is sent as an email, and if the identifier is a Danish CPR number, +the notifications is sent as digital post. + +See [Opret flow-notifikationer](https://os2forms.os2.eu/node/457) (in Danish) +for details. + +### Settings + +Settings for OS2Forms forløb are defined on `/admin/config/system/os2forms_forloeb`. + +#### Known anonymous roles + +In order to make the notifications work, Maestro workflow tasks must be assigned +to a *known anonymous role* and these roles are defined under *Known anonymous +roles*. + +#### Processing + +A notification is not sent to a user immediately, but added to a queue which +must be processed asynchronously. Specify the queue handling notification jobs. + +#### Templates + +Define templates for emails and digital post (PDF). + +### Note on digital post + +Digital post is sent using the API provided by the [OS2Forms Digital Post +module](https://github.com/itk-dev/os2forms_digital_post) +(`os2forms_digital_post`) which in turn uses [SF1600: Print på +serviceplatformen](https://digitaliseringskataloget.dk/integration/sf1600). Not +all OS2Forms projects use `os2forms_digital_post` and in the future we should +generalize the API for sending digital post to allow other implementations (not +based on [SF1600](https://digitaliseringskataloget.dk/integration/sf1600)). diff --git a/modules/os2forms_forloeb/os2forms_forloeb.info.yml b/modules/os2forms_forloeb/os2forms_forloeb.info.yml index 3796741..cb8da31 100644 --- a/modules/os2forms_forloeb/os2forms_forloeb.info.yml +++ b/modules/os2forms_forloeb/os2forms_forloeb.info.yml @@ -2,8 +2,9 @@ name: 'OS2forms med Forløb Module' description: 'Adds a Maestro workflow engine and advanced workflow functionality to OS2forms.' package: OS2Forms type: module -core: 8.x -core_version_requirement: ^8 || ^9 +core_version_requirement: ^9 || ^10 + +configure: os2forms_forloeb.settings dependencies: - 'drupal:admin_toolbar_tools' @@ -48,5 +49,6 @@ dependencies: - 'drupal:webform_submission_log' - 'drupal:webform_templates' - 'drupal:workflow_participants' + - 'os2forms_digital_post:os2forms_digital_post' 'interface translation project': os2forms_forloeb 'interface translation server pattern': modules/contrib/os2forms_forloeb/translations/os2forms_forloeb.da.po diff --git a/modules/os2forms_forloeb/os2forms_forloeb.links.menu.yml b/modules/os2forms_forloeb/os2forms_forloeb.links.menu.yml new file mode 100644 index 0000000..a561d49 --- /dev/null +++ b/modules/os2forms_forloeb/os2forms_forloeb.links.menu.yml @@ -0,0 +1,5 @@ +os2forms_forloeb.settings: + title: OS2Forms forløb + description: Configure the OS2Forms forløb module + parent: system.admin_config_system + route_name: os2forms_forloeb.settings diff --git a/modules/os2forms_forloeb/os2forms_forloeb.module b/modules/os2forms_forloeb/os2forms_forloeb.module index 4c5fac0..1878073 100644 --- a/modules/os2forms_forloeb/os2forms_forloeb.module +++ b/modules/os2forms_forloeb/os2forms_forloeb.module @@ -5,18 +5,13 @@ * Install, update and uninstall functions for the os2forms_forloeb. */ -use Drupal\Core\Access\AccessResult; -use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\BubbleableMetadata; -use Drupal\Core\Session\AccountInterface; -use Drupal\Core\Url; use Drupal\maestro\Engine\MaestroEngine; +use Drupal\os2forms_forloeb\MaestroHelper; use Drupal\os2forms_forloeb\Plugin\EngineTasks\MaestroWebformInheritTask; use Drupal\user\Entity\User; use Drupal\webform\Entity\WebformSubmission; use Drupal\webform\WebformInterface; -use Drupal\webform\WebformSubmissionInterface; /** * Implements hook_maestro_interactive_handlers(). @@ -289,66 +284,93 @@ function os2forms_forloeb_preprocess_page(&$variables) { } /** - * Implements hook_token_info_alter(). + * Implements hook_webform_submission_form_alter(). */ -function os2forms_forloeb_token_info_alter(&$data) { - $data['tokens']['webform_submission']['os2forms_forloeb_execute_task'] = [ - 'name' => t('Execute task path for webform submission'), - 'description' => t("The token that can be user to get path for webform submission redirect URL."), - 'type' => 'webform_submission', - ]; +function os2forms_forloeb_webform_submission_form_alter(array &$form, FormStateInterface $formState, string $formId) { + MaestroWebformInheritTask::webformSubmissionFormAlter($form, $formState, $formId); +} + +/** + * Implements hook_maestro_zero_user_notification(). + */ +function os2forms_forloeb_maestro_zero_user_notification($templateMachineName, $taskMachineName, $queueID, $notificationType) { + _os2forms_forloeb_helper()->maestroZeroUserNotification($templateMachineName, $taskMachineName, $queueID, $notificationType); } /** - * Implements hook_tokens(). + * Implements hook_maestro_can_user_execute_task_alter(). * - * Provides token value for webform_submission:os2forms_forloeb_execute_task. + * For OS2Forms, you may have a consistent assignment to an "anonymous" user via + * a role. Use the QueueID and userID to drill into the task and alter the + * returnValue to TRUE if this is a user that should be looking at this task. + * + * You can make this as complex as you'd like it to be, checking things like + * sessions, login tokens, email addresses etc. */ -function os2forms_forloeb_tokens($type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) { - $replacements = []; - - if ($type === 'webform_submission' && !empty($data['webform_submission']) && isset($tokens['os2forms_forloeb_execute_task'])) { - $replacements[$tokens['os2forms_forloeb_execute_task']] = Url::fromRoute( - 'os2forms_forloeb.forloeb_task_console_controller_execute', - ['os2forms-forloeb-ws-token' => $data['webform_submission']->getToken()], - ['absolute' => TRUE] - )->toString(TRUE)->getGeneratedUrl(); - } +function os2forms_forloeb_maestro_can_user_execute_task_alter(&$returnValue, $queueID, $userID) { + _os2forms_forloeb_helper()->maestroCanUserExecuteTaskAlter($returnValue, $queueID, $userID); +} - return $replacements; +/** + * Implements hook_mail(). + */ +function os2forms_forloeb_mail($key, &$message, $params) { + _os2forms_forloeb_helper()->mail($key, $message, $params); } /** - * Implements hook_entity_access(). - * - * Allows requests with tokens to view the entity. + * Implements hook_mail_alter(). */ -function os2forms_forloeb_entity_access(EntityInterface $entity, $operation, AccountInterface $account) { - if ($operation == 'update' && $entity instanceof WebformSubmission) { - $token = \Drupal::request()->query->get('os2forms-forloeb-ws-token'); - if ($token && $token === $entity->getToken()) { - return AccessResult::allowed(); - } - } - return AccessResult::neutral(); +function os2forms_forloeb_mail_alter(&$message) { + _os2forms_forloeb_helper()->mailAlter($message); } /** - * Implements hook_maestro_post_fetch_assigned_queue_tasks(). + * Implements hook_theme(). */ -function os2forms_forloeb_maestro_post_fetch_assigned_queue_tasks($userID, &$queueIDs) { - $token = \Drupal::request()->query->get('os2forms-forloeb-ws-token', ''); - if ($token) { - $forloebTaskConsole = Drupal::service('os2forms_forloeb.task_console'); - $queueRecord = $forloebTaskConsole->getQueueIdByWebformSubmissionToken($token); - $queueIDs[] = $queueRecord->id(); - $queueIDs = array_unique($queueIDs); - } +function os2forms_forloeb_theme(array &$variables) { + $theme['os2forms_forloeb_notification_preview'] = [ + 'variables' => [ + 'webform' => NULL, + 'handler' => NULL, + 'notification_type' => NULL, + 'subject' => NULL, + 'recipient' => NULL, + 'content_type' => NULL, + 'submission' => NULL, + 'return_url' => NULL, + 'render_url' => NULL, + 'preview_urls' => [ + 'prev' => NULL, + 'self' => NULL, + 'next' => NULL, + ], + ], + ]; + + $theme['os2forms_forloeb_notification_message_email_html'] = [ + 'variables' => [ + 'message' => [ + 'content' => [ + 'value' => NULL, + 'format' => NULL, + ], + ], + 'task_url' => NULL, + 'action_label' => NULL, + 'webform_submission' => NULL, + 'handler' => NULL, + ], + ]; + + $theme['os2forms_forloeb_notification_message_pdf_html'] = $theme['os2forms_forloeb_notification_message_email_html']; + + return $theme; } /** - * Implements hook_ENTITY_TYPE_prepare_form(). + * Get MaestroHelper. */ -function os2forms_forloeb_webform_submission_prepare_form(WebformSubmissionInterface $webform_submission, string $operation, FormStateInterface $form_state) { - MaestroWebformInheritTask::webformSubmissionPrepareForm($webform_submission, $operation, $form_state); +function _os2forms_forloeb_helper(): MaestroHelper { + return Drupal::service(MaestroHelper::class); } diff --git a/modules/os2forms_forloeb/os2forms_forloeb.routing.yml b/modules/os2forms_forloeb/os2forms_forloeb.routing.yml index 8304af4..a51f54e 100644 --- a/modules/os2forms_forloeb/os2forms_forloeb.routing.yml +++ b/modules/os2forms_forloeb/os2forms_forloeb.routing.yml @@ -1,19 +1,42 @@ -os2forms_forloeb.forloeb_task_console_controller_execute: - path: 'os2forms-forloeb/execute-task' +os2forms_forloeb.settings: + path: '/admin/config/system/os2forms_forloeb' defaults: - _controller: '\Drupal\os2forms_forloeb\Controller\ForloebTaskConsoleController::execute' - _title: 'Execute task' + _form: '\Drupal\os2forms_forloeb\Form\SettingsForm' + _title: 'OS2Forms forløb' requirements: - _permission: 'access content' - options: - no_cache: TRUE + _permission: 'administer site configuration' -os2forms_forloeb.forloeb_task_console_controller_execute_retry: - path: 'os2forms-forloeb/execute-task-retry' +os2forms_forloeb.meastro_notification.preview: + path: '/admin/structure/webform/manage/{webform}/os2forms_forloeb/notification/{handler}/preview/{notification_type}/{content_type}' defaults: - _controller: '\Drupal\os2forms_forloeb\Controller\ForloebTaskConsoleController::retry' - _title: 'Task not yet ready' + _controller: '\Drupal\os2forms_forloeb\Controller\MaestroNotificationController::preview' + _title: 'Maestro notification preview' + notification_type: assignment + options: + parameters: + webform: + type: 'entity:webform' requirements: - _permission: 'access content' + _permission: 'view any webform submission' + +os2forms_forloeb.meastro_notification.preview_render: + path: '/admin/structure/webform/manage/{webform}/os2forms_forloeb/notification/{handler}/preview/{notification_type}/{content_type}/render/{submission}' + defaults: + _controller: '\Drupal\os2forms_forloeb\Controller\MaestroNotificationController::previewRender' + _title: 'Maestro notification render preview' options: - no_cache: TRUE + parameters: + webform: + type: 'entity:webform' + submission: + type: 'entity:webform_submission' + requirements: + _permission: 'view any webform submission' + +os2forms_forloeb.meastro_notification.preview_message: + path: '/os2forms_forloeb/notification/message' + defaults: + _controller: '\Drupal\os2forms_forloeb\Controller\MaestroNotificationController::message' + _title: 'Maestro notification message' + requirements: + _permission: 'view any webform submission' diff --git a/modules/os2forms_forloeb/os2forms_forloeb.services.yml b/modules/os2forms_forloeb/os2forms_forloeb.services.yml index eb0fa89..2d2b73c 100644 --- a/modules/os2forms_forloeb/os2forms_forloeb.services.yml +++ b/modules/os2forms_forloeb/os2forms_forloeb.services.yml @@ -2,6 +2,21 @@ services: logger.channel.os2forms_forloeb: parent: logger.channel_base arguments: ['os2forms_forloeb'] - os2forms_forloeb.task_console: - class: Drupal\os2forms_forloeb\ForloebTaskConsole - arguments: ['@entity_type.manager', '@logger.channel.os2forms_forloeb'] + + logger.channel.os2forms_forloeb_submission: + parent: logger.channel_base + arguments: ['webform_submission'] + + Drupal\os2forms_forloeb\MaestroHelper: + arguments: + - '@entity_type.manager' + - '@config.factory' + - '@webform.token_manager' + - '@plugin.manager.mail' + - '@language_manager' + - '@webform.theme_manager' + - '@logger.channel.os2forms_forloeb' + - '@logger.channel.os2forms_forloeb_submission' + - '@module_handler' + - '@plugin.manager.entity_print.print_engine' + - '@Drupal\os2forms_digital_post\Helper\DigitalPostHelper' diff --git a/modules/os2forms_forloeb/src/Controller/ForloebTaskConsoleController.php b/modules/os2forms_forloeb/src/Controller/ForloebTaskConsoleController.php deleted file mode 100644 index 8f2e95b..0000000 --- a/modules/os2forms_forloeb/src/Controller/ForloebTaskConsoleController.php +++ /dev/null @@ -1,177 +0,0 @@ -forloebTaskConsole = $forloeb_task_console; - $this->entityTypeManager = $entity_type_manager; - $this->requestStack = $request_stack; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('os2forms_forloeb.task_console'), - $container->get('entity_type.manager'), - $container->get('request_stack') - ); - } - - /** - * Redirects to the task execution URL. - * - * In case it's not possible to define task, redirects to task console. - * - * @return \Symfony\Component\HttpFoundation\RedirectResponse - * Redirect object. - */ - public function execute() { - $redirect_to = Url::fromRoute('maestro_taskconsole.taskconsole'); - - $request = $this->requestStack->getCurrentRequest(); - // Check webform submission token. - $token = $request->query->get('os2forms-forloeb-ws-token', ''); - if ($token) { - $queueRecord = $this->forloebTaskConsole->getQueueIdByWebformSubmissionToken($token); - if (empty($queueRecord)) { - return new RedirectResponse( - Url::fromRoute('os2forms_forloeb.forloeb_task_console_controller_execute_retry', - ['referrer' => $request->getRequestUri()])->toString() - ); - } - } - else { - // For empty token there is user last task from taskconsole queue. - $queueIDs = MaestroEngine::getAssignedTaskQueueIds($this->currentUser()->id()); - $queueRecord = count($queueIDs) ? $this->entityTypeManager->getStorage('maestro_queue')->load(end($queueIDs)) : NULL; - - // In case there are more than 1 task warning message will be shown. - if (count($queueIDs) > 1) { - $this->messenger()->addWarning($this->t('You have @amount @tasks available for you. See list of the all tasks on taskconsole', [ - ':tasksonsole' => Url::fromRoute('maestro_taskconsole.taskconsole')->toString(), - '@amount' => count($queueIDs), - '@tasks' => new PluralTranslatableMarkup(count($queueIDs), 'task', 'tasks'), - ])); - } - } - - if (empty($queueRecord)) { - $this->messenger()->addWarning($this->t('No tasks found to execute.')); - return new RedirectResponse($redirect_to->toString()); - } - - // Processing QueueRecord to get execution URL to redirect to. - $handler = $queueRecord->handler->getString(); - $query_options = [ - 'queueid' => $queueRecord->id(), - 'modal' => 'notmodal', - ]; - - // As inspiration MaestroTaskConsoleController::getTasks() method was used. - if ($handler && !empty($handler) && $queueRecord->is_interactive->getString() == '1') { - global $base_url; - $handler = str_replace($base_url, '', $handler); - $handler_type = TaskHandler::getType($handler); - - $handler_url_parts = UrlHelper::parse($handler); - $query_options += $handler_url_parts['query']; - - } - elseif ($queueRecord->is_interactive->getString() == '1' && empty($handler)) { - // Handler is empty. - // If this is an interactive task and has no handler, we're still OK. - // This is an interactive function that uses a default handler then. - $handler_type = 'function'; - } - else { - $this->messenger()->addWarning($this->t('Undefined handler')); - } - - switch ($handler_type) { - case 'external': - $redirect_to = Url::fromUri($handler, ['query' => $query_options]); - break; - - case 'internal': - $redirect_to = Url::fromUserInput($handler, ['query' => $query_options]); - break; - - case 'function': - if ($token) { - $query_options['os2forms-forloeb-ws-token'] = $token; - } - $redirect_to = Url::fromRoute('maestro.execute', $query_options); - break; - } - - return new RedirectResponse($redirect_to->toString()); - } - - /** - * Show message about task not yet ready. - * - * @return array - * The render array. - */ - public function retry() { - $referrer = $this->requestStack->getCurrentRequest()->query->get('referrer', '#'); - - return [ - '#markup' => $this->t('Your task is not yet ready. Please try again in 5 minutes.', [':referrer' => $referrer]), - ]; - } - -} diff --git a/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php b/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php new file mode 100644 index 0000000..1264d73 --- /dev/null +++ b/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php @@ -0,0 +1,135 @@ +get('entity_type.manager')->getStorage('webform_submission'), + $container->get(MaestroHelper::class) + ); + } + + /** + * Preview action. + */ + public function preview(Request $request, WebformInterface $webform, string $handler, string $notification_type, string $content_type, RouteMatchInterface $routeMatch) { + $handler = $webform->getHandler($handler); + $submissionIds = array_keys($this->webformSubmissionStorage->getQuery() + ->condition('webform_id', $webform->id()) + ->sort('created', 'DESC') + ->execute()); + $currentSubmission = (int) $request->query->get('submission'); + $index = array_search($currentSubmission, $submissionIds); + if (FALSE === $index) { + $currentSubmission = reset($submissionIds) ?: NULL; + $index = array_search($currentSubmission, $submissionIds); + } + + $previewUrls = array_map( + static fn ($submission) => Url::fromRoute('os2forms_forloeb.meastro_notification.preview', [ + 'webform' => $webform->id(), + 'handler' => $handler->getHandlerId(), + 'content_type' => $content_type, + 'submission' => $submission, + ]), + array_filter([ + 'prev' => $submissionIds[$index + 1] ?? NULL, + 'self' => $currentSubmission, + 'next' => $submissionIds[$index - 1] ?? NULL, + ]) + ); + + $renderUrl = NULL !== $currentSubmission + ? Url::fromRoute('os2forms_forloeb.meastro_notification.preview_render', [ + 'webform' => $webform->id(), + 'handler' => $handler->getHandlerId(), + 'notification_type' => $notification_type, + 'content_type' => $content_type, + 'submission' => $currentSubmission, + ]) + : NULL; + + $submission = $this->webformSubmissionStorage->load($currentSubmission); + $templateTask = []; + $maestroQueueID = 0; + [ + 'recipient' => $recipient, + 'subject' => $subject, + ] = $this->maestroHelper->renderNotification($submission, $handler->getHandlerId(), $notification_type, $templateTask, $maestroQueueID, $content_type); + + return [ + '#theme' => 'os2forms_forloeb_notification_preview', + '#webform' => $webform, + '#handler' => $handler, + '#notification_type' => $notification_type, + '#subject' => $subject, + '#recipient' => $recipient, + '#content_type' => $content_type, + '#submission' => $currentSubmission, + '#return_url' => $webform->toUrl('handlers'), + '#render_url' => $renderUrl, + '#preview_urls' => $previewUrls, + ]; + } + + /** + * Render notification preview. + */ + public function previewRender(Request $request, WebformInterface $webform, string $handler, string $notification_type, string $content_type, WebformSubmissionInterface $submission) { + $templateTask = []; + $maestroQueueID = 0; + [ + 'content' => $content, + 'contentType' => $contentType, + ] = $this->maestroHelper->renderNotification($submission, $handler, $notification_type, $templateTask, $maestroQueueID, $content_type); + + $response = new Response($content); + if ('pdf' === $contentType) { + $response->headers->set('content-type', Document::MIME_TYPE_PDF); + } + + return $response; + } + + /** + * Message action. + */ + public function message(Request $request): Response { + $content[] = '

' . $request->get('message') . '

'; + if ($referer = $request->headers->get('referer')) { + $content[] = sprintf('Go back', $referer); + } + + return new Response(implode(PHP_EOL, $content)); + } + +} diff --git a/modules/os2forms_forloeb/src/Exception/RuntimeException.php b/modules/os2forms_forloeb/src/Exception/RuntimeException.php new file mode 100644 index 0000000..004049d --- /dev/null +++ b/modules/os2forms_forloeb/src/Exception/RuntimeException.php @@ -0,0 +1,10 @@ +get('config.factory'), + $container->get('entity_type.manager')->getStorage('user_role'), + $container->get('entity_type.manager')->getStorage('advancedqueue_queue'), + $container->get('extension.list.module'), + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'os2forms_forloeb_config'; + } + + /** + * {@inheritdoc} + */ + protected function getEditableConfigNames() { + return [ + self::SETTINGS, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $config = $this->config(static::SETTINGS); + + $roles = $this->roleStorage->loadMultiple(); + $form['known_anonymous_roles'] = [ + '#title' => $this->t('Known anonymous roles'), + '#type' => 'checkboxes', + '#options' => array_map(static fn (RoleInterface $role) => $role->label(), $roles), + '#default_value' => $config->get('known_anonymous_roles') ?: [], + '#description' => $this->t('Roles that can act as “known anonymous”'), + ]; + + $form['processing'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Processing'), + '#tree' => TRUE, + ]; + + $defaultValue = $config->get('processing')['queue'] ?? NULL; + $form['processing']['queue'] = [ + '#type' => 'select', + '#required' => TRUE, + '#title' => $this->t('Queue'), + '#options' => array_map( + static fn(EntityInterface $queue) => $queue->label(), + $this->queueStorage->loadMultiple() + ), + '#default_value' => $defaultValue, + '#description' => $this->t("Queue for handling notification jobs. The queue must be run via Drupal's cron or via drush advancedqueue:queue:process @queue (in a cron job).", [ + '@queue' => $defaultValue, + ':queue_url' => '/admin/config/system/queues/jobs/' . urlencode($defaultValue ?? ''), + ]), + ]; + + $form['templates'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Templates'), + '#tree' => TRUE, + ]; + + $templatePath = $this->moduleHandler->getPath('os2forms_forloeb') . '/templates/os2forms-forloeb-notification-message-email-html.html.twig'; + $defaultTemplate = file_exists($templatePath) ? file_get_contents($templatePath) : NULL; + $form['templates']['notification_email'] = [ + '#type' => 'textarea', + '#rows' => 20, + '#required' => TRUE, + '#title' => $this->t('Email template'), + '#default_value' => $config->get('templates')['notification_email'] ?? $defaultTemplate, + '#description' => $this->t('HTML template for email notifications. If the template is a path, e.g. @templatePath, the template will be loaded from this path.', [ + '@templatePath' => $templatePath, + ]), + ]; + + $templatePath = $this->moduleHandler->getPath('os2forms_forloeb') . '/templates/os2forms-forloeb-notification-message-pdf-html.html.twig'; + $defaultTemplate = file_exists($templatePath) ? file_get_contents($templatePath) : NULL; + $form['templates']['notification_pdf'] = [ + '#type' => 'textarea', + '#rows' => 20, + '#required' => TRUE, + '#title' => $this->t('PDF template'), + '#default_value' => $config->get('templates')['notification_pdf'] ?? $defaultTemplate, + '#description' => $this->t('HTML template for PDF notifications (digital post). If the template is a path, e.g. @templatePath, the template will be loaded from this path.', [ + '@templatePath' => $templatePath, + ]), + ]; + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $formState) { + $this->config(static::SETTINGS) + ->set('known_anonymous_roles', $formState->getValue('known_anonymous_roles')) + ->set('processing', $formState->getValue('processing')) + ->set('templates', $formState->getValue('templates')) + ->save(); + + parent::submitForm($form, $formState); + } + +} diff --git a/modules/os2forms_forloeb/src/MaestroHelper.php b/modules/os2forms_forloeb/src/MaestroHelper.php new file mode 100644 index 0000000..4554c1a --- /dev/null +++ b/modules/os2forms_forloeb/src/MaestroHelper.php @@ -0,0 +1,627 @@ +config = $configFactory->get(SettingsForm::SETTINGS); + $this->webformSubmissionStorage = $entityTypeManager->getStorage('webform_submission'); + $this->queueStorage = $entityTypeManager->getStorage('advancedqueue_queue'); + + // The OS2Forms Digital Post (os2forms_digital_post) module may not be + // installed. + $this->digitalPostHelper = \Drupal::hasService(DigitalPostHelper::class) + ? \Drupal::service(DigitalPostHelper::class) + : NULL; + } + + /** + * Implements hook_maestro_zero_user_notification(). + */ + public function maestroZeroUserNotification($templateMachineName, $taskMachineName, $queueID, $notificationType) { + // @todo Clean up and align with MaestroWebformInheritTask::webformSubmissionFormAlter(). + $templateTask = MaestroEngine::getTemplateTaskByID($templateMachineName, $taskMachineName); + if (MaestroWebformInheritTask::isWebformTask($templateTask)) { + if ($inheritWebformUniqueId = ($templateTask['data'][MaestroWebformInheritTask::INHERIT_WEBFORM_UNIQUE_ID] ?? NULL)) { + if ($processID = (MaestroEngine::getProcessIdFromQueueId($queueID) ?: NULL)) { + if ($entityIdentifier = (self::getWebformSubmissionIdentifiersForProcess($processID)[$inheritWebformUniqueId] ?? NULL)) { + $submission = $this->webformSubmissionStorage->load($entityIdentifier['entity_id']); + if ($submission) { + $this->handleSubmissionNotification($notificationType, $submission, $templateTask, $queueID); + } + } + } + } + } + } + + /** + * Get webform submission identifiers for a process. + * + * @param int $processID + * The Maestro Process ID. + * + * @return array + * The webform submission identifiers sorted ascendingly by creation time. + */ + public static function getWebformSubmissionIdentifiersForProcess(int $processID): array { + // Get webform submissions in process. + $entityIdentifiers = array_filter( + MaestroEngine::getAllEntityIdentifiersForProcess($processID), + static fn (array $entityIdentifier) => 'webform_submission' === ($entityIdentifier['entity_type'] ?? NULL) + ); + + // Sort by entity ID. + uasort($entityIdentifiers, static fn (array $a, array $b) => ($b['entity_id'] ?? 0) <=> ($a['entity_id'] ?? 0)); + + return $entityIdentifiers; + } + + /** + * Handle submission notification. + */ + private function handleSubmissionNotification( + string $notificationType, + WebformSubmissionInterface $submission, + array $templateTask, + int $maestroQueueID + ): ?Job { + $context = [ + 'webform_submission' => $submission, + ]; + + try { + $job = Job::create(SendMeastroNotification::class, [ + 'notificationType' => $notificationType, + 'templateTask' => $templateTask, + 'queueID' => $maestroQueueID, + 'submissionID' => $submission->id(), + 'webformID' => $submission->getWebform()->id(), + ]); + + $queue = $this->loadQueue(); + $queue->enqueueJob($job); + $context['@queue'] = $queue->id(); + $this->notice('Job for sending notification added to the queue @queue.', $context + [ + 'handler_id' => 'os2forms_forloeb', + 'operation' => 'notification queued for sending', + ]); + + return $job; + } + catch (\Exception $exception) { + $this->error('Error creating job for sending notification: @message', $context + [ + '@message' => $exception->getMessage(), + 'handler_id' => 'os2forms_forloeb', + 'operation' => 'notification failed', + 'exception' => $exception, + ]); + + return NULL; + } + } + + /** + * Process a job. + */ + public function processJob(Job $job): JobResult { + $payload = $job->getPayload(); + [ + 'notificationType' => $notificationType, + 'templateTask' => $templateTask, + 'queueID' => $maestroQueueID, + 'submissionID' => $submissionID, + ] = $payload; + + $submission = $this->webformSubmissionStorage->load($submissionID); + + $this->sendNotification($notificationType, $submission, $templateTask, $maestroQueueID); + + return JobResult::success(); + } + + /** + * Send notification. + */ + private function sendNotification( + string $notificationType, + WebformSubmissionInterface $submission, + array $templateTask, + int $maestroQueueID + ) { + $context = [ + 'webform_submission' => $submission, + ]; + + try { + $handlers = $submission->getWebform()->getHandlers(); + foreach ($handlers as $handler) { + if (!($handler instanceof MaestroNotificationHandler) + || $handler->isDisabled() + || $handler->isExcluded() + || !$handler->isNotificationEnabled($notificationType) + ) { + continue; + } + + [ + 'content' => $content, + 'contentType' => $contentType, + 'recipient' => $recipient, + 'subject' => $subject, + 'taskUrl' => $taskUrl, + 'actionLabel' => $actionLabel, + ] = $this->renderNotification($submission, $handler->getHandlerId(), $notificationType, $templateTask, $maestroQueueID); + + if ('email' === $contentType) { + $this->sendNotificationEmail($recipient, $subject, $content, $submission, $notificationType); + } + else { + $this->sendNotificationDigitalPost($recipient, $subject, $content, $taskUrl, $actionLabel, $submission, $notificationType); + } + } + } + catch (\Exception $exception) { + $this->error('Error sending notification: @message', $context + [ + '@message' => $exception->getMessage(), + 'handler_id' => 'os2forms_forloeb', + 'operation' => 'notification failed', + 'exception' => $exception, + ]); + + return NULL; + } + } + + /** + * Load advanced queue if any. + * + * @return \Drupal\advancedqueue\Entity\QueueInterface + * The queue. + */ + private function loadQueue(): QueueInterface { + $queueId = $this->config->get('processing')['queue'] ?? NULL; + + if (NULL === $queueId) { + throw new RuntimeException('Cannot get queue ID'); + } + + $queue = $this->queueStorage->load($queueId); + if (NULL === $queue) { + throw new RuntimeException(sprintf('Cannot load queue %s', $queueId)); + } + + return $queue; + } + + /** + * Send notification email. + */ + private function sendNotificationEmail( + string $recipient, + string $subject, + string $body, + WebformSubmissionInterface $submission, + string $notificationType + ): void { + try { + $message = [ + 'subject' => $subject, + 'body' => $body, + 'html' => TRUE, + ]; + + $langcode = $this->languageManager->getCurrentLanguage()->getId(); + + $result = $this->mailManager->mail( + 'os2forms_forloeb', + 'notification', + $recipient, + $langcode, + $message + ); + + if (!$result['result']) { + throw new RuntimeException(sprintf('Error sending notification (%s) email to %s', $notificationType, $recipient)); + } + + $this->notice('Email notification (@type) sent to @recipient', [ + '@type' => $notificationType, + 'webform_submission' => $submission, + '@recipient' => $recipient, + 'handler_id' => 'os2forms_forloeb', + 'operation' => 'notification sent', + ]); + } + catch (\Exception $exception) { + $this->error('Error sending email notification (@type): @message', [ + '@type' => $notificationType, + '@message' => $exception->getMessage(), + 'webform_submission' => $submission, + 'handler_id' => 'os2forms_forloeb', + 'operation' => 'failed sending notification', + ]); + } + } + + /** + * Send notification digital post. + */ + private function sendNotificationDigitalPost( + string $recipient, + string $subject, + string $content, + string $taskUrl, + string $actionLabel, + WebformSubmissionInterface $submission, + string $notificationType + ): void { + if (!$this->moduleHandler->moduleExists('os2forms_digital_post')) { + throw new RuntimeException('Cannot send digital post. Module os2forms_digital_post not installed.'); + } + + try { + $document = new Document( + $content, + Document::MIME_TYPE_PDF, + $subject . '.pdf' + ); + + $senderLabel = $subject; + $messageLabel = $subject; + + $recipientLookupResult = $this->digitalPostHelper->lookupRecipient($recipient); + $actions = [ + (new Action()) + ->setActionCode(SF1601::ACTION_SELVBETJENING) + ->setEntryPoint((new EntryPoint()) + ->setUrl($taskUrl) + ) + ->setLabel($actionLabel), + ]; + + $message = $this->digitalPostHelper->getMeMoHelper()->buildMessage($recipientLookupResult, $senderLabel, + $messageLabel, $document, $actions); + $forsendelse = $this->digitalPostHelper->getForsendelseHelper()->buildForsendelse($recipientLookupResult, + $messageLabel, $document); + $this->digitalPostHelper->sendDigitalPost( + SF1601::TYPE_AUTOMATISK_VALG, + $message, + $forsendelse, + $submission + ); + + $this->notice('Digital post notification sent (@type)', [ + '@type' => $notificationType, + 'webform_submission' => $submission, + 'handler_id' => 'os2forms_forloeb', + 'operation' => 'notification sent', + ]); + } + catch (\Exception $exception) { + $this->error('Error sending digital post notification (@type): @message', [ + '@type' => $notificationType, + '@message' => $exception->getMessage(), + 'webform_submission' => $submission, + 'handler_id' => 'os2forms_forloeb', + 'operation' => 'failed sending notification', + ]); + } + } + + /** + * Render notification. + * + * @param \Drupal\webform\WebformSubmissionInterface $submission + * The submission. + * @param string $handlerId + * The handler ID. + * @param string $notificationType + * The notification type. + * @param array $templateTask + * The Maestro template task. + * @param int $maestroQueueID + * The Maestro queue ID. + * @param string|null $contentType + * Optional content type. If not set the content type will be compoted based + * on the recipient. + * + * @return array + * The rendered notification with keys + * - content + * - contentType + * - recipient + * - subject + * - taskUrl (for digital post) + * - actionLabel (for digital post) + */ + public function renderNotification(WebformSubmissionInterface $submission, string $handlerId, string $notificationType, array $templateTask, int $maestroQueueID, string $contentType = NULL): array { + $handler = $submission->getWebform()->getHandler($handlerId); + $settings = $handler->getSettings(); + + $data = $submission->getData(); + $recipientElement = $settings[MaestroNotificationHandler::NOTIFICATION][MaestroNotificationHandler::RECIPIENT_ELEMENT] ?? NULL; + // Handle os2forms_person_lookup element. + $recipient = $data[$recipientElement]['cpr_number'] + // Simple element. + ?? $data[$recipientElement] + ?? NULL; + + if ($notificationType === self::OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION) { + $recipient = $settings[MaestroNotificationHandler::NOTIFICATION][$notificationType][MaestroNotificationHandler::NOTIFICATION_RECIPIENT] ?? NULL; + } + + if (NULL !== $recipient) { + // Lifted from MaestroEngine. + $maestroTokenData = [ + 'maestro' => [ + 'task' => $templateTask, + 'queueID' => $maestroQueueID, + ], + ]; + + $notificationSetting = $settings[MaestroNotificationHandler::NOTIFICATION][$notificationType] ?? NULL; + if (NULL === $notificationSetting) { + throw new RuntimeException(sprintf('Cannot get setting for %s notification', $notificationType)); + } + + $processValue = static fn (string $value) => $value; + + // Handle a preview, i.e. not a real Maestro context. + if (empty($templateTask) || 0 === $maestroQueueID) { + $taskUrl = Url::fromRoute('os2forms_forloeb.meastro_notification.preview_message', ['message' => 'This is just a preview'])->toString(TRUE)->getGeneratedUrl(); + + $processValue = static function (string $value) use ($taskUrl) { + // Replace href="[maestro:task-url]" with href="«$taskUrl»". + $value = preg_replace('/href\s*=\s*["\']\[maestro:task-url\]["\']/', sprintf('href="%s"', htmlspecialchars($taskUrl)), $value); + $value = preg_replace('/\[(maestro:[^]]+)\]/', '[\1]', $value); + + return $value; + }; + } + else { + $taskUrl = TaskHandler::getHandlerURL($maestroQueueID); + } + + $subject = $this->tokenManager->replace( + $processValue($notificationSetting[MaestroNotificationHandler::NOTIFICATION_SUBJECT]), + $submission, + $maestroTokenData + ); + + $content = $notificationSetting[MaestroNotificationHandler::NOTIFICATION_CONTENT]; + if (isset($content['value'])) { + // Process tokens in content. + $content['value'] = $this->tokenManager->replace( + $processValue($content['value']), + $submission, + $maestroTokenData + ); + } + + $actionLabel = $this->tokenManager->replace($notificationSetting[MaestroNotificationHandler::NOTIFICATION_ACTION_LABEL], $submission); + + if (NULL === $contentType) { + $contentType = filter_var($recipient, FILTER_VALIDATE_EMAIL) ? 'email' : 'pdf'; + } + + switch ($contentType) { + case 'email': + $content = $this->renderHtml($contentType, $subject, $content, $taskUrl, $actionLabel, $submission); + break; + + case 'pdf': + $pdfContent = $this->renderHtml($contentType, $subject, $content, $taskUrl, $actionLabel, $submission); + + // Get dompdf plugin from entity_print module. + /** @var \Drupal\entity_print\Plugin\EntityPrint\PrintEngine\PdfEngineBase $printer */ + $printer = $this->entityPrintPluginManager->createInstance('dompdf'); + $printer->addPage($pdfContent); + $content = $printer->getBlob(); + break; + + default: + throw new RuntimeException(sprintf('Invalid content type: %s', $contentType)); + } + + return [ + 'content' => $content, + 'contentType' => $contentType, + 'recipient' => $recipient, + 'subject' => $subject, + 'taskUrl' => $taskUrl, + 'actionLabel' => $actionLabel, + ]; + } + + throw new RuntimeException(); + } + + /** + * Build HTML. + */ + private function renderHtml( + string $type, + string $subject, + array $content, + string $taskUrl, + string $actionLabel, + WebformSubmissionInterface $submission + ): string|MarkupInterface { + $template = $this->config->get('templates')['notification_' . $type] ?? NULL; + if (file_exists($template)) { + $template = file_get_contents($template) ?: NULL; + } + if (NULL === $template) { + $template = 'Missing or invalid template'; + } + + $build = [ + '#type' => 'inline_template', + '#template' => $template, + '#context' => [ + 'message' => [ + 'subject' => $subject, + 'content' => $content, + ], + 'task_url' => $taskUrl, + 'action_label' => $actionLabel, + 'webform_submission' => $submission, + 'handler' => $this, + ], + ]; + + return Markup::create(trim((string) $this->webformThemeManager->renderPlain($build))); + } + + /** + * Implements hook_mail(). + */ + public function mail(string $key, array &$message, array $params) { + switch ($key) { + case 'notification': + $message['subject'] = $params['subject']; + $message['body'][] = $params['body']; + if (isset($params['attachments'])) { + foreach ($params['attachments'] as $attachment) { + $message['params']['attachments'][] = $attachment; + } + } + break; + } + } + + /** + * Implements hook_mail_alter(). + */ + public function mailAlter(array &$message) { + if (str_starts_with($message['id'], 'os2forms_forloeb')) { + if (isset($message['params']['html']) && $message['params']['html']) { + $message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed'; + } + } + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = []): void { + $this->logger->log($level, $message, $context); + // @see https://www.drupal.org/node/3020595 + if (isset($context['webform_submission']) && $context['webform_submission'] instanceof WebformSubmissionInterface) { + $this->submissionLogger->log($level, $message, $context); + } + } + + /** + * Implements hook_maestro_can_user_execute_task_alter(). + */ + public function maestroCanUserExecuteTaskAlter(bool &$returnValue, int $queueID, int $userID): void { + // Perform our checks only if an anonymous user has been barred access. + if (0 === $userID && FALSE === $returnValue) { + $templateTask = MaestroEngine::getTemplateTaskByQueueID($queueID); + if (isset($templateTask['assigned'])) { + $assignments = explode(',', $templateTask['assigned']); + + // Check if one of the assignments match our known anonymous roles. + $knownAnonymousAssignments = array_map( + static fn(string $role) => 'role:fixed:' . $role, + array_filter($this->config->get('known_anonymous_roles') ?: []) + ); + + foreach ($assignments as $assignment) { + if (in_array($assignment, $knownAnonymousAssignments, TRUE)) { + $returnValue = TRUE; + } + } + } + } + } + +} diff --git a/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php b/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php new file mode 100644 index 0000000..ab4eb94 --- /dev/null +++ b/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php @@ -0,0 +1,55 @@ +get(MaestroHelper::class) + ); + } + + /** + * {@inheritdoc} + */ + public function __construct( + array $configuration, + $plugin_id, + $plugin_definition, + readonly private MaestroHelper $helper + ) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public function process(Job $job): JobResult { + return $this->helper->processJob($job); + } + +} diff --git a/modules/os2forms_forloeb/src/Plugin/EngineTasks/MaestroWebformInheritTask.php b/modules/os2forms_forloeb/src/Plugin/EngineTasks/MaestroWebformInheritTask.php index b33bf4b..c461163 100644 --- a/modules/os2forms_forloeb/src/Plugin/EngineTasks/MaestroWebformInheritTask.php +++ b/modules/os2forms_forloeb/src/Plugin/EngineTasks/MaestroWebformInheritTask.php @@ -3,14 +3,10 @@ namespace Drupal\os2forms_forloeb\Plugin\EngineTasks; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Url; use Drupal\maestro\Engine\MaestroEngine; -use Drupal\maestro\Form\MaestroExecuteInteractive; use Drupal\maestro_webform\Plugin\EngineTasks\MaestroWebformTask; use Drupal\webform\Entity\WebformSubmission; -use Drupal\webform\WebformSubmissionForm; -use Drupal\webform\WebformSubmissionInterface; -use Symfony\Component\HttpFoundation\RedirectResponse; +use Drupal\webform\Utility\WebformArrayHelper; /** * Maestro Webform Task Plugin for Multiple Submissions. @@ -21,6 +17,7 @@ * ) */ class MaestroWebformInheritTask extends MaestroWebformTask { + public const INHERIT_WEBFORM_UNIQUE_ID = 'inherit_webform_unique_id'; /** * Constructor. @@ -65,25 +62,66 @@ public function getPluginId() { * {@inheritDoc} */ public function getTaskEditForm(array $task, $templateMachineName) { + // Get all webform tasks in template excluding the current task. + $template = MaestroEngine::getTemplate($templateMachineName); + $webformTasks = array_filter( + $template->tasks, + static fn(array $t) => $t['id'] !== $task['id'] && self::isWebformTask($t) + ); // We call the parent, as we need to add a field to the inherited form. $form = parent::getTaskEditForm($task, $templateMachineName); - $form['inherit_webform_unique_id'] = [ - '#type' => 'textfield', + $form[self::INHERIT_WEBFORM_UNIQUE_ID] = [ + '#type' => 'select', + '#options' => ['submission' => $this->t('Start')] + + array_map( + static fn(array $task) => sprintf('%s (%s)', $task['label'], $task['id']), + $webformTasks + ), '#title' => $this->t('Inherit Webform from:'), '#description' => $this->t('Put the unique identifier of the webform you want to inherit from (start-task=submission'), - '#default_value' => $task['data']['inherit_webform_unique_id'] ?? '', + '#default_value' => $task['data'][self::INHERIT_WEBFORM_UNIQUE_ID] ?? '', '#required' => TRUE, - ]; - $form['inherit_webform_create_submission'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Create submission'), - '#description' => $this->t('Create submission'), - '#default_value' => $task['data']['inherit_webform_create_submission'] ?? FALSE, + '#empty_option' => $this->t('Select task'), ]; return $form; } + /** + * {@inheritdoc} + */ + public function getAssignmentsAndNotificationsForm(array $task, $templateMachineName) { + $form = parent::getAssignmentsAndNotificationsForm($task, $templateMachineName); + + // @todo Find task by unique_id = $task['data']['inherit_webform_unique_id'] and point to webform. + $anonymousNotificationMessage = $this->t('Add a Meastro notification handler to the webform for the task selected under %inherit_webform_from', [ + '%inherit_webform_from' => $this->t('Inherit Webform from:'), + ]); + + WebformArrayHelper::insertBefore( + $form['edit_task_notifications'], 'token_tree', + 'anonymous_notification_message', + [ + '#theme' => 'status_messages', + '#message_list' => [ + 'status' => [$anonymousNotificationMessage], + ], + ] + ); + + return $form; + } + + /** + * Deside if a task is a webform task. + */ + public static function isWebformTask(array $task): bool { + return in_array($task['tasktype'] ?? NULL, [ + 'MaestroWebform', + 'MaestroWebformInherit', + ], TRUE); + } + /** * {@inheritDoc} */ @@ -92,157 +130,51 @@ public function prepareTaskForSave(array &$form, FormStateInterface $form_state, // Inherit from parent. parent::prepareTaskForSave($form, $form_state, $task); // Add custom field(s) to the inherited prepareTaskForSave method. - $task['data']['inherit_webform_unique_id'] = $form_state->getValue('inherit_webform_unique_id'); - $task['data']['inherit_webform_create_submission'] = $form_state->getValue('inherit_webform_create_submission'); + $task['data'][self::INHERIT_WEBFORM_UNIQUE_ID] = $form_state->getValue(self::INHERIT_WEBFORM_UNIQUE_ID); } /** - * {@inheritDoc} + * Implements hook_webform_submission_form_alter(). */ - public function getExecutableForm($modal, MaestroExecuteInteractive $parent) { - - // First, get hold of the interesting previous tasks. - $templateMachineName = MaestroEngine::getTemplateIdFromProcessId($this->processID); - $taskMachineName = MaestroEngine::getTaskIdFromQueueId($this->queueID); - $task = MaestroEngine::getTemplateTaskByID($templateMachineName, $taskMachineName); - - // Get user input from 'inherit_webform_unique_id'. - $webformInheritID = $task['data']['inherit_webform_unique_id']; - - // Load its corresponding webform submission. - $sid = MaestroEngine::getEntityIdentiferByUniqueID($this->processID, $webformInheritID); - if ($sid) { - $webform_submission = WebformSubmission::load($sid); - } - if (!isset($webform_submission)) { - \Drupal::logger('os2forms_forloeb')->error( - "Predecessors must have submissions with webforms attached." - ); - return FALSE; - } - // Copy the fields of the webform submission to the values array. - foreach ($webform_submission->getData() as $key => $value) { - if ($value) { - $field_values[$key] = $value; - } - } - // Now create webform submission, submit and attach to current process. - $templateTask = MaestroEngine::getTemplateTaskByQueueID($this->queueID); - $webformMachineName = $templateTask['data']['webform_machine_name']; - - $values = []; - $values['webform_id'] = $webformMachineName; - $values['data'] = $field_values; - - $createSubmission = (bool) ($task['data']['inherit_webform_create_submission'] ?? FALSE); - if ($createSubmission) { - // Create submission. - $new_submission = WebformSubmission::create($values); - - // Submit the webform submission. - $submission = WebformSubmissionForm::submitWebformSubmission($new_submission); - - // WebformSubmissionForm::submitWebformSubmission returns an array - // if the submission is not valid. - if (is_array($submission)) { - \Drupal::logger('os2forms_forloeb')->error( - "Can't create new submission: " . json_encode($submission) - ); - \Drupal::messenger()->addError('Webform data is invalid and could not be submitted.'); - return FALSE; - } - - $taskUniqueSubmissionId = $templateTask['data']['unique_id']; - - // Attach it to the Maestro process. - $sid = $new_submission->id(); - MaestroEngine::createEntityIdentifier( - $this->processID, $new_submission->getEntityTypeId(), - $new_submission->bundle(), $taskUniqueSubmissionId, $sid - ); - - // Important: Apparently the form must be generated after calling - // MaestroEngine::createEntityIdentifier for this to work. - $form = parent::getExecutableForm($modal, $parent); - // Catch os2forms-forloeb access token and pass it further. - if ($form instanceof RedirectResponse && $token = \Drupal::request()->query->get('os2forms-forloeb-ws-token')) { - // Check token to previous submission and update it to new one. - if ($token === $webform_submission->getToken()) { - $token = $new_submission->getToken(); - $url = Url::fromUserInput($form->getTargetUrl(), ['query' => ['os2forms-forloeb-ws-token' => $token]]); - $form = new RedirectResponse($url->toString()); + public static function webformSubmissionFormAlter(array &$form, FormStateInterface $formState, string $formId) { + // @todo Clean up and align with MaestroHelper::maestroZeroUserNotification(). + if ($queueID = self::getQueueIdFromRequest()) { + $templateTask = MaestroEngine::getTemplateTaskByQueueID($queueID); + if (self::isWebformTask($templateTask)) { + if ($inheritWebformUniqueId = ($templateTask['data'][self::INHERIT_WEBFORM_UNIQUE_ID] ?? NULL)) { + $processID = MaestroEngine::getProcessIdFromQueueId($queueID); + $entityIdentifier = MaestroEngine::getAllEntityIdentifiersForProcess($processID)[$inheritWebformUniqueId] ?? NULL; + if ('webform_submission' === ($entityIdentifier['entity_type'] ?? NULL)) { + $submission = WebformSubmission::load($entityIdentifier['entity_id']); + $data = $submission->getData(); + foreach ($data as $key => $value) { + if (isset($form['elements'][$key])) { + $form['elements'][$key]['#default_value'] = $value; + } + } + } } } } - else { - // Store values in session. - $values['processID'] = $this->processID; - $values['queueID'] = $this->queueID; - $values['webformInheritID'] = $webformInheritID; - - self::setTaskValues($this->queueID, $values); - - $form = parent::getExecutableForm($modal, $parent); - } - - return $form; } /** - * Implements hook_ENTITY_TYPE_prepare_form(). + * Get Maestro queue ID from request. */ - public static function webformSubmissionPrepareForm(WebformSubmissionInterface $webformSubmission, string $operation, FormStateInterface $formState): void { + public static function getQueueIdFromRequest(): ?int { + $queueID = NULL; $request = \Drupal::request(); - $isMaestro = (bool) $request->query->get('maestro', 0); - $queueID = (int) $request->query->get('queueid', 0); - if ($isMaestro && $queueID > 0) { - $values = self::getTaskValues($queueID); - if (isset($values['data'])) { - foreach ($values['data'] as $name => $value) { - $webformSubmission->setElementData($name, $value); - } + if ($sitewideToken = \Drupal::service('config.factory')->get('maestro.settings')->get('maestro_sitewide_token')) { + $token = $request->query->get($sitewideToken); + if (is_string($token)) { + $queueID = MaestroEngine::getQueueIdFromToken($token); } } - } - - /** - * Get task values from session. - * - * @param int $queueID - * The queue ID. - * - * @return array - * The task values if any. - */ - private static function getTaskValues($queueID) { - $sessionKey = self::formatTaskValuesSessionKey($queueID); - return \Drupal::request()->getSession()->get($sessionKey); - } - - /** - * Set task values in session. - * - * @param int $queueID - * The queue ID. - * @param array $values - * The values. - */ - private static function setTaskValues($queueID, array $values) { - $sessionKey = self::formatTaskValuesSessionKey($queueID); - \Drupal::request()->getSession()->set($sessionKey, $values); - } + if (empty($queueID)) { + $queueID = $request->query->get('queueid'); + } - /** - * Format task values session key. - * - * @param int $queueID - * The queue ID. - * - * @return string - * The formatted session key. - */ - private static function formatTaskValuesSessionKey($queueID) { - return sprintf('os2forms_forloeb_inherited_values_%s', $queueID); + return (int) $queueID ?: NULL; } } diff --git a/modules/os2forms_forloeb/src/Plugin/WebformHandler/MaestroNotificationHandler.php b/modules/os2forms_forloeb/src/Plugin/WebformHandler/MaestroNotificationHandler.php new file mode 100644 index 0000000..90262db --- /dev/null +++ b/modules/os2forms_forloeb/src/Plugin/WebformHandler/MaestroNotificationHandler.php @@ -0,0 +1,290 @@ +loggerFactory = $container->get('logger.factory'); + $instance->configFactory = $container->get('config.factory'); + $instance->renderer = $container->get('renderer'); + $instance->entityTypeManager = $container->get('entity_type.manager'); + $instance->conditionsValidator = $container->get('webform_submission.conditions_validator'); + + $instance->setConfiguration($configuration); + + return $instance; + } + + /** + * {@inheritdoc} + */ + public function getSummary() { + return [ + 'info' => [ + '#prefix' => '
', + '#suffix' => '
', + '#markup' => $this->t('Sends notification (@enabled_notification_types) when triggered by Maestro. The notification will be sent to the person identified by the value of the %element element.', [ + '@enabled_notification_types' => implode(', ', $this->getEnabledNotifications()), + '%element' => $this->configuration[self::NOTIFICATION][self::RECIPIENT_ELEMENT] ?? NULL, + ]), + ], + 'preview' => [ + '#prefix' => '
', + '#suffix' => '
', + ] + + Link::createFromRoute( + $this->t('Preview notifications'), + 'os2forms_forloeb.meastro_notification.preview', [ + 'webform' => $this->getWebform()->id(), + 'handler' => $this->getHandlerId(), + 'content_type' => 'email', + ] + )->toRenderable(), + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $formState) { + $form[self::NOTIFICATION] = [ + '#type' => 'fieldset', + '#title' => $this->t('Notification'), + ]; + + $availableElements = $this->getRecipientElements(); + $form[self::NOTIFICATION][static::RECIPIENT_ELEMENT] = [ + '#type' => 'select', + '#title' => $this->t('Element that contains the recipient identifier (email, CPR or CVR) of the notification'), + '#required' => TRUE, + '#default_value' => $this->configuration[self::NOTIFICATION][self::RECIPIENT_ELEMENT] ?? NULL, + '#options' => $availableElements, + ]; + + $form[self::NOTIFICATION][self::SENDER_LABEL] = [ + '#type' => 'textfield', + '#title' => $this->t('Sender label'), + '#required' => TRUE, + '#default_value' => $this->configuration[self::NOTIFICATION][self::SENDER_LABEL] ?? NULL, + '#maxlength' => self::SENDER_LABEL_MAX_LENGTH, + ]; + + foreach ([ + MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ASSIGNMENT => $this->t('Assignment'), + MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_REMINDER => $this->t('Reminder'), + MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION => $this->t('Escalation'), + ] as $notificationType => $label) { + $states = static function (bool $required = TRUE) use ($notificationType): array { + $states = [ + 'visible' => [ + ':input[name="settings[notification][' . $notificationType . '][notification_enable]"]' => ['checked' => TRUE], + ], + ]; + + if ($required) { + $states['required'] = [ + ':input[name="settings[notification][' . $notificationType . '][notification_enable]"]' => ['checked' => TRUE], + ]; + } + + return $states; + }; + + $form[self::NOTIFICATION][$notificationType] = [ + '#type' => 'fieldset', + '#title' => $label, + ]; + + $form[self::NOTIFICATION][$notificationType][self::NOTIFICATION_ENABLE] = [ + '#type' => 'checkbox', + '#title' => $this->t('Enable @type notification', ['@type' => $label]), + '#default_value' => $this->configuration[self::NOTIFICATION][$notificationType][self::NOTIFICATION_ENABLE] ?? ($notificationType === MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ASSIGNMENT), + ]; + + if ($notificationType === MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION) { + $form[self::NOTIFICATION][$notificationType][self::NOTIFICATION_RECIPIENT] = [ + '#type' => 'email', + '#title' => $this->t('@type recipient', ['@type' => $label]), + '#default_value' => $this->configuration[self::NOTIFICATION][$notificationType][self::NOTIFICATION_RECIPIENT] ?? NULL, + '#states' => $states(), + ]; + } + + $form[self::NOTIFICATION][$notificationType][self::NOTIFICATION_SUBJECT] = [ + '#type' => 'textfield', + '#title' => $this->t('Subject'), + '#default_value' => $this->configuration[self::NOTIFICATION][$notificationType][self::NOTIFICATION_SUBJECT] ?? NULL, + '#maxlength' => self::NOTIFICATION_SUBJECT_MAX_LENGTH, + '#states' => $states(), + ]; + + $content = $this->configuration[self::NOTIFICATION][$notificationType][self::NOTIFICATION_CONTENT] ?? NULL; + if (isset($content['value'])) { + $content = $content['value']; + } + $form[self::NOTIFICATION][$notificationType][self::NOTIFICATION_CONTENT] = [ + '#type' => 'text_format', + '#format' => 'restricted_html', + '#title' => $this->t('Message'), + '#default_value' => $content ?? self::TOKEN_MAESTRO_TASK_URL, + '#description' => $this->t('The actual notification content. Must contain the @token_maestro_task_url token which is the URL to the Maestro task.', + [ + '@token_maestro_task_url' => self::TOKEN_MAESTRO_TASK_URL, + ]), + '#states' => $states(), + ]; + + $form[self::NOTIFICATION][$notificationType][self::NOTIFICATION_ACTION_LABEL] = [ + '#type' => 'textfield', + '#title' => $this->t('Action label'), + '#default_value' => $this->configuration[self::NOTIFICATION][$notificationType][self::NOTIFICATION_ACTION_LABEL] ?? NULL, + '#description' => $this->t('Label of the action in digital post'), + '#states' => $states(required: FALSE), + ]; + } + + return $this->setSettingsParents($form); + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $formState) { + parent::validateConfigurationForm($form, $formState); + + foreach ([ + MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ASSIGNMENT, + MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_REMINDER, + MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION, + ] as $notificationType) { + $key = [self::NOTIFICATION, $notificationType, self::NOTIFICATION_ENABLE]; + $enabled = $formState->getValue($key); + if (!$enabled) { + break; + } + $key = [self::NOTIFICATION, $notificationType, self::NOTIFICATION_CONTENT]; + $content = $formState->getValue($key); + if (isset($content['value'])) { + $content = $content['value']; + } + if (!str_contains($content, self::TOKEN_MAESTRO_TASK_URL)) { + $formState->setErrorByName( + implode('][', [self::NOTIFICATION, self::NOTIFICATION_CONTENT]), + $this->t('The notification content must contain the @token_maestro_task_url token', [ + '@token_maestro_task_url' => self::TOKEN_MAESTRO_TASK_URL, + ]) + ); + } + } + } + + /** + * Get recipient elements. + */ + private function getRecipientElements(): array { + $elements = $this->getWebform()->getElementsDecodedAndFlattened(); + + $elementTypes = [ + 'email', + 'textfield', + 'cpr_element', + 'cpr_value_element', + 'cvr_element', + 'cvr_value_element', + 'os2forms_person_lookup', + ]; + $elements = array_filter( + $elements, + static function (array $element) use ($elementTypes) { + return in_array($element['#type'], $elementTypes, TRUE); + } + ); + + return array_map(static function (array $element) { + return $element['#title']; + }, $elements); + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $formState) { + parent::submitConfigurationForm($form, $formState); + + $this->configuration[self::NOTIFICATION] = $formState->getValue(self::NOTIFICATION); + } + + /** + * Get all notification types. + */ + public function getEnabledNotifications(): array { + $enabledNotificationTypes = []; + + foreach ([ + MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ASSIGNMENT, + MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_REMINDER, + MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION, + ] as $notificationType) { + if ($this->configuration[self::NOTIFICATION][$notificationType][self::NOTIFICATION_ENABLE] ?? FALSE) { + $enabledNotificationTypes[$notificationType] = $notificationType; + } + } + + return $enabledNotificationTypes; + } + + /** + * Check if a notification type is enabled. + */ + public function isNotificationEnabled(string $notificationType): bool { + return isset($this->getEnabledNotifications()[$notificationType]); + } + +} diff --git a/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-email-html.html.twig b/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-email-html.html.twig new file mode 100644 index 0000000..17e0f8e --- /dev/null +++ b/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-email-html.html.twig @@ -0,0 +1,35 @@ +{# +/** + * @file + * Template for Maestro notification email. + * + * Available variables: + * - message: The notification message + * - subject: the notification subject + * - contect: the notification content. Must be rendered as `processed_text`, i.e.: + * @code + * {{ { + * '#type': 'processed_text', + * '#text': message.content.value, + * '#format': message.content.format, + * } }} + * @endcode + * - notification_type: The type of notification () + * - task_url: URL of the task. + * - action_label: Optional label for the task action. + */ +#} +
+ {# @see https://api.drupal.org/api/drupal/core%21modules%21filter%21filter.module/function/check_markup/9 #} +
+ {{ { + '#type': 'processed_text', + '#text': message.content.value, + '#format': message.content.format, + } }} +
+ + {% if task_url|default(false) %} + {{ action_label|default('Go to your task'|t) }} + {% endif %} +
diff --git a/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-pdf-html.html.twig b/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-pdf-html.html.twig new file mode 100644 index 0000000..a3a671d --- /dev/null +++ b/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-pdf-html.html.twig @@ -0,0 +1,63 @@ +{# +/** + * @file + * Template for Maestro notification PDF. + * + * Available variables: + * - message: The notification message + * - subject: the notification subject + * - contect: the notification content. Must be rendered as `processed_text`, i.e.: + * @code + * {{ { + * '#type': 'processed_text', + * '#text': message.content.value, + * '#format': message.content.format, + * } }} + * @endcode + * - notification_type: The type of notification () + * - task_url: URL of the task. + * - action_label: Optional label for the task action. + */ +#} + + + + + + {{ message.subject }} + + + +
+ Aarhus Kommune-logo +
+ +
+ {# @see https://api.drupal.org/api/drupal/core%21modules%21filter%21filter.module/function/check_markup/9 #} +
+ {{ { + '#type': 'processed_text', + '#text': message.content.value, + '#format': message.content.format, + } }} +
+
+ + diff --git a/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-preview.html.twig b/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-preview.html.twig new file mode 100644 index 0000000..18fa347 --- /dev/null +++ b/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-preview.html.twig @@ -0,0 +1,87 @@ +{# + /** + * @file + * Template for Maestro notification preview. + * + * Available variables: + * - preview_urls: The preview URLs + * - prev: Previous submission preview URL (if any) + * - self: The current preview URL (if any) + * - next: Next submission preview URL (if any) + * - webform: The webform + * - handler: The handler ID + * - notification_type: The notification type (assignment, reminder, escalation) + * - content_type: The content type (email, pdf) + * - submission: The submission ID + * - return_url: The return URL (to list of webform handlers) + * - render_url: The render URL to render the actual preview + */ + #} +
+ + + + +
+
{{ 'Subject'|t }}: {{ subject }}
+
{{ 'Recipient'|t }}: {{ recipient }}
+
+ + {% if render_url %} + + {% endif %} +
From 6f0fa1573ec5d0bee9a614a63a4baa23ba325814 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Thu, 14 Sep 2023 15:45:18 +0200 Subject: [PATCH 02/15] Made digital post module optional --- composer.json | 4 +++- modules/os2forms_forloeb/os2forms_forloeb.info.yml | 4 +++- modules/os2forms_forloeb/os2forms_forloeb.services.yml | 1 - modules/os2forms_forloeb/src/MaestroHelper.php | 5 ++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index cc8c44d..7e3b296 100644 --- a/composer.json +++ b/composer.json @@ -67,7 +67,6 @@ "drupal/webform_validation": "^2.0", "drupal/webform_views": "^5.0@alpha", "drupal/workflow_participants": "^2.4", - "os2forms/os2forms_digital_post": "^3.0", "os2web/os2web_datalookup": "^1.0", "os2web/os2web_nemlogin": "^1.0", "phpoffice/phpword": "^0.18.2", @@ -75,6 +74,9 @@ "webmozart/path-util": "^2.3", "zaporylie/composer-drupal-optimizations": "^1.2" }, + "suggest": { + "os2forms/os2forms_digital_post": "Send Maestro notifications via digital post (see https://github.com/itk-dev/os2forms_digital_post/blob/main/README.md)" + }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1", "drupal/coder": "^8.3", diff --git a/modules/os2forms_forloeb/os2forms_forloeb.info.yml b/modules/os2forms_forloeb/os2forms_forloeb.info.yml index cb8da31..fdf9f36 100644 --- a/modules/os2forms_forloeb/os2forms_forloeb.info.yml +++ b/modules/os2forms_forloeb/os2forms_forloeb.info.yml @@ -49,6 +49,8 @@ dependencies: - 'drupal:webform_submission_log' - 'drupal:webform_templates' - 'drupal:workflow_participants' - - 'os2forms_digital_post:os2forms_digital_post' + # os2forms_digital_post may be used for sending Meastro notifications via digital post + # - 'os2forms_digital_post:os2forms_digital_post' + 'interface translation project': os2forms_forloeb 'interface translation server pattern': modules/contrib/os2forms_forloeb/translations/os2forms_forloeb.da.po diff --git a/modules/os2forms_forloeb/os2forms_forloeb.services.yml b/modules/os2forms_forloeb/os2forms_forloeb.services.yml index 2d2b73c..132f3f3 100644 --- a/modules/os2forms_forloeb/os2forms_forloeb.services.yml +++ b/modules/os2forms_forloeb/os2forms_forloeb.services.yml @@ -19,4 +19,3 @@ services: - '@logger.channel.os2forms_forloeb_submission' - '@module_handler' - '@plugin.manager.entity_print.print_engine' - - '@Drupal\os2forms_digital_post\Helper\DigitalPostHelper' diff --git a/modules/os2forms_forloeb/src/MaestroHelper.php b/modules/os2forms_forloeb/src/MaestroHelper.php index 4554c1a..e5f01fa 100644 --- a/modules/os2forms_forloeb/src/MaestroHelper.php +++ b/modules/os2forms_forloeb/src/MaestroHelper.php @@ -88,7 +88,6 @@ public function __construct( readonly private LoggerChannelInterface $submissionLogger, readonly private ModuleHandlerInterface $moduleHandler, readonly private EntityPrintPluginManagerInterface $entityPrintPluginManager, - readonly private DigitalPostHelper $digitalPostHelper ) { $this->config = $configFactory->get(SettingsForm::SETTINGS); $this->webformSubmissionStorage = $entityTypeManager->getStorage('webform_submission'); @@ -342,8 +341,8 @@ private function sendNotificationDigitalPost( WebformSubmissionInterface $submission, string $notificationType ): void { - if (!$this->moduleHandler->moduleExists('os2forms_digital_post')) { - throw new RuntimeException('Cannot send digital post. Module os2forms_digital_post not installed.'); + if (NULL === $this->digitalPostHelper) { + throw new RuntimeException('Cannot send digital post. Module OS2Forms Digital Post (os2forms_digital_post) not installed.'); } try { From 7e5a35fd4ff3917203d2aecfdd63ccd00048a510 Mon Sep 17 00:00:00 2001 From: Stanislav Kutasevits Date: Wed, 27 Sep 2023 10:48:02 +0300 Subject: [PATCH 03/15] os2forms_permissions_by_term: removing node access control --- .../os2forms_permissions_by_term/README.md | 2 +- .../os2forms_permissions_by_term.module | 28 ---- .../src/Helper/Helper.php | 140 ------------------ 3 files changed, 1 insertion(+), 169 deletions(-) diff --git a/modules/os2forms_permissions_by_term/README.md b/modules/os2forms_permissions_by_term/README.md index 6d305f7..2c57293 100644 --- a/modules/os2forms_permissions_by_term/README.md +++ b/modules/os2forms_permissions_by_term/README.md @@ -19,7 +19,7 @@ Alternative change your site configuration on admin/permissions-by-term/settings be thoroughly tested. ## Usage -- The user affiliation taxonomy is added to webform config form, nodes (of type webform) and Maestro workflow forms. +- The user affiliation taxonomy is added to webform config form and Maestro workflow forms. - The Permissions by Term module adds a form element to the user form. - When a user visits an entity of the above mentioned this module checks for match between the entity and the users affiliation. If no match is found access is denied. diff --git a/modules/os2forms_permissions_by_term/os2forms_permissions_by_term.module b/modules/os2forms_permissions_by_term/os2forms_permissions_by_term.module index 12ea729..31ab4b7 100644 --- a/modules/os2forms_permissions_by_term/os2forms_permissions_by_term.module +++ b/modules/os2forms_permissions_by_term/os2forms_permissions_by_term.module @@ -8,7 +8,6 @@ use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; -use Drupal\node\NodeInterface; use Drupal\os2forms_permissions_by_term\Form\SettingsForm; use Drupal\views\Plugin\views\query\QueryPluginBase; use Drupal\views\ViewExecutable; @@ -50,15 +49,6 @@ function os2forms_permissions_by_term_form_webform_settings_form_alter(array &$f \Drupal::service('os2forms_permissions_by_term.helper')->webformAlter($form, $form_state, 'settings'); } -/** - * Implements hook_form_FORM_ID_alter(). - * - * Alter the node add/edit form. - */ -function os2forms_permissions_by_term_form_node_form_alter(array &$form, FormStateInterface $form_state) { - \Drupal::service('os2forms_permissions_by_term.helper')->nodeFormAlter($form, $form_state); -} - /** * Implements hook_ENTITY_TYPE_access() for webform entities. * @@ -68,15 +58,6 @@ function os2forms_permissions_by_term_webform_access(WebformInterface $webform, return \Drupal::service('os2forms_permissions_by_term.helper')->webformAccess($webform, $operation, $account); } -/** - * Implements hook_ENTITY_TYPE_access() for node entities. - * - * Allow/deny access to node. - */ -function os2forms_permissions_by_term_node_access(NodeInterface $node, $operation, AccountInterface $account) { - return \Drupal::service('os2forms_permissions_by_term.helper')->nodeAccess($node, $operation, $account); -} - /** * Implements hook_form_FORM_ID_alter(). * @@ -122,15 +103,6 @@ function os2forms_permissions_by_term_form_alter(array &$form, FormStateInterfac \Drupal::service('os2forms_permissions_by_term.maestro_template_helper')->maestroFormAlter($form, $form_state, $form_id); } -/** - * Implements hook_options_list_alter(). - * - * Change options list field for node.field_os2forms_permissions. - */ -function os2forms_permissions_by_term_options_list_alter(array &$options, array $context) { - \Drupal::service('os2forms_permissions_by_term.helper')->optionsListAlter($options, $context); -} - /** * Implements hook_views_query_alter(). * diff --git a/modules/os2forms_permissions_by_term/src/Helper/Helper.php b/modules/os2forms_permissions_by_term/src/Helper/Helper.php index 06a916d..47c6d30 100644 --- a/modules/os2forms_permissions_by_term/src/Helper/Helper.php +++ b/modules/os2forms_permissions_by_term/src/Helper/Helper.php @@ -11,10 +11,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountProxyInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; -use Drupal\Core\Url; -use Drupal\node\NodeInterface; use Drupal\permissions_by_term\Service\AccessStorage; -use Drupal\user\Entity\User; use Drupal\webform\WebformInterface; /** @@ -209,34 +206,6 @@ public function webformAccess(WebformInterface $webform, $operation, AccountInte return AccessResult::neutral(); } - /** - * Implementation of hook_ENTITY_TYPE_access(). - * - * Check access on node related operations. - * - * @param \Drupal\node\NodeInterface $node - * The node entity. - * @param string $operation - * The operation being performed on the node. - * @param \Drupal\Core\Session\AccountInterface $account - * The current user. - * - * @return mixed - * The resulting access permission. - */ - public function nodeAccess(NodeInterface $node, $operation, AccountInterface $account) { - if ('webform' === $node->bundle()) { - switch ($operation) { - case 'view': - // Deny access to node view if no permission by term is set. - $nodePermissionsByTerm = $node->field_os2forms_permissions->getValue(); - return empty($nodePermissionsByTerm) - ? AccessResult::forbidden() - : AccessResult::neutral(); - } - } - } - /** * Custom submit handler for webform add/edit form. * @@ -263,54 +232,6 @@ public function webformSubmit(array $form, FormStateInterface $form_state) { $webform->save(); } - /** - * Implementation of hook_form_FORM_ID_alter(). - * - * Add permission by term selection to node "add" and "edit". - * - * @param array $form - * The form being altered. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The state of the form. - * - * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException - * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException - */ - public function nodeFormAlter(array &$form, FormStateInterface $form_state) { - /** @var \Drupal\Core\Entity\EntityForm $formObject */ - $formObject = $form_state->getFormObject(); - $nodeBundle = $formObject->getEntity()->bundle(); - if (1 === (int) $this->account->id() || 'webform' !== $nodeBundle) { - return; - } - - // Run custom submit handler before default node submission. - array_unshift( - $form['actions']['submit']['#submit'], - [$this, 'nodeWebformPermisisonsByTermSubmit'] - ); - $user = $this->entityTypeManager->getStorage('user')->load($this->account->id()); - $userTerms = $this->accessStorage->getPermittedTids($user->id(), $user->getRoles()); - $anonymousTerms = $this->accessStorage->getPermittedTids(0, ['anonymous']); - $webformReference = $form['webform']['widget'][0]['target_id']['#default_value']; - // If a webform is referenced from the node add message. - if ($webformReference) { - $url = URL::fromRoute('entity.webform.settings_access', ['webform' => $webformReference])->toString(); - $form['field_os2forms_permissions']['widget'][0]['#prefix'] = - '
' . $this->t('Anonymous access to view this content is set on the related webform access page . (Create submissions permission)', ['@url' => $url]) . '
'; - } - // Disable anonymous terms. They should always be fetched from webform. - foreach ($anonymousTerms as $termId) { - $form['field_os2forms_permissions']['widget'][$termId]['#disabled'] = TRUE; - } - - // Set access value automatically if user only has one term option. - if (1 === count($userTerms)) { - $form['field_os2forms_permissions']['widget']['#disabled'] = TRUE; - $form['field_os2forms_permissions']['widget']['#default_value'][] = $userTerms[0]; - } - } - /** * Implements hook_field_widget_multivalue_WIDGET_TYPE_form_alter(). * @@ -330,67 +251,6 @@ public function fieldWidgetWebformEntityReferenceFormAlter(array &$elements) { $elements[0]['target_id']['#options'] = $result; } - /** - * Implements hook_options_list_alter(). - * - * Change options list field for node.field_os2forms_permissions. - * Add anonymous option to allow the form to be displayed for anonymous users. - * - * @param array $options - * The options of the list. - * @param array $context - * The context of the options list. - * - * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException - * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException - */ - public function optionsListAlter(array &$options, array $context) { - // Alter the field_os2forms_permissions options list. - if ('node.field_os2forms_permissions' !== $context['fieldDefinition']->getFieldStorageDefinition()->id()) { - return; - } - // Limit options to those available on user profile. - $options = []; - $user = $this->entityTypeManager->getStorage('user')->load($this->account->id()); - $userTerms = $this->accessStorage->getPermittedTids($user->id(), $user->getRoles()); - foreach ($userTerms as $userTerm) { - $term = $this->entityTypeManager->getStorage('taxonomy_term')->load($userTerm); - $options[$userTerm] = $term->label(); - } - $anonymousTerms = $this->accessStorage->getPermittedTids(0, ['anonymous']); - foreach ($anonymousTerms as $termId) { - $term = $this->entityTypeManager->getStorage('taxonomy_term')->load($termId); - $label = $this->t('@term_label (Note: View permission only. This setting depends on the related webform.)', ['@term_label' => $term->label()]); - $options = [$termId => $label] + $options; - } - } - - /** - * Custom submit handler for setting permissions by term on node. - * - * @param array $form - * The form that is being submitted. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The state of the form being submitted. - */ - public function nodeWebformPermisisonsByTermSubmit(array $form, FormStateInterface $form_state) { - $webformReference = $form_state->getValue('webform'); - $webformTarget = $webformReference['0']['target_id'] ?? NULL; - if (!$webformTarget) { - return; - } - $existingValues = $form_state->getValue('field_os2forms_permissions'); - $anonymousTerms = $this->accessStorage->getPermittedTids(0, ['anonymous']); - $anonymousUser = User::getAnonymousUser(); - $referencedWebform = $this->entityTypeManager->getStorage('webform')->load($webformTarget); - foreach ($anonymousTerms as $termId) { - if ($referencedWebform->access('submission_create', $anonymousUser)) { - $existingValues[] = ['target_id' => $termId]; - } - } - $form_state->setValue('field_os2forms_permissions', $existingValues); - } - /** * Add to the private variable webformSelectOptions. * From 3a9ee78d6135ffbc6ddb628057447f507697d7a7 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Thu, 28 Sep 2023 10:09:41 +0200 Subject: [PATCH 04/15] Fixed inherit from options --- .../src/Plugin/EngineTasks/MaestroWebformInheritTask.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/os2forms_forloeb/src/Plugin/EngineTasks/MaestroWebformInheritTask.php b/modules/os2forms_forloeb/src/Plugin/EngineTasks/MaestroWebformInheritTask.php index c461163..b04137c 100644 --- a/modules/os2forms_forloeb/src/Plugin/EngineTasks/MaestroWebformInheritTask.php +++ b/modules/os2forms_forloeb/src/Plugin/EngineTasks/MaestroWebformInheritTask.php @@ -68,6 +68,11 @@ public function getTaskEditForm(array $task, $templateMachineName) { $template->tasks, static fn(array $t) => $t['id'] !== $task['id'] && self::isWebformTask($t) ); + // Index by tasks' unique id. + $webformTasksByUniqueId = []; + foreach ($webformTasks as $id => $webformTask) { + $webformTasksByUniqueId[$webformTask['data']['unique_id'] ?? $id] = $webformTask; + } // We call the parent, as we need to add a field to the inherited form. $form = parent::getTaskEditForm($task, $templateMachineName); @@ -75,8 +80,8 @@ public function getTaskEditForm(array $task, $templateMachineName) { '#type' => 'select', '#options' => ['submission' => $this->t('Start')] + array_map( - static fn(array $task) => sprintf('%s (%s)', $task['label'], $task['id']), - $webformTasks + static fn(array $task) => sprintf('%s (%s)', $task['label'], $task['data']['unique_id'] ?? $task['id']), + $webformTasksByUniqueId ), '#title' => $this->t('Inherit Webform from:'), '#description' => $this->t('Put the unique identifier of the webform you want to inherit from (start-task=submission'), From 09cad5b0ebfa58471fedc4dc7d34c8f7586dbfdb Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Thu, 28 Sep 2023 16:11:04 +0200 Subject: [PATCH 05/15] Added Advanced Queue module --- composer.json | 1 + os2forms.info.yml | 31 ++++++++++++++++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/composer.json b/composer.json index 7e3b296..5b9c8af 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "cweagans/composer-patches": "^1.6.5", "dompdf/dompdf": "^2.0", "drupal/admin_toolbar": "^3.0", + "drupal/advancedqueue": "^1.0", "drupal/chosen": "^2.10", "drupal/ckeditor_a11ychecker": "^2.0@alpha", "drupal/clientside_validation": "^3.0", diff --git a/os2forms.info.yml b/os2forms.info.yml index 078e804..98c9b1a 100644 --- a/os2forms.info.yml +++ b/os2forms.info.yml @@ -6,27 +6,28 @@ core: 8.x core_version_requirement: ^8 || ^9 dependencies: + - 'drupal:advancedqueue' + - 'drupal:ckeditor' + - 'drupal:editor' + - 'drupal:entity_print' + - 'drupal:eu_cookie_compliance' + - 'drupal:honeypot' + - 'drupal:mailsystem' + - 'drupal:pathauto' + - 'drupal:redirect' + - 'drupal:smtp' + - 'drupal:taxonomy' + - 'drupal:token' - 'drupal:webform' - - 'drupal:webform_ui' - 'drupal:webform_access' - 'drupal:webform_attachment' + - 'drupal:webform_composite' + - 'drupal:webform_embed' - 'drupal:webform_entity_print' - 'drupal:webform_entity_print_attachment' + - 'drupal:webform_migrate' - 'drupal:webform_scheduled_email' - 'drupal:webform_submission_export_import' - 'drupal:webform_submission_log' - 'drupal:webform_templates' - - 'drupal:webform_composite' - - 'drupal:webform_embed' - - 'drupal:entity_print' - - 'drupal:webform_migrate' - - 'drupal:redirect' - - 'drupal:pathauto' - - 'drupal:token' - - 'drupal:smtp' - - 'drupal:mailsystem' - - 'drupal:honeypot' - - 'drupal:eu_cookie_compliance' - - 'drupal:taxonomy' - - 'drupal:editor' - - 'drupal:ckeditor' + - 'drupal:webform_ui' From 77ed50dbb6b8e63dc24b0078e9aa6a0db50286e9 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Thu, 28 Sep 2023 16:17:07 +0200 Subject: [PATCH 06/15] Cleaned up class constants --- .../os2forms_forloeb/src/MaestroHelper.php | 8 +++---- .../MaestroNotificationHandler.php | 22 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/os2forms_forloeb/src/MaestroHelper.php b/modules/os2forms_forloeb/src/MaestroHelper.php index e5f01fa..94ad9a5 100644 --- a/modules/os2forms_forloeb/src/MaestroHelper.php +++ b/modules/os2forms_forloeb/src/MaestroHelper.php @@ -42,9 +42,9 @@ class MaestroHelper implements LoggerInterface { use LoggerTrait; - public const OS2FORMS_FORLOEB_NOTIFICATION_ASSIGNMENT = 'assignment'; - public const OS2FORMS_FORLOEB_NOTIFICATION_REMINDER = 'reminder'; - public const OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION = 'escalation'; + const NOTIFICATION_ASSIGNMENT = 'assignment'; + const NOTIFICATION_REMINDER = 'reminder'; + const NOTIFICATION_ESCALATION = 'escalation'; /** * The config. @@ -432,7 +432,7 @@ public function renderNotification(WebformSubmissionInterface $submission, strin ?? $data[$recipientElement] ?? NULL; - if ($notificationType === self::OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION) { + if ($notificationType === self::NOTIFICATION_ESCALATION) { $recipient = $settings[MaestroNotificationHandler::NOTIFICATION][$notificationType][MaestroNotificationHandler::NOTIFICATION_RECIPIENT] ?? NULL; } diff --git a/modules/os2forms_forloeb/src/Plugin/WebformHandler/MaestroNotificationHandler.php b/modules/os2forms_forloeb/src/Plugin/WebformHandler/MaestroNotificationHandler.php index 90262db..d0afc20 100644 --- a/modules/os2forms_forloeb/src/Plugin/WebformHandler/MaestroNotificationHandler.php +++ b/modules/os2forms_forloeb/src/Plugin/WebformHandler/MaestroNotificationHandler.php @@ -117,9 +117,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $formStat ]; foreach ([ - MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ASSIGNMENT => $this->t('Assignment'), - MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_REMINDER => $this->t('Reminder'), - MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION => $this->t('Escalation'), + MaestroHelper::NOTIFICATION_ASSIGNMENT => $this->t('Assignment'), + MaestroHelper::NOTIFICATION_REMINDER => $this->t('Reminder'), + MaestroHelper::NOTIFICATION_ESCALATION => $this->t('Escalation'), ] as $notificationType => $label) { $states = static function (bool $required = TRUE) use ($notificationType): array { $states = [ @@ -145,10 +145,10 @@ public function buildConfigurationForm(array $form, FormStateInterface $formStat $form[self::NOTIFICATION][$notificationType][self::NOTIFICATION_ENABLE] = [ '#type' => 'checkbox', '#title' => $this->t('Enable @type notification', ['@type' => $label]), - '#default_value' => $this->configuration[self::NOTIFICATION][$notificationType][self::NOTIFICATION_ENABLE] ?? ($notificationType === MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ASSIGNMENT), + '#default_value' => $this->configuration[self::NOTIFICATION][$notificationType][self::NOTIFICATION_ENABLE] ?? ($notificationType === MaestroHelper::NOTIFICATION_ASSIGNMENT), ]; - if ($notificationType === MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION) { + if ($notificationType === MaestroHelper::NOTIFICATION_ESCALATION) { $form[self::NOTIFICATION][$notificationType][self::NOTIFICATION_RECIPIENT] = [ '#type' => 'email', '#title' => $this->t('@type recipient', ['@type' => $label]), @@ -200,9 +200,9 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form parent::validateConfigurationForm($form, $formState); foreach ([ - MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ASSIGNMENT, - MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_REMINDER, - MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION, + MaestroHelper::NOTIFICATION_ASSIGNMENT, + MaestroHelper::NOTIFICATION_REMINDER, + MaestroHelper::NOTIFICATION_ESCALATION, ] as $notificationType) { $key = [self::NOTIFICATION, $notificationType, self::NOTIFICATION_ENABLE]; $enabled = $formState->getValue($key); @@ -268,9 +268,9 @@ public function getEnabledNotifications(): array { $enabledNotificationTypes = []; foreach ([ - MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ASSIGNMENT, - MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_REMINDER, - MaestroHelper::OS2FORMS_FORLOEB_NOTIFICATION_ESCALATION, + MaestroHelper::NOTIFICATION_ASSIGNMENT, + MaestroHelper::NOTIFICATION_REMINDER, + MaestroHelper::NOTIFICATION_ESCALATION, ] as $notificationType) { if ($this->configuration[self::NOTIFICATION][$notificationType][self::NOTIFICATION_ENABLE] ?? FALSE) { $enabledNotificationTypes[$notificationType] = $notificationType; From 859355af527a3ce6c44feb262aec55819d7e61d8 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Thu, 28 Sep 2023 16:24:17 +0200 Subject: [PATCH 07/15] `private readonly` --- .../MaestroNotificationController.php | 4 ++-- .../src/Form/SettingsForm.php | 6 ++--- .../os2forms_forloeb/src/MaestroHelper.php | 24 +++++++++---------- .../JobType/SendMeastroNotification.php | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php b/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php index 1264d73..0da7b32 100644 --- a/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php +++ b/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php @@ -23,8 +23,8 @@ class MaestroNotificationController extends ControllerBase { * Constructor. */ public function __construct( - readonly private WebformSubmissionStorageInterface $webformSubmissionStorage, - readonly private MaestroHelper $maestroHelper + private readonly WebformSubmissionStorageInterface $webformSubmissionStorage, + private readonly MaestroHelper $maestroHelper ) { } diff --git a/modules/os2forms_forloeb/src/Form/SettingsForm.php b/modules/os2forms_forloeb/src/Form/SettingsForm.php index 5b8bdfe..d6f1eb8 100644 --- a/modules/os2forms_forloeb/src/Form/SettingsForm.php +++ b/modules/os2forms_forloeb/src/Form/SettingsForm.php @@ -28,9 +28,9 @@ class SettingsForm extends ConfigFormBase { */ public function __construct( ConfigFactoryInterface $configFactory, - readonly private RoleStorageInterface $roleStorage, - readonly private EntityStorageInterface $queueStorage, - readonly private ModuleExtensionList $moduleHandler + private readonly RoleStorageInterface $roleStorage, + private readonly EntityStorageInterface $queueStorage, + private readonly ModuleExtensionList $moduleHandler ) { parent::__construct($configFactory); } diff --git a/modules/os2forms_forloeb/src/MaestroHelper.php b/modules/os2forms_forloeb/src/MaestroHelper.php index 94ad9a5..9401b6e 100644 --- a/modules/os2forms_forloeb/src/MaestroHelper.php +++ b/modules/os2forms_forloeb/src/MaestroHelper.php @@ -51,28 +51,28 @@ class MaestroHelper implements LoggerInterface { * * @var \Drupal\Core\Config\ImmutableConfig */ - readonly private ImmutableConfig $config; + private readonly ImmutableConfig $config; /** * The webform submission storage. * * @var \Drupal\webform\WebformSubmissionStorageInterface|\Drupal\Core\Entity\EntityStorageInterface */ - readonly private WebformSubmissionStorageInterface $webformSubmissionStorage; + private readonly WebformSubmissionStorageInterface $webformSubmissionStorage; /** * The queue storage. * * @var \Drupal\Core\Entity\EntityStorageInterface */ - readonly private EntityStorageInterface $queueStorage; + private readonly EntityStorageInterface $queueStorage; /** * The Digital post helper. * * @var \Drupal\os2forms_digital_post\Helper\DigitalPostHelper|null */ - readonly private ?DigitalPostHelper $digitalPostHelper; + private readonly ?DigitalPostHelper $digitalPostHelper; /** * Constructor. @@ -80,14 +80,14 @@ class MaestroHelper implements LoggerInterface { public function __construct( EntityTypeManagerInterface $entityTypeManager, ConfigFactoryInterface $configFactory, - readonly private WebformTokenManagerInterface $tokenManager, - readonly private MailManagerInterface $mailManager, - readonly private LanguageManagerInterface $languageManager, - readonly private WebformThemeManagerInterface $webformThemeManager, - readonly private LoggerChannelInterface $logger, - readonly private LoggerChannelInterface $submissionLogger, - readonly private ModuleHandlerInterface $moduleHandler, - readonly private EntityPrintPluginManagerInterface $entityPrintPluginManager, + private readonly WebformTokenManagerInterface $tokenManager, + private readonly MailManagerInterface $mailManager, + private readonly LanguageManagerInterface $languageManager, + private readonly WebformThemeManagerInterface $webformThemeManager, + private readonly LoggerChannelInterface $logger, + private readonly LoggerChannelInterface $submissionLogger, + private readonly ModuleHandlerInterface $moduleHandler, + private readonly EntityPrintPluginManagerInterface $entityPrintPluginManager, ) { $this->config = $configFactory->get(SettingsForm::SETTINGS); $this->webformSubmissionStorage = $entityTypeManager->getStorage('webform_submission'); diff --git a/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php b/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php index ab4eb94..8968e03 100644 --- a/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php +++ b/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php @@ -40,7 +40,7 @@ public function __construct( array $configuration, $plugin_id, $plugin_definition, - readonly private MaestroHelper $helper + private readonly MaestroHelper $helper ) { parent::__construct($configuration, $plugin_id, $plugin_definition); } From 160dfd71a682ec0b4db2102f6311fdf11acb2425 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Thu, 28 Sep 2023 16:43:00 +0200 Subject: [PATCH 08/15] Cleaned up comments --- .../os2forms_forloeb/src/MaestroHelper.php | 82 +++++++++++++++++-- 1 file changed, 75 insertions(+), 7 deletions(-) diff --git a/modules/os2forms_forloeb/src/MaestroHelper.php b/modules/os2forms_forloeb/src/MaestroHelper.php index 9401b6e..82c3912 100644 --- a/modules/os2forms_forloeb/src/MaestroHelper.php +++ b/modules/os2forms_forloeb/src/MaestroHelper.php @@ -104,7 +104,6 @@ public function __construct( * Implements hook_maestro_zero_user_notification(). */ public function maestroZeroUserNotification($templateMachineName, $taskMachineName, $queueID, $notificationType) { - // @todo Clean up and align with MaestroWebformInheritTask::webformSubmissionFormAlter(). $templateTask = MaestroEngine::getTemplateTaskByID($templateMachineName, $taskMachineName); if (MaestroWebformInheritTask::isWebformTask($templateTask)) { if ($inheritWebformUniqueId = ($templateTask['data'][MaestroWebformInheritTask::INHERIT_WEBFORM_UNIQUE_ID] ?? NULL)) { @@ -144,6 +143,19 @@ public static function getWebformSubmissionIdentifiersForProcess(int $processID) /** * Handle submission notification. + * + * Creates job for sending notification to assigned user. + * + * @param string $notificationType + * The notification type (one of the NOTIFICATION_* constannts). + * @param \Drupal\webform\WebformSubmissionInterface $submission + * The webform submission. + * @param array $templateTask + * The template task. + * @param int $maestroQueueID + * The Maestro queue ID. + * + * @see self::processJob() */ private function handleSubmissionNotification( string $notificationType, @@ -187,7 +199,12 @@ private function handleSubmissionNotification( } /** - * Process a job. + * Process a job to send out a notification. + * + * @param \Drupal\advancedqueue\Job $job + * The job (created by handleSubmissionNotification) + * + * @see self::handleSubmissionNotification() */ public function processJob(Job $job): JobResult { $payload = $job->getPayload(); @@ -207,6 +224,15 @@ public function processJob(Job $job): JobResult { /** * Send notification. + * + * @param string $notificationType + * The notification type (one of the NOTIFICATION_* constannts). + * @param \Drupal\webform\WebformSubmissionInterface $submission + * The webform submission. + * @param array $templateTask + * The template task. + * @param int $maestroQueueID + * The Maestro queue ID. */ private function sendNotification( string $notificationType, @@ -253,13 +279,11 @@ private function sendNotification( 'operation' => 'notification failed', 'exception' => $exception, ]); - - return NULL; } } /** - * Load advanced queue if any. + * Load advanced queue used to process notification jobs. * * @return \Drupal\advancedqueue\Entity\QueueInterface * The queue. @@ -281,6 +305,17 @@ private function loadQueue(): QueueInterface { /** * Send notification email. + * + * @param string $recipient + * The recipient. + * @param string $subject + * The subject. + * @param string $body + * The body. + * @param \Drupal\webform\WebformSubmissionInterface $submission + * The webform submission. + * @param string $notificationType + * The notification type (one of the NOTIFICATION_* constannts). */ private function sendNotificationEmail( string $recipient, @@ -331,6 +366,21 @@ private function sendNotificationEmail( /** * Send notification digital post. + * + * @param string $recipient + * The recipient. + * @param string $subject + * The subject. + * @param string $content + * The content. + * @param string $taskUrl + * The Maestro task URL. + * @param string $actionLabel + * The action label. + * @param \Drupal\webform\WebformSubmissionInterface $submission + * The webform submission. + * @param string $notificationType + * The notification type (one of the NOTIFICATION_* constannts). */ private function sendNotificationDigitalPost( string $recipient, @@ -402,7 +452,7 @@ private function sendNotificationDigitalPost( * @param string $handlerId * The handler ID. * @param string $notificationType - * The notification type. + * The notification type (one of the NOTIFICATION_* constannts). * @param array $templateTask * The Maestro template task. * @param int $maestroQueueID @@ -419,6 +469,8 @@ private function sendNotificationDigitalPost( * - subject * - taskUrl (for digital post) * - actionLabel (for digital post) + * + * @see self::renderHtml() */ public function renderNotification(WebformSubmissionInterface $submission, string $handlerId, string $notificationType, array $templateTask, int $maestroQueueID, string $contentType = NULL): array { $handler = $submission->getWebform()->getHandler($handlerId); @@ -523,7 +575,23 @@ public function renderNotification(WebformSubmissionInterface $submission, strin } /** - * Build HTML. + * Render HTML for a notification. + * + * @param string $type + * The notification content type ('email' or 'pdf'). + * @param string $subject + * The subject. + * @param array $content + * The content. + * @param string $taskUrl + * The Maestro taks URL. + * @param string $actionLabel + * The action label. + * @param \Drupal\webform\WebformSubmissionInterface $submission + * The webform submission. + * + * @return string|MarkupInterface + * The rendered content. */ private function renderHtml( string $type, From 897911847ae5e48d1385838de178687a0679bfc7 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Fri, 29 Sep 2023 12:41:25 +0200 Subject: [PATCH 09/15] Moved dependency to proper location --- modules/os2forms_forloeb/os2forms_forloeb.info.yml | 9 +++++---- os2forms.info.yml | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/os2forms_forloeb/os2forms_forloeb.info.yml b/modules/os2forms_forloeb/os2forms_forloeb.info.yml index fdf9f36..7a3d3a5 100644 --- a/modules/os2forms_forloeb/os2forms_forloeb.info.yml +++ b/modules/os2forms_forloeb/os2forms_forloeb.info.yml @@ -8,6 +8,7 @@ configure: os2forms_forloeb.settings dependencies: - 'drupal:admin_toolbar_tools' + - 'drupal:advancedqueue' - 'drupal:diff' - 'drupal:entity_print' - 'drupal:eu_cookie_compliance' @@ -19,20 +20,19 @@ dependencies: - 'drupal:mailsystem' - 'drupal:masquerade' - 'drupal:os2forms' - - 'drupal:os2forms_nemid' - 'drupal:os2forms_dawa' + - 'drupal:os2forms_nemid' - 'drupal:os2forms_sbsys' - 'drupal:os2web_simplesaml' - 'drupal:pathauto' - 'drupal:r4032login' - 'drupal:redirect' - 'drupal:smtp' - - 'drupal:system' - 'drupal:switch_page_theme' + - 'drupal:system' - 'drupal:token' - 'drupal:ultimate_cron' - 'drupal:webform' - - 'drupal:webform_ui' - 'drupal:webform_access' - 'drupal:webform_attachment' - 'drupal:webform_composite' @@ -43,11 +43,12 @@ dependencies: - 'drupal:webform_node_element' - 'drupal:webform_remote_handlers' - 'drupal:webform_rest' - - 'drupal:webform_share' - 'drupal:webform_scheduled_email' + - 'drupal:webform_share' - 'drupal:webform_submission_export_import' - 'drupal:webform_submission_log' - 'drupal:webform_templates' + - 'drupal:webform_ui' - 'drupal:workflow_participants' # os2forms_digital_post may be used for sending Meastro notifications via digital post # - 'os2forms_digital_post:os2forms_digital_post' diff --git a/os2forms.info.yml b/os2forms.info.yml index 98c9b1a..e604477 100644 --- a/os2forms.info.yml +++ b/os2forms.info.yml @@ -6,7 +6,6 @@ core: 8.x core_version_requirement: ^8 || ^9 dependencies: - - 'drupal:advancedqueue' - 'drupal:ckeditor' - 'drupal:editor' - 'drupal:entity_print' From a0904ab8cbf0376b5a3995f4f4ffde09aa83607d Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Fri, 29 Sep 2023 13:06:56 +0200 Subject: [PATCH 10/15] Made link text translatable --- .../src/Controller/MaestroNotificationController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php b/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php index 0da7b32..116f18e 100644 --- a/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php +++ b/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php @@ -126,7 +126,7 @@ public function previewRender(Request $request, WebformInterface $webform, strin public function message(Request $request): Response { $content[] = '

' . $request->get('message') . '

'; if ($referer = $request->headers->get('referer')) { - $content[] = sprintf('Go back', $referer); + $content[] = $this->t('Back to preview', [':url' => $referer]); } return new Response(implode(PHP_EOL, $content)); From 0a576fcd75ebceb51cd49cb4e5b2f46b99e791e7 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Fri, 29 Sep 2023 13:07:21 +0200 Subject: [PATCH 11/15] Cleaned up --- .../MaestroNotificationController.php | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php b/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php index 116f18e..989607a 100644 --- a/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php +++ b/modules/os2forms_forloeb/src/Controller/MaestroNotificationController.php @@ -3,7 +3,6 @@ namespace Drupal\os2forms_forloeb\Controller; use Drupal\Core\Controller\ControllerBase; -use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; use Drupal\os2forms_digital_post\Model\Document; use Drupal\os2forms_forloeb\MaestroHelper; @@ -39,9 +38,23 @@ public static function create(ContainerInterface $container) { } /** - * Preview action. + * Preview notification action. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * @param \Drupal\webform\WebformInterface $webform + * The webform. + * @param string $handler + * The handler ID. + * @param string $notification_type + * The notification type. + * @param string $content_type + * The content type. + * + * @return array + * A render array. */ - public function preview(Request $request, WebformInterface $webform, string $handler, string $notification_type, string $content_type, RouteMatchInterface $routeMatch) { + public function preview(Request $request, WebformInterface $webform, string $handler, string $notification_type, string $content_type): array { $handler = $webform->getHandler($handler); $submissionIds = array_keys($this->webformSubmissionStorage->getQuery() ->condition('webform_id', $webform->id()) @@ -103,8 +116,20 @@ public function preview(Request $request, WebformInterface $webform, string $han /** * Render notification preview. + * + * @param string $handler + * The handler ID. + * @param string $notification_type + * The notification type. + * @param string $content_type + * The content type. + * @param \Drupal\webform\WebformSubmissionInterface $submission + * The webform submission. + * + * @return \Symfony\Component\HttpFoundation\Response + * The response. */ - public function previewRender(Request $request, WebformInterface $webform, string $handler, string $notification_type, string $content_type, WebformSubmissionInterface $submission) { + public function previewRender(string $handler, string $notification_type, string $content_type, WebformSubmissionInterface $submission) { $templateTask = []; $maestroQueueID = 0; [ @@ -122,6 +147,15 @@ public function previewRender(Request $request, WebformInterface $webform, strin /** * Message action. + * + * Used only to show a message when a Maestro task link from a notification + * preview is clicked. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * + * @return \Symfony\Component\HttpFoundation\Response + * The response. */ public function message(Request $request): Response { $content[] = '

' . $request->get('message') . '

'; From 1473dd67d764bdf139ab0a5ad88cb8883494d945 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Fri, 29 Sep 2023 13:31:57 +0200 Subject: [PATCH 12/15] Anonymized default PDF template --- .../os2forms-forloeb-notification-message-pdf-html.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-pdf-html.html.twig b/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-pdf-html.html.twig index a3a671d..835dab6 100644 --- a/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-pdf-html.html.twig +++ b/modules/os2forms_forloeb/templates/os2forms-forloeb-notification-message-pdf-html.html.twig @@ -46,7 +46,7 @@
- Aarhus Kommune-logo + OS2Forms
From 6343e401392061d4b6e59abf87f4d54e9d1de3f9 Mon Sep 17 00:00:00 2001 From: Stanislav Kutasevits Date: Fri, 29 Sep 2023 14:40:29 +0300 Subject: [PATCH 13/15] OS-60 disabling webform_embed --- modules/os2forms_forloeb/os2forms_forloeb.info.yml | 1 - os2forms.info.yml | 1 - os2forms.install | 9 +++++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/os2forms_forloeb/os2forms_forloeb.info.yml b/modules/os2forms_forloeb/os2forms_forloeb.info.yml index 3796741..cd582bf 100644 --- a/modules/os2forms_forloeb/os2forms_forloeb.info.yml +++ b/modules/os2forms_forloeb/os2forms_forloeb.info.yml @@ -35,7 +35,6 @@ dependencies: - 'drupal:webform_access' - 'drupal:webform_attachment' - 'drupal:webform_composite' - - 'drupal:webform_embed' - 'drupal:webform_entity_print' - 'drupal:webform_entity_print_attachment' - 'drupal:webform_migrate' diff --git a/os2forms.info.yml b/os2forms.info.yml index 078e804..5745e73 100644 --- a/os2forms.info.yml +++ b/os2forms.info.yml @@ -17,7 +17,6 @@ dependencies: - 'drupal:webform_submission_log' - 'drupal:webform_templates' - 'drupal:webform_composite' - - 'drupal:webform_embed' - 'drupal:entity_print' - 'drupal:webform_migrate' - 'drupal:redirect' diff --git a/os2forms.install b/os2forms.install index 74a0284..fe2c001 100644 --- a/os2forms.install +++ b/os2forms.install @@ -169,6 +169,15 @@ function os2forms_update_8904() { os2forms_read_in_new_config('pathauto.pattern.os2forms_wf_webforms'); } +/** + * Implements hook_update_N(). + * + * Uninstalling webform_embed module. + */ +function os2forms_update_8905() { + \Drupal::service('module_installer')->uninstall(['webform_embed']); +} + /** * Creates a list of predefined terms for Application vocabulary. */ From 5eb78315c1790399159f3371f53ea05b20871866 Mon Sep 17 00:00:00 2001 From: Stanislav Kutasevits Date: Fri, 29 Sep 2023 14:41:59 +0300 Subject: [PATCH 14/15] OS-60 updating changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a367903..b30f241 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ before starting to add changes. Use example [placed in the end of the page](#exa ## [Unreleased] +- Removing webform_embed + +## [3.11.0] 2023-09-25 + - [OS-58] New company address fields - Custom permissions by term field - Removing dependency to config_entity_revisions, webform_revisions, coc_forms_auto_export From 85ad4d20a980cd23ececbadb40ceff1407021386 Mon Sep 17 00:00:00 2001 From: Stanislav Kutasevits Date: Mon, 2 Oct 2023 13:27:37 +0300 Subject: [PATCH 15/15] Preparing 3.12.0 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54c7126..824f81d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,11 @@ before starting to add changes. Use example [placed in the end of the page](#exa ## [Unreleased] +## [3.12.0] 2023-10-02 + - Removing webform_embed +- os2forms_permissions_by_term: removing node access control + ## [3.11.0] 2023-09-25