summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2005-06-05 10:52:04 +0000
committerDries Buytaert <dries@buytaert.net>2005-06-05 10:52:04 +0000
commit78cb173247c158e4362e67df8cb2acd397b2d6f0 (patch)
tree086e8c5856a4a4ca83a2f26167992a5d33ab044f
parentefdd76ad3db1407def398bb1c5aaab3876fec212 (diff)
downloadbrdo-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.module245
-rw-r--r--modules/book/book.module245
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)) {