- <?php
- * @file
- * Allows users to create and organize related content in an outline.
- */
-
- * Implements hook_theme().
- */
- function book_theme() {
- $base = array(
- 'file' => 'book.theme.inc',
- );
-
- return array(
- 'book_navigation' => array(
- 'variables' => array('book_link' => NULL),
- 'template' => 'templates/book-navigation',
- ) + $base,
- 'book_admin_table' => array(
- 'render element' => 'form',
- ) + $base,
- 'book_title_link' => array(
- 'variables' => array('link' => NULL),
- ) + $base,
- 'book_all_books_block' => array(
- 'render element' => 'book_menus',
- 'template' => 'templates/book-all-books-block',
- ) + $base,
- );
- }
-
- * Implements hook_permission().
- */
- function book_permission() {
- return array(
- 'administer book outlines' => array(
- 'title' => t('Administer book outlines'),
- ),
- 'create new books' => array(
- 'title' => t('Create new books'),
- ),
- 'add content to books' => array(
- 'title' => t('Add content and child pages to books'),
- ),
- );
- }
-
- * Implements hook_config_info().
- */
- function book_config_info() {
- $prefixes['book.settings'] = array(
- 'label' => t('Book settings'),
- 'group' => t('Configuration'),
- );
- return $prefixes;
- }
-
- * Implements hook_views_api().
- */
- function book_views_api() {
- return array(
- 'api' => '3.0',
- 'path' => backdrop_get_path('module', 'book') . '/views',
- );
- }
-
- * Implements hook_autoload_info().
- */
- function book_autoload_info() {
- return array(
- 'views_plugin_argument_default_book_root' => 'views/views_plugin_argument_default_book_root.inc',
- );
- }
-
- * Adds relevant book links to the node's links.
- *
- * @param Node $node
- * The book page node to add links to.
- * @param $view_mode
- * The display mode of the node.
- */
- function book_node_view_link(Node $node, $view_mode) {
- $links = array();
- $config = config('book.settings');
- $link_options = $config->get('book_links');
-
- if (isset($node->book['depth'])) {
- if ($view_mode == 'full' && node_is_page($node)) {
- $child_type = $config->get('book_child_type');
- $admin_access = user_access('add content to books') || user_access('administer book outlines');
- $link_options = $config->get('book_links');
- $create_access = node_access('create', $child_type);
- $depth_allowed = $node->book['depth'] < MENU_MAX_DEPTH;
- $show_add_child_link = in_array('book_add_child', $link_options, TRUE);
- $show_reorder_link = in_array('book_reorder', $link_options, TRUE);
- if ($admin_access && $create_access && $node->status == 1 && $depth_allowed && $show_add_child_link) {
- $links['book_add_child'] = array(
- 'title' => t('Add child page'),
- 'href' => 'node/add/' . str_replace('_', '-', $child_type),
- 'query' => array('parent' => $node->book['mlid']),
- );
- }
- if (user_access('administer book outlines') && $show_reorder_link) {
- $links['book_reorder'] = array(
- 'title' => t('Reorder book'),
- 'href' => 'admin/content/book/' . $node->book['bid'],
- );
- }
- }
- }
-
- if (!empty($links)) {
- $node->content['links']['book'] = array(
- '#theme' => 'links__node__book',
- '#links' => $links,
- '#attributes' => array('class' => array('links', 'inline')),
- );
- }
- }
-
- * Implements hook_menu().
- */
- function book_menu() {
- $items['admin/content/book'] = array(
- 'title' => 'Manage Books',
- 'description' => "Manage your site's book outlines.",
- 'page callback' => 'book_admin_overview',
- 'access arguments' => array('administer book outlines'),
- 'type' => MENU_LOCAL_TASK,
- 'file' => 'book.admin.inc',
- );
- $items['admin/content/book/list'] = array(
- 'title' => 'List',
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- );
- $items['admin/content/book/settings'] = array(
- 'title' => 'Settings',
- 'page callback' => 'backdrop_get_form',
- 'page arguments' => array('book_admin_settings'),
- 'access arguments' => array('administer site configuration'),
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 8,
- 'file' => 'book.admin.inc',
- );
- $items['admin/content/book/%node'] = array(
- 'title' => 'Re-order book pages and change titles',
- 'page callback' => 'backdrop_get_form',
- 'page arguments' => array('book_admin_edit', 3),
- 'access callback' => '_book_outline_access',
- 'access arguments' => array(3),
- 'file' => 'book.admin.inc',
- );
- $items['book'] = array(
- 'title' => 'Books',
- 'page callback' => 'book_render',
- 'access arguments' => array('access content'),
- 'type' => MENU_SUGGESTED_ITEM,
- 'file' => 'book.pages.inc',
- );
- $items['node/%node/outline-remove'] = array(
- 'title' => 'Remove from book outline',
- 'page callback' => 'backdrop_get_form',
- 'page arguments' => array('book_remove_form', 1),
- 'access callback' => '_book_outline_remove_access',
- 'access arguments' => array(1),
- 'file' => 'book.pages.inc',
- );
-
- return $items;
- }
-
- * Access callback: Determines if the outline tab is accessible.
- *
- * Path:
- * - admin/content/book/%node
- * - node/%node/outline
- *
- * @param Node $node
- * The node whose outline tab is to be viewed.
- *
- * @see book_menu()
- */
- function _book_outline_access(Node $node) {
- return user_access('administer book outlines') && node_access('view', $node);
- }
-
- * Access callback: Determines if the user can remove nodes from the outline.
- *
- * @param Node $node
- * The node to remove from the outline.
- *
- * @see book_menu()
- */
- function _book_outline_remove_access(Node $node) {
- return _book_node_is_removable($node) && _book_outline_access($node);
- }
-
- * Determines if a node can be removed from the book.
- *
- * A node can be removed from a book if it is actually in a book and it either
- * is not a top-level page or is a top-level page with no children.
- *
- * @param $node
- * The node to remove from the outline.
- */
- function _book_node_is_removable($node) {
- return (!empty($node->book['bid']) && (($node->book['bid'] != $node->nid) || !$node->book['has_children']));
- }
-
- * Implements hook_admin_paths().
- */
- function book_admin_paths() {
- if (config_get('system.core', 'node_admin_theme')) {
- $paths = array(
- 'node/*/outline-remove' => TRUE,
- );
- return $paths;
- }
- }
-
- * Implements hook_entity_info_alter().
- */
- function book_entity_info_alter(&$info) {
-
- $info['node']['view modes'] += array(
- 'print' => array(
- 'label' => t('Print'),
- 'custom settings' => FALSE,
- ),
- );
- }
-
- * Implements hook_block_info().
- */
- function book_block_info() {
- $blocks['navigation'] = array(
- 'info' => t('Book navigation'),
- 'description' => t('Shows the table of contents expanded to the current page if viewing book content.'),
- 'cache' => BACKDROP_CACHE_PER_PAGE | BACKDROP_CACHE_PER_ROLE,
- );
-
- return $blocks;
- }
-
- * Implements hook_block_view().
- *
- * Displays the book table of contents in a block when the current page is a
- * single-node view of a book node.
- */
- function book_block_view($delta = '', $settings = array()) {
- $block = array();
- $current_bid = 0;
- if ($node = menu_get_object()) {
- $current_bid = empty($node->book['bid']) ? 0 : $node->book['bid'];
- }
-
- if ($settings['book_mode'] == 'all pages') {
- $block['subject'] = t('Book navigation');
- $book_menus = array();
- $pseudo_tree = array(0 => array('below' => FALSE));
- foreach (book_get_books() as $book_id => $book) {
- if ($book['bid'] == $current_bid) {
-
-
- $book_menus[$book_id] = menu_tree_output(menu_tree_all_data($node->book['menu_name'], $node->book));
- }
- else {
-
-
- $book['in_active_trail'] = FALSE;
-
- $book_node = node_load($book['nid']);
- $book['access'] = node_access('view', $book_node);
- $pseudo_tree[0]['link'] = $book;
- $book_menus[$book_id] = menu_tree_output($pseudo_tree);
- }
- }
- if ($block['content'] = $book_menus) {
- $book_menus['#theme'] = 'book_all_books_block';
- }
- }
- elseif ($current_bid) {
-
- $select = db_select('node', 'n')
- ->fields('n', array('title'))
- ->condition('n.nid', $node->book['bid'])
- ->addTag('node_access');
- $title = $select->execute()->fetchField();
-
- if ($title) {
- $tree = menu_tree_all_data($node->book['menu_name'], $node->book);
-
- $data = array_shift($tree);
- $block['subject'] = l($data['link']['link_title'], $data['link']['link_path'], array('attributes' => array('class' => array('book-title'))));
- $block['content'] = ($data['below']) ? menu_tree_output($data['below']) : '';
- }
- }
-
- return $block;
- }
-
- * Implements hook_block_configure().
- */
- function book_block_configure($delta = '', $settings = array()) {
- $settings += array(
- 'book_mode' => 'all pages',
- );
- $options = array(
- 'all pages' => t('Show block on all pages'),
- 'book pages' => t('Show block only on book pages'),
- );
- $form['book_mode'] = array(
- '#type' => 'radios',
- '#title' => t('Book navigation block display'),
- '#options' => $options,
- '#default_value' => $settings['book_mode'],
- );
- $form['book_mode']['all pages']['#description'] = t("The block will contain the automatically generated menus for all of the site's books.");
- $form['book_mode']['book pages']['#description'] = t("The block will contain only the one menu corresponding to the current page's book. If the current page is not in a book, no block will be displayed.");
-
- return $form;
- }
-
- * Returns an array of all books.
- *
- * This list may be used for generating a list of all the books, or for building
- * the options for a form select.
- *
- * @return
- * An array of all books.
- */
- function book_get_books() {
- $all_books = &backdrop_static(__FUNCTION__);
-
- if (!isset($all_books)) {
- $all_books = array();
- $nids = db_query("SELECT DISTINCT(bid) FROM {book}")->fetchCol();
-
- if ($nids) {
- $query = db_select('book', 'b', array('fetch' => PDO::FETCH_ASSOC));
- $query->join('node', 'n', 'b.nid = n.nid');
- $query->join('menu_links', 'ml', 'b.mlid = ml.mlid');
- $query->addField('n', 'type', 'type');
- $query->addField('n', 'title', 'title');
- $query->fields('b');
- $query->fields('ml');
- $query->condition('n.nid', $nids, 'IN');
- $query->condition('n.status', 1);
- $query->orderBy('ml.weight');
- $query->orderBy('ml.link_title');
- $query->addTag('node_access');
- $result2 = $query->execute();
- foreach ($result2 as $link) {
- $link['href'] = $link['link_path'];
- $link['options'] = unserialize($link['options']);
- $all_books[$link['bid']] = $link;
- }
- }
- }
-
- return $all_books;
- }
-
- * Implements hook_form_BASE_FORM_ID_alter().
- *
- * Adds the book fieldset to the node form.
- *
- * @see book_pick_book_nojs_submit()
- */
- function book_form_node_form_alter(&$form, &$form_state, $form_id) {
- $node = $form['#node'];
- $access = book_all_types_allowed();
- if (!$access) {
- if (user_access('add content to books') && ((!empty($node->book['mlid']) && !empty($node->nid)) || book_type_is_allowed($node->type))) {
-
- $access = TRUE;
- }
- }
-
- if ($access) {
- _book_add_form_elements($form, $form_state, $node);
-
-
-
- $form['book']['pick-book'] = array(
- '#type' => 'submit',
- '#value' => t('Change book (update list of parents)'),
- '#submit' => array('book_pick_book_nojs_submit'),
- '#weight' => 20,
- '#attached' => array(
- 'css' => array(backdrop_get_path('module', 'book') . '/css/book.admin.css'),
- ),
- );
- if (!empty($node->nid)) {
- $form['book']['actions'] = array(
- '#theme' => 'links',
- '#links' => array(),
- '#attributes' => array('class' => array('links', 'inline')),
- '#weight' => 40,
- );
- if (_book_outline_remove_access($node)) {
- $form['book']['actions']['#links']['remove'] = array(
- 'title' => t('Remove from book outline'),
- 'href' => 'node/' . $node->nid . '/outline-remove',
- );
- }
- if (!empty($node->book['bid']) && _book_outline_access($node)) {
- $form['book']['actions']['#links']['reorder'] = array(
- 'title' => t('Reorder book pages'),
- 'href' => 'admin/content/book/' . $node->book['bid'],
- );
- }
- }
- }
- }
-
- * Form submission handler for node_form().
- *
- * This handler is run when JavaScript is disabled. It triggers the form to
- * rebuild so that the "Parent item" options are changed to reflect the newly
- * selected book. When JavaScript is enabled, the submit button that triggers
- * this handler is hidden, and the "Book" dropdown directly triggers the
- * book_form_update() Ajax callback instead.
- *
- * @see book_form_update()
- * @see book_form_node_form_alter()
- */
- function book_pick_book_nojs_submit($form, &$form_state) {
- $form_state['node']->book = $form_state['values']['book'];
- $form_state['rebuild'] = TRUE;
- }
-
- * Builds the parent selection form element for the node form or outline tab.
- *
- * This function is also called when generating a new set of options during the
- * Ajax callback, so an array is returned that can be used to replace an
- * existing form element.
- *
- * @param $book_link
- * A fully loaded menu link that is part of the book hierarchy.
- *
- * @return
- * A parent selection form element.
- */
- function _book_parent_select($book_link) {
-
- $form = array(
- '#type' => 'hidden',
- '#value' => -1,
- '#prefix' => '<div id="edit-book-plid-wrapper">',
- '#suffix' => '</div>',
- );
-
- if ($book_link['nid'] === $book_link['bid']) {
-
- if ($book_link['original_bid'] === $book_link['bid']) {
- $form['#prefix'] .= '<em>' . t('This is the top-level page in this book.') . '</em>';
- }
- else {
- $form['#prefix'] .= '<em>' . t('This will be the top-level page in this book.') . '</em>';
- }
- }
- elseif (!$book_link['bid']) {
- $form['#prefix'] .= '<em>' . t('No book selected.') . '</em>';
- }
- else {
- $form = array(
- '#type' => 'select',
- '#title' => t('Parent item'),
- '#default_value' => $book_link['plid'],
- '#description' => t('The parent page in the book. The maximum depth for a book and all child pages is !maxdepth. Some pages in the selected book may not be available as parents if selecting them would exceed this limit.', array('!maxdepth' => MENU_MAX_DEPTH)),
- '#options' => book_toc($book_link['bid'], $book_link['parent_depth_limit'], array($book_link['mlid'])),
- '#attributes' => array('class' => array('book-title-select')),
- '#prefix' => '<div id="edit-book-plid-wrapper">',
- '#suffix' => '</div>',
- );
- }
-
- return $form;
- }
-
- * Builds the common elements of the book form for the node and outline forms.
- *
- * @param Node $node
- * The node whose form is being viewed.
- */
- function _book_add_form_elements(&$form, &$form_state, Node $node) {
-
-
- if (isset($form_state['values']['book'])) {
- $node->book = $form_state['values']['book'];
- }
-
- $form['book'] = array(
- '#type' => 'fieldset',
- '#title' => t('Book outline'),
- '#weight' => 10,
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#group' => 'additional_settings',
- '#attributes' => array(
- 'class' => array('book-outline-form'),
- ),
- '#attached' => array(
- 'js' => array(backdrop_get_path('module', 'book') . '/js/book.js'),
- ),
- '#tree' => TRUE,
- );
- foreach (array('menu_name', 'mlid', 'nid', 'router_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) {
- $form['book'][$key] = array(
- '#type' => 'value',
- '#value' => $node->book[$key],
- );
- }
-
- $form['book']['plid'] = _book_parent_select($node->book);
-
-
- $form['book']['weight'] = array(
- '#type' => 'number',
- '#title' => t('Weight'),
- '#default_value' => $node->book['weight'],
- '#weight' => 5,
- '#description' => t('Pages at a given level are ordered first by weight and then by title.'),
- );
- $options = array();
- $nid = isset($node->nid) ? $node->nid : 'new';
-
- if (isset($node->nid) && ($nid == $node->book['original_bid']) && ($node->book['parent_depth_limit'] == 0)) {
-
- $options[$node->nid] = $node->title;
- }
- else {
- foreach (book_get_books() as $book) {
- $options[$book['nid']] = $book['title'];
- }
- }
-
- if (user_access('create new books') && ($nid == 'new' || ($nid != $node->book['original_bid']))) {
-
- $options = array($nid => '<' . t('create a new book') . '>') + $options;
- }
- if (!$node->book['mlid']) {
-
- $options = array(0 => '<' . t('none') . '>') + $options;
- }
-
-
- $form['book']['bid'] = array(
- '#type' => 'select',
- '#title' => t('Book'),
- '#default_value' => $node->book['bid'],
- '#options' => $options,
- '#access' => (bool) $options,
- '#description' => t('Your page will be a part of the selected book.'),
- '#weight' => -5,
- '#attributes' => array('class' => array('book-title-select')),
- '#ajax' => array(
- 'callback' => 'book_form_update',
- 'wrapper' => 'edit-book-plid-wrapper',
- 'effect' => 'fade',
- 'speed' => 'fast',
- ),
- );
- }
-
- * Renders a new parent page select element when the book selection changes.
- *
- * This function is called via Ajax when the selected book is changed on a node
- * or book outline form.
- *
- * @return
- * The rendered parent page select element.
- */
- function book_form_update($form, $form_state) {
- return $form['book']['plid'];
- }
-
- * Handles additions and updates to the book outline.
- *
- * This common helper function performs all additions and updates to the book
- * outline through node addition, node editing, node deletion, or the outline
- * tab.
- *
- * @param Node $node
- * The node that is being saved, added, deleted, or moved.
- *
- * @return
- * TRUE if the menu link was saved; FALSE otherwise.
- */
- function _book_update_outline(Node $node) {
- if (empty($node->book['bid'])) {
- return FALSE;
- }
- $new = empty($node->book['mlid']);
-
- $node->book['link_path'] = 'node/' . $node->nid;
- $node->book['link_title'] = $node->title;
- $node->book['parent_mismatch'] = FALSE;
-
- if ($node->book['bid'] == $node->nid) {
- $node->book['plid'] = 0;
- $node->book['menu_name'] = book_menu_name($node->nid);
- }
- else {
-
- if (!empty($node->book['plid'])) {
- $parent = db_query("SELECT * FROM {book} WHERE mlid = :mlid", array(
- ':mlid' => $node->book['plid'],
- ))->fetchAssoc();
- }
- if (empty($node->book['plid']) || !$parent || $parent['bid'] != $node->book['bid']) {
- $node->book['plid'] = db_query("SELECT mlid FROM {book} WHERE nid = :nid", array(
- ':nid' => $node->book['bid'],
- ))->fetchField();
- $node->book['parent_mismatch'] = TRUE;
- }
- }
-
- if (menu_link_save($node->book)) {
- if ($new) {
-
- db_insert('book')
- ->fields(array(
- 'nid' => $node->nid,
- 'mlid' => $node->book['mlid'],
- 'bid' => $node->book['bid'],
- ))
- ->execute();
-
- backdrop_static_reset('book_get_books');
- }
- else {
- if ($node->book['bid'] != db_query("SELECT bid FROM {book} WHERE nid = :nid", array(
- ':nid' => $node->nid,
- ))->fetchField()) {
-
- book_update_bid($node->book);
-
- backdrop_static_reset('book_get_books');
- }
- }
-
- $affected_nids = db_query("SELECT nid FROM {book} WHERE bid = :bid", array(
- ':bid' => $node->book['bid'],
- ))->fetchAllAssoc('nid');
- entity_get_controller('node')->resetCache(array_keys($affected_nids));
-
- return TRUE;
- }
-
-
- return FALSE;
- }
-
- * Updates the book ID of a page and its children when it moves to a new book.
- *
- * @param $book_link
- * A fully loaded menu link that is part of the book hierarchy.
- */
- function book_update_bid($book_link) {
- $query = db_select('menu_links');
- $query->addField('menu_links', 'mlid');
- for ($i = 1; $i <= MENU_MAX_DEPTH && $book_link["p$i"]; $i++) {
- $query->condition("p$i", $book_link["p$i"]);
- }
- $mlids = $query->execute()->fetchCol();
-
- if ($mlids) {
- db_update('book')
- ->fields(array('bid' => $book_link['bid']))
- ->condition('mlid', $mlids, 'IN')
- ->execute();
- }
- }
-
- * 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_get_flat_menu($book_link) {
- $flat = &backdrop_static(__FUNCTION__, array());
-
- if (!isset($flat[$book_link['mlid']])) {
-
- $tree = menu_tree_all_data($book_link['menu_name'], $book_link, $book_link['depth'] + 1);
- $flat[$book_link['mlid']] = array();
- _book_flatten_menu($tree, $flat[$book_link['mlid']]);
- }
-
- return $flat[$book_link['mlid']];
- }
-
- * Recursively converts a tree of menu links to a flat array.
- *
- * @param $tree
- * A tree of menu links in an array.
- * @param $flat
- * A flat array of the menu links from $tree, passed by reference.
- *
- * @see book_get_flat_menu().
- */
- function _book_flatten_menu($tree, &$flat) {
- foreach ($tree as $data) {
- if (!$data['link']['hidden']) {
- $flat[$data['link']['mlid']] = $data['link'];
- if ($data['below']) {
- _book_flatten_menu($data['below'], $flat);
- }
- }
- }
- }
-
- * Fetches the menu link for the previous page of the book.
- *
- * @param $book_link
- * A fully loaded menu link that is part of the book hierarchy.
- *
- * @return
- * A fully loaded menu link for the page before the one represented in
- * $book_link.
- */
- function book_prev($book_link) {
-
- if ($book_link['plid'] == 0) {
- return NULL;
- }
- $flat = book_get_flat_menu($book_link);
- reset($flat);
- $curr = NULL;
- do {
- $prev = $curr;
- $curr = current($flat);
- $key = key($flat);
- next($flat);
- } while ($key && $key != $book_link['mlid']);
-
- if ($key == $book_link['mlid']) {
-
- if ($prev['depth'] == $book_link['depth'] && $prev['has_children']) {
-
- $tree = book_menu_subtree_data($prev);
- $data = array_shift($tree);
-
- while ($data['below']) {
- $data = end($data['below']);
- }
-
- return $data['link'];
- }
- else {
- return $prev;
- }
- }
- }
-
- * Fetches the menu link for the next page of the book.
- *
- * @param $book_link
- * A fully loaded menu link that is part of the book hierarchy.
- *
- * @return
- * A fully loaded menu link for the page after the one represented in
- * $book_link.
- */
- function book_next($book_link) {
- $flat = book_get_flat_menu($book_link);
- reset($flat);
- do {
- $key = key($flat);
- next($flat);
- }
- while ($key && $key != $book_link['mlid']);
-
- if ($key == $book_link['mlid']) {
- return current($flat);
- }
- }
-
- * Formats the menu links for the child pages of the current page.
- *
- * @param $book_link
- * A fully loaded menu link that is part of the book hierarchy.
- *
- * @return
- * HTML for the links to the child pages of the current page.
- */
- function book_children($book_link) {
- $flat = book_get_flat_menu($book_link);
-
- $children = array();
-
- if ($book_link['has_children']) {
-
- do {
- $link = array_shift($flat);
- }
- while ($link && ($link['mlid'] != $book_link['mlid']));
-
- while (($link = array_shift($flat)) && $link['plid'] == $book_link['mlid']) {
- $data['link'] = $link;
- $data['below'] = '';
- $children[] = $data;
- }
- }
-
- if ($children) {
- $elements = menu_tree_output($children);
- return backdrop_render($elements);
- }
- return '';
- }
-
- * Generates the corresponding menu name from a book ID.
- *
- * @param $bid
- * The book ID for which to make a menu name.
- *
- * @return
- * The menu name.
- */
- function book_menu_name($bid) {
- return 'book-toc-' . $bid;
- }
-
- * Implements hook_node_load().
- */
- function book_node_load($nodes, $types) {
- $result = db_query("SELECT * FROM {book} b INNER JOIN {menu_links} ml ON b.mlid = ml.mlid WHERE b.nid IN (:nids)", array(':nids' => array_keys($nodes)), array('fetch' => PDO::FETCH_ASSOC));
- foreach ($result as $record) {
- $nodes[$record['nid']]->book = $record;
- $nodes[$record['nid']]->book['href'] = $record['link_path'];
- $nodes[$record['nid']]->book['title'] = $record['link_title'];
- $nodes[$record['nid']]->book['options'] = unserialize($record['options']);
- }
- }
-
- * Implements hook_node_view().
- */
- function book_node_view(Node $node, $view_mode) {
- if ($view_mode == 'full') {
- if (!empty($node->book['bid']) && empty($node->in_preview) && config_get('book.settings', 'book_navigation')) {
- $node->content['book_navigation'] = array(
- '#markup' => theme('book_navigation', array('book_link' => $node->book)),
- '#weight' => 100,
- '#attached' => array(
- 'css' => array(
- backdrop_get_path('module', 'book') . '/css/book.theme.css',
- ),
- ),
- );
- }
- }
-
- if ($view_mode != 'rss') {
- book_node_view_link($node, $view_mode);
- }
- }
-
- * Implements hook_init().
- *
- * Adds the book menu to the list of menus used to build the active trail when
- * viewing a book page.
- */
- function book_init() {
- if (($node = menu_get_object()) && !empty($node->book['bid'])) {
- $active_menus = menu_get_active_menu_names();
- $active_menus[] = $node->book['menu_name'];
- menu_set_active_menu_names($active_menus);
- }
- }
-
- * Implements hook_node_presave().
- */
- function book_node_presave(Node $node) {
-
- if (!empty($node->book['bid']) && !user_access('administer nodes')) {
- $node->revision = 1;
- }
-
- if (empty($node->nid)) {
- $node->book['mlid'] = NULL;
- }
- }
-
- * Implements hook_node_insert().
- */
- function book_node_insert(Node $node) {
- if (!empty($node->book['bid'])) {
- if ($node->book['bid'] == 'new') {
-
- $node->book['bid'] = $node->nid;
- }
- $node->book['nid'] = $node->nid;
- $node->book['menu_name'] = book_menu_name($node->book['bid']);
- _book_update_outline($node);
- $destination = backdrop_get_destination();
- if ($destination['destination'] != 'admin/content/book/' . $node->book['bid'] && _book_outline_access($node)) {
- backdrop_set_message(t('Reorder book pages from the <a href="@url">book outline</a>.', array('@url' => url('admin/content/book/' . $node->book['bid']))));
- }
- }
- }
-
- * Implements hook_node_update().
- */
- function book_node_update(Node $node) {
- if (!empty($node->book['bid'])) {
- if ($node->book['bid'] == 'new') {
-
- $node->book['bid'] = $node->nid;
- }
- $node->book['nid'] = $node->nid;
- $node->book['menu_name'] = book_menu_name($node->book['bid']);
- _book_update_outline($node);
- $destination = backdrop_get_destination();
- if ($destination['destination'] != 'admin/content/book/' . $node->book['bid'] && _book_outline_access($node)) {
- backdrop_set_message(t('Reorder book pages from the <a href="@url">book outline</a>.', array('@url' => url('admin/content/book/' . $node->book['bid']))));
- }
- }
- }
-
- * Implements hook_node_predelete().
- */
- function book_node_predelete(Node $node) {
- if (!empty($node->book['bid'])) {
- if ($node->nid == $node->book['bid']) {
-
- $result = db_query("SELECT b.nid FROM {menu_links} ml INNER JOIN {book} b on b.mlid = ml.mlid WHERE ml.plid = :plid", array(
- ':plid' => $node->book['mlid']
- ));
- foreach ($result as $child) {
- $child_node = node_load($child->nid);
- $child_node->book['bid'] = $child_node->nid;
- _book_update_outline($child_node);
- }
- }
- menu_link_delete($node->book['mlid']);
- db_delete('book')
- ->condition('mlid', $node->book['mlid'])
- ->execute();
- backdrop_static_reset('book_get_books');
- }
- }
-
- * Implements hook_node_prepare().
- */
- function book_node_prepare(Node $node) {
-
- if (empty($node->book) && (user_access('add content to books') || user_access('administer book outlines'))) {
- $node->book = array();
-
- if (empty($node->nid) && isset($_GET['parent']) && is_numeric($_GET['parent'])) {
-
- $parent = book_link_load($_GET['parent']);
-
- if ($parent && $parent['access']) {
- $node->book['bid'] = $parent['bid'];
- $node->book['plid'] = $parent['mlid'];
- $node->book['menu_name'] = $parent['menu_name'];
- }
- }
-
- $node->book += _book_link_defaults(!empty($node->nid) ? $node->nid : 'new');
- }
- else {
- if (isset($node->book['bid']) && !isset($node->book['original_bid'])) {
- $node->book['original_bid'] = $node->book['bid'];
- }
- }
-
- if (isset($node->book['bid']) && !isset($node->book['parent_depth_limit'])) {
- $node->book['parent_depth_limit'] = _book_parent_depth_limit($node->book);
- }
- }
-
- * Finds the depth limit for items in the parent select.
- *
- * @param $book_link
- * A fully loaded menu link that is part of the book hierarchy.
- *
- * @return
- * The depth limit for items in the parent select.
- */
- function _book_parent_depth_limit($book_link) {
- return MENU_MAX_DEPTH - 1 - (($book_link['mlid'] && $book_link['has_children']) ? menu_link_children_relative_depth($book_link) : 0);
- }
-
- * Implements hook_form_FORM_ID_alter().
- *
- * Alters the confirm form for a single node deletion.
- *
- * @see node_delete_confirm()
- */
- function book_form_node_delete_confirm_alter(&$form, $form_state) {
- $node = node_load($form['nid']['#value']);
-
- if (isset($node->book) && $node->book['has_children']) {
- $form['book_warning'] = array(
- '#type' => 'help',
- '#markup' => t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', array('%title' => $node->title)),
- '#weight' => -10,
- );
- }
- }
-
- * Returns an array with default values for a book page's menu link.
- *
- * @param $nid
- * The ID of the node whose menu link is being created.
- *
- * @return
- * The default values for the menu link.
- */
- function _book_link_defaults($nid) {
- return array('original_bid' => 0, 'menu_name' => '', 'nid' => $nid, 'bid' => 0, 'router_path' => 'node/%', 'plid' => 0, 'mlid' => 0, 'has_children' => 0, 'weight' => 0, 'module' => 'book', 'options' => array());
- }
-
-
- * Implements hook_preprocess_block().
- */
- function book_preprocess_block(&$variables) {
- if ($variables['block']->module == 'book') {
- $variables['attributes']['role'] = 'navigation';
- }
- }
-
- * Recursively processes and formats menu items for book_toc().
- *
- * This helper function recursively modifies the table of contents array for
- * each item in the menu tree, ignoring items in the exclude array or at a depth
- * greater than the limit. Truncates titles over thirty characters and appends
- * an indentation string incremented by depth.
- *
- * @param $tree
- * The data structure of the book's menu tree. Includes hidden links.
- * @param $indent
- * A string appended to each menu item title. Increments by '--' per depth
- * level.
- * @param $toc
- * Reference to the table of contents array. This is modified in place, so the
- * function does not have a return value.
- * @param $exclude
- * (optional) An array of menu link ID values. Any link whose menu link ID is
- * in this array will be excluded (along with its children). Defaults to an
- * empty array.
- * @param $depth_limit
- * Any link deeper than this value will be excluded (along with its children).
- */
- function _book_toc_recurse($tree, $indent, &$toc, $exclude, $depth_limit) {
- foreach ($tree as $data) {
- if ($data['link']['depth'] > $depth_limit) {
-
- break;
- }
-
- if (!in_array($data['link']['mlid'], $exclude)) {
- $toc[$data['link']['mlid']] = $indent . ' ' . truncate_utf8($data['link']['title'], 30, TRUE, TRUE);
- if ($data['below']) {
- _book_toc_recurse($data['below'], $indent . '--', $toc, $exclude, $depth_limit);
- }
- }
- }
- }
-
- * Returns an array of book pages in table of contents order.
- *
- * @param $bid
- * The ID of the book whose pages are to be listed.
- * @param $depth_limit
- * Any link deeper than this value will be excluded (along with its children).
- * @param $exclude
- * Optional array of menu link ID values. Any link whose menu link ID is in
- * this array will be excluded (along with its children).
- *
- * @return
- * An array of (menu link ID, title) pairs for use as options for selecting a
- * book page.
- */
- function book_toc($bid, $depth_limit, $exclude = array()) {
- $tree = menu_tree_all_data(book_menu_name($bid));
- $toc = array();
- _book_toc_recurse($tree, '', $toc, $exclude, $depth_limit);
-
- return $toc;
- }
-
- * Determine if a given node type is in the list of types allowed for books.
- *
- * @param $type
- * A node type.
- *
- * @return
- * A Boolean TRUE if the node type can be included in books; otherwise, FALSE.
- */
- function book_type_is_allowed($type) {
- $book_allowed_types = array_filter(config_get('book.settings', 'book_allowed_types'));
- return in_array($type, $book_allowed_types);
- }
-
- * Determine if users are allowed to add any content type to books.
- *
- * @return bool
- * A Boolean TRUE if all node types can be included in books for this user;
- * otherwise, FALSE.
- */
- function book_all_types_allowed() {
- return user_access('administer book outlines') && config_get('book.settings', 'book_admin_allowed_all');
- }
-
- * Implements hook_node_type_update().
- *
- * Updates the Book module's persistent variables if the machine-readable name
- * of a node type is changed.
- */
- function book_node_type_update($type) {
- if (!empty($type->old_type) && $type->old_type != $type->type) {
- $config = config('book.settings');
-
- $allowed_types = $config->get('book_allowed_types');
- $key = array_search($type->old_type, $allowed_types);
-
- if ($key !== FALSE) {
- $allowed_types[$type->type] = $allowed_types[$key] ? $type->type : 0;
- unset($allowed_types[$key]);
- $config->set('book_allowed_types', $allowed_types);
- }
-
-
- if ($config->get('book_child_type') == $type->old_type) {
- $config->set('book_child_type', $type->type);
- }
- $config->save();
- }
- }
-
- * Get a translated book menu link by its menu link ID.
- *
- * Like menu_link_load(), but adds additional data from the {book} table.
- *
- * Do not call when loading a node, since this function may call node_load().
- *
- * @param $mlid
- * The menu link ID of the book menu item.
- *
- * @return
- * A menu link, with the link translated for rendering and data added from the
- * {book} table. EMPTY if there is no item. FALSE if there is an error.
- *
- * @see _menu_link_translate()
- */
- function book_link_load($mlid, $skip_access_check = FALSE) {
- $cache = &backdrop_static(__FUNCTION__, array());
-
- if (!is_numeric($mlid)) {
- return FALSE;
- }
-
- if (!isset($cache[$mlid])) {
- $item = db_query("SELECT * FROM {menu_links} ml INNER JOIN {book} b ON b.mlid = ml.mlid LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = :mlid", array(':mlid' => $mlid))->fetchAssoc();
- if (!empty($item)) {
- if ($skip_access_check) {
- $item['access'] = TRUE;
- }
- _menu_link_translate($item);
- }
- $cache[$mlid] = $item;
- }
-
- return $cache[$mlid];
- }
-
- * Gets the data representing a subtree of the book hierarchy.
- *
- * The root of the subtree will be the link passed as a parameter, so the
- * returned tree will contain this item and all its descendants in the menu
- * tree.
- *
- * @param $link
- * A fully loaded menu link.
- *
- * @return
- * A subtree of menu links in an array, in the order they should be rendered.
- */
- function book_menu_subtree_data($link) {
- $tree = &backdrop_static(__FUNCTION__, array());
-
-
- $cid = 'links:' . $link['menu_name'] . ':subtree-cid:' . $link['mlid'];
-
- if (!isset($tree[$cid])) {
- $cache = cache('menu')->get($cid);
-
- if ($cache && isset($cache->data)) {
-
-
- $cache = cache('menu')->get($cache->data);
-
- if ($cache && isset($cache->data)) {
- $data = $cache->data;
- }
- }
-
-
- if (!isset($data)) {
- $query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
- $query->join('menu_router', 'm', 'm.path = ml.router_path');
- $query->join('book', 'b', 'ml.mlid = b.mlid');
- $query->fields('b');
- $query->fields('m', array('load_functions', 'to_arg_functions', 'access_callback', 'access_arguments', 'page_callback', 'page_arguments', 'delivery_callback', 'title', 'title_callback', 'title_arguments', 'type'));
- $query->fields('ml');
- $query->condition('menu_name', $link['menu_name']);
- for ($i = 1; $i <= MENU_MAX_DEPTH && $link["p$i"]; ++$i) {
- $query->condition("p$i", $link["p$i"]);
- }
- for ($i = 1; $i <= MENU_MAX_DEPTH; ++$i) {
- $query->orderBy("p$i");
- }
- $links = array();
- foreach ($query->execute() as $item) {
- $links[] = $item;
- }
- $data['tree'] = menu_tree_data($links, array(), $link['depth']);
- $data['node_links'] = array();
- menu_tree_collect_node_links($data['tree'], $data['node_links']);
-
- $tree_cid = 'links:' . $item['menu_name'] . ':subtree-data:' . hash('sha256', serialize($data));
-
-
- if (!cache('menu')->get($tree_cid)) {
- cache('menu')->set($tree_cid, $data);
- }
-
- cache('menu')->set($cid, $tree_cid);
- }
-
- menu_tree_check_access($data['tree'], $data['node_links']);
- $tree[$cid] = $data['tree'];
- }
-
- return $tree[$cid];
- }