From 5baf187d83b357587e9a613018e53652cd593891 Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Wed, 13 Nov 2024 15:16:30 +0000 Subject: [PATCH 1/8] Add a view display extender for the localgov_page_header for the lede Fix #127 Adds a setting to views that allows for a page header category, with the option to set a custom lede inside the view, which will then be used by the page header block. --- config/schema/localgov_core.views.schema.yml | 9 + src/Plugin/Block/PageHeaderBlock.php | 22 ++ .../PageHeaderDisplayExtender.php | 251 ++++++++++++++++++ .../install/views.view.recent_content.yml | 214 +++++++++++++++ ...algov_core_views_page_header_test.info.yml | 9 + .../src/Functional/ViewsPageExtenderTest.php | 79 ++++++ 6 files changed, 584 insertions(+) create mode 100644 config/schema/localgov_core.views.schema.yml create mode 100644 src/Plugin/views/display_extender/PageHeaderDisplayExtender.php create mode 100644 tests/localgov_core_views_page_header_test/config/install/views.view.recent_content.yml create mode 100644 tests/localgov_core_views_page_header_test/localgov_core_views_page_header_test.info.yml create mode 100644 tests/src/Functional/ViewsPageExtenderTest.php diff --git a/config/schema/localgov_core.views.schema.yml b/config/schema/localgov_core.views.schema.yml new file mode 100644 index 0000000..fb74976 --- /dev/null +++ b/config/schema/localgov_core.views.schema.yml @@ -0,0 +1,9 @@ +views.display_extender.localgov_page_header_display_extender: + type: views_display_extender + mapping: + lede: + type: text + label: 'Lede' + tokenize: + type: boolean + label: 'Should replacement tokens be used from the first row' diff --git a/src/Plugin/Block/PageHeaderBlock.php b/src/Plugin/Block/PageHeaderBlock.php index 0a1b838..b858b31 100644 --- a/src/Plugin/Block/PageHeaderBlock.php +++ b/src/Plugin/Block/PageHeaderBlock.php @@ -14,6 +14,8 @@ use Drupal\localgov_core\Event\PageHeaderDisplayEvent; use Drupal\node\Entity\Node; use Drupal\taxonomy\Entity\Term; +use Drupal\views\ViewExecutable; +use Drupal\views\Views; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -130,6 +132,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $route = $this->currentRouteMatch->getRouteObject(); if (!is_null($route)) { $parameters = $route->getOption('parameters'); + $defaults = $route->getDefaults(); if (!is_null($parameters)) { foreach ($parameters as $name => $options) { if (!isset($options['type'])) { @@ -152,6 +155,13 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition } } } + if (isset($defaults['view_id']) && isset($defaults['display_id'])) { + $view = Views::getView($defaults['view_id']); + if ($view) { + $view->setDisplay($defaults['display_id']); + $this->entity = $view; + } + } } // Dispatch event to allow modules to alter block content. @@ -224,6 +234,18 @@ protected function getLede() { ]; } + // Return view custom summary. + if ($this->entity instanceof ViewExecutable) { + $extender = $this->entity->getDisplay()->getExtenders()['localgov_page_header_display_extender'] ?? NULL; + $this->entity->render(); + $lede = $extender->getLede(); + return [ + '#type' => 'html_tag', + '#tag' => 'p', + '#value' => $lede, + ]; + } + return NULL; } diff --git a/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php b/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php new file mode 100644 index 0000000..f4e5b74 --- /dev/null +++ b/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php @@ -0,0 +1,251 @@ + '']; + $options['tokenize'] = ['default' => FALSE]; + + return $options; + } + + /** + * Provide a form to edit options for this plugin. + */ + public function buildOptionsForm(&$form, FormStateInterface $form_state) { + + if ($form_state->get('section') == 'page_header') { + $form['#title'] .= $this->t('The page header summary'); + + // Build/inject the Metatag form. + $form['lede'] = [ + '#type' => 'textarea', + '#title' => $this->t('Lede'), + '#description' => $this->t('Summary displayed under the page title.'), + '#default_value' => $this->options['lede'], + ]; + $this->tokenForm($form['page_header'], $form_state); + } + } + + /** + * Handle any special handling on the validate form. + */ + public function submitOptionsForm(&$form, FormStateInterface $form_state) { + if ($form_state->get('section') == 'page_header') { + // Process submitted metatag values and remove empty tags. + $page_header_values = $form_state->cleanValues()->getValues(); + $this->options['tokenize'] = $page_header_values['tokenize'] ?? FALSE; + unset($page_header_values['tokenize']); + $this->options['lede'] = $page_header_values['lede']; + } + } + + /** + * Verbatim copy of TokenizeAreaPluginBase::tokenForm(). + */ + public function tokenForm(&$form, FormStateInterface $form_state) { + $form['tokenize'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Use replacement tokens from the first row'), + '#default_value' => $this->options['tokenize'], + ]; + + // Get a list of the available fields and arguments for token replacement. + $options = []; + $optgroup_arguments = (string) new TranslatableMarkup('Arguments'); + $optgroup_fields = (string) new TranslatableMarkup('Fields'); + foreach ($this->view->display_handler->getHandlers('field') as $field => $handler) { + $options[$optgroup_fields]["{{ $field }}"] = $handler->adminLabel(); + } + + foreach ($this->view->display_handler->getHandlers('argument') as $arg => $handler) { + $options[$optgroup_arguments]["{{ arguments.$arg }}"] = $this->t('@argument title', ['@argument' => $handler->adminLabel()]); + $options[$optgroup_arguments]["{{ raw_arguments.$arg }}"] = $this->t('@argument input', ['@argument' => $handler->adminLabel()]); + } + + if (!empty($options)) { + $form['tokens'] = [ + '#type' => 'details', + '#title' => $this->t('Replacement patterns'), + '#open' => TRUE, + '#id' => 'edit-options-token-help', + '#states' => [ + 'visible' => [ + ':input[name="options[tokenize]"]' => ['checked' => TRUE], + ], + ], + ]; + $form['tokens']['help'] = [ + '#markup' => '

