Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EWPP-452: Add RSS feeds for nodes based on list page metadata. #41

Merged
merged 13 commits into from
Nov 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions oe_list_pages.module
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ function oe_list_pages_theme($existing, $type, $theme, $path) {
'label' => NULL,
],
],
'oe_list_pages_rss' => [
'variables' => [
'title' => '',
'link' => '',
'description' => '',
'language' => '',
'copyright' => '',
'image' => [],
'channel_elements' => [],
upchuk marked this conversation as resolved.
Show resolved Hide resolved
'items' => [],
],
],
];
}

Expand Down
7 changes: 7 additions & 0 deletions oe_list_pages.routing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
entity.node.list_page_rss:
path: '/node/{node}/rss'
defaults:
_controller: '\Drupal\oe_list_pages\Controller\ListPageRssController::build'
requirements:
_custom_access: '\Drupal\oe_list_pages\Controller\ListPageRssController::checkAccess'
_entity_access: 'node.view'
278 changes: 278 additions & 0 deletions src/Controller/ListPageRssController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
<?php

declare(strict_types = 1);

namespace Drupal\oe_list_pages\Controller;

use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\Core\Url;
use Drupal\emr\Entity\EntityMetaInterface;
use Drupal\node\NodeInterface;
use Drupal\oe_list_pages\ListBuilderInterface;
use Drupal\oe_list_pages\ListPageRssAlterEvent;
use Drupal\oe_list_pages\ListPageEvents;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
* Controller for the List pages RSS feed routes.
*/
class ListPageRssController extends ControllerBase {

/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;

/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;

/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;

/**
* The list builder.
*
* @var \Drupal\oe_list_pages\ListBuilderInterface
*/
protected $listBuilder;

/**
* The renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;

/**
* The theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface
*/
protected $themeManager;

/**
* ListPageRssController constructor.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The entity type manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher.
* @param \Drupal\oe_list_pages\ListBuilderInterface $list_builder
* The list builder.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The event dispatcher.
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
* The theme manager.
*/
public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, ListBuilderInterface $list_builder, RendererInterface $renderer, ThemeManagerInterface $theme_manager) {
$this->configFactory = $config_factory;
$this->entityTypeManager = $entity_type_manager;
$this->eventDispatcher = $event_dispatcher;
$this->listBuilder = $list_builder;
$this->renderer = $renderer;
$this->themeManager = $theme_manager;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): ListPageRssController {
return new static(
$container->get('config.factory'),
$container->get('entity_type.manager'),
$container->get('event_dispatcher'),
$container->get('oe_list_pages.builder'),
$container->get('renderer'),
$container->get('theme.manager')
);
}

/**
* Render the RSS feed of a list page result.
*
* @param \Drupal\node\NodeInterface $node
* The list page node.
*
* @return \Drupal\Core\Cache\CacheableResponse
* The RSS feed response.
*/
public function build(NodeInterface $node): CacheableResponse {
// Build the render array for the RSS feed.
$cache_metadata = CacheableMetadata::createFromObject($node);
$default_title = $this->getChannelTitle($node, $cache_metadata);
$default_link = $node->toUrl('canonical', ['absolute' => TRUE])->toString(TRUE);
$build = [
'#theme' => 'oe_list_pages_rss',
'#title' => $default_title,
'#link' => $default_link->getGeneratedUrl(),
'#description' => $this->getChannelDescription($node, $cache_metadata),
'#language' => $node->language()->getId(),
'#copyright' => $this->getChannelCopyright(),
'#image' => $this->getChannelImage($cache_metadata),
'#channel_elements' => [],
'#items' => $this->getItemList($node),
];
$cache_metadata->addCacheableDependency($default_link);
$cache_metadata->applyTo($build);

// Dispatch event to allow modules to alter the build before being rendered.
$event = new ListPageRssAlterEvent($build, $node);
$this->eventDispatcher->dispatch(ListPageEvents::ALTER_RSS_BUILD, $event);
$build = $event->getBuild();

// Create the response and add the xml type header.
$response = new CacheableResponse('', 200);
$response->headers->set('Content-Type', 'application/rss+xml; charset=utf-8');

// Render the list and add it to the response.
$output = (string) $this->renderer->renderRoot($build);
if (empty($output)) {
throw new NotFoundHttpException();
}
$response->setContent($output);
$response->addCacheableDependency($cache_metadata);

return $response;
}

/**
* Returns a list of items to be rendered.
*
* @param \Drupal\node\NodeInterface $node
* The node containing the list.
*
* @return array
* Array of items to be rendered.
*/
protected function getItemList(NodeInterface $node): array {
return [];
}

