From 974956c93dc36ec7f98c3ed861c747af28788559 Mon Sep 17 00:00:00 2001 From: Carson Oldson Date: Mon, 18 Nov 2024 11:26:31 -0600 Subject: [PATCH 1/6] [EPAD8-2652] Adding custom patch that adds a 'MarkUnseen' method to Notifications and a custom ViewsBulkOperation Action for marking notifications seen/unseen in bulk. --- services/drupal/composer.patches.json | 4 ++ .../notification-seen-unseen-action.diff | 45 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 services/drupal/patches/notification-seen-unseen-action.diff diff --git a/services/drupal/composer.patches.json b/services/drupal/composer.patches.json index 484452109..8e73d344a 100644 --- a/services/drupal/composer.patches.json +++ b/services/drupal/composer.patches.json @@ -256,6 +256,10 @@ }, "drupal/danse_content_moderation": { "Add Last revision to payload and fix canonical error on DANSE report page": "patches/last_revision-3452539.patch" + }, + "drupal/danse": { + "Add Bulk Operation for marking notifications as seen or unseen": "patches/notification-seen-unseen-action.diff" + } } } diff --git a/services/drupal/patches/notification-seen-unseen-action.diff b/services/drupal/patches/notification-seen-unseen-action.diff new file mode 100644 index 000000000..b8d821e5f --- /dev/null +++ b/services/drupal/patches/notification-seen-unseen-action.diff @@ -0,0 +1,45 @@ +diff --git a/src/Entity/Notification.php b/src/Entity/Notification.php +index 5aebcd8..2a47972 100644 +--- a/src/Entity/Notification.php ++++ b/src/Entity/Notification.php +@@ -78,6 +78,21 @@ class Notification extends ContentEntityBase implements NotificationInterface { + return $this; + } + ++ /** ++ * {@inheritdoc} ++ */ ++ public function markUnseen(): NotificationInterface { ++ try { ++ $this ++ ->set('seen', FALSE) ++ ->save(); ++ } ++ catch (EntityStorageException $e) { ++ // @todo Log this exception. ++ } ++ return $this; ++ } ++ + /** + * {@inheritdoc} + */ +diff --git a/src/Entity/NotificationInterface.php b/src/Entity/NotificationInterface.php +index e3c4082..39e02eb 100644 +--- a/src/Entity/NotificationInterface.php ++++ b/src/Entity/NotificationInterface.php +@@ -33,6 +33,14 @@ interface NotificationInterface extends ContentEntityInterface { + */ + public function markSeen(): NotificationInterface; + ++ /** ++ * Marks the notification as un-seen. ++ * ++ * @return \Drupal\danse\Entity\NotificationInterface ++ * Self. ++ */ ++ public function markUnseen(): NotificationInterface; ++ + /** + * Marks the notification as delivered. + * From 43dfe739972e637827922e54b27a16753cd273d5 Mon Sep 17 00:00:00 2001 From: Carson Oldson Date: Mon, 18 Nov 2024 11:27:27 -0600 Subject: [PATCH 2/6] [EPAD8-2652] Adjusting code for custom field type to look at new payload property (last_revision). --- .../Plugin/views/field/EpaWorkflowReferenceTransitionBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/drupal/web/modules/custom/epa_workflow/src/Plugin/views/field/EpaWorkflowReferenceTransitionBase.php b/services/drupal/web/modules/custom/epa_workflow/src/Plugin/views/field/EpaWorkflowReferenceTransitionBase.php index b26bf3a5e..73a74bd3a 100644 --- a/services/drupal/web/modules/custom/epa_workflow/src/Plugin/views/field/EpaWorkflowReferenceTransitionBase.php +++ b/services/drupal/web/modules/custom/epa_workflow/src/Plugin/views/field/EpaWorkflowReferenceTransitionBase.php @@ -88,7 +88,7 @@ public function getTransitionRevisionIds(ResultRow $values) { ) { $payload = $event->get('payload')->value; $payload_values = json_decode($payload, TRUE); - $transition_revision_ids['from'] = $payload_values['entity']['last_revision']; + $transition_revision_ids['from'] = $payload_values['entity']['prev_revision']; /** @var \Drupal\node\NodeStorageInterface $node_storage */ $node_storage = $this->entityTypeManager->getStorage('node'); From d5ecfe4989c3328f10ebe884ef20236c995f0156 Mon Sep 17 00:00:00 2001 From: Carson Oldson Date: Mon, 18 Nov 2024 11:28:32 -0600 Subject: [PATCH 3/6] [EPAD8-2652] Adding web area and seen exposed filters to user notification view. --- .../config/sync/views.view.notifications.yml | 263 +++++++++++++++++- 1 file changed, 260 insertions(+), 3 deletions(-) diff --git a/services/drupal/config/sync/views.view.notifications.yml b/services/drupal/config/sync/views.view.notifications.yml index 16131051f..64fcbf964 100644 --- a/services/drupal/config/sync/views.view.notifications.yml +++ b/services/drupal/config/sync/views.view.notifications.yml @@ -10,6 +10,7 @@ dependencies: - group - node - user + - views_bulk_operations id: notifications label: Notifications module: views @@ -26,6 +27,68 @@ display: display_options: title: 'All Notifications' fields: + views_bulk_operations_bulk_form: + id: views_bulk_operations_bulk_form + table: views + field: views_bulk_operations_bulk_form + relationship: none + group_type: group + admin_label: '' + plugin_id: views_bulk_operations_bulk_form + label: 'Views bulk operations' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + batch: true + batch_size: 10 + form_step: true + ajax_loader: false + buttons: false + action_title: Action + clear_on_exposed: true + force_selection_info: false + selected_actions: + - + action_id: danse_notification_seen_action + preconfiguration: + add_confirmation: false reference_node_transition: id: reference_node_transition table: danse_event @@ -76,6 +139,73 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true + seen: + id: seen + table: danse_notification + field: seen + relationship: none + group_type: group + admin_label: '' + entity_type: danse_notification + entity_field: seen + plugin_id: field + label: Seen + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: boolean + settings: + format: unicode-yes-no + format_custom_false: '' + format_custom_true: '' + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false title: id: title table: node_field_data @@ -622,7 +752,119 @@ display: validate_options: { } break_phrase: false not: false - filters: { } + filters: + seen: + id: seen + table: danse_notification + field: seen + relationship: none + group_type: group + admin_label: '' + entity_type: danse_notification + entity_field: seen + plugin_id: boolean + operator: '=' + value: '0' + group: 1 + exposed: true + expose: + operator_id: '' + label: Seen + description: '' + use_operator: false + operator: seen_op + operator_limit_selection: false + operator_list: { } + identifier: seen + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + paragraphs_library_contributor: '0' + layout_editor: '0' + alerts_manager: '0' + block_manager: '0' + system_editor: '0' + system_webmaster: '0' + menu_admin: '0' + administrator: '0' + beta_tester: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + gid: + id: gid + table: group_content_field_data + field: gid + relationship: group_content + group_type: group + admin_label: '' + entity_type: group_content + entity_field: gid + plugin_id: entity_reference + operator: or + value: { } + group: 1 + exposed: true + expose: + operator_id: gid_op + label: 'Web Area' + description: '' + use_operator: false + operator: gid_op + operator_limit_selection: false + operator_list: { } + identifier: gid + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + paragraphs_library_contributor: '0' + layout_editor: '0' + alerts_manager: '0' + block_manager: '0' + system_editor: '0' + system_webmaster: '0' + menu_admin: '0' + administrator: '0' + beta_tester: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + reduce_duplicates: false + handler: 'default:group' + widget: autocomplete + handler_settings: + target_bundles: + web_area: web_area + sort: + field: _none + direction: ASC + auto_create: false + auto_create_bundle: '' style: type: table options: @@ -760,7 +1002,7 @@ display: views_ajax_get: ajax_get: false cache_metadata: - max-age: -1 + max-age: 0 contexts: - 'languages:language_content' - 'languages:language_interface' @@ -775,7 +1017,22 @@ display: display_plugin: page position: 1 display_options: + empty: + area: + id: area + table: views + field: area + relationship: none + group_type: group + admin_label: '' + plugin_id: text + empty: true + content: + value: 'All caught up! No new notifications.' + format: filtered_html + tokenize: false defaults: + empty: false sorts: true display_extenders: views_ajax_get: @@ -791,7 +1048,7 @@ display: parent: system.admin_content context: '0' cache_metadata: - max-age: -1 + max-age: 0 contexts: - 'languages:language_content' - 'languages:language_interface' From 8901e25d5e038584fadeb2eb062379f1e107ac64 Mon Sep 17 00:00:00 2001 From: Carson Oldson Date: Mon, 18 Nov 2024 12:34:36 -0600 Subject: [PATCH 4/6] [EPAD8-2652] Adding updated patch. --- .../notification-seen-unseen-action.diff | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/services/drupal/patches/notification-seen-unseen-action.diff b/services/drupal/patches/notification-seen-unseen-action.diff index b8d821e5f..b9958c054 100644 --- a/services/drupal/patches/notification-seen-unseen-action.diff +++ b/services/drupal/patches/notification-seen-unseen-action.diff @@ -43,3 +43,96 @@ index e3c4082..39e02eb 100644 /** * Marks the notification as delivered. * +diff --git a/src/Plugin/Action/NotificationSeenAction.php b/src/Plugin/Action/NotificationSeenAction.php +new file mode 100644 +index 0000000..7881c95 +--- /dev/null ++++ b/src/Plugin/Action/NotificationSeenAction.php +@@ -0,0 +1,87 @@ ++get('seen')->value; ++ // If it's 'seen' (TRUE) then mark it unseen and vice versa ++ filter_var($seen, FILTER_VALIDATE_BOOL) ? $entity->markUnseen() : $entity->markSeen(); ++ return $this->t('Successfully marked notification as @seen.', ['@seen' => filter_var($seen, FILTER_VALIDATE_BOOL) ? $this->t('unseen') : $this->t('seen')]); ++ } ++ catch (\LogicException $e) { ++ // @todo Error handling? ++ } ++ } ++ } ++ ++ /** ++ * {@inheritDoc} ++ */ ++ public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { ++ if ($object instanceof Notification) { ++ return $object->access('view', $account, TRUE); ++ } ++ ++ return AccessResult::forbidden(); ++ } ++ ++ /** ++ * @param bool $success ++ * ++ * @param array $results ++ * @param array $operations ++ * ++ * @return \Symfony\Component\HttpFoundation\RedirectResponse|null ++ */ ++ public static function finished($success, array $results, array $operations): ?RedirectResponse { ++ if ($success) { ++ foreach ($results['operations'] as $item) { ++ // Default fallback to maintain backwards compatibility: ++ // if api version equals to "1" and type equals to "status", ++ // previous message is displayed, otherwise we display exactly what's ++ // specified in the action. ++ if ($item['type'] === 'status' && $results['api_version'] === '1') { ++ $message = static::translate('@operation', [ ++ '@operation' => $item['message'], ++ ]); ++ } ++ else { ++ $message = new FormattableMarkup('@message', [ ++ '@message' => $item['message'], ++ ]); ++ } ++ static::message($message, $item['type']); ++ } ++ } ++ else { ++ $message = static::translate('Finished with an error.'); ++ static::message($message, 'error'); ++ } ++ return NULL; ++ } ++ ++} From 9d9bdb52e02afce951a4b833c3e4b893476ceada Mon Sep 17 00:00:00 2001 From: Carson Oldson Date: Wed, 20 Nov 2024 16:07:07 -0600 Subject: [PATCH 5/6] [EPAD8-2652] Adding action permission for new DANSE action to mark notifications as seen. --- services/drupal/config/sync/user.role.authenticated.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/services/drupal/config/sync/user.role.authenticated.yml b/services/drupal/config/sync/user.role.authenticated.yml index 5768de270..7e80039b9 100644 --- a/services/drupal/config/sync/user.role.authenticated.yml +++ b/services/drupal/config/sync/user.role.authenticated.yml @@ -79,6 +79,7 @@ permissions: - 'create webform content' - 'delete own files' - 'edit own paragraph library items' + - 'execute danse_notification_seen_action danse_notification' - 'execute entity:publish_action block_content' - 'execute entity:publish_action media' - 'execute entity:publish_action menu_link_content' From 0ce462e433841e41656b15fefe55773151848dee Mon Sep 17 00:00:00 2001 From: Carson Oldson Date: Tue, 26 Nov 2024 10:40:10 -0600 Subject: [PATCH 6/6] [EPAD8-2652] Adjusting custom notification action patch to resolve permissions issue. --- .../patches/notification-seen-unseen-action.diff | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/services/drupal/patches/notification-seen-unseen-action.diff b/services/drupal/patches/notification-seen-unseen-action.diff index b9958c054..c1247a2e0 100644 --- a/services/drupal/patches/notification-seen-unseen-action.diff +++ b/services/drupal/patches/notification-seen-unseen-action.diff @@ -5,7 +5,7 @@ index 5aebcd8..2a47972 100644 @@ -78,6 +78,21 @@ class Notification extends ContentEntityBase implements NotificationInterface { return $this; } - + + /** + * {@inheritdoc} + */ @@ -31,7 +31,7 @@ index e3c4082..39e02eb 100644 @@ -33,6 +33,14 @@ interface NotificationInterface extends ContentEntityInterface { */ public function markSeen(): NotificationInterface; - + + /** + * Marks the notification as un-seen. + * @@ -45,10 +45,10 @@ index e3c4082..39e02eb 100644 * diff --git a/src/Plugin/Action/NotificationSeenAction.php b/src/Plugin/Action/NotificationSeenAction.php new file mode 100644 -index 0000000..7881c95 +index 0000000..feae3ab --- /dev/null +++ b/src/Plugin/Action/NotificationSeenAction.php -@@ -0,0 +1,87 @@ +@@ -0,0 +1,84 @@ +access('view', $account, TRUE); -+ } -+ -+ return AccessResult::forbidden(); ++ // @todo: Is there any permissions we want to have for this? DANSE doesn't offer any permissions for the entities it provides ++ return AccessResult::allowed(); + } + + /**