' . $this->t('The following tokens are available. You may use Twig syntax in this field.') . '

', + ]; + foreach (array_keys($options) as $type) { + if (!empty($options[$type])) { + $items = []; + foreach ($options[$type] as $key => $value) { + $items[] = $key . ' == ' . $value; + } + $form['tokens'][$type]['tokens'] = [ + '#theme' => 'item_list', + '#items' => $items, + ]; + } + } + } + + $this->globalTokenForm($form, $form_state); + } + + /** + * Provide the default summary for options in the views UI. + * + * This output is returned as an array. + */ + public function optionsSummary(&$categories, &$options) { + $categories['page_header'] = [ + 'title' => $this->t('Page header'), + 'column' => 'second', + ]; + $options['page_header'] = [ + 'category' => 'page_header', + 'title' => $this->t('Page header'), + 'value' => $this->options['lede'] ? $this->t('Custom lede') : $this->t('No lede'), + ]; + } + + /** + * Lists defaultable sections and items contained in each section. + */ + public function defaultableSections(&$sections, $section = NULL) { + } + + /** + * Get the lede for display. + * + * @param bool $raw + * Flag if to return raw (untokenised) output. + * + * @return string + * Lede for display. + */ + public function getLede(bool $raw = FALSE) : string { + $view = $this->view; + $lede = ''; + + if (!empty($this->options['lede'])) { + $lede = $this->options['lede']; + } + + if ($this->options['lede'] && !$raw) { + if (!empty(self::$firstRowTokens[$view->current_display])) { + self::setFirstRowTokensOnStylePlugin($view, self::$firstRowTokens[$view->current_display]); + } + // This is copied from TokenizeAreaPluginBase::tokenizeValue(). + $style = $view->getStyle(); + $lede = $style->tokenizeValue($lede, 0); + $lede = $this->globalTokenReplace($lede); + } + + return $lede; + } + + /** + * Store first row tokens on the class. + * + * The function metatag_views_metatag_route_entity() loads the View fresh, to + * avoid rebuilding and re-rendering it, preserve the first row tokens. + */ + public function setFirstRowTokens(array $first_row_tokens) { + self::$firstRowTokens[$this->view->current_display] = $first_row_tokens; + } + + /** + * Set the first row tokens on the style plugin. + * + * @param \Drupal\views\ViewExecutable $view + * The view. + * @param array $first_row_tokens + * The first row tokens. + */ + public static function setFirstRowTokensOnStylePlugin(ViewExecutable $view, array $first_row_tokens) { + $style = $view->getStyle(); + self::getFirstRowTokensReflection($style)->setValue($style, [$first_row_tokens]); + } + + /** + * Get the first row tokens from the style plugin. + * + * @param \Drupal\views\ViewExecutable $view + * The view. + * + * @return array + * The first row tokens. + */ + public static function getFirstRowTokensFromStylePlugin(ViewExecutable $view) { + $style = $view->getStyle(); + return self::getFirstRowTokensReflection($style)->getValue($style)[0] ?? []; + } + + /** + * Get the first row tokens for this Views object iteration. + * + * @param \Drupal\views\Plugin\views\style\StylePluginBase $style + * The style plugin used for this request. + * + * @return \ReflectionProperty + * The rawTokens property. + */ + protected static function getFirstRowTokensReflection(StylePluginBase $style): \ReflectionProperty { + $r = new \ReflectionObject($style); + $p = $r->getProperty('rowTokens'); + $p->setAccessible(TRUE); + return $p; + } + +} diff --git a/tests/localgov_core_views_page_header_test/config/install/views.view.recent_content.yml b/tests/localgov_core_views_page_header_test/config/install/views.view.recent_content.yml new file mode 100644 index 0000000..6fdefdc --- /dev/null +++ b/tests/localgov_core_views_page_header_test/config/install/views.view.recent_content.yml @@ -0,0 +1,214 @@ +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + module: + - node + - user +id: recent_content +label: 'Recent content' +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: 'Recent content' + 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: some + options: + offset: 0 + items_per_page: 10 + 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 + options: + row_class: '' + default_row_class: true + uses_fields: true + row: + type: 'entity:node' + options: + relationship: none + 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' + - 'user.node_grants:view' + - user.permissions + tags: { } + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 1 + display_options: + display_extenders: + localgov_page_header_display_extender: + lede: 'The most recent 10 pages that have been created on [site:name], including {{ title|striptags }}' + tokenize: true + path: recent-content + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - 'user.node_grants:view' + - user.permissions + tags: { } + page_2: + id: page_2 + display_title: 'Page 2' + display_plugin: page + position: 2 + display_options: + title: 'Old content' + 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: ASC + expose: + label: '' + field_identifier: '' + exposed: false + granularity: second + defaults: + title: false + sorts: false + display_extenders: + localgov_page_header_display_extender: + lede: 'The oldest 10 pages that have been created on [site:name], including {{ title|striptags }}' + tokenize: true + path: oldest-content + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/tests/localgov_core_views_page_header_test/localgov_core_views_page_header_test.info.yml b/tests/localgov_core_views_page_header_test/localgov_core_views_page_header_test.info.yml new file mode 100644 index 0000000..de39088 --- /dev/null +++ b/tests/localgov_core_views_page_header_test/localgov_core_views_page_header_test.info.yml @@ -0,0 +1,9 @@ +name: 'Views page header test' +type: module +package: Testing +core_version_requirement: ^10 || ^11 +description: Testing module for the views page header display extender. +dependencies: + - drupal:node + - drupal:views + - localgov_core:localgov_core diff --git a/tests/src/Functional/ViewsPageExtenderTest.php b/tests/src/Functional/ViewsPageExtenderTest.php new file mode 100644 index 0000000..74fc914 --- /dev/null +++ b/tests/src/Functional/ViewsPageExtenderTest.php @@ -0,0 +1,79 @@ +getEditable('views.settings'); + $display_extenders = $config->get('display_extenders') ?: []; + $display_extenders[] = 'localgov_page_header_display_extender'; + $config->set('display_extenders', $display_extenders); + $config->save(); + + $this->drupalPlaceBlock('localgov_page_header_block'); + } + + /** + * Test block display. + */ + public function testViewWithPageHeadeExtender() { + + // Check node title and summary display on a page. + $this->createContentType(['type' => 'page']); + + $node = []; + for ($i = 1; $i <= 10; $i++) { + $node[] = $this->createNode([ + 'type' => 'page', + 'title' => 'page ' . $i . ' title', + 'status' => NodeInterface::PUBLISHED, + 'created' => strtotime('Now -' . (10 - $i) . ' days'), + ]); + } + + $first_node = reset($node); + $last_node = end($node); + $site_name = \Drupal::config('system.site')->get('name'); + + $this->drupalGet('/recent-content'); + $this->assertSession()->pageTextContains('The most recent 10 pages that have been created on ' . $site_name . ', including ' . $last_node->getTitle()); + + $this->drupalGet('/oldest-content'); + $this->assertSession()->pageTextContains('The oldest 10 pages that have been created on ' . $site_name . ', including ' . $first_node->getTitle()); + + } + +} From 1892a8b3c71015582f69e1f52f66f2e0b7cc69fb Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Sat, 16 Nov 2024 12:26:27 +0000 Subject: [PATCH 2/8] Correct page header display extender title --- src/Plugin/views/display_extender/PageHeaderDisplayExtender.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php b/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php index f4e5b74..78e88a9 100644 --- a/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php +++ b/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php @@ -11,7 +11,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Metatag display extender plugin. + * Localgov page header display extender plugin. * * @ingroup views_display_extender_plugins * From f9849a21d8c4290213d32ccd711049013bb908cf Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Tue, 19 Nov 2024 20:18:58 +0000 Subject: [PATCH 3/8] Add seperate view property with setter and getter to pageHeaderDisplayEvent Since a viewExecutable is not a child of EntityInterface, provide seperate $view property and set that when viewing a view page from the pageHeaderBlock. --- src/Event/PageHeaderDisplayEvent.php | 46 ++++++++++++++++++++++++++-- src/Plugin/Block/PageHeaderBlock.php | 34 ++++++++++++++++---- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/Event/PageHeaderDisplayEvent.php b/src/Event/PageHeaderDisplayEvent.php index f5e77f6..8070b0e 100644 --- a/src/Event/PageHeaderDisplayEvent.php +++ b/src/Event/PageHeaderDisplayEvent.php @@ -3,6 +3,8 @@ namespace Drupal\localgov_core\Event; use Drupal\Component\EventDispatcher\Event; +use Drupal\Core\Entity\EntityInterface; +use Drupal\views\ViewExecutable; /** * Event that is fired when displaying the page header. @@ -18,6 +20,13 @@ class PageHeaderDisplayEvent extends Event { */ protected $entity = NULL; + /** + * View executable associated with the current route. + * + * @var \Drupal\views\ViewExecutable|null + */ + protected $view = NULL; + /** * The page lede override. * @@ -56,7 +65,10 @@ class PageHeaderDisplayEvent extends Event { /** * {@inheritdoc} */ - public function __construct($entity) { + public function __construct($entity = NULL) { + // @todo remove the $entity paramater or deprecate it. + // Since we should use the setters and getters. + // Or we can mark this class as internal? $this->entity = $entity; } @@ -66,10 +78,40 @@ public function __construct($entity) { * @return \Drupal\Core\Entity\EntityInterface|null * The entity. */ - public function getEntity() { + public function getEntity() : ?EntityInterface { return $this->entity; } + /** + * Entity setter. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. + */ + public function setEntity(EntityInterface $entity) : void { + $this->entity = $entity; + } + + /** + * View getter. + * + * @return \Drupal\views\ViewExecutable|null + * The view. + */ + public function getView() : ?ViewExecutable { + return $this->view; + } + + /** + * View setter. + * + * @param \Drupal\views\ViewExecutable $view + * The view. + */ + public function setView(ViewExecutable $view) { + $this->view = $view; + } + /** * Lede getter. * diff --git a/src/Plugin/Block/PageHeaderBlock.php b/src/Plugin/Block/PageHeaderBlock.php index b858b31..ffff5ae 100644 --- a/src/Plugin/Block/PageHeaderBlock.php +++ b/src/Plugin/Block/PageHeaderBlock.php @@ -64,6 +64,13 @@ class PageHeaderBlock extends BlockBase implements ContainerFactoryPluginInterfa */ protected $entity = NULL; + /** + * View executable associated with the current route. + * + * @var \Drupal\views\ViewExecutable|null + */ + protected $view = NULL; + /** * The page title override. * @@ -155,17 +162,31 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition } } } + + // If this is a view path, set the view instead. if (isset($defaults['view_id']) && isset($defaults['display_id'])) { $view = Views::getView($defaults['view_id']); if ($view) { $view->setDisplay($defaults['display_id']); - $this->entity = $view; + $this->view = $view; } } } // Dispatch event to allow modules to alter block content. - $event = new PageHeaderDisplayEvent($this->entity); + $event = new PageHeaderDisplayEvent(); + + // If an entity is set, pass to the event. + if ($this->entity instanceof EntityInterface) { + $event->setEntity($entity); + } + + // If a view is set, pass to the event. + if ($this->view instanceof ViewExecutable) { + $event->setView($view); + } + + // Dispatch event. $this->eventDispatcher->dispatch($event, PageHeaderDisplayEvent::EVENT_NAME); // Set the title, lede, visibility and cache tags. @@ -174,7 +195,8 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $this->lede = is_null($event->getLede()) ? $this->getLede() : $event->getLede(); $this->visible = $event->getVisibility(); $entityCacheTags = is_null($this->entity) ? [] : $this->entity->getCacheTags(); - $this->cacheTags = is_null($event->getCacheTags()) ? $entityCacheTags : $event->getCacheTags(); + $viewCacheTags = is_null($this->view) ? [] : $this->view->getCacheTags(); + $this->cacheTags = is_null($event->getCacheTags()) ? array_merge($entityCacheTags, $viewCacheTags) : $event->getCacheTags(); } /** @@ -235,9 +257,9 @@ protected function getLede() { } // Return view custom summary. - if ($this->entity instanceof ViewExecutable) { - $extender = $this->entity->getDisplay()->getExtenders()['localgov_page_header_display_extender'] ?? NULL; - $this->entity->render(); + if ($this->view instanceof ViewExecutable) { + $extender = $this->view->getDisplay()->getExtenders()['localgov_page_header_display_extender'] ?? NULL; + $this->view->render(); $lede = $extender->getLede(); return [ '#type' => 'html_tag', From c9c7ad9cafa1096e425c08e31c1fc38d3cb8560a Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Tue, 19 Nov 2024 20:23:20 +0000 Subject: [PATCH 4/8] Add comment on why $view has to be rendered and cs fixes --- src/Plugin/Block/PageHeaderBlock.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Plugin/Block/PageHeaderBlock.php b/src/Plugin/Block/PageHeaderBlock.php index ffff5ae..ec4e213 100644 --- a/src/Plugin/Block/PageHeaderBlock.php +++ b/src/Plugin/Block/PageHeaderBlock.php @@ -178,12 +178,12 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition // If an entity is set, pass to the event. if ($this->entity instanceof EntityInterface) { - $event->setEntity($entity); + $event->setEntity($this->entity); } // If a view is set, pass to the event. if ($this->view instanceof ViewExecutable) { - $event->setView($view); + $event->setView($this->view); } // Dispatch event. @@ -259,6 +259,8 @@ protected function getLede() { // Return view custom summary. if ($this->view instanceof ViewExecutable) { $extender = $this->view->getDisplay()->getExtenders()['localgov_page_header_display_extender'] ?? NULL; + + // Need to render view to apply tokens. $this->view->render(); $lede = $extender->getLede(); return [ From 1992eb341cc5e41c57d9a3822e50d89909327f26 Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Tue, 17 Dec 2024 12:05:19 +0000 Subject: [PATCH 5/8] Fix create docblock event in views display extender --- src/Plugin/views/display_extender/PageHeaderDisplayExtender.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php b/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php index 78e88a9..f5caf60 100644 --- a/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php +++ b/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php @@ -37,7 +37,7 @@ class PageHeaderDisplayExtender extends DisplayExtenderPluginBase { * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - /** @var \Drupal\metatag_views\Plugin\views\display_extender\MetatagDisplayExtender */ + /** @var \Drupal\localgov_core\Plugin\views\display_extender\PageHeaderDisplayExtender */ $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); return $instance; From 71dc3608103d9f843c051db4ed5e3cecdd7a4099 Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Tue, 17 Dec 2024 12:06:29 +0000 Subject: [PATCH 6/8] Add views as dependency as pageDisplayExtender requires it --- localgov_core.info.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/localgov_core.info.yml b/localgov_core.info.yml index bcd4f69..f90bd7b 100644 --- a/localgov_core.info.yml +++ b/localgov_core.info.yml @@ -8,6 +8,7 @@ dependencies: - drupal:block - drupal:field - drupal:node + - drupal:views - field_group:field_group test_dependencies: From 5f276de21226c91320b2b266e81913e44f5a86b7 Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Tue, 17 Dec 2024 12:12:31 +0000 Subject: [PATCH 7/8] Enable the views page header display extender by default - Enables by default and adds update hook. - Removes the install from the test. --- localgov_core.install | 25 +++++++++++++++++++ .../src/Functional/ViewsPageExtenderTest.php | 9 ------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/localgov_core.install b/localgov_core.install index ba66c6d..95ed67f 100644 --- a/localgov_core.install +++ b/localgov_core.install @@ -7,6 +7,13 @@ use Drupal\localgov_core\FieldRenameHelper; +/** + * Implements hook_install(). + */ +function localgov_core_install() { + _localgov_core_enabled_views_page_display_extender(); +} + /** * Update Field names for localgov_core provided fields. * @@ -95,3 +102,21 @@ function localgov_core_update_8004() { // Save the configuration. $config->save(); } + +/** + * Enables the views page header display extender. + */ +function localgov_core_update_8005() { + _localgov_core_enabled_views_page_display_extender(); +} + +/** + * Enable localgov_page_header_display_extender plugin. + */ +function _localgov_core_enabled_views_page_display_extender() { + $config = \Drupal::service('config.factory')->getEditable('views.settings'); + $display_extenders = $config->get('display_extenders') ?: []; + $display_extenders[] = 'localgov_page_header_display_extender'; + $config->set('display_extenders', $display_extenders); + $config->save(); +} diff --git a/tests/src/Functional/ViewsPageExtenderTest.php b/tests/src/Functional/ViewsPageExtenderTest.php index 74fc914..e71bc03 100644 --- a/tests/src/Functional/ViewsPageExtenderTest.php +++ b/tests/src/Functional/ViewsPageExtenderTest.php @@ -34,15 +34,6 @@ class ViewsPageExtenderTest extends BrowserTestBase { */ protected function setUp(): void { parent::setUp(); - - // Enable localgov_page_header_display_extender plugin. - // @todo do this in the module install. - $config = \Drupal::service('config.factory')->getEditable('views.settings'); - $display_extenders = $config->get('display_extenders') ?: []; - $display_extenders[] = 'localgov_page_header_display_extender'; - $config->set('display_extenders', $display_extenders); - $config->save(); - $this->drupalPlaceBlock('localgov_page_header_block'); } From aed1ab41c6acf7963b6a737c922f867e1d8548eb Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Tue, 17 Dec 2024 12:27:46 +0000 Subject: [PATCH 8/8] Fix missing return type errors --- localgov_core.install | 6 +++--- src/Event/PageHeaderDisplayEvent.php | 2 +- .../PageHeaderDisplayExtender.php | 20 +++++++------------ .../src/Functional/ViewsPageExtenderTest.php | 2 +- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/localgov_core.install b/localgov_core.install index 95ed67f..281e28c 100644 --- a/localgov_core.install +++ b/localgov_core.install @@ -10,7 +10,7 @@ use Drupal\localgov_core\FieldRenameHelper; /** * Implements hook_install(). */ -function localgov_core_install() { +function localgov_core_install(): void { _localgov_core_enabled_views_page_display_extender(); } @@ -106,14 +106,14 @@ function localgov_core_update_8004() { /** * Enables the views page header display extender. */ -function localgov_core_update_8005() { +function localgov_core_update_8005(): void { _localgov_core_enabled_views_page_display_extender(); } /** * Enable localgov_page_header_display_extender plugin. */ -function _localgov_core_enabled_views_page_display_extender() { +function _localgov_core_enabled_views_page_display_extender(): void { $config = \Drupal::service('config.factory')->getEditable('views.settings'); $display_extenders = $config->get('display_extenders') ?: []; $display_extenders[] = 'localgov_page_header_display_extender'; diff --git a/src/Event/PageHeaderDisplayEvent.php b/src/Event/PageHeaderDisplayEvent.php index 8070b0e..7958209 100644 --- a/src/Event/PageHeaderDisplayEvent.php +++ b/src/Event/PageHeaderDisplayEvent.php @@ -108,7 +108,7 @@ public function getView() : ?ViewExecutable { * @param \Drupal\views\ViewExecutable $view * The view. */ - public function setView(ViewExecutable $view) { + public function setView(ViewExecutable $view) : void { $this->view = $view; } diff --git a/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php b/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php index f5caf60..8bf8f88 100644 --- a/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php +++ b/src/Plugin/views/display_extender/PageHeaderDisplayExtender.php @@ -58,7 +58,7 @@ protected function defineOptions() { /** * Provide a form to edit options for this plugin. */ - public function buildOptionsForm(&$form, FormStateInterface $form_state) { + public function buildOptionsForm(&$form, FormStateInterface $form_state): void { if ($form_state->get('section') == 'page_header') { $form['#title'] .= $this->t('The page header summary'); @@ -77,7 +77,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { /** * Handle any special handling on the validate form. */ - public function submitOptionsForm(&$form, FormStateInterface $form_state) { + public function submitOptionsForm(&$form, FormStateInterface $form_state): void { if ($form_state->get('section') == 'page_header') { // Process submitted metatag values and remove empty tags. $page_header_values = $form_state->cleanValues()->getValues(); @@ -90,7 +90,7 @@ public function submitOptionsForm(&$form, FormStateInterface $form_state) { /** * Verbatim copy of TokenizeAreaPluginBase::tokenForm(). */ - public function tokenForm(&$form, FormStateInterface $form_state) { + public function tokenForm(&$form, FormStateInterface $form_state): void { $form['tokenize'] = [ '#type' => 'checkbox', '#title' => $this->t('Use replacement tokens from the first row'), @@ -147,7 +147,7 @@ public function tokenForm(&$form, FormStateInterface $form_state) { * * This output is returned as an array. */ - public function optionsSummary(&$categories, &$options) { + public function optionsSummary(&$categories, &$options): void { $categories['page_header'] = [ 'title' => $this->t('Page header'), 'column' => 'second', @@ -159,12 +159,6 @@ public function optionsSummary(&$categories, &$options) { ]; } - /** - * Lists defaultable sections and items contained in each section. - */ - public function defaultableSections(&$sections, $section = NULL) { - } - /** * Get the lede for display. * @@ -201,7 +195,7 @@ public function getLede(bool $raw = FALSE) : string { * The function metatag_views_metatag_route_entity() loads the View fresh, to * avoid rebuilding and re-rendering it, preserve the first row tokens. */ - public function setFirstRowTokens(array $first_row_tokens) { + public function setFirstRowTokens(array $first_row_tokens): void { self::$firstRowTokens[$this->view->current_display] = $first_row_tokens; } @@ -213,7 +207,7 @@ public function setFirstRowTokens(array $first_row_tokens) { * @param array $first_row_tokens * The first row tokens. */ - public static function setFirstRowTokensOnStylePlugin(ViewExecutable $view, array $first_row_tokens) { + public static function setFirstRowTokensOnStylePlugin(ViewExecutable $view, array $first_row_tokens): void { $style = $view->getStyle(); self::getFirstRowTokensReflection($style)->setValue($style, [$first_row_tokens]); } @@ -227,7 +221,7 @@ public static function setFirstRowTokensOnStylePlugin(ViewExecutable $view, arra * @return array * The first row tokens. */ - public static function getFirstRowTokensFromStylePlugin(ViewExecutable $view) { + public static function getFirstRowTokensFromStylePlugin(ViewExecutable $view): array { $style = $view->getStyle(); return self::getFirstRowTokensReflection($style)->getValue($style)[0] ?? []; } diff --git a/tests/src/Functional/ViewsPageExtenderTest.php b/tests/src/Functional/ViewsPageExtenderTest.php index e71bc03..466aa58 100644 --- a/tests/src/Functional/ViewsPageExtenderTest.php +++ b/tests/src/Functional/ViewsPageExtenderTest.php @@ -40,7 +40,7 @@ protected function setUp(): void { /** * Test block display. */ - public function testViewWithPageHeadeExtender() { + public function testViewWithPageHeadeExtender(): void { // Check node title and summary display on a page. $this->createContentType(['type' => 'page']);