Index: book.module =================================================================== RCS file: /cvs/drupal/drupal/modules/book.module,v retrieving revision 1.298 diff -u -F^f -r1.298 book.module --- book.module 21 May 2005 09:59:15 -0000 1.298 +++ book.module 3 Jun 2005 08:21:39 -0000 @@ -1,5 +1,5 @@ nid"); } $links[] = l(t('printer-friendly version'), 'book/print/'. $node->nid, array('title' => t('Show a printer-friendly version of this book page and its sub-pages.'))); + $links[] = l(t('export XML'), 'book/export/'. $node->nid, array('title' => t('Export this book page and its sub-pages as XML.'))); } } @@ -90,6 +91,12 @@ function book_menu($may_cache) { 'callback' => 'book_render', 'access' => user_access('access content'), 'type' => MENU_SUGGESTED_ITEM); + $items[] = array( + 'path' => 'book/export', + 'title' => t('export XML'), + 'callback' => 'book_export', + 'access' => user_access('access content'), + 'type' => MENU_CALLBACK); $items[] = array('path' => 'book/print', 'title' => t('printer-friendly version'), 'callback' => 'book_print', 'access' => user_access('access content'), @@ -324,6 +331,9 @@ function book_location($node, $nodes = a return $nodes; } +/** + * Accumulates the nodes up to the root of the book from the given node in the $nodes array. + */ function book_location_down($node, $nodes = array()) { $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.nid = b.nid WHERE b.parent = %d ORDER BY b.weight DESC, n.title DESC'), $node->nid)); if ($last_direct_child) { @@ -334,7 +344,7 @@ function book_location_down($node, $node } /** - * Fetch the node object of the previous page of the book. + * Fetches the node object of the previous page of the book. */ function book_prev($node) { // If the parent is zero, we are at the start of a book so there is no previous. @@ -358,7 +368,7 @@ function book_prev($node) { } /** - * Fetch the node object of the next page of the book. + * Fetches the node object of the next page of the book. */ function book_next($node) { // get first direct child @@ -378,6 +388,12 @@ function book_next($node) { } } +/** + * Returns the content of a given node. If $teaser if true, returns + * the teaser rather than full content. Displays the most recently + * approved revision of a node (if any) unless we have to display this + * page in the context of the moderation queue. + */ function book_content($node, $teaser = FALSE) { $op = $_POST['op']; @@ -500,6 +516,9 @@ function theme_book_navigation($node) { return $node; } +/** + * This is a helper function for book_toc(). + */ function book_toc_recurse($nid, $indent, $toc, $children, $exclude) { if ($children[$nid]) { foreach ($children[$nid] as $foo => $node) { @@ -513,6 +532,9 @@ function book_toc_recurse($nid, $indent, return $toc; } +/** + * Returns an array of titles and nid entries of book pages in table of contents order. + */ function book_toc($exclude = 0) { $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 ORDER BY b.weight, n.title')); @@ -536,6 +558,9 @@ function book_toc($exclude = 0) { return $toc; } +/** + * This is a helper function for book_tree() + */ function book_tree_recurse($nid, $depth, $children, $unfold = array()) { if ($depth > 0) { if ($children[$nid]) { @@ -566,6 +591,10 @@ function book_tree_recurse($nid, $depth, return $output; } +/** + * Returns an HTML nested list (wrapped in a menu-class div) representing the book nodes + * as a tree. + */ function book_tree($parent = 0, $depth = 3, $unfold = array()) { $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND n.moderate = 0 ORDER BY b.weight, n.title')); @@ -590,44 +619,58 @@ function book_render() { } /** - * Menu callback; generates printer-friendly book page with all descendants. + * Menu callback; generates a printer-friendly book page with all descendants. */ function book_print($nid = 0, $depth = 1) { global $base_url; - $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND n.nid = %d AND n.moderate = 0 ORDER BY b.weight, n.title'), $nid); - while ($page = db_fetch_object($result)) { - // load the node: - $node = node_load(array('nid' => $page->nid)); + $output .= book_recurse($nid, $depth, 'book_node_visitor_print_pre', 'book_node_visitor_print_post'); - if ($node) { - // output the content: - if (node_hook($node, 'content')) { - $node = node_invoke($node, 'content'); - } - // Allow modules to change $node->body before viewing. - node_invoke_nodeapi($node, 'view', $node->body, false); - - $output .= '