/**
* Asserts whether we can access the RSS route for a given node.
*
* @param \Drupal\node\NodeInterface $node
* The node being checked.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function checkAccess(NodeInterface $node): AccessResultInterface {
$entity_meta = $node->get('emr_entity_metas')->getEntityMeta('oe_list_page');
if ($entity_meta instanceof EntityMetaInterface && !$entity_meta->isNew()) {
return AccessResult::allowed()->addCacheableDependency($node);
}

return AccessResult::forbidden($this->t('Node type does not have List Page meta configured.'))->addCacheableDependency($node);
}

/**
* Get the channel title.
*
* @param \Drupal\node\NodeInterface $node
* The node being rendered.
* @param \Drupal\Core\Cache\CacheableMetadata $cache_metadata
* The current cache metadata.
*
* @return string
* The channel title.
*/
protected function getChannelTitle(NodeInterface $node, CacheableMetadata $cache_metadata): string {
$site_config = $this->configFactory->get('system.site');
$cache_metadata->addCacheableDependency($site_config);
$site_name = $site_config->get('name');
return $site_name . ' | ' . $node->label();
}

/**
* Get the channel image array.
*
* @param \Drupal\Core\Cache\CacheableMetadata $cache_metadata
* The current cache metadata.
*
* @return array
* The image information array.
*/
protected function getChannelImage(CacheableMetadata $cache_metadata): array {
$title = $this->t('@title logo', ['@title' => 'European Commission']);
// Get the logo location.
$theme = $this->themeManager->getActiveTheme();
$site_url = Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString(TRUE);
$cache_metadata->addCacheableDependency($site_url);

return [
'url' => file_create_url($theme->getLogo()),
'title' => $title,
'link' => $site_url->getGeneratedUrl(),
];
}

/**
* Get the channel description.
*
* The default channel description shows a complete list of selected
* filter labels and values.
*
* @param \Drupal\node\NodeInterface $node
* The node being rendered.
* @param \Drupal\Core\Cache\CacheableMetadata $cache_metadata
* The current cache metadata.
*
* @return string
* The default description.
*/
protected function getChannelDescription(NodeInterface $node, CacheableMetadata $cache_metadata): string {
Copy link
Member

@hernani hernani Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's explain what it is the logic applied here.

$selected_filter_information = $this->listBuilder->buildSelectedFilters($node);
$filter_cache = CacheableMetadata::createFromRenderArray($selected_filter_information);
$cache_metadata->addCacheableDependency($filter_cache);
// Extract the selected filter information.
$selected_filters = [];
foreach (Element::children($selected_filter_information) as $child) {
upchuk marked this conversation as resolved.
Show resolved Hide resolved
$filter_information = $selected_filter_information[$child];
$filter_values = [];
if (!isset($filter_information['#items'])) {
continue;
}
foreach ($filter_information['#items'] as $filter_value_information) {
$filter_values[] = $filter_value_information['label'];
}
$selected_filters[] = $filter_information['#label'] . ': ' . implode(', ', $filter_values);
}
return implode(' | ', $selected_filters);
}

/**
* Get the channel copyright value.
*
* @return \Drupal\Component\Render\FormattableMarkup
* The default copyright value.
*/
protected function getChannelCopyright(): FormattableMarkup {
return new FormattableMarkup('© @copyright_name, 1995-@enddate', ['@copyright_name' => $this->t('European Union'), '@enddate' => date('Y')]);
}

}
7 changes: 7 additions & 0 deletions src/ListPageEvents.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,11 @@ final class ListPageEvents {
*/
const ALTER_BUNDLES = 'oe_list_pages.bundles_alter';

/**
* Event fired when the RSS build before its rendered.
*
* @var string
*/
const ALTER_RSS_BUILD = "oe_list_pages.rss_build_alter";

}
72 changes: 72 additions & 0 deletions src/ListPageRssAlterEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types = 1);

namespace Drupal\oe_list_pages;

use Drupal\node\NodeInterface;
use Symfony\Component\EventDispatcher\Event;

/**
* Event thrown in order to alter the RSS build before its rendered.
*/
class ListPageRssAlterEvent extends Event {

/**
* The render array for the list page RSS list.
*
* @var array
*/
protected $build;

/**
* The node being rendered.
*
* @var \Drupal\node\NodeInterface
*/
protected $node;

/**
* Constructs the object.
*
* @param array $build
* The render array for the list page RSS list.
* @param \Drupal\node\NodeInterface $node
* The node being rendered.
*/
public function __construct(array $build, NodeInterface $node) {
$this->build = $build;
$this->node = $node;
}

/**
* Returns the build.
*
* @return array
* The current build.
*/
public function getBuild(): array {
return $this->build;
}

/**
* Sets the build.
*
* @param array $build
* The modified build.
*/
public function setBuild(array $build): void {
$this->build = $build;
}

/**
* Returns the list page.
*
* @return \Drupal\node\NodeInterface
* The list page being processed.
*/
public function getNode(): NodeInterface {
return $this->node;
}

}
Loading