From acfa2a592e18274b300898f3a83ef2df3f4efd75 Mon Sep 17 00:00:00 2001 From: Daniel Sasser Date: Fri, 1 Mar 2024 07:42:36 -0800 Subject: [PATCH] VACMS-17116: Create Views result condition plugin (#17357) * VACMS-17116: Adds new Views Display plugin for ECA. * VACMS-17116: Updates labe for Views Result display plugin. * VACMS-17116: Adds ViewsResult Condition Pluging for ECA. * VACMS-17116: Rename the display. * VACMS-17116: Removes test eca model. * VACMS-17116: Refactor condition form to use select element and improve how views values are captured and stored. * VACMS-17116: Installs va_gov_eca. * VACMS-17116: Adds initial test for Views Result Condition plugin. * VACMS-17116: Use correct config keys in condition evaluation. * VACMS-17116: Update test to use real condition evaluations. * VACMS-17116: DRY changes. * VACMS-17116: Renames view and test module. * VACMS-17116: Adds initial FWB expiring content View and related ECA. --- config/sync/core.extension.yml | 1 + config/sync/eca.eca.expiring_fwb.yml | 39 +++ config/sync/eca.eca.test.yml | 13 - ...heduled_notifications_full_width_alert.yml | 288 ++++++++++++++++++ .../config/schema/va_gov_eca.views.schema.yml | 3 + .../ECA/Condition/ViewsResultCondition.php | 188 ++++++++++++ .../views/display/EcaViewsResultDisplay.php | 70 +++++ .../views.view.va_gov_eca_default_content.yml | 244 +++++++++++++++ .../va_gov_eca_test_views.info.yml | 5 + .../src/Kernel/ViewsResultConditionTest.php | 98 ++++++ .../custom/va_gov_eca/va_gov_eca.info.yml | 7 + 11 files changed, 943 insertions(+), 13 deletions(-) create mode 100644 config/sync/eca.eca.expiring_fwb.yml delete mode 100644 config/sync/eca.eca.test.yml create mode 100644 config/sync/views.view.scheduled_notifications_full_width_alert.yml create mode 100644 docroot/modules/custom/va_gov_eca/config/schema/va_gov_eca.views.schema.yml create mode 100644 docroot/modules/custom/va_gov_eca/src/Plugin/ECA/Condition/ViewsResultCondition.php create mode 100644 docroot/modules/custom/va_gov_eca/src/Plugin/views/display/EcaViewsResultDisplay.php create mode 100644 docroot/modules/custom/va_gov_eca/tests/modules/va_gov_eca_test_views/test_views/views.view.va_gov_eca_default_content.yml create mode 100644 docroot/modules/custom/va_gov_eca/tests/modules/va_gov_eca_test_views/va_gov_eca_test_views.info.yml create mode 100644 docroot/modules/custom/va_gov_eca/tests/src/Kernel/ViewsResultConditionTest.php create mode 100644 docroot/modules/custom/va_gov_eca/va_gov_eca.info.yml diff --git a/config/sync/core.extension.yml b/config/sync/core.extension.yml index fea07b9194..bd1af12f53 100644 --- a/config/sync/core.extension.yml +++ b/config/sync/core.extension.yml @@ -259,6 +259,7 @@ module: va_gov_content_types: 0 va_gov_dashboards: 0 va_gov_db: 0 + va_gov_eca: 0 va_gov_entity_browser: 0 va_gov_environment: 0 va_gov_events: 0 diff --git a/config/sync/eca.eca.expiring_fwb.yml b/config/sync/eca.eca.expiring_fwb.yml new file mode 100644 index 0000000000..cf53a4e455 --- /dev/null +++ b/config/sync/eca.eca.expiring_fwb.yml @@ -0,0 +1,39 @@ +uuid: 363d384e-b996-41b9-8cbd-f6b0dbe01e76 +langcode: en +status: false +dependencies: + module: + - eca_base + - va_gov_eca +id: expiring_fwb +modeller: core +label: 'Scheduled Notifications: Expiring FWB' +version: 0.1.0 +weight: null +events: + eca_base_eca_cron: + plugin: 'eca_base:eca_cron' + label: 'ECA cron event' + configuration: + frequency: '* * * * *' + successors: + - + id: action_message_action + condition: views_result +conditions: + views_result: + plugin: views_result + configuration: + negate: false + view_name: scheduled_notifications_full_width_alert + display_name: eca_result_1 + arguments: { } +gateways: { } +actions: + action_message_action: + plugin: action_message_action + label: 'Display a message to the user' + configuration: + message: 'The Scheduled Notifications: Full Width Alert View has results.' + replace_tokens: false + successors: { } diff --git a/config/sync/eca.eca.test.yml b/config/sync/eca.eca.test.yml deleted file mode 100644 index 039b2e051c..0000000000 --- a/config/sync/eca.eca.test.yml +++ /dev/null @@ -1,13 +0,0 @@ -uuid: 107ef63e-ce69-45fd-b6f4-4723b95f1994 -langcode: en -status: false -dependencies: { } -id: test -modeller: core -label: Test -version: null -weight: null -events: { } -conditions: { } -gateways: { } -actions: { } diff --git a/config/sync/views.view.scheduled_notifications_full_width_alert.yml b/config/sync/views.view.scheduled_notifications_full_width_alert.yml new file mode 100644 index 0000000000..66ccb5ed6e --- /dev/null +++ b/config/sync/views.view.scheduled_notifications_full_width_alert.yml @@ -0,0 +1,288 @@ +uuid: c4871759-4d7d-4aa5-bf57-9184da4a487b +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + - node.type.banner + module: + - node + - user + - va_gov_eca +id: scheduled_notifications_full_width_alert +label: 'Scheduled Notifications: Full Width Alert' +module: views +description: '' +tag: '' +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + fields: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: title + plugin_id: field + label: '' + exclude: false + alter: + alter_text: false + make_link: false + absolute: false + word_boundary: false + ellipsis: false + strip_tags: false + trim: false + 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: string + settings: + link_to_entity: 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 + pager: + type: mini + options: + offset: 0 + items_per_page: 10 + total_pages: null + id: 0 + tags: + next: ›› + previous: ‹‹ + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + pagination_heading_level: h2 + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + access: + type: perm + options: + perm: 'access content' + cache: + type: tag + options: { } + empty: { } + sorts: + created: + id: created + table: node_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: created + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: '' + exposed: false + granularity: second + arguments: { } + filters: + status: + id: status + table: node_field_data + field: status + entity_type: node + entity_field: status + plugin_id: boolean + value: '1' + group: 1 + expose: + operator: '' + type: + id: type + table: node_field_data + field: type + entity_type: node + entity_field: type + plugin_id: bundle + value: + banner: banner + style: + type: default + options: + grouping: { } + row_class: '' + default_row_class: true + uses_fields: false + row: + type: fields + options: + default_field_elements: true + inline: { } + separator: '' + hide_empty: false + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: { } + header: { } + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + eca_result_1: + id: eca_result_1 + display_title: 'ECA Result' + display_plugin: eca_result + position: 1 + display_options: + display_extenders: + jsonapi_views: + enabled: true + row: + type: 'entity:node' + options: + relationship: none + view_mode: teaser + filters: + type: + id: type + table: node_field_data + field: type + relationship: none + group_type: group + admin_label: '' + operator: in + value: + banner: banner + group: '1' + exposed: false + expose: + operator_id: false + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: 0 + default_group: All + default_group_multiple: { } + group_items: { } + entity_type: node + entity_field: type + plugin_id: bundle + status: + id: status + table: node_field_data + field: status + relationship: none + group_type: group + admin_label: '' + operator: '=' + value: '1' + group: '1' + exposed: false + expose: + operator_id: false + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: 0 + default_group: All + default_group_multiple: { } + group_items: { } + entity_type: node + entity_field: status + plugin_id: boolean + title: 'Expiring Alerts' + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - 'user.node_grants:view' + tags: { } diff --git a/docroot/modules/custom/va_gov_eca/config/schema/va_gov_eca.views.schema.yml b/docroot/modules/custom/va_gov_eca/config/schema/va_gov_eca.views.schema.yml new file mode 100644 index 0000000000..e948873fe3 --- /dev/null +++ b/docroot/modules/custom/va_gov_eca/config/schema/va_gov_eca.views.schema.yml @@ -0,0 +1,3 @@ +views.display.eca_result: + type: views_display + label: 'ECA Result display' diff --git a/docroot/modules/custom/va_gov_eca/src/Plugin/ECA/Condition/ViewsResultCondition.php b/docroot/modules/custom/va_gov_eca/src/Plugin/ECA/Condition/ViewsResultCondition.php new file mode 100644 index 0000000000..fe5c5fc21a --- /dev/null +++ b/docroot/modules/custom/va_gov_eca/src/Plugin/ECA/Condition/ViewsResultCondition.php @@ -0,0 +1,188 @@ +moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): ConditionBase { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager'), + $container->get('entity_type.bundle.info'), + $container->get('request_stack'), + $container->get('eca.token_services'), + $container->get('current_user'), + $container->get('datetime.time'), + $container->get('eca.state'), + $container->get('module_handler') + ); + } + + /** + * {@inheritDoc} + */ + public function evaluate(): bool { + $result = views_get_view_result($this->configuration['view_name'], $this->configuration['display_name'], $this->configuration['arguments']); + return count($result) > 0; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { + $view_storage = $this->entityTypeManager->getStorage('view'); + $displays = Views::getApplicableViews('eca_views_display'); + $options = []; + foreach ($displays as $data) { + [$view_id, $display_id] = $data; + $view = $view_storage->load($view_id); + $display = $view->get('display'); + $options[$view_id . ':' . $display_id] = $view_id . ' - ' . $display[$display_id]['display_title']; + } + + if ($options) { + $default = !empty($this->configuration['view_name']) && !empty($this->configuration['view_display']) ? $this->configuration['view_name'] . ':' . $this->configuration['view_display'] : ''; + $form['view']['view_and_display'] = [ + '#type' => 'select', + '#title' => $this->t('View to query'), + '#required' => TRUE, + '#options' => $options, + '#default_value' => $default, + '#description' => '

' . $this->t('Choose the view and display to retrieve results from.
Only views with a display of type "ECA Result" are eligible.') . '

', + ]; + + $default = !empty($this->configuration['arguments']) ? implode(', ', $this->configuration['arguments']) : ''; + $form['view']['arguments'] = [ + '#type' => 'textfield', + '#title' => $this->t('View arguments'), + '#default_value' => $default, + '#required' => FALSE, + '#description' => $this->t('Provide a comma separated list of arguments to pass to the view.'), + ]; + } + else { + if ($this->currentUser->hasPermission('administer views') && $this->moduleHandler->moduleExists('views_ui')) { + $form['view']['no_view_help'] = [ + '#markup' => '

' . $this->t('No eligible views were found. Create a view with an ECA Result display, or add such a display to an existing view.', [ + ':create' => Url::fromRoute('views_ui.add')->toString(), + ':existing' => Url::fromRoute('entity.view.collection')->toString(), + ]) . '

', + ]; + } + else { + $form['view']['no_view_help']['#markup'] = '

' . $this->t('No eligible views were found.') . '

'; + } + } + return parent::buildConfigurationForm($form, $form_state); + } + + /** + * {@inheritDoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void { + $value = $form_state->getValue('view'); + // Split view name and display name from the 'view_and_display' value. + if (!empty($value)) { + [$view, $display] = explode(':', $value['view_and_display']); + } + else { + $form_state->setError($form['view']['view_and_display'], new TranslatableMarkup('The views entity selection mode requires a view.')); + return; + } + + // Explode the 'arguments' string into an actual array. + $arguments_string = trim($value['arguments']); + if ($arguments_string === '') { + $arguments = []; + } + else { + $arguments = array_map('trim', explode(',', $arguments_string)); + } + + $value = [ + 'view_name' => $view, + 'display_name' => $display, + 'arguments' => $arguments, + ]; + $form_state->setValue('view', $value); + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void { + $view = $form_state->getValue('view'); + $this->configuration['view_name'] = $view['view_name']; + $this->configuration['display_name'] = $view['display_name']; + $this->configuration['arguments'] = $view['arguments']; + parent::submitConfigurationForm($form, $form_state); + } + +} diff --git a/docroot/modules/custom/va_gov_eca/src/Plugin/views/display/EcaViewsResultDisplay.php b/docroot/modules/custom/va_gov_eca/src/Plugin/views/display/EcaViewsResultDisplay.php new file mode 100644 index 0000000000..03a57fa5cf --- /dev/null +++ b/docroot/modules/custom/va_gov_eca/src/Plugin/views/display/EcaViewsResultDisplay.php @@ -0,0 +1,70 @@ + 'default']; + $options['defaults']['default']['style'] = FALSE; + $options['row']['contains']['type'] = ['default' => 'fields']; + $options['defaults']['default']['row'] = FALSE; + + // Set the display title to an empty string (not used in this display type). + $options['title']['default'] = ''; + $options['defaults']['default']['title'] = FALSE; + + return $options; + } + +} diff --git a/docroot/modules/custom/va_gov_eca/tests/modules/va_gov_eca_test_views/test_views/views.view.va_gov_eca_default_content.yml b/docroot/modules/custom/va_gov_eca/tests/modules/va_gov_eca_test_views/test_views/views.view.va_gov_eca_default_content.yml new file mode 100644 index 0000000000..48d4c53255 --- /dev/null +++ b/docroot/modules/custom/va_gov_eca/tests/modules/va_gov_eca_test_views/test_views/views.view.va_gov_eca_default_content.yml @@ -0,0 +1,244 @@ +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + - node.type.default + module: + - node + - user + - va_gov_eca +id: va_gov_eca_default_content +label: 'Test Default' +module: views +description: '' +tag: '' +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: 'Test Default' + fields: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: title + plugin_id: field + label: '' + exclude: false + alter: + alter_text: false + make_link: false + absolute: false + word_boundary: false + ellipsis: false + strip_tags: false + trim: false + 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: string + settings: + link_to_entity: 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 + pager: + type: mini + options: + offset: 0 + items_per_page: 10 + total_pages: null + id: 0 + tags: + next: ›› + previous: ‹‹ + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + access: + type: perm + options: + perm: 'access content' + cache: + type: tag + options: { } + empty: { } + sorts: + created: + id: created + table: node_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: created + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: '' + exposed: false + granularity: second + arguments: { } + filters: + status: + id: status + table: node_field_data + field: status + entity_type: node + entity_field: status + plugin_id: boolean + value: '1' + group: 1 + expose: + operator: '' + style: + type: default + row: + type: 'entity:node' + options: + view_mode: teaser + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: { } + header: { } + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + eca_result_1: + id: eca_result_1 + display_title: 'ECA Result' + display_plugin: eca_result + position: 2 + display_options: + display_extenders: { } + row: + type: 'entity:node' + options: + relationship: none + view_mode: teaser + filters: + type: + id: type + table: node_field_data + field: type + relationship: none + group_type: group + admin_label: '' + operator: in + value: + default: default + group: '1' + exposed: false + expose: + operator_id: false + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: 0 + default_group: All + default_group_multiple: { } + group_items: { } + entity_type: node + entity_field: type + plugin_id: bundle + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - 'user.node_grants:view' + tags: { } + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 1 + display_options: + display_extenders: { } + path: test-default + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/docroot/modules/custom/va_gov_eca/tests/modules/va_gov_eca_test_views/va_gov_eca_test_views.info.yml b/docroot/modules/custom/va_gov_eca/tests/modules/va_gov_eca_test_views/va_gov_eca_test_views.info.yml new file mode 100644 index 0000000000..2bc7369d66 --- /dev/null +++ b/docroot/modules/custom/va_gov_eca/tests/modules/va_gov_eca_test_views/va_gov_eca_test_views.info.yml @@ -0,0 +1,5 @@ +name: 'VA.gov ECA Views Tests' +type: module +description: 'Support module for VA.gov ECA Views tests.' +package: Testing +core_version_requirement: ^10 diff --git a/docroot/modules/custom/va_gov_eca/tests/src/Kernel/ViewsResultConditionTest.php b/docroot/modules/custom/va_gov_eca/tests/src/Kernel/ViewsResultConditionTest.php new file mode 100644 index 0000000000..83ec272378 --- /dev/null +++ b/docroot/modules/custom/va_gov_eca/tests/src/Kernel/ViewsResultConditionTest.php @@ -0,0 +1,98 @@ +installEntitySchema('user'); + $this->installEntitySchema('node'); + $this->installConfig(ViewsResultConditionTest::$modules); + $this->conditionManager = \Drupal::service('plugin.manager.eca.condition'); + + // Create a new content type, 'default'. + $this->createContentType(['type' => 'default']); + + // Create View from config. + ViewTestData::createTestViews(ViewsResultConditionTest::class, ['va_gov_eca_test_views']); + $view = View::load('va_gov_eca_default_content'); + + // Create our Condition plugin instance. + $config = [ + 'view_name' => $view->id(), + 'display_name' => 'eca_result_1', + 'arguments' => [], + ]; + /** @var \Drupal\va_gov_eca\Plugin\ECA\Condition\ViewsResultCondition $condition */ + $this->condition = $this->conditionManager->createInstance('views_result', $config); + } + + /** + * Test the condition when a View contains no results. + */ + public function testViewsResultConditionWithNoResult(): void { + $this->assertFalse($this->condition->evaluate()); + } + + /** + * Test the condition when a View contains results. + */ + public function testViewsResultConditionWithResult(): void { + // Create a Default node. + $this->createNode(['type' => 'default']); + $this->assertTrue($this->condition->evaluate()); + } + +} diff --git a/docroot/modules/custom/va_gov_eca/va_gov_eca.info.yml b/docroot/modules/custom/va_gov_eca/va_gov_eca.info.yml new file mode 100644 index 0000000000..371782af80 --- /dev/null +++ b/docroot/modules/custom/va_gov_eca/va_gov_eca.info.yml @@ -0,0 +1,7 @@ +name: 'VA.gov ECA' +type: module +description: 'Custom code for ECA (Event, Condition, Action).' +package: Custom +core_version_requirement: ^10 +dependencies: + - eca:eca