diff options
author | Dries Buytaert <dries@buytaert.net> | 2005-06-05 10:52:04 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2005-06-05 10:52:04 +0000 |
commit | 78cb173247c158e4362e67df8cb2acd397b2d6f0 (patch) | |
tree | 086e8c5856a4a4ca83a2f26167992a5d33ab044f | |
parent | efdd76ad3db1407def398bb1c5aaab3876fec212 (diff) | |
download | brdo-78cb173247c158e4362e67df8cb2acd397b2d6f0.tar.gz brdo-78cb173247c158e4362e67df8cb2acd397b2d6f0.tar.bz2 |
- Modified version of patch #1482 by Djun:
This patch enables export of books as XML documents.
The XML is DocBook "at the level of structure", but
node contents are wrapped as CDATA, since we
can't be sure that the contents are valid XML.
Several other bugs/feature requests are also
addressed with this patch:
- Fixes bugs
http://drupal.org/node/1898
http://drupal.org/node/1482
http://drupal.org/node/8049
http://drupal.org/node/1899
Should go a long way towards implementing feature request
http://drupal.org/node/2062
It should also be easy to extend this to produce OPML,
for example.
- Adds about 170 lines, of which more than 100 are comments
- Added doxygen comments
- Made doxygen comment format consistent; fixed minor grammatical slips
- A proper Doctype and more informative HTML element is generated
for printer-friendly HTML output.
- Refactored book_print() to use book_recurse().
- Refactored book_recurse(). Applies 'visitor' callback functions to nodes
during weight/title order tree-traversal. The parameterized
visitor callbacks can be used to generate different kinds of output.
There are many other kinds of operations on books which can be implemented
by writing a pre-node/post-node pair of callback functions: word-count/
statistics gathering, comparison, copying, search and replace...
- Introduced book_export() which uses book_recurse() to generate
DocBook-like XML to export book contents in a structured form.
An md5 hash is computed for each node to help import code to
decide if a node needs to be updated or not.
-rw-r--r-- | modules/book.module | 245 | ||||
-rw-r--r-- | modules/book/book.module | 245 |
2 files changed, 402 insertions, 88 deletions
diff --git a/modules/book.module b/modules/book.module index abb79bc40..1f3d1df0a 100644 --- a/modules/book.module +++ b/modules/book.module @@ -60,7 +60,8 @@ function book_link($type, $node = 0, $main = 0) { if (book_access('create', $node)) { $links[] = l(t('add child page'), "node/add/book/parent/$node->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('printer-friendly version'), 'book/export/html/'. $node->nid, array('title' => t('Show a printer-friendly version of this book page and its sub-pages.'))); + $links[] = l(t('export as XML'), 'book/export/docbook/'. $node->nid, array('title' => t('Export this book page and its sub-pages as Docbook-like XML.'))); } } @@ -90,8 +91,14 @@ function book_menu($may_cache) { 'callback' => 'book_render', 'access' => user_access('access content'), 'type' => MENU_SUGGESTED_ITEM); - $items[] = array('path' => 'book/print', 'title' => t('printer-friendly version'), - 'callback' => 'book_print', + $items[] = array( + 'path' => 'book/export/docbook', + 'title' => t('export XML'), + 'callback' => 'book_export_docbook', + 'access' => user_access('access content'), + 'type' => MENU_CALLBACK); + $items[] = array('path' => 'book/export/printer', 'title' => t('printer-friendly version'), + 'callback' => 'book_export_html', 'access' => user_access('access content'), 'type' => MENU_CALLBACK); } @@ -324,6 +331,9 @@ function book_location($node, $nodes = array()) { 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, $nodes = array()) { } /** - * 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, $toc, $children, $exclude) { 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, $children, $unfold = array()) { 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) { +function book_export_html($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 .= '<h1 id="'. $node->nid .'" name="'. $node->nid .'" class="book-h'. $depth .'">'. check_plain($node->title) .'</h1>'; + $html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'; + $html .= '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">'; - if ($node->body) { - $output .= $node->body; - } - } - } + $html .= "<head>\n<title>". check_plain($node->title) ."</title>\n"; + $html .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'; + $html .= '<base href="'. $base_url .'/" />' . "\n"; + $html .= "<style type=\"text/css\">\n@import url(misc/print.css);\n</style>\n"; + $html .= "</head>\n<body>\n". $output . "\n</body>\n</html>\n"; - $output .= book_print_recurse($nid, $depth + 1); + print $html; +} - $html = '<html><head><title>'. check_plain($node->title) .'</title>'; - $html .= '<base href="'. $base_url .'/" />'; - $html .= theme_stylesheet_import('misc/print.css', 'print'); - $html .= '</head><body>'. $output .'</body></html>'; +/** + * Menu callback; generates XML output of entire book hierarchy beneath + * the given node. + */ +function book_export_docbook($nid = 0, $depth = 1) { + $xml = "<?xml version='1.0'?>\n"; + $xml .= "<book>\n"; + $xml .= book_recurse($nid, $depth, 'book_node_visitor_xml_pre', 'book_node_visitor_xml_post'); + $xml .= "</book>\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 = '', $depth = 1) { } 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 .= '<h1 id="'. $node->nid .'" name="'. $node->nid .'" class="book-h'. $depth .'">'. check_plain($node->title) .'</h1>'; - - if ($node->body) { - $output .= '<ul>'. $node->body .'</ul>'; + 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 .= '<div id="node-'.$node->nid. '" class="section-'.$depth.'">'."\n"; + $output .= '<h1 class="book-heading">'. check_plain($node->title) ."</h1>\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 "</div>\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 <literallayout> 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 .= '<section id="node-'.$node->nid .'">'."\n"; + $output .= "<sectioninfo>\n"; + $output .= "<releaseinfo>\n"; + $output .= "md5-hash:" . md5($node->body) . "\n"; + $output .= "weight:". $node->weight . "\n"; + $output .= "</releaseinfo>\n"; + $output .= "</sectioninfo>\n"; + $output .= '<title>'. check_plain($node->title) ."</title>\n"; + // wrap the node body in a CDATA declaration + $output .= "<literallayout>"; + $output .= "<![CDATA["; + if ($node->body) { + $output .= $node->body; + } + $output .= "]]>"; + $output .= "</literallayout>\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 "</section>\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('<div style="padding-left: '. (25 * $depth) .'px;">'. form_textfield(NULL, $node->nid .'][title', $node->title, 64, 255) .'</div>', 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, $depth = 0) { 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 = 0) { } } +/** + * 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)) { diff --git a/modules/book/book.module b/modules/book/book.module index abb79bc40..1f3d1df0a 100644 --- a/modules/book/book.module +++ b/modules/book/book.module @@ -60,7 +60,8 @@ function book_link($type, $node = 0, $main = 0) { if (book_access('create', $node)) { $links[] = l(t('add child page'), "node/add/book/parent/$node->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('printer-friendly version'), 'book/export/html/'. $node->nid, array('title' => t('Show a printer-friendly version of this book page and its sub-pages.'))); + $links[] = l(t('export as XML'), 'book/export/docbook/'. $node->nid, array('title' => t('Export this book page and its sub-pages as Docbook-like XML.'))); } } @@ -90,8 +91,14 @@ function book_menu($may_cache) { 'callback' => 'book_render', 'access' => user_access('access content'), 'type' => MENU_SUGGESTED_ITEM); - $items[] = array('path' => 'book/print', 'title' => t('printer-friendly version'), - 'callback' => 'book_print', + $items[] = array( + 'path' => 'book/export/docbook', + 'title' => t('export XML'), + 'callback' => 'book_export_docbook', + 'access' => user_access('access content'), + 'type' => MENU_CALLBACK); + $items[] = array('path' => 'book/export/printer', 'title' => t('printer-friendly version'), + 'callback' => 'book_export_html', 'access' => user_access('access content'), 'type' => MENU_CALLBACK); } @@ -324,6 +331,9 @@ function book_location($node, $nodes = array()) { 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, $nodes = array()) { } /** - * 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, $toc, $children, $exclude) { 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, $children, $unfold = array()) { 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) { +function book_export_html($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 .= '<h1 id="'. $node->nid .'" name="'. $node->nid .'" class="book-h'. $depth .'">'. check_plain($node->title) .'</h1>'; + $html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'; + $html .= '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">'; - if ($node->body) { - $output .= $node->body; - } - } - } + $html .= "<head>\n<title>". check_plain($node->title) ."</title>\n"; + $html .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'; + $html .= '<base href="'. $base_url .'/" />' . "\n"; + $html .= "<style type=\"text/css\">\n@import url(misc/print.css);\n</style>\n"; + $html .= "</head>\n<body>\n". $output . "\n</body>\n</html>\n"; - $output .= book_print_recurse($nid, $depth + 1); + print $html; +} - $html = '<html><head><title>'. check_plain($node->title) .'</title>'; - $html .= '<base href="'. $base_url .'/" />'; - $html .= theme_stylesheet_import('misc/print.css', 'print'); - $html .= '</head><body>'. $output .'</body></html>'; +/** + * Menu callback; generates XML output of entire book hierarchy beneath + * the given node. + */ +function book_export_docbook($nid = 0, $depth = 1) { + $xml = "<?xml version='1.0'?>\n"; + $xml .= "<book>\n"; + $xml .= book_recurse($nid, $depth, 'book_node_visitor_xml_pre', 'book_node_visitor_xml_post'); + $xml .= "</book>\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 = '', $depth = 1) { } 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 .= '<h1 id="'. $node->nid .'" name="'. $node->nid .'" class="book-h'. $depth .'">'. check_plain($node->title) .'</h1>'; - - if ($node->body) { - $output .= '<ul>'. $node->body .'</ul>'; + 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 .= '<div id="node-'.$node->nid. '" class="section-'.$depth.'">'."\n"; + $output .= '<h1 class="book-heading">'. check_plain($node->title) ."</h1>\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 "</div>\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 <literallayout> 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 .= '<section id="node-'.$node->nid .'">'."\n"; + $output .= "<sectioninfo>\n"; + $output .= "<releaseinfo>\n"; + $output .= "md5-hash:" . md5($node->body) . "\n"; + $output .= "weight:". $node->weight . "\n"; + $output .= "</releaseinfo>\n"; + $output .= "</sectioninfo>\n"; + $output .= '<title>'. check_plain($node->title) ."</title>\n"; + // wrap the node body in a CDATA declaration + $output .= "<literallayout>"; + $output .= "<![CDATA["; + if ($node->body) { + $output .= $node->body; + } + $output .= "]]>"; + $output .= "</literallayout>\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 "</section>\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('<div style="padding-left: '. (25 * $depth) .'px;">'. form_textfield(NULL, $node->nid .'][title', $node->title, 64, 255) .'</div>', 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, $depth = 0) { 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 = 0) { } } +/** + * 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)) { |