You are here

book_menus.module in Book Menus 7

Functionality for book_menus.module.

File

book_menus.module
View source
<?php

/**
 * @file
 * Functionality for book_menus.module.
 */

/**
 * Implements hook_block_info().
 */
function book_menus_block_info() {

  // Define the block;
  $block = array();
  $block['navigation']['info'] = t('Book Menus navigation');
  $block['navigation']['cache'] = DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE;

  // Return the renderable block.
  return $block;
}

/**
 * Implements hook_block_view().
 *
 * Main differences from core book.module
 *   - No "All Pages" (use core for that).
 *   - No shifting the tree to remove the first element.
 *   - Do not pass the book menu item to menu_tree_output (want full tree).
 */
function book_menus_block_view($delta = '') {

  // Store the renderable block.
  $block = array();

  // Start with no bid.
  $current_bid = 0;

  // Try to get the node.
  if ($node = menu_get_object()) {

    // Check if this node had a book.
    $current_bid = empty($node->book['bid']) ? 0 : $node->book['bid'];
  }

  // This node has a book.
  if ($current_bid) {

    // Only display this block when the user is browsing a book.
    $select = db_select('node', 'n')
      ->fields('n', array(
      'title',
    ))
      ->condition('n.nid', $node->book['bid'])
      ->addTag('node_access');

    // Get the title.
    $title = $select
      ->execute()
      ->fetchField();

    // Only show the block if the user has view access for the top-level node.
    if ($title) {

      // Build the full menu tree.
      $tree = menu_tree_all_data($node->book['menu_name']);

      // Set title to the main book node.
      $block['subject'] = $title;

      // Add the tree.
      $block['content'] = menu_tree_output($tree);
    }
  }

  // Return the renderable block.
  return $block;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function book_menus_form_book_admin_edit_alter(&$form, &$form_state) {

  // Get the menu name.
  $menu_name = !empty($form['#node']->book['menu_name']) ? $form['#node']->book['menu_name'] : FALSE;

  // Should (will) always be a menu name.
  if (!$menu_name) {
    return NULL;
  }

  // See if this is a book_menus book.
  if (in_array($menu_name, variable_get('book_menus', array()))) {

    // Make sure the user has access.
    if (!user_access('administer menu')) {
      drupal_set_message(t('This is a Book Menu and you do not have the "administer menu" permission'), 'error');
      return array();
    }

    // Go to the menu page instead.
    drupal_goto('admin/structure/menu/manage/' . $menu_name);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * The book admin settings form.
 */
function book_menus_form_book_admin_settings_alter(&$form, &$form_state) {

  // Move actions below new element.
  $form['actions']['#weight'] = 10;

  // Start with empty message.
  $form['book_menus'] = array(
    '#prefix' => '<div class="form-item">',
    '#suffix' => '</div>',
    '#markup' => '<label>' . t('Book Menus') . '</label>' . t('Create some books first.'),
  );

  // Get all the books.
  $books = book_get_books();

  // Make sure there's some books.
  if (count($books)) {

    // Get the original defaults.
    $book_menus_defaults = variable_get('book_menus', array());

    // Save them for submission processing.
    $form_state['storage']['book_menus_original'] = array_flip($book_menus_defaults);

    // Change the form item to checkboxes.
    $form['book_menus'] = array(
      '#type' => 'checkboxes',
      '#title' => t('Book Menus'),
      '#description' => t('Select the books that should get an entry in the Drupal <a href="@menu_link">menu list</a>.', array(
        '@menu_link' => base_path() . 'admin/structure/menu',
      )),
      '#default_value' => $book_menus_defaults,
      '#options' => array(),
    );

    // Go through each book.
    foreach ($books as $book) {
      $form['book_menus']['#options'][$book['menu_name']] = $book['link_title'];
    }

    // Add a submission function.
    $form['#submit'][] = 'book_menus_form_book_admin_settings_submit';
  }
}

/**
 * Submission callback for the book admin settings form.
 */
function book_menus_form_book_admin_settings_submit(&$form, &$form_state) {

  // Get the original settings.
  $book_menus_original = $form_state['storage']['book_menus_original'];

  // Get the new settings.
  $book_menus = $form_state['values']['book_menus'];

  // Go through each settings.
  foreach ($book_menus as $key => $value) {

    // Was it checked in the new but not original.
    if ($value !== 0 && !array_key_exists($key, $book_menus_original)) {

      // Add the item to the custom menu table.
      db_insert('menu_custom')
        ->fields(array(
        'menu_name' => $key,
        'title' => $form['book_menus']['#options'][$key],
        'description' => '',
      ))
        ->execute();

      // Rebuild the menus.
      menu_rebuild();
    }
    elseif ($value === 0 && array_key_exists($key, $book_menus_original)) {

      // Remove the record.
      db_delete('menu_custom')
        ->condition('menu_name', $key)
        ->execute();

      // Rebuild the menus.
      menu_rebuild();
    }
  }
}

/**
 * Implements hook_theme_registry_alter().
 *
 * Make our preprocess function run before book.module's.
 */
function book_menus_theme_registry_alter(&$theme_registry) {

  // Make sure that the book navigation theme entry is present.
  if (!empty($theme_registry['book_navigation']['preprocess functions'])) {

    // Save the template function.
    $template_function = 'template_preprocess_book_navigation';

    // Get the current function order.
    $functions_old = $theme_registry['book_navigation']['preprocess functions'];

    // Make sure the one we want is in there.
    if (!in_array($template_function, $functions_old)) {

      // Functions not there, bail out.
      return NULL;
    }

    // Get the template function index.
    $index = array_search($template_function, $functions_old);

    // Set the new order.
    $theme_registry['book_navigation']['preprocess functions'] = array_merge(array_slice($functions_old, 0, $index), array(
      'book_menus_preprocess_book_navigation_before',
    ), array_slice($functions_old, $index));
  }
}

/**
 * Custom preprocess called before book.module's.
 *
 * This needs to run before because we're hijacking the creation of the book
 * tree so that "non-links" are not considered by book.module when creating
 * the navigation.
 *
 * @see book_menus_theme_registry_alter
 * @see template_preprocess_book_navigation
 */
function book_menus_preprocess_book_navigation_before(&$variables) {

  // Get the book link.
  $book_link = $variables['book_link'];

  // Get the flat menu if there is one.
  $flat =& drupal_static('book_get_flat_menu', array());

  // Create the flat menu if there isn't one.
  if (!isset($flat[$book_link['mlid']])) {

    // Get the flat menu.
    $flat[$book_link['mlid']] = book_menus_get_flat_menu($book_link);

    // Remove any non-linked pages.
    foreach ($flat[$book_link['mlid']] as $key => $value) {
      if ($value['page_callback'] == 'drupal_not_found') {
        unset($flat[$book_link['mlid']][$key]);
      }
    }

    // Make sure the parent was a valid book page.
    if (!array_key_exists($book_link['plid'], $flat[$book_link['mlid']])) {

      // Setting to TRUE tricks book.module not to display the "up" link.
      $variables['book_link']['plid'] = TRUE;
    }
  }
}

/**
 * Gets the book menu tree for a page and returns it as a linear array.
 *
 * @param $book_link
 *   A fully loaded menu link that is part of the book hierarchy.
 *
 * @return
 *   A linear array of menu links in the order that the links are shown in the
 *   menu, so the previous and next pages are the elements before and after the
 *   element corresponding to the current node. The children of the current node
 *   (if any) will come immediately after it in the array, and links will only
 *   be fetched as deep as one level deeper than $book_link.
 */
function book_menus_get_flat_menu($book_link) {
  $flat =& drupal_static(__FUNCTION__, array());
  if (!isset($flat[$book_link['mlid']])) {

    // Call menu_tree_all_data() to take advantage of the menu system's caching.
    // Override book.module and remove the second parameter so we get the full
    // book.
    $tree = menu_tree_all_data($book_link['menu_name']);
    $flat[$book_link['mlid']] = array();
    _book_flatten_menu($tree, $flat[$book_link['mlid']]);
  }
  return $flat[$book_link['mlid']];
}

/**
 * Implements hook_node_presave().
 *
 * Set the original parent for the book before book.module overwrites it.
 */
function book_menus_node_presave($node) {

  // Check if this is a book menu node with a menu entry.
  if (!empty($node->book['plid']) && $node->book['plid'] > 0 && !empty($node->book['bid']) && $node->book['bid'] > 0 && !empty($node->book['menu_name']) && in_array($node->book['menu_name'], variable_get('book_menus', array()))) {

    // Save the original plid.
    $node->original_plid = $node->book['plid'];
  }
}

/**
 * Implements hook_node_update().
 */
function book_menus_node_update($node) {

  // Check if the original plid was set.
  if (!empty($node->original_plid)) {

    // book.module changes the plid if the plid wasn't a book item.
    // Override that and set it to the original parent.
    $node->book['plid'] = $node->original_plid;

    // Save the menu link.
    menu_link_save($node->book);
  }
}

Functions

Namesort descending Description
book_menus_block_info Implements hook_block_info().
book_menus_block_view Implements hook_block_view().
book_menus_form_book_admin_edit_alter Implements hook_form_FORM_ID_alter().
book_menus_form_book_admin_settings_alter Implements hook_form_FORM_ID_alter().
book_menus_form_book_admin_settings_submit Submission callback for the book admin settings form.
book_menus_get_flat_menu Gets the book menu tree for a page and returns it as a linear array.
book_menus_node_presave Implements hook_node_presave().
book_menus_node_update Implements hook_node_update().
book_menus_preprocess_book_navigation_before Custom preprocess called before book.module's.
book_menus_theme_registry_alter Implements hook_theme_registry_alter().