From 1d47058f7cd6522cdf6695d8a316e88b7b9dd956 Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Thu, 18 May 2023 21:07:02 +0000 Subject: [PATCH 1/2] Skip alert type postsave during config sync Fix #251 During config sync operations, if creating a new alert banner type the visibility field will try to be created, even though that is part of config. This checks if the isSyncing flag is set and skips over the postSave actions as we assume they are part of the config being imported. --- src/Entity/AlertBannerEntityType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entity/AlertBannerEntityType.php b/src/Entity/AlertBannerEntityType.php index 8ba92413..0905494c 100644 --- a/src/Entity/AlertBannerEntityType.php +++ b/src/Entity/AlertBannerEntityType.php @@ -71,7 +71,7 @@ class AlertBannerEntityType extends ConfigEntityBundleBase implements AlertBanne public function postSave(EntityStorageInterface $storage, $update = TRUE) { // Add fields and workflow when creating a new alert banner type. - if (!$update) { + if (!$update && !$this->isSyncing) { $bundle = $this->id(); $config_directory = new FileStorage(__DIR__ . '/../../config/install'); From da51533e09939eb02264d7a5650e118ac1c9bc27 Mon Sep 17 00:00:00 2001 From: Andy Broomfield Date: Mon, 22 May 2023 10:03:30 +0100 Subject: [PATCH 2/2] Check that type of alert field exists before using as a sort for current banners During import, and in times where the type_of_alert field has been deleted, the getCurrentBanners method will try to perform an entity query, but it needs to check the field is present before adding a sort. --- src/Plugin/Block/AlertBannerBlock.php | 19 ++++++- .../src/Kernel/AlertBannerBlockOrderTest.php | 55 +++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/Plugin/Block/AlertBannerBlock.php b/src/Plugin/Block/AlertBannerBlock.php index aaa8f82c..837a6e78 100644 --- a/src/Plugin/Block/AlertBannerBlock.php +++ b/src/Plugin/Block/AlertBannerBlock.php @@ -7,6 +7,7 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\field\Entity\FieldStorageConfig; use Drupal\Core\Session\AccountProxyInterface; use Drupal\Core\Block\BlockBase; @@ -168,14 +169,26 @@ protected function getCurrentAlertBanners() { $types = $this->mapTypesConfigToQuery(); $published_alert_banner_query = $this->entityTypeManager->getStorage('localgov_alert_banner') ->getQuery() - ->condition('status', 1) - ->sort('type_of_alert', 'DESC') - ->sort('changed', 'DESC') + ->condition('status', 1); + + // Only order by type of alert if the field is present. + $alert_banner_has_type_of_alert = FieldStorageConfig::loadByName('localgov_alert_banner', 'type_of_alert'); + if (!empty($alert_banner_has_type_of_alert)) { + $published_alert_banner_query->sort('type_of_alert', 'DESC'); + } + + // Continue alert banner query. + $published_alert_banner_query->sort('changed', 'DESC') ->accessCheck(TRUE); + + // If types (bunldes) are selected, add filter condition. if (!empty($types)) { $published_alert_banner_query->condition('type', $types, 'IN'); } + + // Execute alert banner query. $published_alert_banners = $published_alert_banner_query->execute(); + // Load alert banners and add all. // Visibility check happens in build, so we get cache contexts on all. foreach ($published_alert_banners as $alert_banner_id) { diff --git a/tests/src/Kernel/AlertBannerBlockOrderTest.php b/tests/src/Kernel/AlertBannerBlockOrderTest.php index e4729763..ca2224cd 100644 --- a/tests/src/Kernel/AlertBannerBlockOrderTest.php +++ b/tests/src/Kernel/AlertBannerBlockOrderTest.php @@ -160,4 +160,59 @@ public function testAlertBannerBlockOrder() { } + /** + * Test alert banner block order without type of alert. + */ + public function testAlertBannerBlockOrderWithoutTypeOfAlert() { + + // Delete type of alert field. + // This is so we are testing the case where :- + // - Alerts don't have a type, so are in date order. + // - Querying for current banners without the type field is possible. + $this->container + ->get('entity_type.manager') + ->getStorage('field_storage_config') + ->load('localgov_alert_banner.type_of_alert') + ->delete(); + + // Alert times. + $alert_times = [ + (new DrupalDateTime('-4 hours'))->getTimestamp(), + (new DrupalDateTime('-2 hours'))->getTimestamp(), + (new DrupalDateTime('-3 hours'))->getTimestamp(), + (new DrupalDateTime('now'))->getTimestamp(), + ]; + + // Set up alert banners. + foreach ($alert_times as $changed) { + $alert_entity = $this->container->get('entity_type.manager')->getStorage('localgov_alert_banner') + ->create([ + 'type' => 'localgov_alert_banner', + 'title' => $this->randomMachineName(8), + 'moderation_state' => 'published', + 'changed' => $changed, + ]); + $alert_entity->save(); + $alert[] = $alert_entity->id(); + } + + // Create and render the block and get the alert banner IDs as an array. + $block_manager = $this->container->get('plugin.manager.block'); + $config = []; + $plugin_block = $block_manager->createInstance('localgov_alert_banner_block', $config); + $render = $plugin_block->build(); + foreach ($render as $render_value) { + $result[] = $render_value['#localgov_alert_banner']->id(); + } + + // Set expected order, which will be date changed order. + $expected = [ + $alert[3], + $alert[1], + $alert[2], + $alert[0], + ]; + $this->assertEquals($expected, $result); + } + }