From 75ce9e1ffa5ec8ea8edbe4903d551eb1aafd100b Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Mon, 2 Feb 2026 18:46:50 -0600 Subject: [PATCH 01/12] DP-44544: Update advanced search view with `better_exposed_filters` and revise pagination settings Integrated `better_exposed_filters` module into the `all_documents_latest_rev` view. Switched exposed form type to `bef`, revised pager to use full pagination with first/last links, and configured additional filter options to enhance search usability. Signed-off-by: Dmytro Storozhuk --- .../views.view.all_documents_latest_rev.yml | 178 +++++++++++++----- 1 file changed, 135 insertions(+), 43 deletions(-) diff --git a/conf/drupal/config/views.view.all_documents_latest_rev.yml b/conf/drupal/config/views.view.all_documents_latest_rev.yml index 8c18d630e8..b7b9386388 100644 --- a/conf/drupal/config/views.view.all_documents_latest_rev.yml +++ b/conf/drupal/config/views.view.all_documents_latest_rev.yml @@ -21,6 +21,7 @@ dependencies: - taxonomy.vocabulary.label - taxonomy.vocabulary.language module: + - better_exposed_filters - content_moderation - csv_serialization - file @@ -902,7 +903,7 @@ display: hide_alter_empty: true destination: false pager: - type: mini + type: full options: offset: 0 pagination_heading_level: h4 @@ -912,6 +913,8 @@ display: tags: next: ›› previous: ‹‹ + first: '« First' + last: 'Last »' expose: items_per_page: false items_per_page_label: 'Items per page' @@ -920,6 +923,7 @@ display: items_per_page_options_all_label: '- All -' offset: false offset_label: Offset + quantity: 9 exposed_form: type: basic options: @@ -1215,45 +1219,6 @@ display: default_group: All default_group_multiple: { } group_items: { } - latest_revision: - id: latest_revision - table: media_revision - field: latest_revision - relationship: none - group_type: group - admin_label: '' - entity_type: media - plugin_id: latest_revision - operator: '=' - value: '' - group: 1 - exposed: false - expose: - operator_id: '' - 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: false - default_group: All - default_group_multiple: { } - group_items: { } name: id: name table: media_field_revision @@ -2304,6 +2269,7 @@ display: admin_label: 'field_organizations: Content' plugin_id: standard required: false + group_by: false header: area: id: area @@ -5144,17 +5110,143 @@ display: hide_alter_empty: true destination: false exposed_form: - type: input_required_on_request + type: bef options: submit_button: Filter - reset_button: 1 + reset_button: true reset_button_label: Reset exposed_sorts_label: 'Sort by' - expose_sort_order: 1 + expose_sort_order: true sort_asc_label: Asc sort_desc_label: Desc text_input_required: 'Select any filter and click on Filter to see results' text_input_required_format: basic_html + bef: + general: + autosubmit: false + autosubmit_exclude_textfield: false + autosubmit_textfield_delay: 500 + autosubmit_hide: false + input_required: true + allow_secondary: false + secondary_label: 'Advanced options' + secondary_open: false + reset_button_always_show: false + filter: + field_title_value: + plugin_id: default + advanced: + collapsible: false + is_secondary: false + mid: + plugin_id: default + advanced: + placeholder_text: '' + collapsible: false + is_secondary: false + status: + plugin_id: default + advanced: + sort_options: false + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + moderation_state: + plugin_id: default + advanced: + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + name: + plugin_id: default + advanced: + collapsible: false + is_secondary: false + node_org_filter: + plugin_id: default + advanced: + placeholder_text: '' + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + node_parent_org_top_filter: + plugin_id: default + advanced: + placeholder_text: '' + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + uid: + plugin_id: default + advanced: + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + changed: + plugin_id: default + advanced: + collapsible: false + is_secondary: false + search: + plugin_id: default + advanced: + sort_options: false + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + uid_1: + plugin_id: default + advanced: + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + field_document_label_target_id: + plugin_id: default + advanced: + placeholder_text: '' + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + field_collections_target_id: + plugin_id: default + advanced: + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + filesize: + plugin_id: default + advanced: + collapsible: false + is_secondary: false + field_language_target_id_1: + plugin_id: default + advanced: + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + field_category_target_id: + plugin_id: default + advanced: + placeholder_text: '' + rewrite: + filter_rewrite_values: '' + collapsible: false + is_secondary: false + filemime: + plugin_id: default + advanced: + collapsible: false + is_secondary: false defaults: exposed_form: false fields: false From 88fa6bf5eaa3af02f2dd268befa79861232c0f32 Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Mon, 2 Feb 2026 19:48:42 -0600 Subject: [PATCH 02/12] DP-44544: Fix organization filter in `all_documents_latest_rev` view for parent organizations without children Resolved issue with `INNER JOIN` on `node__field_organizations` that excluded parent organizations without child organizations by changing the join type to `LEFT`. Added a reflection-based adjustment to modify the query behavior dynamically. Signed-off-by: Dmytro Storozhuk --- .../custom/mass_views/mass_views.module | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docroot/modules/custom/mass_views/mass_views.module b/docroot/modules/custom/mass_views/mass_views.module index 4c6c5569f0..9edad0f246 100644 --- a/docroot/modules/custom/mass_views/mass_views.module +++ b/docroot/modules/custom/mass_views/mass_views.module @@ -396,8 +396,39 @@ function mass_views_views_query_alter(ViewExecutable $view, QueryPluginBase $que $args_to_pass = array_merge($nids_empty, $nids_unpublished); $query->addWhere(0, 'node_field_data.nid', $args_to_pass, 'IN'); } + + if ($view->id() == 'all_documents_latest_rev' && in_array($view->getDisplay()->display['id'], ['page_1', 'data_export_1'])) { + // Fix the organization filter to work with organizations that don't have + // child organizations. The issue is that the view creates an INNER JOIN + // to node__field_organizations which requires the org to have children. + // We need to change this to a LEFT JOIN. + + // Use reflection to access the protected tableQueue property + try { + $reflection = new \ReflectionObject($query); + $property = $reflection->getProperty('tableQueue'); + $property->setAccessible(TRUE); + $table_queue = $property->getValue($query); + + // Find and modify the node__field_organizations join + foreach ($table_queue as $table_def) { + if (isset($table_def['table']) && $table_def['table'] == 'node__field_organizations') { + // Change join type from INNER to LEFT + $table_def['join']->type = 'LEFT'; + } + } + + $property->setValue($query, $table_queue); + } + catch (\Exception $e) { + // Silently continue if reflection fails + } + + } } + + /** * Implements hook_entity_update(). */ From 29951cdc650008b4db239eac2bb1b867e7cf1314 Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Mon, 2 Feb 2026 20:03:24 -0600 Subject: [PATCH 03/12] DP-44544: Add changelog entry for advanced search documents view fix Signed-off-by: Dmytro Storozhuk --- changelogs/DP-44544.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelogs/DP-44544.yml diff --git a/changelogs/DP-44544.yml b/changelogs/DP-44544.yml new file mode 100644 index 0000000000..0ad5df90cc --- /dev/null +++ b/changelogs/DP-44544.yml @@ -0,0 +1,3 @@ +Fixed: + - description: Fix bug in advanced search for documents view. + issue: DP-44544 From 85c5547c2120c44307f8cf58df14ad2358328c65 Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Mon, 2 Feb 2026 20:10:01 -0600 Subject: [PATCH 04/12] DP-44544: Remove redundant blank lines in `mass_views.module` to improve code clarity Signed-off-by: Dmytro Storozhuk --- docroot/modules/custom/mass_views/mass_views.module | 2 -- 1 file changed, 2 deletions(-) diff --git a/docroot/modules/custom/mass_views/mass_views.module b/docroot/modules/custom/mass_views/mass_views.module index 9edad0f246..183c0ff25f 100644 --- a/docroot/modules/custom/mass_views/mass_views.module +++ b/docroot/modules/custom/mass_views/mass_views.module @@ -427,8 +427,6 @@ function mass_views_views_query_alter(ViewExecutable $view, QueryPluginBase $que } } - - /** * Implements hook_entity_update(). */ From 2b61560d887762f80e3823164399b5b1161248e1 Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Wed, 11 Feb 2026 20:29:50 -0600 Subject: [PATCH 05/12] Fix duplicate rows and add bulk operations to documents advanced search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix duplicate rows caused by LEFT JOIN to multi-value organizations field - Added delta=0 condition to join only first organization per media - Eliminated ~29,576 duplicate rows (468,929 → 439,353 total) - Add Views Bulk Operations (VBO) functionality - Added bulk selection checkboxes to table - Enabled actions: Publish, Unpublish, Bulk Edit, Delete - Configured for 10 items per batch with form step - Changed distinct: true to false in view config - DISTINCT didn't work with multi-value fields - Fixed via join condition instead Technical changes: - Modified hook_views_query_alter() in mass_views.module - Updated views.view.all_documents_latest_rev.yml - Added module dependencies: views_bulk_operations, views_data_export Tested with 100 results - zero duplicates confirmed. --- .../views.view.all_documents_latest_rev.yml | 193 +++++++++++++++++- .../custom/mass_views/mass_views.module | 29 +++ 2 files changed, 221 insertions(+), 1 deletion(-) diff --git a/conf/drupal/config/views.view.all_documents_latest_rev.yml b/conf/drupal/config/views.view.all_documents_latest_rev.yml index b7b9386388..e7342aeb13 100644 --- a/conf/drupal/config/views.view.all_documents_latest_rev.yml +++ b/conf/drupal/config/views.view.all_documents_latest_rev.yml @@ -33,6 +33,8 @@ dependencies: - serialization - taxonomy - user + - views_bulk_operations + - views_data_export - views_data_export id: all_documents_latest_rev label: 'Advanced search - documents' @@ -50,6 +52,81 @@ display: display_options: title: 'Advanced Search - Documents' 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: true + action_title: Action + clear_on_exposed: false + show_multipage_selection_box: default + show_select_all: always_hide + selected_actions: + - + action_id: 'entity:publish_action:media' + preconfiguration: + label_override: '' + - + action_id: 'entity:unpublish_action:media' + preconfiguration: + label_override: '' + - + action_id: views_bulk_edit + preconfiguration: + label_override: '' + - + action_id: 'entity:delete_action:media' + preconfiguration: + label_override: '' mid: id: mid table: media_field_revision @@ -1219,6 +1296,45 @@ display: default_group: All default_group_multiple: { } group_items: { } + latest_revision: + id: latest_revision + table: media_revision + field: latest_revision + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: latest_revision + operator: '=' + value: '' + group: 1 + exposed: false + expose: + operator_id: '' + 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: false + default_group: All + default_group_multiple: { } + group_items: { } name: id: name table: media_field_revision @@ -2213,7 +2329,7 @@ display: options: query_comment: '' disable_sql_rewrite: false - distinct: true + distinct: false disable_automatic_base_fields: false replica: false query_tags: { } @@ -4254,6 +4370,81 @@ display: position: 1 display_options: 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: true + action_title: Action + clear_on_exposed: false + show_multipage_selection_box: default + show_select_all: always_hide + selected_actions: + - + action_id: 'entity:publish_action:media' + preconfiguration: + label_override: '' + - + action_id: 'entity:unpublish_action:media' + preconfiguration: + label_override: '' + - + action_id: views_bulk_edit + preconfiguration: + label_override: '' + - + action_id: 'entity:delete_action:media' + preconfiguration: + label_override: '' mid: id: mid table: media_field_revision diff --git a/docroot/modules/custom/mass_views/mass_views.module b/docroot/modules/custom/mass_views/mass_views.module index 183c0ff25f..51edd70da9 100644 --- a/docroot/modules/custom/mass_views/mass_views.module +++ b/docroot/modules/custom/mass_views/mass_views.module @@ -281,6 +281,14 @@ function _mass_views_helper_get_numeric_media_ids_from_redirect_table() { * Implements hook_views_query_alter(). */ function mass_views_views_query_alter(ViewExecutable $view, QueryPluginBase $query) { + + // Debug: Log when all_documents_latest_rev view is being altered + if ($view->id() == 'all_documents_latest_rev') { + \Drupal::logger('mass_views')->notice('Hook called for view: @id, display: @display', [ + '@id' => $view->id(), + '@display' => $view->getDisplay()->display['id'], + ]); + } if ($view->id() == 'report_orphaned_documents') { @@ -398,6 +406,27 @@ function mass_views_views_query_alter(ViewExecutable $view, QueryPluginBase $que } if ($view->id() == 'all_documents_latest_rev' && in_array($view->getDisplay()->display['id'], ['page_1', 'data_export_1'])) { + // Fix duplicate rows caused by LEFT JOINs to multi-value fields (organizations, etc.). + // When a media has multiple organizations, the LEFT JOIN creates multiple rows. + // + // Access the table queue to find and modify the organizations join + $table_queue = &$query->getTableQueue(); + + foreach ($table_queue as $alias => &$table_info) { + if (isset($table_info['table']) && $table_info['table'] == 'media_revision__field_organizations') { + // Add a condition to the join to only include the first organization (delta = 0) + // This prevents duplicates when a media has multiple organizations + if (!isset($table_info['join']->extra)) { + $table_info['join']->extra = []; + } + $table_info['join']->extra[] = [ + 'field' => 'delta', + 'value' => 0, + 'operator' => '=', + ]; + } + } + // Fix the organization filter to work with organizations that don't have // child organizations. The issue is that the view creates an INNER JOIN // to node__field_organizations which requires the org to have children. From 12f1ad8a354de129b7bbf84d8702c3a7f61d79b0 Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Thu, 12 Feb 2026 10:15:51 -0600 Subject: [PATCH 06/12] Fix coding standards: Remove trailing whitespace - Remove trailing whitespace on lines 284 and 414 - Fixes Squiz.WhiteSpace.SuperfluousWhitespace.EndLine violations --- docroot/modules/custom/mass_views/mass_views.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docroot/modules/custom/mass_views/mass_views.module b/docroot/modules/custom/mass_views/mass_views.module index 51edd70da9..ce72b02ad1 100644 --- a/docroot/modules/custom/mass_views/mass_views.module +++ b/docroot/modules/custom/mass_views/mass_views.module @@ -281,7 +281,7 @@ function _mass_views_helper_get_numeric_media_ids_from_redirect_table() { * Implements hook_views_query_alter(). */ function mass_views_views_query_alter(ViewExecutable $view, QueryPluginBase $query) { - + // Debug: Log when all_documents_latest_rev view is being altered if ($view->id() == 'all_documents_latest_rev') { \Drupal::logger('mass_views')->notice('Hook called for view: @id, display: @display', [ @@ -411,7 +411,7 @@ function mass_views_views_query_alter(ViewExecutable $view, QueryPluginBase $que // // Access the table queue to find and modify the organizations join $table_queue = &$query->getTableQueue(); - + foreach ($table_queue as $alias => &$table_info) { if (isset($table_info['table']) && $table_info['table'] == 'media_revision__field_organizations') { // Add a condition to the join to only include the first organization (delta = 0) From 77a2c2f4927aa70c4511a3e27598009db713bca4 Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Thu, 12 Feb 2026 11:09:24 -0600 Subject: [PATCH 07/12] Remove debug logging code --- docroot/modules/custom/mass_views/mass_views.module | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docroot/modules/custom/mass_views/mass_views.module b/docroot/modules/custom/mass_views/mass_views.module index ce72b02ad1..c9eb6a0e46 100644 --- a/docroot/modules/custom/mass_views/mass_views.module +++ b/docroot/modules/custom/mass_views/mass_views.module @@ -282,14 +282,6 @@ function _mass_views_helper_get_numeric_media_ids_from_redirect_table() { */ function mass_views_views_query_alter(ViewExecutable $view, QueryPluginBase $query) { - // Debug: Log when all_documents_latest_rev view is being altered - if ($view->id() == 'all_documents_latest_rev') { - \Drupal::logger('mass_views')->notice('Hook called for view: @id, display: @display', [ - '@id' => $view->id(), - '@display' => $view->getDisplay()->display['id'], - ]); - } - if ($view->id() == 'report_orphaned_documents') { // Get the numeric media IDs that need to be excluded. From a6d9946f85c01d798576f7b508d872c12c36a13a Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Thu, 12 Feb 2026 20:40:54 -0600 Subject: [PATCH 08/12] DP-44544: Enable performance statistics in views settings Signed-off-by: Dmytro Storozhuk --- conf/drupal/config/views.settings.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/drupal/config/views.settings.yml b/conf/drupal/config/views.settings.yml index f0cab0f402..d1fc5f9b12 100644 --- a/conf/drupal/config/views.settings.yml +++ b/conf/drupal/config/views.settings.yml @@ -9,7 +9,7 @@ ui: additional_queries: false advanced_column: true default_display: true - performance_statistics: false + performance_statistics: true preview_information: true sql_query: enabled: true From fd6ae7b77113d16ba66c3d7f67316b3d321f0a1c Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Thu, 12 Feb 2026 23:47:54 -0600 Subject: [PATCH 09/12] DP-44544: Refactor and optimize organization filter for documents advanced search - Removed redundant code for handling duplicate rows caused by multi-value fields. - Introduced a container-aware approach in `OrgFilterMedia` to manage joins more effectively. - Updated `all_documents_latest_rev` view configuration to use the new `media_org_filter`. - Ensured join adjustments are processed via dependency injection for improved maintainability. - Adjusted cache metadata to avoid stale results. Signed-off-by: Dmytro Storozhuk --- .../views.view.all_documents_latest_rev.yml | 26 +++++----- .../custom/mass_views/mass_views.module | 49 ------------------ .../Plugin/views/filter/OrgFilterMedia.php | 51 +++++++++++++++++-- 3 files changed, 61 insertions(+), 65 deletions(-) diff --git a/conf/drupal/config/views.view.all_documents_latest_rev.yml b/conf/drupal/config/views.view.all_documents_latest_rev.yml index e7342aeb13..86c53d0413 100644 --- a/conf/drupal/config/views.view.all_documents_latest_rev.yml +++ b/conf/drupal/config/views.view.all_documents_latest_rev.yml @@ -35,7 +35,6 @@ dependencies: - user - views_bulk_operations - views_data_export - - views_data_export id: all_documents_latest_rev label: 'Advanced search - documents' module: views @@ -1416,28 +1415,28 @@ display: default_group: All default_group_multiple: { } group_items: { } - node_org_filter: - id: node_org_filter - table: node_field_data - field: node_org_filter - relationship: field_organizations + media_org_filter: + id: media_org_filter + table: media_field_data + field: media_org_filter + relationship: mid group_type: group admin_label: '' - entity_type: node - plugin_id: mass_views_node_org_filter + entity_type: media + plugin_id: mass_views_media_org_filter operator: '=' value: null group: 1 exposed: true expose: - operator_id: node_org_filter_op + operator_id: '' label: 'Organization (direct match)' description: '' use_operator: false - operator: node_org_filter_op + operator: media_org_filter_op operator_limit_selection: false operator_list: { } - identifier: node_org_filter + identifier: media_org_filter required: false remember: false multiple: false @@ -1460,6 +1459,7 @@ display: prototype_design_access: '0' mmg_editor: '0' viewer: '0' + bulk_edit: '0' is_grouped: false group_info: label: '' @@ -2416,7 +2416,7 @@ display: metatags: { } tokenize: false cache_metadata: - max-age: -1 + max-age: 0 contexts: - 'languages:language_content' - 'languages:language_interface' @@ -5462,7 +5462,7 @@ display: tokenize: false path: admin/ma-dash/documents-advanced-search cache_metadata: - max-age: -1 + max-age: 0 contexts: - 'languages:language_content' - 'languages:language_interface' diff --git a/docroot/modules/custom/mass_views/mass_views.module b/docroot/modules/custom/mass_views/mass_views.module index c9eb6a0e46..380a578b52 100644 --- a/docroot/modules/custom/mass_views/mass_views.module +++ b/docroot/modules/custom/mass_views/mass_views.module @@ -397,55 +397,6 @@ function mass_views_views_query_alter(ViewExecutable $view, QueryPluginBase $que $query->addWhere(0, 'node_field_data.nid', $args_to_pass, 'IN'); } - if ($view->id() == 'all_documents_latest_rev' && in_array($view->getDisplay()->display['id'], ['page_1', 'data_export_1'])) { - // Fix duplicate rows caused by LEFT JOINs to multi-value fields (organizations, etc.). - // When a media has multiple organizations, the LEFT JOIN creates multiple rows. - // - // Access the table queue to find and modify the organizations join - $table_queue = &$query->getTableQueue(); - - foreach ($table_queue as $alias => &$table_info) { - if (isset($table_info['table']) && $table_info['table'] == 'media_revision__field_organizations') { - // Add a condition to the join to only include the first organization (delta = 0) - // This prevents duplicates when a media has multiple organizations - if (!isset($table_info['join']->extra)) { - $table_info['join']->extra = []; - } - $table_info['join']->extra[] = [ - 'field' => 'delta', - 'value' => 0, - 'operator' => '=', - ]; - } - } - - // Fix the organization filter to work with organizations that don't have - // child organizations. The issue is that the view creates an INNER JOIN - // to node__field_organizations which requires the org to have children. - // We need to change this to a LEFT JOIN. - - // Use reflection to access the protected tableQueue property - try { - $reflection = new \ReflectionObject($query); - $property = $reflection->getProperty('tableQueue'); - $property->setAccessible(TRUE); - $table_queue = $property->getValue($query); - - // Find and modify the node__field_organizations join - foreach ($table_queue as $table_def) { - if (isset($table_def['table']) && $table_def['table'] == 'node__field_organizations') { - // Change join type from INNER to LEFT - $table_def['join']->type = 'LEFT'; - } - } - - $property->setValue($query, $table_queue); - } - catch (\Exception $e) { - // Silently continue if reflection fails - } - - } } /** diff --git a/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgFilterMedia.php b/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgFilterMedia.php index 5aac55b313..7ebe3a9540 100644 --- a/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgFilterMedia.php +++ b/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgFilterMedia.php @@ -3,7 +3,10 @@ namespace Drupal\mass_views\Plugin\views\filter; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\views\Plugin\views\filter\FilterPluginBase; +use Drupal\views\Plugin\ViewsHandlerManager; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Filters by media's organization. @@ -14,7 +17,34 @@ * * @ViewsFilter("mass_views_media_org_filter") */ -class OrgFilterMedia extends FilterPluginBase { +class OrgFilterMedia extends FilterPluginBase implements ContainerFactoryPluginInterface { + + /** + * The views join plugin manager. + * + * @var \Drupal\views\Plugin\ViewsHandlerManager + */ + protected $joinManager; + + /** + * Constructs a new OrgFilterMedia object. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, ViewsHandlerManager $join_manager) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->joinManager = $join_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('plugin.manager.views.join') + ); + } /** * {@inheritdoc} @@ -37,10 +67,25 @@ public function valueForm(&$form, FormStateInterface $form_state) { public function query() { // ONLY add the relationships if we have a value to filter on. if ($value = $this->getValue()) { - // Pre-create the join we need, but convert it to an INNER JOIN for - // performance. + // Build the join to media__field_organizations. + // Use getJoinData if the table is already joined, otherwise create + // a new join definition manually. $relationship = $this->relationship ? $this->relationship : $this->view->storage->get('base_table'); $join = $this->query->getJoinData('media__field_organizations', $relationship); + if (!$join) { + $join = $this->joinManager->createInstance('standard', [ + 'table' => 'media__field_organizations', + 'field' => 'entity_id', + 'left_table' => $relationship, + 'left_field' => 'mid', + 'extra' => [ + [ + 'field' => 'deleted', + 'value' => '0', + ], + ], + ]); + } $join->type = 'INNER'; // Ensure we have the tables we need. From 6e2e326cf37ed5481346f739e006a285ab3b0e57 Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Thu, 19 Feb 2026 16:56:57 -0600 Subject: [PATCH 10/12] DP-44544: Update advanced documents search to remove redundant dependencies and fix organization filters - Removed `better_exposed_filters` module dependency from `all_documents_latest_rev` view. - Replaced `OrgTopParentFilter` implementation with a more robust `media_org_filter`. - Fixed joins for organization filters to improve query accuracy and performance. - Added revisions and ID fields to enhance filtering and display functionality. Signed-off-by: Dmytro Storozhuk --- .../views.view.all_documents_latest_rev.yml | 218 +++++++----------- .../views/filter/OrgTopParentFilterMedia.php | 26 ++- 2 files changed, 96 insertions(+), 148 deletions(-) diff --git a/conf/drupal/config/views.view.all_documents_latest_rev.yml b/conf/drupal/config/views.view.all_documents_latest_rev.yml index 86c53d0413..21b19c147f 100644 --- a/conf/drupal/config/views.view.all_documents_latest_rev.yml +++ b/conf/drupal/config/views.view.all_documents_latest_rev.yml @@ -21,7 +21,6 @@ dependencies: - taxonomy.vocabulary.label - taxonomy.vocabulary.language module: - - better_exposed_filters - content_moderation - csv_serialization - file @@ -1472,28 +1471,28 @@ display: default_group: All default_group_multiple: { } group_items: { } - node_parent_org_top_filter: - id: node_parent_org_top_filter - table: node_field_data - field: node_parent_org_top_filter - relationship: field_organizations + media_parent_org_top_filter: + id: media_parent_org_top_filter + table: media_field_data + field: media_parent_org_top_filter + relationship: mid group_type: group admin_label: '' - entity_type: node - plugin_id: mass_views_node_org_top_parent_filter + entity_type: media + plugin_id: mass_views_media_org_top_parent_filter operator: '=' value: null group: 1 exposed: true expose: - operator_id: node_parent_org_top_filter_op + operator_id: media_parent_org_top_filter_op label: 'Organization (including all suborganizations)' description: 'using the parent organization field for each organization' use_operator: false - operator: node_parent_org_top_filter_op + operator: media_parent_org_top_filter_op operator_limit_selection: false operator_list: { } - identifier: node_parent_org_top_filter + identifier: media_parent_org_top_filter required: false remember: false multiple: false @@ -1516,6 +1515,7 @@ display: prototype_design_access: '0' mmg_editor: '0' viewer: '0' + bulk_edit: '0' is_grouped: false group_info: label: '' @@ -4511,6 +4511,72 @@ display: multi_type: separator separator: ', ' field_api_classes: false + vid: + id: vid + table: media_field_data + field: vid + relationship: mid + group_type: group + admin_label: '' + entity_type: media + entity_field: vid + plugin_id: field + label: 'Revision ID' + exclude: true + 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: number_integer + settings: + thousand_separator: '' + prefix_suffix: 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 field_title__revision_id: id: field_title__revision_id table: media_revision__field_title @@ -5301,7 +5367,7 @@ display: hide_alter_empty: true destination: false exposed_form: - type: bef + type: basic options: submit_button: Filter reset_button: true @@ -5310,134 +5376,6 @@ display: expose_sort_order: true sort_asc_label: Asc sort_desc_label: Desc - text_input_required: 'Select any filter and click on Filter to see results' - text_input_required_format: basic_html - bef: - general: - autosubmit: false - autosubmit_exclude_textfield: false - autosubmit_textfield_delay: 500 - autosubmit_hide: false - input_required: true - allow_secondary: false - secondary_label: 'Advanced options' - secondary_open: false - reset_button_always_show: false - filter: - field_title_value: - plugin_id: default - advanced: - collapsible: false - is_secondary: false - mid: - plugin_id: default - advanced: - placeholder_text: '' - collapsible: false - is_secondary: false - status: - plugin_id: default - advanced: - sort_options: false - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - moderation_state: - plugin_id: default - advanced: - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - name: - plugin_id: default - advanced: - collapsible: false - is_secondary: false - node_org_filter: - plugin_id: default - advanced: - placeholder_text: '' - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - node_parent_org_top_filter: - plugin_id: default - advanced: - placeholder_text: '' - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - uid: - plugin_id: default - advanced: - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - changed: - plugin_id: default - advanced: - collapsible: false - is_secondary: false - search: - plugin_id: default - advanced: - sort_options: false - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - uid_1: - plugin_id: default - advanced: - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - field_document_label_target_id: - plugin_id: default - advanced: - placeholder_text: '' - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - field_collections_target_id: - plugin_id: default - advanced: - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - filesize: - plugin_id: default - advanced: - collapsible: false - is_secondary: false - field_language_target_id_1: - plugin_id: default - advanced: - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - field_category_target_id: - plugin_id: default - advanced: - placeholder_text: '' - rewrite: - filter_rewrite_values: '' - collapsible: false - is_secondary: false - filemime: - plugin_id: default - advanced: - collapsible: false - is_secondary: false defaults: exposed_form: false fields: false diff --git a/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgTopParentFilterMedia.php b/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgTopParentFilterMedia.php index 54f5ed5ad6..ec8b2fe029 100644 --- a/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgTopParentFilterMedia.php +++ b/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgTopParentFilterMedia.php @@ -4,8 +4,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\views\Plugin\views\filter\FilterPluginBase; -use Drupal\views\Plugin\views\display\DisplayPluginBase; -use Drupal\views\ViewExecutable; +use Drupal\views\Views; /** * Filters by a TOP-LEVEL Organization and includes all of its descendants. @@ -58,13 +57,24 @@ public function query() { $org_ids[] = (int) $top_id; } - // 2) Create the INNER JOIN to the org reference field table. - $relationship = $this->relationship ? $this->relationship : $this->view->storage->get('base_table'); - $join = $this->query->getJoinData('media__field_organizations', $relationship); - $join->type = 'INNER'; - $org_table_alias = $this->query->ensureTable('media__field_organizations', $this->relationship, $join); + // 2) Create an INNER JOIN to media__field_organizations via the Views join + // plugin. getJoinData() can return NULL when the table is not yet + // registered in the query, so we build the join object directly. + $configuration = [ + 'type' => 'INNER', + 'table' => 'media__field_organizations', + 'field' => 'entity_id', + 'left_table' => 'media_field_data', + 'left_field' => 'mid', + 'extra' => [ + ['field' => 'deleted', 'value' => '0'], + ], + 'operator' => '=', + ]; + $join = Views::pluginManager('join')->createInstance('standard', $configuration); + $org_table_alias = $this->query->addTable('media__field_organizations', $this->relationship, $join); - // 3) Build an OR where-group: (nid IN org_ids) OR (field_organizations_target_id IN org_ids) + // 3) Filter to only rows where the tagged org is in our computed subtree. $this->query->addWhere($this->options['group'], "$org_table_alias.field_organizations_target_id", $org_ids, 'IN'); } From c3302b64d22ab0ab4577648a07dae2de981b547c Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Thu, 19 Feb 2026 18:13:09 -0600 Subject: [PATCH 11/12] DP-44544: Adjust comment formatting in `OrgTopParentFilterMedia` for clarity Signed-off-by: Dmytro Storozhuk --- .../src/Plugin/views/filter/OrgTopParentFilterMedia.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgTopParentFilterMedia.php b/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgTopParentFilterMedia.php index ec8b2fe029..d2725e4754 100644 --- a/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgTopParentFilterMedia.php +++ b/docroot/modules/custom/mass_views/src/Plugin/views/filter/OrgTopParentFilterMedia.php @@ -58,8 +58,8 @@ public function query() { } // 2) Create an INNER JOIN to media__field_organizations via the Views join - // plugin. getJoinData() can return NULL when the table is not yet - // registered in the query, so we build the join object directly. + // plugin. getJoinData() can return NULL when the table is not yet + // registered in the query, so we build the join object directly. $configuration = [ 'type' => 'INNER', 'table' => 'media__field_organizations', From 5a3500ba0c17c06869ab5aba1ebdb22ee2cf868c Mon Sep 17 00:00:00 2001 From: Dmytro Storozhuk Date: Fri, 20 Feb 2026 12:57:38 -0600 Subject: [PATCH 12/12] DP-44544: Fix duplicate rows in `all_documents_latest_rev` view and improve filter usability - Added `delta = 0` condition to LEFT JOIN for `field_organizations` in query alter to eliminate duplicate rows. - Updated exposed form type to `input_required` and added guidance text for filters. - Improved query and filter behavior for advanced search functionality. Signed-off-by: Dmytro Storozhuk --- .../views.view.all_documents_latest_rev.yml | 6 ++++-- .../custom/mass_views/mass_views.module | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/conf/drupal/config/views.view.all_documents_latest_rev.yml b/conf/drupal/config/views.view.all_documents_latest_rev.yml index 21b19c147f..d70f971694 100644 --- a/conf/drupal/config/views.view.all_documents_latest_rev.yml +++ b/conf/drupal/config/views.view.all_documents_latest_rev.yml @@ -1487,7 +1487,7 @@ display: expose: operator_id: media_parent_org_top_filter_op label: 'Organization (including all suborganizations)' - description: 'using the parent organization field for each organization' + description: '' use_operator: false operator: media_parent_org_top_filter_op operator_limit_selection: false @@ -5367,7 +5367,7 @@ display: hide_alter_empty: true destination: false exposed_form: - type: basic + type: input_required options: submit_button: Filter reset_button: true @@ -5376,6 +5376,8 @@ display: expose_sort_order: true sort_asc_label: Asc sort_desc_label: Desc + text_input_required: 'Select any filter and click on Apply to see results' + text_input_required_format: basic_html defaults: exposed_form: false fields: false diff --git a/docroot/modules/custom/mass_views/mass_views.module b/docroot/modules/custom/mass_views/mass_views.module index 380a578b52..92b1ea50fb 100644 --- a/docroot/modules/custom/mass_views/mass_views.module +++ b/docroot/modules/custom/mass_views/mass_views.module @@ -6,6 +6,7 @@ */ use Drupal\Core\Cache\Cache; +use Drupal\Core\Database\Query\AlterableInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Link; use Drupal\Core\Url; @@ -399,6 +400,23 @@ function mass_views_views_query_alter(ViewExecutable $view, QueryPluginBase $que } +/** + * Implements hook_query_TAGNAME_alter() for the all_documents_latest_rev view. + * + * The field_organizations relationship LEFT JOINs media_revision__field_organizations, + * which stores one row per delta (0, 1, 2…). A media item with N orgs produces + * N duplicate rows in the result. DISTINCT can't collapse them because each row + * has a different org NID in the SELECT, and GROUP BY fails because MySQL runs + * with ONLY_FULL_GROUP_BY. Restricting the JOIN to delta = 0 limits it to the + * first org per revision, eliminating the fan-out at the SQL level. + */ +function mass_views_query_views_all_documents_latest_rev_alter(AlterableInterface $query) { + $tables = &$query->getTables(); + if (isset($tables['media_revision__field_organizations'])) { + $tables['media_revision__field_organizations']['condition'] .= ' AND media_revision__field_organizations.delta = 0'; + } +} + /** * Implements hook_entity_update(). */