'. check_plain($node->title) .'

'; + $html = ''; + $html .= ''; - if ($node->body) { - $output .= $node->body; - } - } - } + $html .= "\n". check_plain($node->title) ."\n"; + $html .= ''; + $html .= '' . "\n"; + $html .= "\n"; + $html .= "\n\n". $output . "\n\n\n"; - $output .= book_print_recurse($nid, $depth + 1); + print $html; +} - $html = ''. check_plain($node->title) .''; - $html .= ''; - $html .= theme_stylesheet_import('misc/print.css', 'print'); - $html .= ''. $output .''; +/** + * Menu callback; generates XML output of entire book hierarchy beneath + * the given node. + */ +function book_export($nid = 0, $depth = 1) { + $xml = "\n"; + $xml .= "\n"; + $xml .= book_recurse($nid, $depth, 'book_node_visitor_xml_pre', 'book_node_visitor_xml_post'); + $xml .= "\n"; + print $xml; - print $html; } -function book_print_recurse($parent = '', $depth = 1) { - $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND b.parent = %d AND n.moderate = 0 ORDER BY b.weight, n.title'), $parent); +/** + * Traverses the book tree. Applies the $visit_pre() callback to each + * node, is called recursively for each child of the node (in weight, + * title order). Finally appends the output of the $visit_post() + * callback to the output before returning the generated output. + * + * @param nid + * - the node id (nid) of the root node of the book hierarchy. + * @param depth + * - the depth of the given node in the book hierarchy. + * @param visit_pre + * - a function callback to be called upon visiting a node in the tree + * @param visit_post + * - a function callback to be called after visiting a node in the tree, + * but before recursively visiting children. + * @return + * - the output generated in visiting each node + */ +function book_recurse($nid = 0, $depth = 1, $visit_pre, $visit_post) { + $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND n.nid = %d AND n.moderate = 0 ORDER BY b.weight, n.title'), $nid); while ($page = db_fetch_object($result)) { // Load the node: @@ -639,26 +682,132 @@ function book_print_recurse($parent = '' } if ($node) { - // Output the content: - if (node_hook($node, 'content')) { - $node = node_invoke($node, 'content'); + if (function_exists($visit_pre)) { + $output .= call_user_func($visit_pre, $node, $depth, $nid); } - // Allow modules to change $node->body before viewing. - node_invoke_nodeapi($node, 'view', $node->body, false); - - $output .= '

'. check_plain($node->title) .'

'; - - if ($node->body) { - $output .= ''; + else { # default + $output .= book_node_visitor_print_pre($node, $depth, $nid); } - $output .= book_print_recurse($node->nid, $depth + 1); + $children = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND b.parent = %d AND n.moderate = 0 ORDER BY b.weight, n.title'), $node->nid); + while ($childpage = db_fetch_object($children)) { + $childnode = node_load(array('nid' => $childpage->nid)); + if ($childnode->nid != $node->nid) { + $output .= book_recurse($childnode->nid, $depth+1, $visit_pre, $visit_post); + } + } + if (function_exists($visit_post)) { + $output .= call_user_func($visit_post, $node); + } + else { # default + $output .= book_node_visitor_print_post(); + } } } return $output; } +/** + * Generates printer-friendly HTML for a node. This function + * is a 'pre-node' visitor function for book_recurse(). + * + * @param $node + * - the node to generate output for. + * @param $depth + * - the depth of the given node in the hierarchy. This + * is used only for generating output. + * @param $nid + * - the node id (nid) of the given node. This + * is used only for generating output. + * @return + * - the HTML generated for the given node. + */ +function book_node_visitor_print_pre($node, $depth, $nid) { + // Output the content: + if (node_hook($node, 'content')) { + $node = node_invoke($node, 'content'); + } + // Allow modules to change $node->body before viewing. + node_invoke_nodeapi($node, 'view', $node->body, false); + + $output .= '
'."\n"; + $output .= '

'. check_plain($node->title) ."

\n"; + + if ($node->body) { + $output .= $node->body; + } + return $output; +} + +/** + * Finishes up generation of printer-friendly HTML after visiting a + * node. This function is a 'post-node' visitor function for + * book_recurse(). + */ +function book_node_visitor_print_post() { + return "
\n"; +} + +/** + * Generates XML for a given node. This function is a 'pre-node' + * visitor function for book_recurse(). The generated XML is + * DocBook-like - the node's HTML content wrapped in a CDATA + * processing instruction, and put inside a tag. The + * node body has an md5-hash applied; the value of this is stored as + * node metadata to allow importing code to determine if contents have + * changed. + * + * @param $node + * - the node to generate output for. + * @param $depth + * - the depth of the given node in the hierarchy. This + * is currently not used. + * @param $nid + * - the node id (nid) of the given node. This + * is used only for generating output (e.g., ID attribute) + * @return + * - the generated XML for the given node. + */ +function book_node_visitor_xml_pre($node, $depth, $nid) { + // Output the content: + if (node_hook($node, 'content')) { + $node = node_invoke($node, 'content'); + } + // Allow modules to change $node->body before viewing. + node_invoke_nodeapi($node, 'view', $node->body, false); + + $output .= '
'."\n"; + $output .= "\n"; + $output .= "\n"; + $output .= "md5-hash:" . md5($node->body) . "\n"; + $output .= "weight:". $node->weight . "\n"; + $output .= "\n"; + $output .= "\n"; + $output .= ''. check_plain($node->title) ."\n"; + // wrap the node body in a CDATA declaration + $output .= ""; + $output .= "body) { + $output .= $node->body; + } + $output .= "]]>"; + $output .= "\n"; + return $output; +} + +/** + * Completes the XML generated for the node. This + * function is a 'post-node' visitor function for + * book_recurse(). + */ +function book_node_visitor_xml_post() { + return "
\n"; +} + +/** + * Creates a row for the 'admin' view of a book. Each row represents a page in the book, in the tree representing the book + */ function book_admin_edit_line($node, $depth = 0) { return array('
'. form_textfield(NULL, $node->nid .'][title', $node->title, 64, 255) .'
', form_weight(NULL, $node->nid .'][weight', $node->weight, 15), l(t('view'), 'node/'. $node->nid), l(t('edit'), 'node/'. $node->nid .'/edit'), l(t('delete'), 'node/'.$node->nid.'/delete')); } @@ -666,6 +815,8 @@ function book_admin_edit_line($node, $de function book_admin_edit_book($nid, $depth = 1) { $result = db_query(db_rewrite_sql('SELECT n.nid FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = %d ORDER BY b.weight, n.title'), $nid); + $rows = array(); + while ($node = db_fetch_object($result)) { $node = node_load(array('nid' => $node->nid)); $rows[] = book_admin_edit_line($node, $depth); @@ -696,6 +847,9 @@ function book_admin_edit($nid, $depth = } } +/** + * Saves the changes to a book made by an administrator in the book admin view. + */ function book_admin_save($nid, $edit = array()) { if ($nid) { $book = node_load(array('nid' => $nid)); @@ -765,6 +919,9 @@ function book_admin($nid = 0) { } } +/** + * Returns an administrative overview of all books. + */ function book_admin_overview() { $result = db_query(db_rewrite_sql('SELECT n.nid, n.title FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = 0 ORDER BY b.weight, n.title')); while ($book = db_fetch_object($result)) {