diff --git a/core/modules/book/book.module b/core/modules/book/book.module index c034ed6..58986d6 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -59,25 +59,21 @@ function book_help($route_name, RouteMatchInterface $route_match) { */ function book_theme() { return array( - '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_export_html' => array( - 'variables' => array('title' => NULL, 'contents' => NULL, 'depth' => NULL), - ), - 'book_all_books_block' => array( + 'book_navigation' => [ + 'variables' => ['book_link' => NULL], + ], + 'book_tree' => [ + 'variables' => ['items' => [], 'attributes' => []], + ], + 'book_export_html' => [ + 'variables' => ['title' => NULL, 'contents' => NULL, 'depth' => NULL], + ], + 'book_all_books_block' => [ 'render element' => 'book_menus', - ), - 'book_node_export_html' => array( - 'variables' => array('node' => NULL, 'children' => NULL), - ), + ], + 'book_node_export_html' => [ + 'variables' => ['node' => NULL, 'children' => 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..25359b1 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,16 @@ public function getActiveTrailIds($bid, $link) { /** * {@inheritdoc} */ - public function bookTreeOutput(array $tree) { - $build = array(); - $items = array(); + public function build(array $tree, $level = 0) { + $build = []; + $items = []; - // 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; - } - } - - $num_items = count($items); - foreach ($items as $i => $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 +524,41 @@ 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->build($data['below'], $level + 1) : []; + $element['original_link'] = $data['link']; // Index using the link's unique nid. - $build[$data['link']['nid']] = $element; + $items[$data['link']['nid']] = $element; } - if ($build) { + + if (!$items) { + return []; + } + elseif ($level == 0) { + $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']; + // Allow menu-specific theme overrides. + $build['#theme'] = 'book_tree__book_toc_' . $data['link']['bid']; + $build['#items'] = $items; + // Set cache tag. + $build['#cache']['tags'][] = 'config:system.book.' . $data['link']['bid']; + return $build; + } + else { + return $items; } - - return $build; } /** diff --git a/core/modules/book/src/BookManagerInterface.php b/core/modules/book/src/BookManagerInterface.php index db3d1dd..96269c0 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, $level = 0); /** * 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 30153be..828f5e1 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 %}