epub_reader_framework-2.0.0-alpha2/src/Plugin/Block/ReaderNavigationBlock.php
src/Plugin/Block/ReaderNavigationBlock.php
<?php
namespace Drupal\epub_reader_framework\Plugin\Block;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\node\NodeInterface;
/**
* Provides a Reader Navigation Block.
*
* @Block(
* id = "reader_navigation_block",
* admin_label = @Translation("Reader navigation block"),
* category = @Translation("EPUB Reader"),
* )
*/
class ReaderNavigationBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
$node = \Drupal::routeMatch()->getParameter('node');
if ($node instanceof NodeInterface) {
// Get the reader publication.
$reader_publication = FALSE;
if ($node->bundle() == 'reader_publication') {
$reader_publication = $node;
}
elseif ($node->bundle() == 'reader_chapter') {
/** @var \Drupal\node\NodeInterface $reader_publication */
$reader_publication = $node->field_reader_publication->entity;
}
if ($reader_publication) {
return [
'#theme' => 'reader_navigation',
'#chapter_items' => $this->buildChaptersList($reader_publication),
];
}
}
return [];
}
/**
* Build the list of chapters.
*
* @param \Drupal\node\NodeInterface $publication
*
* @return array
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
protected function buildChaptersList(NodeInterface $publication) {
// Build chapters.
$chapter_items = [];
$chapters = $publication->get('field_reader_chapters');
if ($chapters->count()) {
// Load all chapter children at once.
$chapter_children = $this->loadChapterChildren($publication);
foreach ($chapters as $chapter) {
$reader_chapter = $chapter->entity;
if ($reader_chapter instanceof NodeInterface) {
$chapter_items[] = $this->buildChapterMenuItem($reader_chapter, $chapter_children);
}
}
}
// Build top level unordered list.
return [
'#theme' => 'item_list',
'#list_type' => 'ul',
'#items' => $chapter_items,
'#attributes' => ['class' => ['c-reader-navigation__menu']],
'#wrapper_attributes' => ['class' => ['c-reader-navigation__menu-wrapper']],
];
}
/**
* Build the chapter menu item.
*
* @param \Drupal\node\NodeInterface $chapter
* The reader chapter.
* @param array $chapter_children
* The chapter children render array.
*
* @return array
* The chapter menu item.
*/
protected function buildChapterMenuItem(NodeInterface $chapter, $chapter_children) {
$renderer = \Drupal::service('renderer');
$children = $this->buildChapterChildren($chapter, $chapter_children);
// Container for the link and icon.
$chapter_menu_item = [
'#type' => 'container',
];
// Parent link.
$chapter_label = $chapter->label();
\Drupal::moduleHandler()->alter('epub_reader_framework_navigation_chapter_label', $chapter_label, $chapter);
$link = Link::fromTextAndUrl(
$chapter_label,
Url::fromUserInput('/node/' . $chapter->id() . '/nojs')
)->toRenderable();
$link['#attributes']['class'] = [
'c-reader-navigation__menu-item-link',
'use-ajax',
];
// @see https://www.drupal.org/node/956186.
$link['#attributes']['data-ajax-type'] = ['GET'];
$chapter_menu_item['link'] = $link;
// Add the chapter items.
$wrapper_classes = ['c-reader-navigation__menu-item'];
if ($children) {
$icon = [
'#type' => 'html_tag',
'#tag' => 'span',
'#attributes' => [
'class' => [
'c-reader-navigation__expand',
'js-reader-navigation-expand',
],
'tabindex' => "0",
],
'child' => [
'#type' => 'html_tag',
'#tag' => 'span',
'#value' => '⌄',
'#attributes' => [
'class' => [
'c-reader-navigation__expand-icon',
],
],
],
];
$chapter_menu_item['icon'] = $icon;
$wrapper_classes[] = 'has-children';
$wrapper_classes[] = 'js-reader-navigation-expand-wrapper';
}
\Drupal::moduleHandler()->alter('epub_reader_framework_navigation_chapter_menu_item', $chapter_menu_item);
return [
'#markup' => $renderer->render($chapter_menu_item),
'children' => $children,
'#wrapper_attributes' => ['class' => $wrapper_classes],
];
}
/**
* Load all chapter children at once.
*
* @param \Drupal\node\NodeInterface $publication
* The reader publication containing the reader chapters.
*
* @return array
* An array to store children keyed by chapter ID.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
protected function loadChapterChildren(NodeInterface $publication) {
$chapter_children = [];
// Get all children of all chapters at once.
$entity_type_manager = \Drupal::entityTypeManager();
$reader_chapter_heading_storage = $entity_type_manager->getStorage('reader_chapter_heading');
$query = $reader_chapter_heading_storage->getQuery();
$query->condition('reader_publication_id', $publication->id());
$query->accessCheck();
if ($reader_chapter_heading_ids = $query->execute()) {
$count = 1;
$reader_chapter_headings = $reader_chapter_heading_storage->loadMultiple($reader_chapter_heading_ids);
foreach ($reader_chapter_headings as $chapter_heading) {
$reader_chapter_id = $chapter_heading->get('reader_chapter_id')->value;
if (!isset($chapter_children[$reader_chapter_id])) {
$chapter_children[$reader_chapter_id] = [];
}
\Drupal::moduleHandler()->alter('epub_reader_framework_navigation_chapter_heading', $chapter_heading, $reader_chapter_id, $count);
$chapter_children[$reader_chapter_id][] = $chapter_heading;
$count++;
}
}
return $chapter_children;
}
/**
* Build the chapter children render array.
*
* @param \Drupal\node\NodeInterface $chapter
* The reader chapter.
* @param $chapter_children
* The full list of children keyed by chapter ID.
*
* @return array
* The render array for the children for this particular reader chapter.
*/
protected function buildChapterChildren(NodeInterface $chapter, $chapter_children) {
// Add chapter children links.
$children = [];
if (isset($chapter_children[$chapter->id()])) {
foreach ($chapter_children[$chapter->id()] as $reader_chapter_heading) {
$http_query = UrlHelper::buildQuery([
'heading' => $reader_chapter_heading->get('heading')->value,
'order' => $reader_chapter_heading->get('order')->value,
]);
// Child link.
$link = Link::fromTextAndUrl(
$reader_chapter_heading->get('heading')->value,
Url::fromUserInput('/node/' . $chapter->id() . '/nojs?' . $http_query)
)->toRenderable();
$link['#attributes']['class'] = [
'c-reader-navigation__sub-menu-item-link',
'use-ajax',
];
// @see https://www.drupal.org/node/956186.
$link['#attributes']['data-ajax-type'] = ['GET'];
$children[] = $link;
}
}
return $children;
}
}
