Drupal book navigation menu theming

Here's an example of how to theme a book navigation menu. This example creates a theme function that can be used to output the book outline in any place. There is an example in this module that create a panels pane.

First, in jeffy_book/jeffy_book.info:

name = Jeffy Book
description = Extends the book module
core = 7.x
version = 7.x-1.0
dependencies[] = book

Next, in jeffy_book/jeffy.module:

/**
 * @file
 * Extends the book module. 
 */
 
/**
 * Implements hook_theme();
 */
function jeffy_book_theme($existing, $type, $theme, $path) {
  return array(
    'jeffy_book_nav' => array(
      'variables' => array('book' => FALSE),
    ),
    'jeffy_book_menu_link' => array(
      'render element' => 'element',
    ),
    'jeffy_book_menu_tree' => array(
      'render element' => 'tree',
    ),
  );
}
 
/**
 * Preprocess jeffy book navigation pane
 */
function theme_jeffy_book_nav(array $variables) {
  $book = $variables['book'];
  $menu = _jeffy_book_menu_tree($book['menu_name']);
  $menu['#theme_wrappers'][0] = 'jeffy_book_menu_tree';
  return drupal_render($menu);
}
 
/**
 * Returns HTML for a wrapper for a book menu sub-tree.
 *
 * @see theme_menu_tree()
 */
function theme_jeffy_book_menu_tree(array $variables) {
  return '<ol>' . $variables['tree']['#children'] . '</ol>';
}
 
/**
 * Returns HTML for a book menu link and submenu.
 * 
 * @see theme_menu_link()
 */
function theme_jeffy_book_menu_link(array $variables) {
  $element = $variables['element'];
  $sub_menu = '';
  $current_path = current_path();
  if ($element['#below']) {
    $sub_menu = drupal_render($element['#below']);
  }
  if ($current_path === $element['#href']) {
    $output = '<mark>' . check_plain($element['#title']) . '</mark>';
  } else {
    $output = l($element['#title'], $element['#href'], $element['#localized_options']);
  }
  return '<li>' . $output . $sub_menu . "</li>\n";
}
 
/**
 * Renders a book menu tree
 * 
 * @see menu_tree()
 */
function _jeffy_book_menu_tree($menu_name) {
  $menu_output = &drupal_static(__FUNCTION__, array());
 
  if (!isset($menu_output[$menu_name])) {
    $tree = menu_build_tree($menu_name);
    $key = key($tree);
    $tree = array_merge($tree, $tree[$key]['below']);
    $tree[$key]['below'] = array();
    $tree[$key]['link']['has_children'] = 0;
    $menu = menu_tree_output($tree);
    array_walk_recursive($menu, 'jeffy_book_menu_link_theme_override');
    $menu_output[$menu_name] = $menu;
  }
  return $menu_output[$menu_name];
}
 
/**
 * Changes theme properties for links
 * 
 * Used in _jeffy_book_menu_tree() with array_walk_recursive()
 */
function jeffy_book_menu_link_theme_override(&$item, $key) {
  if ($key === '#theme') {
    $item = 'jeffy_book_menu_link';
  }
}
 
/**
 * Implements hook_ctools_plugin_directory().
 * 
 * @see ctools.api.php
 * @see ctools_plugin_example.module
 */
function jeffy_book_ctools_plugin_directory($module, $plugin) {
  if ($module == 'ctools' && !empty($plugin)) {
    return "plugins/$plugin";
  }
}

Finally, in jeffy_book/plugins/content_types/jeffy_book_navigation.inc:

/**
 * @file
 * 
 * Ctools content type (panel pane)
 * @see ctools_plugin_example.module
 */
$plugin = array(
  'title' => t('Book Navigation'),
  'description' => t('Navigation for book pages'),
  'single' => TRUE,
  'content_types' => array('jeffy_book_navigation'),
  'render callback' => 'jeffy_book_navigation_pane_render',
  'defaults' => array(),
  'category' => array(t('Jeffy'), -9),
);
 
/**
 * Renders a ctools content type (panel pane)
 */
function jeffy_book_navigation_pane_render($subtype, $conf, $context = NULL) {
  $node = menu_get_object();
  $block = new stdClass();
  if (property_exists($node, 'book')) {
    $block->content = theme('jeffy_book_nav', array('book' => $node->book));
  }
  return $block;
}

Tags

Internal References

Article Type

General