[ Index ] |
|
Code source de Drupal 5.3 |
1 <?php 2 // $Id: book.module,v 1.406.2.1 2007/02/14 04:30:33 drumm Exp $ 3 4 /** 5 * @file 6 * Allows users to collaboratively author a book. 7 */ 8 9 /** 10 * Implementation of hook_node_info(). 11 */ 12 function book_node_info() { 13 return array( 14 'book' => array( 15 'name' => t('Book page'), 16 'module' => 'book', 17 'description' => t("A book is a collaborative writing effort: users can collaborate writing the pages of the book, positioning the pages in the right order, and reviewing or modifying pages previously written. So when you have some information to share or when you read a page of the book and you didn't like it, or if you think a certain page could have been written better, you can do something about it."), 18 ) 19 ); 20 } 21 22 /** 23 * Implementation of hook_perm(). 24 */ 25 function book_perm() { 26 return array('outline posts in books', 'create book pages', 'create new books', 'edit book pages', 'edit own book pages', 'see printer-friendly version'); 27 } 28 29 /** 30 * Implementation of hook_access(). 31 */ 32 function book_access($op, $node) { 33 global $user; 34 35 if ($op == 'create') { 36 // Only registered users can create book pages. Given the nature 37 // of the book module this is considered to be a good/safe idea. 38 return user_access('create book pages'); 39 } 40 41 if ($op == 'update') { 42 // Only registered users can update book pages. Given the nature 43 // of the book module this is considered to be a good/safe idea. 44 // One can only update a book page if there are no suggested updates 45 // of that page waiting for approval. That is, only updates that 46 // don't overwrite the current or pending information are allowed. 47 48 if (user_access('edit book pages') || ($node->uid == $user->uid && user_access('edit own book pages'))) { 49 return TRUE; 50 } 51 else { 52 // do nothing. node-access() will determine further access 53 } 54 } 55 } 56 57 /** 58 * Implementation of hook_link(). 59 */ 60 function book_link($type, $node = NULL, $teaser = FALSE) { 61 62 $links = array(); 63 64 if ($type == 'node' && isset($node->parent)) { 65 if (!$teaser) { 66 if (book_access('create', $node) && $node->status == 1) { 67 $links['book_add_child'] = array( 68 'title' => t('Add child page'), 69 'href' => "node/add/book/parent/$node->nid" 70 ); 71 } 72 if (user_access('see printer-friendly version')) { 73 $links['book_printer'] = array( 74 'title' => t('Printer-friendly version'), 75 'href' => 'book/export/html/'. $node->nid, 76 'attributes' => array('title' => t('Show a printer-friendly version of this book page and its sub-pages.')) 77 ); 78 } 79 } 80 } 81 82 return $links; 83 } 84 85 /** 86 * Implementation of hook_menu(). 87 */ 88 function book_menu($may_cache) { 89 $items = array(); 90 91 if ($may_cache) { 92 $items[] = array( 93 'path' => 'admin/content/book', 94 'title' => t('Books'), 95 'description' => t("Manage site's books and orphaned book pages."), 96 'callback' => 'book_admin', 97 'access' => user_access('administer nodes')); 98 $items[] = array( 99 'path' => 'admin/content/book/list', 100 'title' => t('List'), 101 'type' => MENU_DEFAULT_LOCAL_TASK); 102 $items[] = array( 103 'path' => 'admin/content/book/orphan', 104 'title' => t('Orphan pages'), 105 'callback' => 'drupal_get_form', 106 'callback arguments' => array('book_admin_orphan'), 107 'type' => MENU_LOCAL_TASK, 108 'weight' => 8); 109 $items[] = array( 110 'path' => 'book', 111 'title' => t('Books'), 112 'callback' => 'book_render', 113 'access' => user_access('access content'), 114 'type' => MENU_SUGGESTED_ITEM); 115 $items[] = array( 116 'path' => 'book/export', 117 'callback' => 'book_export', 118 'access' => user_access('access content'), 119 'type' => MENU_CALLBACK); 120 } 121 else { 122 // Add the CSS for this module 123 // We put this in !$may_cache so it's only added once per request 124 drupal_add_css(drupal_get_path('module', 'book') .'/book.css'); 125 126 // To avoid SQL overhead, check whether we are on a node page and whether the 127 // user is allowed to outline posts in books. 128 if (arg(0) == 'node' && is_numeric(arg(1)) && user_access('outline posts in books')) { 129 // Only add the outline-tab for non-book pages: 130 $result = db_query(db_rewrite_sql("SELECT n.nid FROM {node} n WHERE n.nid = %d AND n.type != 'book'"), arg(1)); 131 if (db_num_rows($result) > 0) { 132 $items[] = array( 133 'path' => 'node/'. arg(1) .'/outline', 134 'title' => t('Outline'), 135 'callback' => 'drupal_get_form', 136 'callback arguments' => array('book_outline', arg(1)), 137 'access' => user_access('outline posts in books'), 138 'type' => MENU_LOCAL_TASK, 139 'weight' => 2); 140 } 141 } 142 } 143 144 return $items; 145 } 146 147 /** 148 * Implementation of hook_block(). 149 * 150 * Displays the book table of contents in a block when the current page is a 151 * single-node view of a book node. 152 */ 153 function book_block($op = 'list', $delta = 0) { 154 $block = array(); 155 if ($op == 'list') { 156 $block[0]['info'] = t('Book navigation'); 157 return $block; 158 } 159 else if ($op == 'view') { 160 // Only display this block when the user is browsing a book: 161 if (arg(0) == 'node' && is_numeric(arg(1))) { 162 $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d'), arg(1)); 163 if (db_num_rows($result) > 0) { 164 $node = db_fetch_object($result); 165 166 $path = book_location($node); 167 $path[] = $node; 168 169 $expand = array(); 170 foreach ($path as $key => $node) { 171 $expand[] = $node->nid; 172 } 173 174 $block['subject'] = check_plain($path[0]->title); 175 $block['content'] = book_tree($expand[0], 5, $expand); 176 } 177 } 178 179 return $block; 180 } 181 } 182 183 /** 184 * Implementation of hook_insert(). 185 */ 186 function book_insert($node) { 187 db_query("INSERT INTO {book} (nid, vid, parent, weight) VALUES (%d, %d, %d, %d)", $node->nid, $node->vid, $node->parent, $node->weight); 188 } 189 190 /** 191 * Implementation of hook_submit(). 192 */ 193 function book_submit(&$node) { 194 global $user; 195 // Set default values for non-administrators. 196 if (!user_access('administer nodes')) { 197 $node->revision = 1; 198 $node->uid = $user->uid; 199 } 200 } 201 202 /** 203 * Implementation of hook_form(). 204 */ 205 function book_form(&$node) { 206 $type = node_get_types('type', $node); 207 if ($node->nid && !$node->parent && !user_access('create new books')) { 208 $form['parent'] = array('#type' => 'value', '#value' => $node->parent); 209 } 210 else { 211 $form['parent'] = array('#type' => 'select', 212 '#title' => t('Parent'), 213 '#default_value' => ($node->parent ? $node->parent : arg(4)), 214 '#options' => book_toc($node->nid), 215 '#weight' => -4, 216 '#description' => user_access('create new books') ? t('The parent section in which to place this page. Note that each page whose parent is <top-level> is an independent, top-level book.') : t('The parent that this page belongs in.'), 217 ); 218 } 219 220 $form['title'] = array('#type' => 'textfield', 221 '#title' => check_plain($type->title_label), 222 '#required' => TRUE, 223 '#default_value' => $node->title, 224 '#weight' => -5, 225 ); 226 $form['body_filter']['body'] = array('#type' => 'textarea', 227 '#title' => check_plain($type->body_label), 228 '#default_value' => $node->body, 229 '#rows' => 20, 230 '#required' => TRUE, 231 ); 232 $form['body_filter']['format'] = filter_form($node->format); 233 234 if (user_access('administer nodes')) { 235 $form['weight'] = array('#type' => 'weight', 236 '#title' => t('Weight'), 237 '#default_value' => $node->weight, 238 '#delta' => 15, 239 '#weight' => 5, 240 '#description' => t('Pages at a given level are ordered first by weight and then by title.'), 241 ); 242 } 243 else { 244 // If a regular user updates a book page, we preserve the node weight; otherwise 245 // we use 0 as the default for new pages 246 $form['weight'] = array( 247 '#type' => 'value', 248 '#value' => isset($node->weight) ? $node->weight : 0, 249 ); 250 } 251 252 return $form; 253 } 254 255 /** 256 * Implementation of function book_outline() 257 * Handles all book outline operations. 258 */ 259 function book_outline($nid) { 260 $node = node_load($nid); 261 262 $form['parent'] = array('#type' => 'select', 263 '#title' => t('Parent'), 264 '#default_value' => $node->parent, 265 '#options' => book_toc($node->nid), 266 '#description' => t('The parent page in the book.'), 267 ); 268 $form['weight'] = array('#type' => 'weight', 269 '#title' => t('Weight'), 270 '#default_value' => $node->weight, 271 '#delta' => 15, 272 '#description' => t('Pages at a given level are ordered first by weight and then by title.'), 273 ); 274 $form['log'] = array('#type' => 'textarea', 275 '#title' => t('Log message'), 276 '#description' => t('An explanation to help other authors understand your motivations to put this post into the book.'), 277 ); 278 279 $form['nid'] = array('#type' => 'value', '#value' => $nid); 280 if (isset($node->parent)) { 281 $form['update'] = array('#type' => 'submit', 282 '#value' => t('Update book outline'), 283 ); 284 $form['remove'] = array('#type' => 'submit', 285 '#value' => t('Remove from book outline'), 286 ); 287 } 288 else { 289 $form['add'] = array('#type' => 'submit', '#value' => t('Add to book outline')); 290 } 291 292 drupal_set_title(check_plain($node->title)); 293 return $form; 294 } 295 296 /** 297 * Handles book outline form submissions. 298 */ 299 function book_outline_submit($form_id, $form_values) { 300 $op = $form_values['op']; 301 $node = node_load($form_values['nid']); 302 303 switch ($op) { 304 case t('Add to book outline'): 305 db_query('INSERT INTO {book} (nid, vid, parent, weight) VALUES (%d, %d, %d, %d)', $node->nid, $node->vid, $form_values['parent'], $form_values['weight']); 306 db_query("UPDATE {node_revisions} SET log = '%s' WHERE vid = %d", $form_values['log'], $node->vid); 307 drupal_set_message(t('The post has been added to the book.')); 308 break; 309 case t('Update book outline'): 310 db_query('UPDATE {book} SET parent = %d, weight = %d WHERE vid = %d', $form_values['parent'], $form_values['weight'], $node->vid); 311 db_query("UPDATE {node_revisions} SET log = '%s' WHERE vid = %d", $form_values['log'], $node->vid); 312 drupal_set_message(t('The book outline has been updated.')); 313 break; 314 case t('Remove from book outline'): 315 db_query('DELETE FROM {book} WHERE nid = %d', $node->nid); 316 drupal_set_message(t('The post has been removed from the book.')); 317 break; 318 } 319 return "node/$node->nid"; 320 } 321 322 /** 323 * Given a node, this function returns an array of 'book node' objects 324 * representing the path in the book tree from the root to the 325 * parent of the given node. 326 * 327 * @param $node 328 * A book node object for which to compute the path. 329 * 330 * @return 331 * An array of book node objects representing the path nodes root to 332 * parent of the given node. Returns an empty array if the node does 333 * not exist or is not part of a book hierarchy. 334 */ 335 function book_location($node, $nodes = array()) { 336 $parent = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d'), $node->parent)); 337 if (isset($parent->title)) { 338 $nodes = book_location($parent, $nodes); 339 $nodes[] = $parent; 340 } 341 return $nodes; 342 } 343 344 /** 345 * Given a node, this function returns an array of 'book node' objects 346 * representing the path in the book tree from the given node down to 347 * the last sibling of it. 348 * 349 * @param $node 350 * A book node object where the path starts. 351 * 352 * @return 353 * An array of book node objects representing the path nodes from the 354 * given node. Returns an empty array if the node does not exist or 355 * is not part of a book hierarchy or there are no siblings. 356 */ 357 function book_location_down($node, $nodes = array()) { 358 $last_direct_child = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 AND b.parent = %d ORDER BY b.weight DESC, n.title DESC'), $node->nid)); 359 if ($last_direct_child) { 360 $nodes[] = $last_direct_child; 361 $nodes = book_location_down($last_direct_child, $nodes); 362 } 363 return $nodes; 364 } 365 366 /** 367 * Fetches the node object of the previous page of the book. 368 */ 369 function book_prev($node) { 370 // If the parent is zero, we are at the start of a book so there is no previous. 371 if ($node->parent == 0) { 372 return NULL; 373 } 374 375 // Previous on the same level: 376 $direct_above = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d AND n.status = 1 AND (b.weight < %d OR (b.weight = %d AND n.title < '%s')) ORDER BY b.weight DESC, n.title DESC"), $node->parent, $node->weight, $node->weight, $node->title)); 377 if ($direct_above) { 378 // Get last leaf of $above. 379 $path = book_location_down($direct_above); 380 381 return $path ? (count($path) > 0 ? array_pop($path) : NULL) : $direct_above; 382 } 383 else { 384 // Direct parent: 385 $prev = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d AND n.status = 1'), $node->parent)); 386 return $prev; 387 } 388 } 389 390 /** 391 * Fetches the node object of the next page of the book. 392 */ 393 function book_next($node) { 394 // get first direct child 395 $child = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d AND n.status = 1 ORDER BY b.weight ASC, n.title ASC'), $node->nid)); 396 if ($child) { 397 return $child; 398 } 399 400 // No direct child: get next for this level or any parent in this book. 401 $path = book_location($node); // Path to top-level node including this one. 402 $path[] = $node; 403 404 while (($leaf = array_pop($path)) && count($path)) { 405 $next = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d AND n.status = 1 AND (b.weight > %d OR (b.weight = %d AND n.title > '%s')) ORDER BY b.weight ASC, n.title ASC"), $leaf->parent, $leaf->weight, $leaf->weight, $leaf->title)); 406 if ($next) { 407 return $next; 408 } 409 } 410 } 411 412 /** 413 * Returns the content of a given node. If $teaser if TRUE, returns 414 * the teaser rather than full content. Displays the most recently 415 * approved revision of a node (if any) unless we have to display this 416 * page in the context of the moderation queue. 417 */ 418 function book_content($node, $teaser = FALSE) { 419 // Return the page body. 420 return node_prepare($node, $teaser); 421 } 422 423 /** 424 * Implementation of hook_nodeapi(). 425 * 426 * Appends book navigation to all nodes in the book. 427 */ 428 function book_nodeapi(&$node, $op, $teaser, $page) { 429 switch ($op) { 430 case 'load': 431 return db_fetch_array(db_query('SELECT parent, weight FROM {book} WHERE vid = %d', $node->vid)); 432 break; 433 case 'view': 434 if (!$teaser) { 435 if (isset($node->parent)) { 436 $path = book_location($node); 437 // Construct the breadcrumb: 438 $node->breadcrumb = array(); // Overwrite the trail with a book trail. 439 foreach ($path as $level) { 440 $node->breadcrumb[] = array('path' => 'node/'. $level->nid, 'title' => $level->title); 441 } 442 $node->breadcrumb[] = array('path' => 'node/'. $node->nid); 443 444 $node->content['book_navigation'] = array( 445 '#value' => theme('book_navigation', $node), 446 '#weight' => 100, 447 ); 448 449 if ($page) { 450 menu_set_location($node->breadcrumb); 451 } 452 } 453 } 454 break; 455 case 'update': 456 if (isset($node->parent)) { 457 if ($node->revision) { 458 db_query("INSERT INTO {book} (nid, vid, parent, weight) VALUES (%d, %d, %d, %d)", $node->nid, $node->vid, $node->parent, $node->weight); 459 } 460 else { 461 db_query("UPDATE {book} SET parent = %d, weight = %d WHERE vid = %d", $node->parent, $node->weight, $node->vid); 462 } 463 } 464 break; 465 case 'delete revision': 466 db_query('DELETE FROM {book} WHERE vid = %d', $node->vid); 467 break; 468 case 'delete': 469 db_query('DELETE FROM {book} WHERE nid = %d', $node->nid); 470 break; 471 } 472 } 473 474 /** 475 * Prepares the links to children (TOC) and forward/backward 476 * navigation for a node presented as a book page. 477 * 478 * @ingroup themeable 479 */ 480 function theme_book_navigation($node) { 481 $output = ''; 482 $links = ''; 483 484 if ($node->nid) { 485 $tree = book_tree($node->nid); 486 487 if ($prev = book_prev($node)) { 488 drupal_add_link(array('rel' => 'prev', 'href' => url('node/'. $prev->nid))); 489 $links .= l(t('‹ ') . $prev->title, 'node/'. $prev->nid, array('class' => 'page-previous', 'title' => t('Go to previous page'))); 490 } 491 if ($node->parent) { 492 drupal_add_link(array('rel' => 'up', 'href' => url('node/'. $node->parent))); 493 $links .= l(t('up'), 'node/'. $node->parent, array('class' => 'page-up', 'title' => t('Go to parent page'))); 494 } 495 if ($next = book_next($node)) { 496 drupal_add_link(array('rel' => 'next', 'href' => url('node/'. $next->nid))); 497 $links .= l($next->title . t(' ›'), 'node/'. $next->nid, array('class' => 'page-next', 'title' => t('Go to next page'))); 498 } 499 500 if (isset($tree) || isset($links)) { 501 $output = '<div class="book-navigation">'; 502 if (isset($tree)) { 503 $output .= $tree; 504 } 505 if (isset($links)) { 506 $output .= '<div class="page-links clear-block">'. $links .'</div>'; 507 } 508 $output .= '</div>'; 509 } 510 } 511 512 return $output; 513 } 514 515 /** 516 * This is a helper function for book_toc(). 517 */ 518 function book_toc_recurse($nid, $indent, $toc, $children, $exclude) { 519 if ($children[$nid]) { 520 foreach ($children[$nid] as $foo => $node) { 521 if (!$exclude || $exclude != $node->nid) { 522 $toc[$node->nid] = $indent .' '. $node->title; 523 $toc = book_toc_recurse($node->nid, $indent .'--', $toc, $children, $exclude); 524 } 525 } 526 } 527 528 return $toc; 529 } 530 531 /** 532 * Returns an array of titles and nid entries of book pages in table of contents order. 533 */ 534 function book_toc($exclude = 0) { 535 $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 ORDER BY b.weight, n.title')); 536 537 $children = array(); 538 while ($node = db_fetch_object($result)) { 539 if (!$children[$node->parent]) { 540 $children[$node->parent] = array(); 541 } 542 $children[$node->parent][] = $node; 543 } 544 545 $toc = array(); 546 // If the user has permission to create new books, add the top-level book page to the menu; 547 if (user_access('create new books')) { 548 $toc[0] = '<'. t('top-level') .'>'; 549 } 550 551 $toc = book_toc_recurse(0, '', $toc, $children, $exclude); 552 553 return $toc; 554 } 555 556 /** 557 * This is a helper function for book_tree() 558 */ 559 function book_tree_recurse($nid, $depth, $children, $unfold = array()) { 560 $output = ''; 561 if ($depth > 0) { 562 if (isset($children[$nid])) { 563 foreach ($children[$nid] as $foo => $node) { 564 if (in_array($node->nid, $unfold)) { 565 if ($tree = book_tree_recurse($node->nid, $depth - 1, $children, $unfold)) { 566 $output .= '<li class="expanded">'; 567 $output .= l($node->title, 'node/'. $node->nid); 568 $output .= '<ul class="menu">'. $tree .'</ul>'; 569 $output .= '</li>'; 570 } 571 else { 572 $output .= '<li class="leaf">'. l($node->title, 'node/'. $node->nid) .'</li>'; 573 } 574 } 575 else { 576 if ($tree = book_tree_recurse($node->nid, 1, $children)) { 577 $output .= '<li class="collapsed">'. l($node->title, 'node/'. $node->nid) .'</li>'; 578 } 579 else { 580 $output .= '<li class="leaf">'. l($node->title, 'node/'. $node->nid) .'</li>'; 581 } 582 } 583 } 584 } 585 } 586 587 return $output; 588 } 589 590 /** 591 * Returns an HTML nested list (wrapped in a menu-class div) representing the book nodes 592 * as a tree. 593 */ 594 function book_tree($parent = 0, $depth = 3, $unfold = array()) { 595 $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 ORDER BY b.weight, n.title')); 596 597 while ($node = db_fetch_object($result)) { 598 $list = isset($children[$node->parent]) ? $children[$node->parent] : array(); 599 $list[] = $node; 600 $children[$node->parent] = $list; 601 } 602 603 if ($tree = book_tree_recurse($parent, $depth, $children, $unfold)) { 604 return '<ul class="menu">'. $tree .'</ul>'; 605 } 606 } 607 608 /** 609 * Menu callback; prints a listing of all books. 610 */ 611 function book_render() { 612 $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = 0 AND n.status = 1 ORDER BY b.weight, n.title')); 613 614 $books = array(); 615 while ($node = db_fetch_object($result)) { 616 $books[] = l($node->title, 'node/'. $node->nid); 617 } 618 619 return theme('item_list', $books); 620 } 621 622 /** 623 * Menu callback; Generates various representation of a book page with 624 * all descendants and prints the requested representation to output. 625 * 626 * The function delegates the generation of output to helper functions. 627 * The function name is derived by prepending 'book_export_' to the 628 * given output type. So, e.g., a type of 'html' results in a call to 629 * the function book_export_html(). 630 * 631 * @param type 632 * - a string encoding the type of output requested. 633 * The following types are currently supported in book module 634 * html: HTML (printer friendly output) 635 * Other types are supported in contributed modules. 636 * @param nid 637 * - an integer representing the node id (nid) of the node to export 638 * 639 */ 640 function book_export($type = 'html', $nid = 0) { 641 $type = drupal_strtolower($type); 642 $node_result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d'), $nid); 643 if (db_num_rows($node_result) > 0) { 644 $node = db_fetch_object($node_result); 645 } 646 $depth = count(book_location($node)) + 1; 647 $export_function = 'book_export_'. $type; 648 649 if (function_exists($export_function)) { 650 print call_user_func($export_function, $nid, $depth); 651 } 652 else { 653 drupal_set_message(t('Unknown export format.')); 654 drupal_not_found(); 655 } 656 } 657 658 /** 659 * This function is called by book_export() to generate HTML for export. 660 * 661 * The given node is /embedded to its absolute depth in a top level 662 * section/. For example, a child node with depth 2 in the hierarchy 663 * is contained in (otherwise empty) <div> elements 664 * corresponding to depth 0 and depth 1. This is intended to support 665 * WYSIWYG output - e.g., level 3 sections always look like level 3 666 * sections, no matter their depth relative to the node selected to be 667 * exported as printer-friendly HTML. 668 * 669 * @param nid 670 * - an integer representing the node id (nid) of the node to export 671 * @param depth 672 * - an integer giving the depth in the book hierarchy of the node 673 * which is to be exported 674 * 675 * @return 676 * - string containing HTML representing the node and its children in 677 * the book hierarchy 678 */ 679 function book_export_html($nid, $depth) { 680 if (user_access('see printer-friendly version')) { 681 $node = node_load($nid); 682 for ($i = 1; $i < $depth; $i++) { 683 $content .= "<div class=\"section-$i\">\n"; 684 } 685 $content .= book_recurse($nid, $depth, 'book_node_visitor_html_pre', 'book_node_visitor_html_post'); 686 for ($i = 1; $i < $depth; $i++) { 687 $content .= "</div>\n"; 688 } 689 return theme('book_export_html', check_plain($node->title), $content); 690 } 691 else { 692 drupal_access_denied(); 693 } 694 } 695 696 /** 697 * How the book's HTML export should be themed 698 * 699 * @ingroup themeable 700 */ 701 function theme_book_export_html($title, $content) { 702 global $base_url; 703 $html = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 704 $html .= '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">'; 705 $html .= "<head>\n<title>". $title ."</title>\n"; 706 $html .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'; 707 $html .= '<base href="'. $base_url .'/" />' . "\n"; 708 $html .= "<style type=\"text/css\">\n@import url(misc/print.css);\n</style>\n"; 709 $html .= "</head>\n<body>\n". $content ."\n</body>\n</html>\n"; 710 return $html; 711 } 712 713 /** 714 * Traverses the book tree. Applies the $visit_pre() callback to each 715 * node, is called recursively for each child of the node (in weight, 716 * title order). Finally appends the output of the $visit_post() 717 * callback to the output before returning the generated output. 718 * 719 * @todo This is duplicitous with node_build_content(). 720 * 721 * @param nid 722 * - the node id (nid) of the root node of the book hierarchy. 723 * @param depth 724 * - the depth of the given node in the book hierarchy. 725 * @param visit_pre 726 * - a function callback to be called upon visiting a node in the tree 727 * @param visit_post 728 * - a function callback to be called after visiting a node in the tree, 729 * but before recursively visiting children. 730 * @return 731 * - the output generated in visiting each node 732 */ 733 function book_recurse($nid = 0, $depth = 1, $visit_pre, $visit_post) { 734 $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 AND n.nid = %d ORDER BY b.weight, n.title'), $nid); 735 while ($page = db_fetch_object($result)) { 736 // Load the node: 737 $node = node_load($page->nid); 738 739 if ($node) { 740 if (function_exists($visit_pre)) { 741 $output .= call_user_func($visit_pre, $node, $depth, $nid); 742 } 743 else { 744 $output .= book_node_visitor_html_pre($node, $depth, $nid); 745 } 746 747 $children = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 AND b.parent = %d ORDER BY b.weight, n.title'), $node->nid); 748 while ($childpage = db_fetch_object($children)) { 749 $childnode = node_load($childpage->nid); 750 if ($childnode->nid != $node->nid) { 751 $output .= book_recurse($childnode->nid, $depth + 1, $visit_pre, $visit_post); 752 } 753 } 754 if (function_exists($visit_post)) { 755 $output .= call_user_func($visit_post, $node, $depth); 756 } 757 else { 758 # default 759 $output .= book_node_visitor_html_post($node, $depth); 760 } 761 } 762 } 763 764 return $output; 765 } 766 767 /** 768 * Generates printer-friendly HTML for a node. This function 769 * is a 'pre-node' visitor function for book_recurse(). 770 * 771 * @param $node 772 * - the node to generate output for. 773 * @param $depth 774 * - the depth of the given node in the hierarchy. This 775 * is used only for generating output. 776 * @param $nid 777 * - the node id (nid) of the given node. This 778 * is used only for generating output. 779 * @return 780 * - the HTML generated for the given node. 781 */ 782 function book_node_visitor_html_pre($node, $depth, $nid) { 783 // Remove the delimiter (if any) that separates the teaser from the body. 784 $node->body = str_replace('<!--break-->', '', $node->body); 785 786 // The 'view' hook can be implemented to overwrite the default function 787 // to display nodes. 788 if (node_hook($node, 'view')) { 789 $node = node_invoke($node, 'view', FALSE, FALSE); 790 } 791 else { 792 $node = node_prepare($node, FALSE); 793 } 794 795 // Allow modules to make their own additions to the node. 796 node_invoke_nodeapi($node, 'print'); 797 798 $output .= "<div id=\"node-". $node->nid ."\" class=\"section-$depth\">\n"; 799 $output .= "<h1 class=\"book-heading\">". check_plain($node->title) ."</h1>\n"; 800 $output .= drupal_render($node->content); 801 802 return $output; 803 } 804 805 /** 806 * Finishes up generation of printer-friendly HTML after visiting a 807 * node. This function is a 'post-node' visitor function for 808 * book_recurse(). 809 */ 810 function book_node_visitor_html_post($node, $depth) { 811 return "</div>\n"; 812 } 813 814 function _book_admin_table($nodes = array()) { 815 $form = array( 816 '#theme' => 'book_admin_table', 817 '#tree' => TRUE, 818 ); 819 820 foreach ($nodes as $node) { 821 $form = array_merge($form, _book_admin_table_tree($node, 0)); 822 } 823 824 return $form; 825 } 826 827 function _book_admin_table_tree($node, $depth) { 828 $form = array(); 829 830 $form[] = array( 831 'nid' => array('#type' => 'value', '#value' => $node->nid), 832 'depth' => array('#type' => 'value', '#value' => $depth), 833 'title' => array( 834 '#type' => 'textfield', 835 '#default_value' => $node->title, 836 '#maxlength' => 255, 837 ), 838 'weight' => array( 839 '#type' => 'weight', 840 '#default_value' => $node->weight, 841 '#delta' => 15, 842 ), 843 ); 844 845 $children = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d ORDER BY b.weight, n.title'), $node->nid); 846 while ($child = db_fetch_object($children)) { 847 $form = array_merge($form, _book_admin_table_tree(node_load($child->nid), $depth + 1)); 848 } 849 850 return $form; 851 } 852 853 function theme_book_admin_table($form) { 854 $header = array(t('Title'), t('Weight'), array('data' => t('Operations'), 'colspan' => '3')); 855 856 $rows = array(); 857 foreach (element_children($form) as $key) { 858 $nid = $form[$key]['nid']['#value']; 859 $pid = $form[0]['nid']['#value']; 860 $rows[] = array( 861 '<div style="padding-left: '. (25 * $form[$key]['depth']['#value']) .'px;">'. drupal_render($form[$key]['title']) .'</div>', 862 drupal_render($form[$key]['weight']), 863 l(t('view'), 'node/'. $nid), 864 l(t('edit'), 'node/'. $nid .'/edit'), 865 l(t('delete'), 'node/'. $nid .'/delete', NULL, 'destination=admin/content/book'. (arg(3) == 'orphan' ? '/orphan' : '') . ($pid != $nid ? '/'.$pid : '')) 866 ); 867 } 868 869 return theme('table', $header, $rows); 870 } 871 872 /** 873 * Display an administrative view of the hierarchy of a book. 874 */ 875 function book_admin_edit($nid) { 876 $node = node_load($nid); 877 if ($node->nid) { 878 drupal_set_title(check_plain($node->title)); 879 $form = array(); 880 881 $form['table'] = _book_admin_table(array($node)); 882 $form['save'] = array( 883 '#type' => 'submit', 884 '#value' => t('Save book pages'), 885 ); 886 887 return $form; 888 } 889 else { 890 drupal_not_found(); 891 } 892 } 893 894 /** 895 * Menu callback; displays a listing of all orphaned book pages. 896 */ 897 function book_admin_orphan() { 898 $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, n.status, b.parent FROM {node} n INNER JOIN {book} b ON n.vid = b.vid')); 899 900 $pages = array(); 901 while ($page = db_fetch_object($result)) { 902 $pages[$page->nid] = $page; 903 } 904 905 $orphans = array(); 906 if (count($pages)) { 907 foreach ($pages as $page) { 908 if ($page->parent && empty($pages[$page->parent])) { 909 $orphans[] = node_load($page->nid); 910 } 911 } 912 } 913 914 if (count($orphans)) { 915 $form['table'] = _book_admin_table($orphans); 916 $form['save'] = array( 917 '#type' => 'submit', 918 '#value' => t('Save book pages'), 919 ); 920 921 } 922 else { 923 $form['error'] = array('#value' => '<p>'. t('There are no orphan pages.') .'</p>'); 924 } 925 $form['#base'] = 'book_admin_edit'; 926 return $form; 927 } 928 929 function book_admin_edit_submit($form_id, $form_values) { 930 foreach ($form_values['table'] as $row) { 931 $node = node_load($row['nid']); 932 933 if ($row['title'] != $node->title || $row['weight'] != $node->weight) { 934 $node->title = $row['title']; 935 $node->weight = $row['weight']; 936 937 node_save($node); 938 watchdog('content', t('%type: updated %title.', array('%type' => t('book'), '%title' => $node->title)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid)); 939 } 940 } 941 942 if (is_numeric(arg(3))) { 943 // Updating pages in a single book. 944 $book = node_load(arg(3)); 945 drupal_set_message(t('Updated book %title.', array('%title' => $book->title))); 946 } 947 else { 948 // Updating the orphan pages. 949 drupal_set_message(t('Updated orphan book pages.')); 950 } 951 } 952 953 /** 954 * Menu callback; displays the book administration page. 955 */ 956 function book_admin($nid = 0) { 957 if ($nid) { 958 return drupal_get_form('book_admin_edit', $nid); 959 } 960 else { 961 return book_admin_overview(); 962 } 963 } 964 965 /** 966 * Returns an administrative overview of all books. 967 */ 968 function book_admin_overview() { 969 $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = 0 ORDER BY b.weight, n.title')); 970 while ($book = db_fetch_object($result)) { 971 $rows[] = array(l($book->title, "node/$book->nid"), l(t('outline'), "admin/content/book/$book->nid")); 972 } 973 $headers = array(t('Book'), t('Operations')); 974 975 return theme('table', $headers, $rows); 976 } 977 978 /** 979 * Implementation of hook_help(). 980 */ 981 function book_help($section) { 982 switch ($section) { 983 case 'admin/help#book': 984 $output = '<p>'. t('The <em>book</em> 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>'; 985 $output .= '<p>'. t('Book pages have navigation elements at the bottom of the page for moving through the text. These link to the previous and next pages in the book, as well as a link labeled <em>up</em>, leading to the level above in the structure. More comprehensive navigation may be provided by enabling the <em>book navigation block</em> on the <a href="@admin-block">block administration page</a>.', array('@admin-block' => url('admin/build/block'))) .'</p>'; 986 $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>'; 987 $output .= '<p>'. t("Posts of type %book are automatically added to the book hierarchy. Users with the <em>outline posts in books</em> permission can also add content of any other type to a book, placing it into the existing book structure through the interface that's available by clicking on the <em>outline</em> tab while viewing that post.", array('%book' => node_get_types('name', 'book'))) .'</p>'; 988 $output .= '<p>'. t('Administrators can view a list of all books on the <a href="@admin-node-book">book administration page</a>. In this list there is a link to an outline page for each book, from which is it possible to change the titles of sections, or to change their weight, thus reordering sections. From this administrative interface, it is also possible to determine whether there are any orphan pages - pages that have become disconnected from the rest of the book structure.', array('@admin-node-book' => url('admin/content/book'))) .'</p>'; 989 $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="@book">Book page</a>.', array('@book' => 'http://drupal.org/handbook/modules/book/')) .'</p>'; 990 return $output; 991 case 'admin/content/book': 992 return '<p>'. t('The book module offers a means to organize content, authored by many users, in an online manual, outline or FAQ.') .'</p>'; 993 case 'admin/content/book/orphan': 994 return '<p>'. t('Pages in a book are like a tree. As pages are edited, reorganized and removed, child pages might be left with no link to the rest of the book. Such pages are referred to as "orphan pages". On this page, administrators can review their books for orphans and reattach those pages as desired.') .'</p>'; 995 } 996 997 if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'outline') { 998 return '<p>'. t('The outline feature allows you to include posts in the <a href="@book">book hierarchy</a>.', array('@book' => url('book'))) .'</p>'; 999 } 1000 } 1001 1002
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Fri Nov 30 16:20:15 2007 | par Balluche grâce à PHPXref 0.7 |
![]() |