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 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 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..d70f971694 100644 --- a/conf/drupal/config/views.view.all_documents_latest_rev.yml +++ b/conf/drupal/config/views.view.all_documents_latest_rev.yml @@ -32,6 +32,7 @@ dependencies: - serialization - taxonomy - user + - views_bulk_operations - views_data_export id: all_documents_latest_rev label: 'Advanced search - documents' @@ -49,6 +50,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 @@ -902,7 +978,7 @@ display: hide_alter_empty: true destination: false pager: - type: mini + type: full options: offset: 0 pagination_heading_level: h4 @@ -912,6 +988,8 @@ display: tags: next: ›› previous: ‹‹ + first: '« First' + last: 'Last »' expose: items_per_page: false items_per_page_label: 'Items per page' @@ -920,6 +998,7 @@ display: items_per_page_options_all_label: '- All -' offset: false offset_label: Offset + quantity: 9 exposed_form: type: basic options: @@ -1335,28 +1414,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 @@ -1379,6 +1458,7 @@ display: prototype_design_access: '0' mmg_editor: '0' viewer: '0' + bulk_edit: '0' is_grouped: false group_info: label: '' @@ -1391,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' + description: '' 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 @@ -1435,6 +1515,7 @@ display: prototype_design_access: '0' mmg_editor: '0' viewer: '0' + bulk_edit: '0' is_grouped: false group_info: label: '' @@ -2248,7 +2329,7 @@ display: options: query_comment: '' disable_sql_rewrite: false - distinct: true + distinct: false disable_automatic_base_fields: false replica: false query_tags: { } @@ -2304,6 +2385,7 @@ display: admin_label: 'field_organizations: Content' plugin_id: standard required: false + group_by: false header: area: id: area @@ -2334,7 +2416,7 @@ display: metatags: { } tokenize: false cache_metadata: - max-age: -1 + max-age: 0 contexts: - 'languages:language_content' - 'languages:language_interface' @@ -4288,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 @@ -4354,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 @@ -5144,16 +5367,16 @@ display: hide_alter_empty: true destination: false exposed_form: - type: input_required_on_request + type: input_required 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: 'Select any filter and click on Apply to see results' text_input_required_format: basic_html defaults: exposed_form: false @@ -5179,7 +5402,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 4c6c5569f0..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; @@ -396,6 +397,24 @@ 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'); } + +} + +/** + * 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'; + } } /** 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. 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..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 @@ -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'); }