diff --git a/core/modules/book/book.module b/core/modules/book/book.module index 40af371..96473b7 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -62,13 +62,9 @@ function book_theme() { 'book_navigation' => array( 'variables' => array('book_link' => NULL), ), - 'book_tree' => array( - 'render element' => 'tree', - ), - 'book_link' => array( - 'render element' => 'element', - 'function' => 'theme_book_link', - ), + 'book_tree' => [ + 'variables' => ['items' => [], 'attributes' => []], + ], 'book_export_html' => array( 'variables' => array('title' => NULL, 'contents' => NULL, 'depth' => NULL), ), @@ -505,37 +501,6 @@ function template_preprocess_book_node_export_html(&$variables) { } /** - * Implements template_preprocess_HOOK() for book-tree.html.twig. - */ -function template_preprocess_book_tree(&$variables) { - $variables['tree'] = $variables['tree']['#children']; -} - -/** - * Returns HTML for a book link and subtree. - * - * @param array $variables - * An associative array containing: - * - element: Structured array data for a book link. - * - * @ingroup themeable - */ -function theme_book_link(array $variables) { - $element = $variables['element']; - $sub_menu = ''; - - if ($element['#below']) { - $sub_menu = drupal_render($element['#below']); - } - $element['#localized_options']['set_active_class'] = TRUE; - /** @var \Drupal\Core\Url $url */ - $url = $element['#url']; - $url->setOptions($element['#localized_options'] + $url->getOptions()); - $output = \Drupal::l($element['#title'], $url); - return '' . $output . $sub_menu . "\n"; -} - -/** * Determines if a given node type is in the list of types allowed for books. * * @param string $type diff --git a/core/modules/book/src/BookManager.php b/core/modules/book/src/BookManager.php index aab9611..03d8a54 100644 --- a/core/modules/book/src/BookManager.php +++ b/core/modules/book/src/BookManager.php @@ -16,6 +16,7 @@ use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Template\Attribute; use Drupal\node\NodeInterface; /** @@ -500,21 +501,49 @@ public function getActiveTrailIds($bid, $link) { /** * {@inheritdoc} */ - public function bookTreeOutput(array $tree) { - $build = array(); - $items = array(); + public function build(array $tree) { + $items = $this->buildItems($tree); - // Pull out just the book links we are going to render so that we - // get an accurate count for the first/last classes. - foreach ($tree as $data) { - if ($data['link']['access']) { - $items[] = $data; - } + $build = []; + + if ($items) { + // Make sure drupal_render() does not re-order the links. + $build['#sorted'] = TRUE; + // Get the book id from the last link. + $item = end($items); + // Add the theme wrapper for outer markup. + // Allow menu-specific theme overrides. + $build['#theme'] = 'book_tree__book_toc_' . $item['link']['bid']; + $build['#items'] = $items; + // Set cache tag. + $build['#cache']['tags'][] = 'config:system.book.' . $item['link']['bid']; } - $num_items = count($items); - foreach ($items as $i => $data) { + return $build; + } + + /** + * Builds the #items property for a book tree's renderable array. + * + * Helper function for ::build(). + * + * @param array $tree + * A data structure representing the tree. + * + * @return array + * The value to use for the #items property of a renderable menu. + * + * @throws \DomainException + */ + protected function buildItems(array $tree) { + $items = []; + + foreach ($tree as $data) { $class = ['menu-item']; + // Generally we only deal with visible links, but just in case. + if (!$data['link']['access']) { + continue; + } // Set a class for the
  • -tag. Since $data['below'] may contain local // tasks, only set 'expanded' class if the link also has children within // the current book. @@ -528,30 +557,24 @@ public function bookTreeOutput(array $tree) { // Set a class if the link is in the active trail. if ($data['link']['in_active_trail']) { $class[] = 'menu-item--active-trail'; - $data['link']['localized_options']['attributes']['class'][] = 'menu-item--active-trail'; } // Allow book-specific theme overrides. - $element['#theme'] = 'book_link__book_toc_' . $data['link']['bid']; - $element['#attributes']['class'] = $class; - $element['#title'] = $data['link']['title']; + $element = []; + $element['attributes'] = new Attribute(); + $element['attributes']['class'] = $class; + $element['title'] = $data['link']['title']; $node = $this->entityManager->getStorage('node')->load($data['link']['nid']); - $element['#url'] = $node->urlInfo(); - $element['#localized_options'] = !empty($data['link']['localized_options']) ? $data['link']['localized_options'] : array(); - $element['#below'] = $data['below'] ? $this->bookTreeOutput($data['below']) : $data['below']; - $element['#original_link'] = $data['link']; + $element['url'] = $node->urlInfo(); + $element['localized_options'] = !empty($data['link']['localized_options']) ? $data['link']['localized_options'] : []; + $element['localized_options']['set_active_class'] = TRUE; + $element['below'] = $data['below'] ? $this->buildItems($data['below']) : []; + $element['original_link'] = $data['link']; // Index using the link's unique nid. - $build[$data['link']['nid']] = $element; - } - if ($build) { - // Make sure drupal_render() does not re-order the links. - $build['#sorted'] = TRUE; - // Add the theme wrapper for outer markup. - // Allow book-specific theme overrides. - $build['#theme_wrappers'][] = 'book_tree__book_toc_' . $data['link']['bid']; + $items[$data['link']['nid']] = $element; } - return $build; + return $items; } /** diff --git a/core/modules/book/src/BookManagerInterface.php b/core/modules/book/src/BookManagerInterface.php index db3d1dd..e45f4e4 100644 --- a/core/modules/book/src/BookManagerInterface.php +++ b/core/modules/book/src/BookManagerInterface.php @@ -254,11 +254,8 @@ public function deleteFromBook($nid); * * @return array * A structured array to be rendered by drupal_render(). - * - * @todo This was copied from menu_tree_output() but with some changes that - * may be obsolete. Attempt to resolve the differences. */ - public function bookTreeOutput(array $tree); + public function build(array $tree); /** * Checks access and performs dynamic operations for each link in the tree. diff --git a/core/modules/book/src/BookOutline.php b/core/modules/book/src/BookOutline.php index 484fd2c..d87a1e5 100644 --- a/core/modules/book/src/BookOutline.php +++ b/core/modules/book/src/BookOutline.php @@ -126,7 +126,7 @@ public function childrenLinks(array $book_link) { } if ($children) { - $elements = $this->bookManager->bookTreeOutput($children); + $elements = $this->bookManager->build($children); return drupal_render($elements); } return ''; diff --git a/core/modules/book/src/Plugin/Block/BookNavigationBlock.php b/core/modules/book/src/Plugin/Block/BookNavigationBlock.php index 1a68622..767ae0e 100644 --- a/core/modules/book/src/Plugin/Block/BookNavigationBlock.php +++ b/core/modules/book/src/Plugin/Block/BookNavigationBlock.php @@ -137,7 +137,7 @@ public function build() { // If the current page is a node associated with a book, the menu // needs to be retrieved. $data = $this->bookManager->bookTreeAllData($node->book['bid'], $node->book); - $book_menus[$book_id] = $this->bookManager->bookTreeOutput($data); + $book_menus[$book_id] = $this->bookManager->build($data); } else { // Since we know we will only display a link to the top node, there @@ -147,7 +147,7 @@ public function build() { $book_node = $this->nodeStorage->load($book['nid']); $book['access'] = $book_node->access('view'); $pseudo_tree[0]['link'] = $book; - $book_menus[$book_id] = $this->bookManager->bookTreeOutput($pseudo_tree); + $book_menus[$book_id] = $this->bookManager->build($pseudo_tree); } $book_menus[$book_id] += array( '#book_title' => $book['title'], @@ -169,7 +169,7 @@ public function build() { $tree = $this->bookManager->bookTreeAllData($node->book['bid'], $node->book); // There should only be one element at the top level. $data = array_shift($tree); - $below = $this->bookManager->bookTreeOutput($data['below']); + $below = $this->bookManager->build($data['below']); if (!empty($below)) { return $below; } diff --git a/core/modules/book/templates/book-tree.html.twig b/core/modules/book/templates/book-tree.html.twig index 46068c7..d9c31db 100644 --- a/core/modules/book/templates/book-tree.html.twig +++ b/core/modules/book/templates/book-tree.html.twig @@ -1,16 +1,44 @@ {# /** * @file - * Default theme implementation for a book tree. + * Default theme implementation to display a book tree. * * Returns HTML for a wrapper for a book sub-tree. * * Available variables: - * - tree: An HTML string containing the tree's items. - * - * @see template_preprocess_book_tree() + * - items: A nested list of book items. Each book item contains: + * - attributes: HTML attributes for the book item. + * - below: The book item child items. + * - title: The book link title. + * - url: The book link url, instance of \Drupal\Core\Url * * @ingroup themeable */ #} - +{% import _self as book_tree %} + +{# + We call a macro which calls itself to render the full tree. + @see http://twig.sensiolabs.org/doc/tags/macro.html +#} +{{ book_tree.book_links(items, attributes, 0) }} + +{% macro book_links(items, attributes, menu_level) %} + {% import _self as book_tree %} + {% if items %} + {% if menu_level == 0 %} + + {% else %} +
      + {% endif %} + {% for item in items %} + + {{ link(item.title, item.url) }} + {% if item.below %} + {{ book_tree.book_links(item.below, attributes, menu_level + 1) }} + {% endif %} + + {% endfor %} +
    + {% endif %} +{% endmacro %} diff --git a/core/themes/classy/templates/navigation/book-tree.html.twig b/core/themes/classy/templates/navigation/book-tree.html.twig index be69457..c819bbc 100644 --- a/core/themes/classy/templates/navigation/book-tree.html.twig +++ b/core/themes/classy/templates/navigation/book-tree.html.twig @@ -1,14 +1,44 @@ {# /** * @file - * Theme override for a book tree. + * Default theme implementation to display a book tree. * * Returns HTML for a wrapper for a book sub-tree. * * Available variables: - * - tree: An HTML string containing the tree's items. + * - items: A nested list of book items. Each book item contains: + * - attributes: HTML attributes for the book item. + * - below: The book item child items. + * - title: The book link title. + * - url: The book link url, instance of \Drupal\Core\Url * - * @see template_preprocess_book_tree() + * @ingroup themeable */ #} - +{% import _self as book_tree %} + +{# + We call a macro which calls itself to render the full tree. + @see http://twig.sensiolabs.org/doc/tags/macro.html +#} +{{ book_tree.book_links(items, attributes, 0) }} + +{% macro book_links(items, attributes, menu_level) %} + {% import _self as book_tree %} + {% if items %} + {% if menu_level == 0 %} + + {% else %} + + {% endif %} +{% endmacro %}