From 529c793230d18bd8667e195ccab66231924a727c Mon Sep 17 00:00:00 2001 From: Lauri Eskola <lauri.eskola@acquia.com> Date: Fri, 27 Oct 2023 14:20:51 +0300 Subject: [PATCH] Issue #3349394 by plopesc, fjgarlin, hestenet, penyaskito, friera: Provide announcement feed as a block --- .../announcements_feed.services.yml | 4 + .../src/AnnounceRenderer.php | 84 +++++++++++++++++++ .../src/Controller/AnnounceController.php | 56 ++----------- .../src/Plugin/Block/AnnounceBlock.php | 71 ++++++++++++++++ .../AnnounceBlockTest.php | 70 ++++++++++++++++ .../tests/src/Kernel/AnnounceRendererTest.php | 66 +++++++++++++++ 6 files changed, 301 insertions(+), 50 deletions(-) create mode 100644 core/modules/announcements_feed/src/AnnounceRenderer.php create mode 100644 core/modules/announcements_feed/src/Plugin/Block/AnnounceBlock.php create mode 100644 core/modules/announcements_feed/tests/src/FunctionalJavascript/AnnounceBlockTest.php create mode 100644 core/modules/announcements_feed/tests/src/Kernel/AnnounceRendererTest.php diff --git a/core/modules/announcements_feed/announcements_feed.services.yml b/core/modules/announcements_feed/announcements_feed.services.yml index d6cc2d80198d..98e2ce7e3e8b 100644 --- a/core/modules/announcements_feed/announcements_feed.services.yml +++ b/core/modules/announcements_feed/announcements_feed.services.yml @@ -15,3 +15,7 @@ services: class: Drupal\announcements_feed\LazyBuilders arguments: [ '@plugin.manager.element_info'] Drupal\announcements_feed\LazyBuilders: '@announcements_feed.lazy_builders' + announcements_feed.renderer: + class: Drupal\announcements_feed\AnnounceRenderer + arguments: ['@announcements_feed.fetcher', '%announcements_feed.feed_link%'] + Drupal\announcements_feed\AnnounceRenderer: '@announcements_feed.renderer' diff --git a/core/modules/announcements_feed/src/AnnounceRenderer.php b/core/modules/announcements_feed/src/AnnounceRenderer.php new file mode 100644 index 000000000000..ec19d5e9844c --- /dev/null +++ b/core/modules/announcements_feed/src/AnnounceRenderer.php @@ -0,0 +1,84 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\announcements_feed; + +use Drupal\Core\StringTranslation\StringTranslationTrait; + +/** + * Service to render announcements from the external feed. + * + * @internal + */ +final class AnnounceRenderer { + + use StringTranslationTrait; + + /** + * Constructs an AnnouncementRenderer object. + * + * @param \Drupal\announcements_feed\AnnounceFetcher $announceFetcher + * The AnnounceFetcher service. + * @param string $feedLink + * The feed url path. + */ + public function __construct( + protected AnnounceFetcher $announceFetcher, + protected string $feedLink + ) { + } + + /** + * Generates the announcements feed render array. + * + * @return array + * Render array containing the announcements feed. + */ + public function render(): array { + try { + $announcements = $this->announceFetcher->fetch(); + } + catch (\Exception $e) { + return [ + '#theme' => 'status_messages', + '#message_list' => [ + 'error' => [ + $this->t('An error occurred while parsing the announcements feed, check the logs for more information.'), + ], + ], + '#status_headings' => [ + 'error' => $this->t('Error Message'), + ], + ]; + } + + $build = []; + foreach ($announcements as $announcement) { + $key = $announcement->featured ? '#featured' : '#standard'; + $build[$key][] = $announcement; + } + + $build += [ + '#theme' => 'announcements_feed', + '#count' => count($announcements), + '#feed_link' => $this->feedLink, + '#cache' => [ + 'contexts' => [ + 'url.query_args:_wrapper_format', + ], + 'tags' => [ + 'announcements_feed:feed', + ], + ], + '#attached' => [ + 'library' => [ + 'announcements_feed/drupal.announcements_feed.dialog', + ], + ], + ]; + + return $build; + } + +} diff --git a/core/modules/announcements_feed/src/Controller/AnnounceController.php b/core/modules/announcements_feed/src/Controller/AnnounceController.php index 816cae36efc1..0776ae3fc615 100644 --- a/core/modules/announcements_feed/src/Controller/AnnounceController.php +++ b/core/modules/announcements_feed/src/Controller/AnnounceController.php @@ -4,9 +4,9 @@ namespace Drupal\announcements_feed\Controller; +use Drupal\announcements_feed\AnnounceRenderer; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; -use Drupal\announcements_feed\AnnounceFetcher; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -20,14 +20,11 @@ class AnnounceController extends ControllerBase implements ContainerInjectionInt /** * Constructs an AnnounceController object. * - * @param \Drupal\announcements_feed\AnnounceFetcher $announceFetcher - * The AnnounceFetcher service. - * @param string $feedLink - * The feed url path. + * @param \Drupal\announcements_feed\AnnounceRenderer $announceRenderer + * The AnnounceRenderer service. */ public function __construct( - protected AnnounceFetcher $announceFetcher, - protected string $feedLink + protected AnnounceRenderer $announceRenderer, ) { } @@ -36,8 +33,7 @@ public function __construct( */ public static function create(ContainerInterface $container): AnnounceController { return new static( - $container->get('announcements_feed.fetcher'), - $container->getParameter('announcements_feed.feed_link') + $container->get('announcements_feed.renderer'), ); } @@ -51,47 +47,7 @@ public static function create(ContainerInterface $container): AnnounceController * A build array with announcements. */ public function getAnnouncements(Request $request): array { - try { - $announcements = $this->announceFetcher->fetch(); - } - catch (\Exception $e) { - return [ - '#theme' => 'status_messages', - '#message_list' => [ - 'error' => [ - $this->t('An error occurred while parsing the announcements feed, check the logs for more information.'), - ], - ], - '#status_headings' => [ - 'error' => $this->t('Error Message'), - ], - ]; - } - - $build = []; - foreach ($announcements as $announcement) { - $key = $announcement->featured ? '#featured' : '#standard'; - $build[$key][] = $announcement; - } - - $build += [ - '#theme' => 'announcements_feed', - '#count' => count($announcements), - '#feed_link' => $this->feedLink, - '#cache' => [ - 'contexts' => [ - 'url.query_args:_wrapper_format', - ], - 'tags' => [ - 'announcements_feed:feed', - ], - ], - '#attached' => [ - 'library' => [ - 'announcements_feed/drupal.announcements_feed.dialog', - ], - ], - ]; + $build = $this->announceRenderer->render(); if ($request->query->get('_wrapper_format') != 'drupal_dialog.off_canvas') { $build['#theme'] = 'announcements_feed_admin'; $build['#attached'] = []; diff --git a/core/modules/announcements_feed/src/Plugin/Block/AnnounceBlock.php b/core/modules/announcements_feed/src/Plugin/Block/AnnounceBlock.php new file mode 100644 index 000000000000..5540eac9ac75 --- /dev/null +++ b/core/modules/announcements_feed/src/Plugin/Block/AnnounceBlock.php @@ -0,0 +1,71 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\announcements_feed\Plugin\Block; + +use Drupal\announcements_feed\AnnounceRenderer; +use Drupal\Core\Access\AccessResult; +use Drupal\Core\Block\BlockBase; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Session\AccountInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Provides an 'Announcements Feed' block. + * + * @Block( + * id = "announce_block", + * admin_label = @Translation("Announcements Feed"), + * ) + * + * @internal + */ +class AnnounceBlock extends BlockBase implements ContainerFactoryPluginInterface { + + /** + * Constructs a new AnnouncementsFeedBlock instance. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\announcements_feed\AnnounceRenderer $announceRenderer + * The AnnounceRenderer service. + * @param \Drupal\Core\Session\AccountInterface $currentUser + * The current user. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, protected AnnounceRenderer $announceRenderer, protected AccountInterface $currentUser) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('announcements_feed.renderer'), + $container->get('current_user') + ); + } + + /** + * {@inheritdoc} + */ + public function access(AccountInterface $account, $return_as_object = FALSE) { + return AccessResult::allowedIfHasPermission($this->currentUser, 'access announcements'); + } + + /** + * {@inheritdoc} + */ + public function build() { + return $this->announceRenderer->render(); + } + +} diff --git a/core/modules/announcements_feed/tests/src/FunctionalJavascript/AnnounceBlockTest.php b/core/modules/announcements_feed/tests/src/FunctionalJavascript/AnnounceBlockTest.php new file mode 100644 index 000000000000..d04974112820 --- /dev/null +++ b/core/modules/announcements_feed/tests/src/FunctionalJavascript/AnnounceBlockTest.php @@ -0,0 +1,70 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\announcements_feed\FunctionalJavascript; + +use Drupal\announce_feed_test\AnnounceTestHttpClientMiddleware; +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; + +/** + * Test the announcement block test visibility. + * + * @group announcements_feed + */ +class AnnounceBlockTest extends WebDriverTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'announcements_feed', + 'block', + ]; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * The announce block instance. + * + * @var \Drupal\block\Entity\Block + */ + protected $announceBlock; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + AnnounceTestHttpClientMiddleware::setAnnounceTestEndpoint('/announce-feed-json/community-feeds'); + $this->announceBlock = $this->placeBlock('announce_block', [ + 'label' => 'Announcements Feed', + ]); + } + + /** + * Testing announce feed block visibility. + */ + public function testAnnounceWithoutPermission() { + // User with "access announcements" permission. + $account = $this->drupalCreateUser([ + 'access announcements', + ]); + $this->drupalLogin($account); + $this->drupalGet('<front>'); + + $assert_session = $this->assertSession(); + + // Block should be visible for the user. + $assert_session->pageTextContains('Announcements Feed'); + + // Block is not accessible without permission. + $this->drupalLogout(); + $assert_session->pageTextNotContains('Announcements Feed'); + + } + +} diff --git a/core/modules/announcements_feed/tests/src/Kernel/AnnounceRendererTest.php b/core/modules/announcements_feed/tests/src/Kernel/AnnounceRendererTest.php new file mode 100644 index 000000000000..d91eebca43da --- /dev/null +++ b/core/modules/announcements_feed/tests/src/Kernel/AnnounceRendererTest.php @@ -0,0 +1,66 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\announcements_feed\Kernel; + +use GuzzleHttp\Psr7\Response; + +/** + * @coversDefaultClass \Drupal\announcements_feed\AnnounceRenderer + * + * @group announcements_feed + */ +class AnnounceRendererTest extends AnnounceTestBase { + + /** + * Tests rendered valid when something goes wrong. + */ + public function testRendererException() { + $this->setTestFeedResponses([ + new Response(403), + ]); + $render = $this->container->get('announcements_feed.renderer')->render(); + $this->assertEquals('status_messages', $render['#theme']); + $this->assertEquals('An error occurred while parsing the announcements feed, check the logs for more information.', $render['#message_list']['error'][0]); + } + + /** + * Tests rendered valid content. + */ + public function testRendererContent() { + $feed_item_1 = [ + 'id' => '1001', + 'content_html' => 'Test teaser 1', + 'url' => 'https://www.drupal.org/project/announce', + '_drupalorg' => [ + 'featured' => TRUE, + 'version' => '^10||^11', + ], + 'date_modified' => "2021-09-02T15:09:42+00:00", + 'date_published' => "2021-09-01T15:09:42+00:00", + ]; + $feed_item_2 = [ + 'id' => '1002', + 'content_html' => 'Test teaser 1', + 'url' => 'https://www.drupal.org/project/announce', + '_drupalorg' => [ + 'featured' => FALSE, + 'version' => '^10||^11', + ], + 'date_modified' => "2021-09-02T15:09:42+00:00", + 'date_published' => "2021-09-01T15:09:42+00:00", + ]; + $this->setFeedItems([$feed_item_1, $feed_item_2]); + $render = $this->container->get('announcements_feed.renderer')->render(); + $this->assertEquals('announcements_feed', $render['#theme']); + $this->assertEquals(1, $render['#count']); + $this->assertEquals(1001, $render['#featured'][0]->id); + + $render = $this->container->get('announcements_feed.renderer')->render(); + $this->assertEquals('announcements_feed', $render['#theme']); + $this->assertEquals(1, $render['#count']); + $this->assertEquals(1002, $render['#standard'][0]->id); + } + +} -- GitLab