source: sipes/cord/modules/book/book.module @ 8a8efa8

stableversion-3.0
Last change on this file since 8a8efa8 was d7a822e, checked in by José Gregorio Puentes <jpuentes@…>, 8 años ago

se agrego el directorio del cord

  • Propiedad mode establecida a 100755
File size: 38.7 KB
Línea 
1<?php
2
3/**
4 * @file
5 * Allows users to structure the pages of a site in a hierarchy or outline.
6 */
7
8/**
9 * Implementation of hook_theme()
10 */
11function book_theme() {
12  return array(
13    'book_navigation' => array(
14      'arguments' => array('book_link' => NULL),
15      'template' => 'book-navigation',
16    ),
17    'book_export_html' => array(
18      'arguments' => array('title' => NULL, 'contents' => NULL, 'depth' => NULL),
19      'template' => 'book-export-html',
20    ),
21    'book_admin_table' => array(
22      'arguments' => array('form' => NULL),
23    ),
24    'book_title_link' => array(
25      'arguments' => array('link' => NULL),
26    ),
27    'book_all_books_block' => array(
28      'arguments' => array('book_menus' => array()),
29      'template' => 'book-all-books-block',
30    ),
31    'book_node_export_html' => array(
32      'arguments' => array('node' => NULL, 'children' => NULL),
33      'template' => 'book-node-export-html',
34    ),
35  );
36}
37
38/**
39 * Implementation of hook_perm().
40 */
41function book_perm() {
42  return array('add content to books', 'administer book outlines', 'create new books', 'access printer-friendly version');
43}
44
45/**
46 * Implementation of hook_link().
47 */
48function book_link($type, $node = NULL, $teaser = FALSE) {
49  $links = array();
50
51  if ($type == 'node' && isset($node->book)) {
52    if (!$teaser) {
53      $child_type = variable_get('book_child_type', 'book');
54      if ((user_access('add content to books') || user_access('administer book outlines')) && node_access('create', $child_type) && $node->status == 1 && $node->book['depth'] < MENU_MAX_DEPTH) {
55        $links['book_add_child'] = array(
56          'title' => t('Add child page'),
57          'href' => "node/add/". str_replace('_', '-', $child_type),
58          'query' => "parent=". $node->book['mlid'],
59        );
60      }
61      if (user_access('access printer-friendly version')) {
62        $links['book_printer'] = array(
63          'title' => t('Printer-friendly version'),
64          'href' => 'book/export/html/'. $node->nid,
65          'attributes' => array('title' => t('Show a printer-friendly version of this book page and its sub-pages.'))
66        );
67      }
68    }
69  }
70  return $links;
71}
72
73/**
74 * Implementation of hook_menu().
75 */
76function book_menu() {
77  $items['admin/content/book'] = array(
78    'title' => 'Books',
79    'description' => "Manage your site's book outlines.",
80    'page callback' => 'book_admin_overview',
81    'access arguments' => array('administer book outlines'),
82    'file' => 'book.admin.inc',
83  );
84  $items['admin/content/book/list'] = array(
85    'title' => 'List',
86    'type' => MENU_DEFAULT_LOCAL_TASK,
87  );
88  $items['admin/content/book/settings'] = array(
89    'title' => 'Settings',
90    'page callback' => 'drupal_get_form',
91    'page arguments' => array('book_admin_settings'),
92    'access arguments' => array('administer site configuration'),
93    'type' => MENU_LOCAL_TASK,
94    'weight' => 8,
95    'file' => 'book.admin.inc',
96  );
97  $items['admin/content/book/%node'] = array(
98    'title' => 'Re-order book pages and change titles',
99    'page callback' => 'drupal_get_form',
100    'page arguments' => array('book_admin_edit', 3),
101    'access callback' => '_book_outline_access',
102    'access arguments' => array(3),
103    'type' => MENU_CALLBACK,
104    'file' => 'book.admin.inc',
105  );
106  $items['book'] = array(
107    'title' => 'Books',
108    'page callback' => 'book_render',
109    'access arguments' => array('access content'),
110    'type' => MENU_SUGGESTED_ITEM,
111    'file' => 'book.pages.inc',
112  );
113  $items['book/export/%/%'] = array(
114    'page callback' => 'book_export',
115    'page arguments' => array(2, 3),
116    'access arguments' => array('access printer-friendly version'),
117    'type' => MENU_CALLBACK,
118    'file' => 'book.pages.inc',
119  );
120  $items['node/%node/outline'] = array(
121    'title' => 'Outline',
122    'page callback' => 'book_outline',
123    'page arguments' => array(1),
124    'access callback' => '_book_outline_access',
125    'access arguments' => array(1),
126    'type' => MENU_LOCAL_TASK,
127    'weight' => 2,
128    'file' => 'book.pages.inc',
129  );
130  $items['node/%node/outline/remove'] = array(
131    'title' => 'Remove from outline',
132    'page callback' => 'drupal_get_form',
133    'page arguments' => array('book_remove_form', 1),
134    'access callback' => '_book_outline_remove_access',
135    'access arguments' => array(1),
136    'type' => MENU_CALLBACK,
137    'file' => 'book.pages.inc',
138  );
139  $items['book/js/form'] = array(
140    'page callback' => 'book_form_update',
141    'access arguments' => array('access content'),
142    'type' => MENU_CALLBACK,
143    'file' => 'book.pages.inc',
144  );
145  return $items;
146}
147
148/**
149 * Menu item access callback - determine if the outline tab is accessible.
150 */
151function _book_outline_access($node) {
152  return user_access('administer book outlines') && node_access('view', $node);
153}
154
155/**
156 * Menu item access callback - determine if the user can remove nodes from the outline.
157 */
158function _book_outline_remove_access($node) {
159  return isset($node->book) && ($node->book['bid'] != $node->nid) && _book_outline_access($node);
160}
161
162/**
163 * Implementation of hook_init(). Add's the book module's CSS.
164 */
165function book_init() {
166  drupal_add_css(drupal_get_path('module', 'book') .'/book.css');
167}
168
169/**
170 * Implementation of hook_block().
171 *
172 * Displays the book table of contents in a block when the current page is a
173 * single-node view of a book node.
174 */
175function book_block($op = 'list', $delta = 0, $edit = array()) {
176  $block = array();
177  switch ($op) {
178    case 'list':
179      $block[0]['info'] = t('Book navigation');
180      $block[0]['cache'] = BLOCK_CACHE_PER_PAGE | BLOCK_CACHE_PER_ROLE;
181      return $block;
182    case 'view':
183      $current_bid = 0;
184      if ($node = menu_get_object()) {
185        $current_bid = empty($node->book['bid']) ? 0 : $node->book['bid'];
186      }
187      if (variable_get('book_block_mode', 'all pages') == 'all pages') {
188        $block['subject'] = t('Book navigation');
189        $book_menus = array();
190        $pseudo_tree = array(0 => array('below' => FALSE));
191        foreach (book_get_books() as $book_id => $book) {
192          if ($book['bid'] == $current_bid) {
193            // If the current page is a node associated with a book, the menu
194            // needs to be retrieved.
195            $book_menus[$book_id] = menu_tree_output(menu_tree_all_data($node->book['menu_name'], $node->book));
196          }
197          else {
198            // Since we know we will only display a link to the top node, there
199            // is no reason to run an additional menu tree query for each book.
200            $book['in_active_trail'] = FALSE;
201            $pseudo_tree[0]['link'] = $book;
202            $book_menus[$book_id] = menu_tree_output($pseudo_tree);
203          }
204        }
205        $block['content'] = theme('book_all_books_block', $book_menus);
206      }
207      elseif ($current_bid) {
208        // Only display this block when the user is browsing a book.
209        $title = db_result(db_query(db_rewrite_sql('SELECT n.title FROM {node} n WHERE n.nid = %d'), $node->book['bid']));
210        // Only show the block if the user has view access for the top-level node.
211        if ($title) {
212          $tree = menu_tree_all_data($node->book['menu_name'], $node->book);
213          // There should only be one element at the top level.
214          $data = array_shift($tree);
215          $block['subject'] = theme('book_title_link', $data['link']);
216          $block['content'] = ($data['below']) ? menu_tree_output($data['below']) : '';
217        }
218      }
219      return $block;
220    case 'configure':
221      $options = array(
222        'all pages' => t('Show block on all pages'),
223        'book pages' => t('Show block only on book pages'),
224      );
225      $form['book_block_mode'] = array(
226        '#type' => 'radios',
227        '#title' => t('Book navigation block display'),
228        '#options' => $options,
229        '#default_value' => variable_get('book_block_mode', 'all pages'),
230        '#description' => t("If <em>Show block on all pages</em> is selected, the block will contain the automatically generated menus for all of the site's books. If <em>Show block only on book pages</em> is selected, the block will contain only the one menu corresponding to the current page's book. In this case, if the current page is not in a book, no block will be displayed. The <em>Page specific visibility settings</em> or other visibility settings can be used in addition to selectively display this block."),
231        );
232      return $form;
233    case 'save':
234      variable_set('book_block_mode', $edit['book_block_mode']);
235      break;
236  }
237}
238
239/**
240 * Generate the HTML output for a link to a book title when used as a block title.
241 *
242 * @ingroup themeable
243 */
244function theme_book_title_link($link) {
245  $link['options']['attributes']['class'] =  'book-title';
246  return l($link['title'], $link['href'], $link['options']);
247}
248
249/**
250 * Returns an array of all books.
251 *
252 * This list may be used for generating a list of all the books, or for building
253 * the options for a form select.
254 */
255function book_get_books() {
256  static $all_books;
257
258  if (!isset($all_books)) {
259    $all_books = array();
260    $result = db_query("SELECT DISTINCT(bid) FROM {book}");
261    $nids = array();
262    while ($book = db_fetch_array($result)) {
263      $nids[] = $book['bid'];
264    }
265    if ($nids) {
266      $result2 = db_query(db_rewrite_sql("SELECT n.type, n.title, b.*, ml.* FROM {book} b INNER JOIN {node} n on b.nid = n.nid INNER JOIN {menu_links} ml ON b.mlid = ml.mlid WHERE n.nid IN (". implode(',', $nids) .") AND n.status = 1 ORDER BY ml.weight, ml.link_title"));
267      while ($link = db_fetch_array($result2)) {
268        $link['href'] = $link['link_path'];
269        $link['options'] = unserialize($link['options']);
270        $all_books[$link['bid']] = $link;
271      }
272    }
273  }
274  return $all_books;
275}
276
277/**
278 * Implementation of hook_form_alter(). Adds the book fieldset to the node form.
279 *
280 * @see book_pick_book_submit()
281 * @see book_submit()
282 */
283function book_form_alter(&$form, $form_state, $form_id) {
284
285  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
286    // Add elements to the node form
287    $node = $form['#node'];
288
289    $access = user_access('administer book outlines');
290    if (!$access) {
291      if (user_access('add content to books') && ((!empty($node->book['mlid']) && !empty($node->nid)) || book_type_is_allowed($node->type))) {
292        // Already in the book hierarchy or this node type is allowed
293        $access = TRUE;
294      }
295    }
296
297    if ($access) {
298      _book_add_form_elements($form, $node);
299      $form['book']['pick-book'] = array(
300        '#type' => 'submit',
301        '#value' => t('Change book (update list of parents)'),
302         // Submit the node form so the parent select options get updated.
303         // This is typically only used when JS is disabled.  Since the parent options
304         // won't be changed via AJAX, a button is provided in the node form to submit
305         // the form and generate options in the parent select corresponding to the
306         // selected book.  This is similar to what happens during a node preview.
307        '#submit' => array('node_form_submit_build_node'),
308        '#weight' => 20,
309      );
310    }
311  }
312}
313
314/**
315 * Build the parent selection form element for the node form or outline tab
316 *
317 * This function is also called when generating a new set of options during the
318 * AJAX callback, so an array is returned that can be used to replace an existing
319 * form element.
320 */
321function _book_parent_select($book_link) {
322  if (variable_get('menu_override_parent_selector', FALSE)) {
323    return array();
324  }
325  // Offer a message or a drop-down to choose a different parent page.
326  $form = array(
327    '#type' => 'hidden',
328    '#value' => -1,
329    '#prefix' => '<div id="edit-book-plid-wrapper">',
330    '#suffix' => '</div>',
331  );
332
333  if ($book_link['nid'] === $book_link['bid']) {
334    // This is a book - at the top level.
335    if ($book_link['original_bid'] === $book_link['bid']) {
336      $form['#prefix'] .= '<em>'. t('This is the top-level page in this book.') .'</em>';
337    }
338    else {
339      $form['#prefix'] .= '<em>'. t('This will be the top-level page in this book.') .'</em>';
340    }
341  }
342  elseif (!$book_link['bid']) {
343    $form['#prefix'] .= '<em>'. t('No book selected.') .'</em>';
344  }
345  else {
346    $form = array(
347      '#type' => 'select',
348      '#title' => t('Parent item'),
349      '#default_value' => $book_link['plid'],
350      '#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)),
351      '#options' => book_toc($book_link['bid'], array($book_link['mlid']), $book_link['parent_depth_limit']),
352      '#attributes' => array('class' => 'book-title-select'),
353    );
354  }
355  return $form;
356}
357
358/**
359 * Build the common elements of the book form for the node and outline forms.
360 */
361function _book_add_form_elements(&$form, $node) {
362  // Need this for AJAX.
363  $form['#cache'] = TRUE;
364  drupal_add_js("if (Drupal.jsEnabled) { $(document).ready(function() { $('#edit-book-pick-book').css('display', 'none'); }); }", 'inline');
365
366  $form['book'] = array(
367    '#type' => 'fieldset',
368    '#title' => t('Book outline'),
369    '#weight' => 10,
370    '#collapsible' => TRUE,
371    '#collapsed' => TRUE,
372    '#tree' => TRUE,
373    '#attributes' => array('class' => 'book-outline-form'),
374  );
375  foreach (array('menu_name', 'mlid', 'nid', 'router_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) {
376    $form['book'][$key] = array(
377      '#type' => 'value',
378      '#value' => $node->book[$key],
379    );
380  }
381
382  $form['book']['plid'] = _book_parent_select($node->book);
383
384  $form['book']['weight'] = array(
385    '#type' => 'weight',
386    '#title' => t('Weight'),
387    '#default_value' => $node->book['weight'],
388    '#delta' => 15,
389    '#weight' => 5,
390    '#description' => t('Pages at a given level are ordered first by weight and then by title.'),
391  );
392  $options = array();
393  $nid = isset($node->nid) ? $node->nid : 'new';
394
395  if (isset($node->nid) && ($nid == $node->book['original_bid']) && ($node->book['parent_depth_limit'] == 0)) {
396    // This is the top level node in a maximum depth book and thus cannot be moved.
397    $options[$node->nid] = $node->title;
398  }
399  else {
400    foreach (book_get_books() as $book) {
401      $options[$book['nid']] = $book['title'];
402    }
403  }
404
405  if (user_access('create new books') && ($nid == 'new' || ($nid != $node->book['original_bid']))) {
406    // The node can become a new book, if it is not one already.
407    $options = array($nid => '<'. t('create a new book') .'>') + $options;
408  }
409  if (!$node->book['mlid']) {
410    // The node is not currently in a the hierarchy.
411    $options = array(0 => '<'. t('none') .'>') + $options;
412  }
413
414  // Add a drop-down to select the destination book.
415  $form['book']['bid'] = array(
416    '#type' => 'select',
417    '#title' => t('Book'),
418    '#default_value' => $node->book['bid'],
419    '#options' => $options,
420    '#access' => (bool)$options,
421    '#description' => t('Your page will be a part of the selected book.'),
422    '#weight' => -5,
423    '#attributes' => array('class' => 'book-title-select'),
424    '#ahah' => array(
425      'path' => 'book/js/form',
426      'wrapper' => 'edit-book-plid-wrapper',
427      'effect' => 'slide',
428    ),
429  );
430}
431
432/**
433 * Common helper function to handles additions and updates to the book outline.
434 *
435 * Performs all additions and updates to the book outline through node addition,
436 * node editing, node deletion, or the outline tab.
437 */
438function _book_update_outline(&$node) {
439  if (empty($node->book['bid'])) {
440    return FALSE;
441  }
442  $new = empty($node->book['mlid']);
443
444  $node->book['link_path'] = 'node/'. $node->nid;
445  $node->book['link_title'] = $node->title;
446  $node->book['parent_mismatch'] = FALSE; // The normal case.
447
448  if ($node->book['bid'] == $node->nid) {
449    $node->book['plid'] = 0;
450    $node->book['menu_name'] = book_menu_name($node->nid);
451  }
452  else {
453    // Check in case the parent is not is this book; the book takes precedence.
454    if (!empty($node->book['plid'])) {
455      $parent = db_fetch_array(db_query("SELECT * FROM {book} WHERE mlid = %d", $node->book['plid']));
456    }
457    if (empty($node->book['plid']) || !$parent || $parent['bid'] != $node->book['bid']) {
458      $node->book['plid'] = db_result(db_query("SELECT mlid FROM {book} WHERE nid = %d", $node->book['bid']));
459      $node->book['parent_mismatch'] = TRUE; // Likely when JS is disabled.
460    }
461  }
462  if (menu_link_save($node->book)) {
463    if ($new) {
464      // Insert new.
465      db_query("INSERT INTO {book} (nid, mlid, bid) VALUES (%d, %d, %d)", $node->nid, $node->book['mlid'], $node->book['bid']);
466    }
467    else {
468      if ($node->book['bid'] != db_result(db_query("SELECT bid FROM {book} WHERE nid = %d", $node->nid))) {
469        // Update the bid for this page and all children.
470        book_update_bid($node->book);
471      }
472    }
473    return TRUE;
474  }
475  // Failed to save the menu link
476  return FALSE;
477}
478
479/**
480 * Update the bid for a page and its children when it is moved to a new book.
481 *
482 * @param $book_link
483 *   A fully loaded menu link that is part of the book hierarchy.
484 */
485function book_update_bid($book_link) {
486
487  for ($i = 1; $i <= MENU_MAX_DEPTH && $book_link["p$i"]; $i++) {
488    $match[] = "p$i = %d";
489    $args[] = $book_link["p$i"];
490  }
491  $result = db_query("SELECT mlid FROM {menu_links} WHERE ". implode(' AND ', $match), $args);
492
493  $mlids = array();
494  while ($a = db_fetch_array($result)) {
495    $mlids[] = $a['mlid'];
496  }
497  if ($mlids) {
498    db_query("UPDATE {book} SET bid = %d WHERE mlid IN (". implode(',', $mlids) .")", $book_link['bid']);
499  }
500}
501
502/**
503 * Get the book menu tree for a page, and return it as a linear array.
504 *
505 * @param $book_link
506 *   A fully loaded menu link that is part of the book hierarchy.
507 * @return
508 *   A linear array of menu links in the order that the links are shown in the
509 *   menu, so the previous and next pages are the elements before and after the
510 *   element corresponding to $node.  The children of $node (if any) will come
511 *   immediately after it in the array.
512 */
513function book_get_flat_menu($book_link) {
514  static $flat = array();
515
516  if (!isset($flat[$book_link['mlid']])) {
517    // Call menu_tree_all_data() to take advantage of the menu system's caching.
518    $tree = menu_tree_all_data($book_link['menu_name'], $book_link);
519    $flat[$book_link['mlid']] = array();
520    _book_flatten_menu($tree, $flat[$book_link['mlid']]);
521  }
522  return $flat[$book_link['mlid']];
523}
524
525/**
526 * Recursive helper function for book_get_flat_menu().
527 */
528function _book_flatten_menu($tree, &$flat) {
529  foreach ($tree as $data) {
530    if (!$data['link']['hidden']) {
531      $flat[$data['link']['mlid']] = $data['link'];
532      if ($data['below']) {
533        _book_flatten_menu($data['below'], $flat);
534      }
535    }
536  }
537}
538
539/**
540 * Fetches the menu link for the previous page of the book.
541 */
542function book_prev($book_link) {
543  // If the parent is zero, we are at the start of a book.
544  if ($book_link['plid'] == 0) {
545    return NULL;
546  }
547  $flat = book_get_flat_menu($book_link);
548  // Assigning the array to $flat resets the array pointer for use with each().
549  $curr = NULL;
550  do {
551    $prev = $curr;
552    list($key, $curr) = each($flat);
553  } while ($key && $key != $book_link['mlid']);
554
555  if ($key == $book_link['mlid']) {
556    // The previous page in the book may be a child of the previous visible link.
557    if ($prev['depth'] == $book_link['depth'] && $prev['has_children']) {
558      // The subtree will have only one link at the top level - get its data.
559      $data = array_shift(book_menu_subtree_data($prev));
560      // The link of interest is the last child - iterate to find the deepest one.
561      while ($data['below']) {
562        $data = end($data['below']);
563      }
564      return $data['link'];
565    }
566    else {
567      return $prev;
568    }
569  }
570}
571
572/**
573 * Fetches the menu link for the next page of the book.
574 */
575function book_next($book_link) {
576  $flat = book_get_flat_menu($book_link);
577  // Assigning the array to $flat resets the array pointer for use with each().
578  do {
579    list($key, $curr) = each($flat);
580  } while ($key && $key != $book_link['mlid']);
581  if ($key == $book_link['mlid']) {
582    return current($flat);
583  }
584}
585
586/**
587 * Format the menu links for the child pages of the current page.
588 */
589function book_children($book_link) {
590  $flat = book_get_flat_menu($book_link);
591
592  $children = array();
593
594  if ($book_link['has_children']) {
595    // Walk through the array until we find the current page.
596    do {
597      $link = array_shift($flat);
598    } while ($link && ($link['mlid'] != $book_link['mlid']));
599    // Continue though the array and collect the links whose parent is this page.
600    while (($link = array_shift($flat)) && $link['plid'] == $book_link['mlid']) {
601      $data['link'] = $link;
602      $data['below'] = '';
603      $children[] = $data;
604    }
605  }
606  return $children ? menu_tree_output($children) : '';
607}
608
609/**
610 * Generate the corresponding menu name from a book ID.
611 */
612function book_menu_name($bid) {
613  return 'book-toc-'. $bid;
614}
615
616/**
617 * Build an active trail to show in the breadcrumb.
618 */
619function book_build_active_trail($book_link) {
620  static $trail;
621
622  if (!isset($trail)) {
623    $trail = array();
624    $trail[] = array('title' => t('Home'), 'href' => '<front>', 'localized_options' => array());
625
626    $tree = menu_tree_all_data($book_link['menu_name'], $book_link);
627    $curr = array_shift($tree);
628
629    while ($curr) {
630      if ($curr['link']['href'] == $book_link['href']) {
631        $trail[] = $curr['link'];
632        $curr = FALSE;
633      }
634      else {
635        if ($curr['below'] && $curr['link']['in_active_trail']) {
636          $trail[] = $curr['link'];
637          $tree = $curr['below'];
638        }
639        $curr = array_shift($tree);
640      }
641    }
642  }
643  return $trail;
644}
645
646/**
647 * Implementation of hook_nodeapi().
648 *
649 * Appends book navigation to all nodes in the book, and handles book outline
650 * insertions and updates via the node form.
651 */
652function book_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
653  switch ($op) {
654    case 'load':
655      // Note - we cannot use book_link_load() because it will call node_load()
656      $info['book'] = db_fetch_array(db_query('SELECT * FROM {book} b INNER JOIN {menu_links} ml ON b.mlid = ml.mlid WHERE b.nid = %d', $node->nid));
657      if ($info['book']) {
658        $info['book']['href'] = $info['book']['link_path'];
659        $info['book']['title'] = $info['book']['link_title'];
660        $info['book']['options'] = unserialize($info['book']['options']);
661        return $info;
662      }
663      break;
664    case 'view':
665    if (!$teaser) {
666        if (!empty($node->book['bid']) && $node->build_mode == NODE_BUILD_NORMAL) {
667
668          $node->content['book_navigation'] = array(
669            '#value' => theme('book_navigation', $node->book),
670            '#weight' => 100,
671          );
672
673          if ($page) {
674            menu_set_active_trail(book_build_active_trail($node->book));
675            menu_set_active_menu_name($node->book['menu_name']);
676          }
677        }
678      }
679      break;
680    case 'presave':
681      // Always save a revision for non-administrators.
682      if (!empty($node->book['bid']) && !user_access('administer nodes')) {
683        $node->revision = 1;
684      }
685      // Make sure a new node gets a new menu link.
686      if (empty($node->nid)) {
687        $node->book['mlid'] = NULL;
688      }
689      break;
690    case 'insert':
691    case 'update':
692      if (!empty($node->book['bid'])) {
693        if ($node->book['bid'] == 'new') {
694          // New nodes that are their own book.
695          $node->book['bid'] = $node->nid;
696        }
697        $node->book['nid'] = $node->nid;
698        $node->book['menu_name'] = book_menu_name($node->book['bid']);
699        _book_update_outline($node);
700      }
701      break;
702    case 'delete':
703      if (!empty($node->book['bid'])) {
704        if ($node->nid == $node->book['bid']) {
705          // Handle deletion of a top-level post.
706          $result = db_query("SELECT b.nid FROM {menu_links} ml INNER JOIN {book} b on b.mlid = ml.mlid WHERE ml.plid = %d", $node->book['mlid']);
707          while ($child = db_fetch_array($result)) {
708            $child_node = node_load($child['nid']);
709            $child_node->book['bid'] = $child_node->nid;
710            _book_update_outline($child_node);
711          }
712        }
713        menu_link_delete($node->book['mlid']);
714        db_query('DELETE FROM {book} WHERE mlid = %d', $node->book['mlid']);
715      }
716      break;
717    case 'prepare':
718      // Prepare defaults for the add/edit form.
719      if (empty($node->book) && (user_access('add content to books') || user_access('administer book outlines'))) {
720        $node->book = array();
721        if (empty($node->nid) && isset($_GET['parent']) && is_numeric($_GET['parent'])) {
722          // Handle "Add child page" links:
723          $parent = book_link_load($_GET['parent']);
724          if ($parent && $parent['access']) {
725            $node->book['bid'] = $parent['bid'];
726            $node->book['plid'] = $parent['mlid'];
727            $node->book['menu_name'] = $parent['menu_name'];
728          }
729        }
730        // Set defaults.
731        $node->book += _book_link_defaults(!empty($node->nid) ? $node->nid : 'new');
732      }
733      else {
734        if (isset($node->book['bid']) && !isset($node->book['original_bid'])) {
735          $node->book['original_bid'] = $node->book['bid'];
736        }
737      }
738      // Find the depth limit for the parent select.
739      if (isset($node->book['bid']) && !isset($node->book['parent_depth_limit'])) {
740        $node->book['parent_depth_limit'] = _book_parent_depth_limit($node->book);
741      }
742      break;
743  }
744}
745
746/**
747 * Find the depth limit for items in the parent select.
748 */
749function _book_parent_depth_limit($book_link) {
750  return MENU_MAX_DEPTH - 1 - (($book_link['mlid'] && $book_link['has_children']) ? menu_link_children_relative_depth($book_link) : 0);
751}
752
753/**
754 * Form altering function for the confirm form for a single node deletion.
755 */
756function book_form_node_delete_confirm_alter(&$form, $form_state) {
757
758  $node = node_load($form['nid']['#value']);
759
760  if (isset($node->book) && $node->book['has_children']) {
761    $form['book_warning'] = array(
762      '#value' => '<p>'. 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)) .'</p>',
763      '#weight' => -10,
764    );
765  }
766}
767
768/**
769 * Return an array with default values for a book link.
770 */
771function _book_link_defaults($nid) {
772  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());
773}
774
775/**
776 * Process variables for book-navigation.tpl.php.
777 *
778 * The $variables array contains the following arguments:
779 * - $book_link
780 *
781 * @see book-navigation.tpl.php
782 */
783function template_preprocess_book_navigation(&$variables) {
784  $book_link = $variables['book_link'];
785
786  // Provide extra variables for themers. Not needed by default.
787  $variables['book_id'] = $book_link['bid'];
788  $variables['book_title'] = check_plain($book_link['link_title']);
789  $variables['book_url'] = 'node/'. $book_link['bid'];
790  $variables['current_depth'] = $book_link['depth'];
791
792  $variables['tree'] = '';
793  if ($book_link['mlid']) {
794    $variables['tree'] = book_children($book_link);
795
796    if ($prev = book_prev($book_link)) {
797      $prev_href = url($prev['href']);
798      drupal_add_link(array('rel' => 'prev', 'href' => $prev_href));
799      $variables['prev_url'] = $prev_href;
800      $variables['prev_title'] = check_plain($prev['title']);
801    }
802    if ($book_link['plid'] && $parent = book_link_load($book_link['plid'])) {
803      $parent_href = url($parent['href']);
804      drupal_add_link(array('rel' => 'up', 'href' => $parent_href));
805      $variables['parent_url'] = $parent_href;
806      $variables['parent_title'] = check_plain($parent['title']);
807    }
808    if ($next = book_next($book_link)) {
809      $next_href = url($next['href']);
810      drupal_add_link(array('rel' => 'next', 'href' => $next_href));
811      $variables['next_url'] = $next_href;
812      $variables['next_title'] = check_plain($next['title']);
813    }
814  }
815
816  $variables['has_links'] = FALSE;
817  // Link variables to filter for values and set state of the flag variable.
818  $links = array('prev_url', 'prev_title', 'parent_url', 'parent_title', 'next_url', 'next_title');
819  foreach ($links as $link) {
820    if (isset($variables[$link])) {
821      // Flag when there is a value.
822      $variables['has_links'] = TRUE;
823    }
824    else {
825      // Set empty to prevent notices.
826      $variables[$link] = '';
827    }
828  }
829}
830
831/**
832 * A recursive helper function for book_toc().
833 */
834function _book_toc_recurse($tree, $indent, &$toc, $exclude, $depth_limit) {
835  foreach ($tree as $data) {
836    if ($data['link']['depth'] > $depth_limit) {
837      // Don't iterate through any links on this level.
838      break;
839    }
840    if (!in_array($data['link']['mlid'], $exclude)) {
841      $toc[$data['link']['mlid']] = $indent .' '. truncate_utf8($data['link']['title'], 30, TRUE, TRUE);
842      if ($data['below']) {
843        _book_toc_recurse($data['below'], $indent .'--', $toc, $exclude, $depth_limit);
844      }
845    }
846  }
847}
848
849/**
850 * Returns an array of book pages in table of contents order.
851 *
852 * @param $bid
853 *   The ID of the book whose pages are to be listed.
854 * @param $exclude
855 *   Optional array of mlid values.  Any link whose mlid is in this array
856 *   will be excluded (along with its children).
857 * @param $depth_limit
858 *   Any link deeper than this value will be excluded (along with its children).
859 * @return
860 *   An array of mlid, title pairs for use as options for selecting a book page.
861 */
862function book_toc($bid, $exclude = array(), $depth_limit) {
863
864  $tree = menu_tree_all_data(book_menu_name($bid));
865  $toc = array();
866  _book_toc_recurse($tree, '', $toc, $exclude, $depth_limit);
867
868  return $toc;
869}
870
871/**
872 * Process variables for book-export-html.tpl.php.
873 *
874 * The $variables array contains the following arguments:
875 * - $title
876 * - $contents
877 * - $depth
878 *
879 * @see book-export-html.tpl.php
880 */
881function template_preprocess_book_export_html(&$variables) {
882  global $base_url, $language;
883
884  $variables['title'] = check_plain($variables['title']);
885  $variables['base_url'] = $base_url;
886  $variables['language'] = $language;
887  $variables['language_rtl'] = ($language->direction == LANGUAGE_RTL);
888  $variables['head'] = drupal_get_html_head();
889}
890
891/**
892 * Traverse the book tree to build printable or exportable output.
893 *
894 * During the traversal, the $visit_func() callback is applied to each
895 * node, and is called recursively for each child of the node (in weight,
896 * title order).
897 *
898 * @param $tree
899 *   A subtree of the book menu hierarchy, rooted at the current page.
900 * @param $visit_func
901 *   A function callback to be called upon visiting a node in the tree.
902 * @return
903 *   The output generated in visiting each node.
904 */
905function book_export_traverse($tree, $visit_func) {
906  $output = '';
907
908  foreach ($tree as $data) {
909    // Note- access checking is already performed when building the tree.
910    if ($node = node_load($data['link']['nid'], FALSE)) {
911      $children = '';
912      if ($data['below']) {
913        $children = book_export_traverse($data['below'], $visit_func);
914      }
915
916      if (function_exists($visit_func)) {
917        $output .= call_user_func($visit_func, $node, $children);
918      }
919      else {
920        // Use the default function.
921        $output .= book_node_export($node, $children);
922      }
923    }
924  }
925  return $output;
926}
927
928/**
929 * Generates printer-friendly HTML for a node.
930 *
931 * @see book_export_traverse()
932 *
933 * @param $node
934 *   The node to generate output for.
935 * @param $children
936 *   All the rendered child nodes within the current node.
937 * @return
938 *   The HTML generated for the given node.
939 */
940function book_node_export($node, $children = '') {
941
942  $node->build_mode = NODE_BUILD_PRINT;
943  $node = node_build_content($node, FALSE, FALSE);
944  $node->body = drupal_render($node->content);
945
946  return theme('book_node_export_html', $node, $children);
947}
948
949/**
950 * Process variables for book-node-export-html.tpl.php.
951 *
952 * The $variables array contains the following arguments:
953 * - $node
954 * - $children
955 *
956 * @see book-node-export-html.tpl.php
957 */
958function template_preprocess_book_node_export_html(&$variables) {
959  $variables['depth'] = $variables['node']->book['depth'];
960  $variables['title'] = check_plain($variables['node']->title);
961  $variables['content'] = $variables['node']->body;
962}
963
964/**
965 * Determine if a given node type is in the list of types allowed for books.
966 */
967function book_type_is_allowed($type) {
968  return in_array($type, variable_get('book_allowed_types', array('book')));
969}
970
971/**
972 * Implementation of hook_node_type().
973 *
974 * Update book module's persistent variables if the machine-readable name of a
975 * node type is changed.
976 */
977function book_node_type($op, $type) {
978
979  switch ($op) {
980    case 'update':
981      if (!empty($type->old_type) && $type->old_type != $type->type) {
982        // Update the list of node types that are allowed to be added to books.
983        $allowed_types = variable_get('book_allowed_types', array('book'));
984        $key = array_search($type->old_type, $allowed_types);
985        if ($key !== FALSE) {
986          $allowed_types[$type->type] = $allowed_types[$key] ? $type->type : 0;
987          unset($allowed_types[$key]);
988          variable_set('book_allowed_types', $allowed_types);
989        }
990        // Update the setting for the "Add child page" link.
991        if (variable_get('book_child_type', 'book') == $type->old_type) {
992          variable_set('book_child_type', $type->type);
993        }
994      }
995      break;
996  }
997}
998
999/**
1000 * Implementation of hook_help().
1001 */
1002function book_help($path, $arg) {
1003  switch ($path) {
1004    case 'admin/help#book':
1005      $output = '<p>'. t('The book module is suited for creating structured, multi-page hypertexts such as site resource guides, manuals, and Frequently Asked Questions (FAQs). It permits a document to have chapters, sections, subsections, etc. Authors with suitable permissions can add pages to a collaborative book, placing them into the existing document by adding them to a table of contents menu.') .'</p>';
1006      $output .= '<p>'. t('Pages in the book hierarchy have navigation elements at the bottom of the page for moving through the text. These links lead to the previous and next pages in the book, and to the level above the current page in the book\'s structure. More comprehensive navigation may be provided by enabling the <em>book navigation block</em> on the <a href="@admin-block">blocks administration page</a>.', array('@admin-block' => url('admin/build/block'))) .'</p>';
1007      $output .= '<p>'. t('Users can select the <em>printer-friendly version</em> link visible at the bottom of a book page to generate a printer-friendly display of the page and all of its subsections. ') .'</p>';
1008      $output .= '<p>'. t("Users with the <em>administer book outlines</em> permission can add a post of any content type to a book, by selecting the appropriate book while editing the post or by using the interface available on the post's <em>outline</em> tab.") .'</p>';
1009      $output .= '<p>'. t('Administrators can view a list of all books on the <a href="@admin-node-book">book administration page</a>. The <em>Outline</em> page for each book allows section titles to be edited or rearranged.', array('@admin-node-book' => url('admin/content/book'))) .'</p>';
1010      $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@book">Book module</a>.', array('@book' => 'http://drupal.org/handbook/modules/book/')) .'</p>';
1011      return $output;
1012    case 'admin/content/book':
1013      return '<p>'. t('The book module offers a means to organize a collection of related posts, collectively known as a book. When viewed, these posts automatically display links to adjacent book pages, providing a simple navigation system for creating and reviewing structured content.') .'</p>';
1014    case 'node/%/outline':
1015      return '<p>'. t('The outline feature allows you to include posts in the <a href="@book">book hierarchy</a>, as well as move them within the hierarchy or to <a href="@book-admin">reorder an entire book</a>.', array('@book' => url('book'), '@book-admin' => url('admin/content/book'))) .'</p>';
1016  }
1017}
1018
1019/**
1020 * Like menu_link_load(), but adds additional data from the {book} table.
1021 *
1022 * Do not call when loading a node, since this function may call node_load().
1023 */
1024function book_link_load($mlid) {
1025  if ($item = db_fetch_array(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 = %d", $mlid))) {
1026    _menu_link_translate($item);
1027    return $item;
1028  }
1029  return FALSE;
1030}
1031
1032/**
1033 * Get the data representing a subtree of the book hierarchy.
1034 *
1035 * The root of the subtree will be the link passed as a parameter, so the
1036 * returned tree will contain this item and all its descendents in the menu tree.
1037 *
1038 * @param $item
1039 *   A fully loaded menu link.
1040 * @return
1041 *   An subtree of menu links in an array, in the order they should be rendered.
1042 */
1043function book_menu_subtree_data($item) {
1044  static $tree = array();
1045
1046  // Generate a cache ID (cid) specific for this $menu_name and $item.
1047  $cid = 'links:'. $item['menu_name'] .':subtree-cid:'. $item['mlid'];
1048
1049  if (!isset($tree[$cid])) {
1050    $cache = cache_get($cid, 'cache_menu');
1051    if ($cache && isset($cache->data)) {
1052      // If the cache entry exists, it will just be the cid for the actual data.
1053      // This avoids duplication of large amounts of data.
1054      $cache = cache_get($cache->data, 'cache_menu');
1055      if ($cache && isset($cache->data)) {
1056        $data = $cache->data;
1057      }
1058    }
1059    // If the subtree data was not in the cache, $data will be NULL.
1060    if (!isset($data)) {
1061      $match = array("menu_name = '%s'");
1062      $args = array($item['menu_name']);
1063      $i = 1;
1064      while ($i <= MENU_MAX_DEPTH && $item["p$i"]) {
1065        $match[] = "p$i = %d";
1066        $args[] = $item["p$i"];
1067        $i++;
1068      }
1069      $sql = "
1070        SELECT b.*, m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, ml.*
1071        FROM {menu_links} ml INNER JOIN {menu_router} m ON m.path = ml.router_path
1072        INNER JOIN {book} b ON ml.mlid = b.mlid
1073        WHERE ". implode(' AND ', $match) ."
1074        ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC";
1075
1076      $data['tree'] = menu_tree_data(db_query($sql, $args), array(), $item['depth']);
1077      $data['node_links'] = array();
1078      menu_tree_collect_node_links($data['tree'], $data['node_links']);
1079      // Compute the real cid for book subtree data.
1080      $tree_cid = 'links:'. $item['menu_name'] .':subtree-data:'. md5(serialize($data));
1081      // Cache the data, if it is not already in the cache.
1082      if (!cache_get($tree_cid, 'cache_menu')) {
1083        cache_set($tree_cid, $data, 'cache_menu');
1084      }
1085      // Cache the cid of the (shared) data using the menu and item-specific cid.
1086      cache_set($cid, $tree_cid, 'cache_menu');
1087    }
1088    // Check access for the current user to each item in the tree.
1089    menu_tree_check_access($data['tree'], $data['node_links']);
1090    $tree[$cid] = $data['tree'];
1091  }
1092
1093  return $tree[$cid];
1094}
1095
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.