? book_manager_d7.patch
Index: book_manager.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/book_manager/book_manager.info,v
retrieving revision 1.1
diff -u -p -r1.1 book_manager.info
--- book_manager.info	31 Oct 2008 00:34:50 -0000	1.1
+++ book_manager.info	23 Jan 2010 23:42:32 -0000
@@ -2,5 +2,5 @@
 name = Book Manager
 description = Extends the book module to provide personal books that can be maintained by the book owner.
 dependencies[] = book
-core = 6.x
-version = "6.x-1.0"
+core = 7.x
+files[] = book_manager.module
\ No newline at end of file
Index: book_manager.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/book_manager/book_manager.module,v
retrieving revision 1.1
diff -u -p -r1.1 book_manager.module
--- book_manager.module	31 Oct 2008 00:34:50 -0000	1.1
+++ book_manager.module	23 Jan 2010 23:42:32 -0000
@@ -1,13 +1,16 @@
 <?php
 // $Id: book_manager.module,v 1.1 2008/10/31 00:34:50 jgraham Exp $
-// @file
-define('BOOK_MANAGER_IS_BOOK', 1);          // indicates this node is the top-level in a book (includes personal books)
-define('BOOK_MANAGER_IS_IN_BOOK', 2);       // indicates this node is in A book (includes personal books)
-define('BOOK_MANAGER_IS_IN_PERSONAL', 4);   // indicates this node is in a personal book
+/**
+ * @file
+ * Allows users to create "personal books."
+ */
+define('BOOK_MANAGER_IS_BOOK',        1); // indicates this node is the top-level in a book (includes personal books)
+define('BOOK_MANAGER_IS_IN_BOOK',     2); // indicates this node is in A book (includes personal books)
+define('BOOK_MANAGER_IS_IN_PERSONAL', 4); // indicates this node is in a personal book
 
 /**
  * Menu callback; prints a listing of all personal books.
- * 
+ *
  * @param $user
  *   User whose personal books are being rendered.  If NULL, show all personal books.
  */
@@ -15,7 +18,7 @@ function book_manager_render($user = NUL
   if ($user) {
     drupal_set_title(t('Personal books for !name', array('!name' => $user->name)));
   }
-  
+
   $header = array(t('Book'), t('Author'));
   $rows = array();
   foreach (book_manager_get_books($user) as $book) {
@@ -24,28 +27,11 @@ function book_manager_render($user = NUL
     $row[] = theme('username', $book);
     $rows[] = $row;
   }
-  return theme('table', $header, $rows, array('id' => 'personal-books'));
-}
-
-/**
- * Implementatin of hook_link()
- */
-function book_manager_link($type, $node = NULL, $teaser = FALSE) {
-  global $user;
-  $links = array();
-  
-  if ($type == 'node' && isset($node->book) && !$teaser) {
-    $book = node_load($node->book['bid']);
-
-    if (_book_manager_outline_access($book)) {
-      $links['book_manager_outline'] = array(
-        'title' => t('Customize Book'),
-        'href' => "personal-outline/". $book->nid,
-      );
-    }
-    
-  }
-  return $links;
+  return theme('table', array(
+    'header' => $header,
+    'data'   => $rows,
+    'attributes' => array('id' => 'personal-books'),
+  ));
 }
 
 /**
@@ -53,27 +39,38 @@ function book_manager_link($type, $node 
  *
  * This list may be used for generating a list of all the books, or for building
  * the options for a form select.
- * 
+ *
  * @param $user
  *   User object whose personal books are being rendered.  Defaults to 0 for all personal books.
  */
 function book_manager_get_books($user = NULL) {
+  $result = $user
+    ? db_query("SELECT pb.bid FROM {book_manager} pb JOIN {node} n ON pb.bid = n.nid WHERE n.uid = $uid",
+      array(':uid' => $user->uid), array('fetch' => PDO::FETCH_ASSOC))->execute()
+    : db_query("SELECT pb.bid FROM {book_manager} pb", array(), array('fetch' => PDO::FETCH_ASSOC))->execute();
+
   $books = array();
-  if ($user) {
-    $result = db_query("SELECT pb.bid FROM {book_manager} pb JOIN {node} n ON pb.bid = n.nid WHERE n.uid = %d", $user->uid);
-  }
-  else {
-    $result = db_query("SELECT pb.bid FROM {book_manager} pb");
-  }
   $nids = array();
-  while ($book = db_fetch_array($result)) {
+  foreach ($result as $book) {
     $nids[] = $book['bid'];
   }
   if ($nids) {
     // This differs from the book module's query -- we added n.uid, which should be the same as book_manager.uid,
     // and u.name for the owner's name
-    $result2 = db_query(db_rewrite_sql("SELECT n.type, n.title, n.uid, u.name, b.*, ml.* FROM {book} b INNER JOIN {node} n ON b.nid = n.nid INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {menu_links} ml ON b.mlid = ml.mlid WHERE n.nid IN (". implode(',', $nids) .") AND n.status = 1 ORDER BY ml.weight, ml.link_title"));
-    while ($book = db_fetch_object($result2)) {
+    $q = db_select('book', 'b');
+    $q->join('node',       'n',  'b.nid  = n.nid');
+    $q->join('users',      'u',  'n.uid  = u.uid');
+    $q->join('menu_links', 'ml', 'b.mlid = ml.mlid');
+    $q->fields('b')
+      ->fields('n', array('type', 'title', 'uid'))
+      ->addFields('u.name')
+      ->fields('ml')
+      ->condition('n.status', 1)->condition('n.nid', $nids, 'IN')
+      ->orderBy('ml.weight')->orderBy('ml.link_title')
+      ->addTag('node_access');
+    $result2 = $q->execute();
+
+    foreach ($result2 as $book) {
       $book->href = $book->link_path;
       $book->options = unserialize($book->options);
       $books[$book->bid] = $book;
@@ -83,180 +80,280 @@ function book_manager_get_books($user = 
 }
 
 /**
- * Implementation of hook_perm()
+ * Implement hook_permission().
  */
-function book_manager_perm() {
-  return array('add content to personal books', 'create personal books');
+function book_manager_permission() {
+  return array(
+    'add content to personal books' => array('title' => t('Add content to personal books')),
+    'create personal books' => array('title' => t('Create personal books')),
+  );
 }
 
 /**
- * Implementation of hook_menu().
+ * Implement hook_menu().
  */
 function book_manager_menu() {
   $items['book_manager'] = array(
-    'title' => 'Book Manager',
-    'page callback' => 'book_manager_render',
-    'page arguments' => array(NULL),
+    'title'            => 'Book Manager',
+    'page callback'    => 'book_manager_render',
+    'page arguments'   => array(NULL),
     'access arguments' => array('access content'),
-    'type' => MENU_SUGGESTED_ITEM
+    'type'             => MENU_SUGGESTED_ITEM
   );
-  
+
   $items['book_manager/%user_uid_optional'] = array(
-    'title' => 'Book Manager',
-    'page callback' => 'book_manager_render',
-    'page arguments' => array(1),
+    'title'            => 'Book Manager',
+    'page callback'    => 'book_manager_render',
+    'page arguments'   => array(1),
     'access arguments' => array('access content'),
-    'type' => MENU_SUGGESTED_ITEM
+    'type'             => MENU_SUGGESTED_ITEM
   );
-  
+
   // we could adjust the access routine for books standard outline feature, but it is nested under admin
   $items['personal-outline/%node'] = array(
-    'title' => 'Re-order personal book pages',
-    'page callback' => 'book_manager_outline_edit',
-    'page arguments' => array(1),
-    'access callback' => '_book_manager_outline_access',
+    'title'            => 'Re-order personal book pages',
+    'page callback'    => 'book_manager_outline_edit',
+    'page arguments'   => array(1),
+    'access callback'  => '_book_manager_outline_access',
     'access arguments' => array(1),
-    'type' => MENU_CALLBACK,
+    'type'             => MENU_CALLBACK,
   );
 
   return $items;
 }
 
-function book_manager_menu_alter(&$callbacks) {
-  $callbacks['node/%node/outline']['access callback'] = '_book_manager_outline_access_menu_callback';
-  $callbacks['node/%node/outline/remove']['access callback'] = '_book_manager_outline_remove_access_menu_callback';
+/**
+ * Implement hook_menu_alter().
+ *
+ * @param array $callbacks
+ * @return void
+ */
+function book_manager_menu_alter(&$items) {
+  $items['node/%node/outline']['access callback'] = '_book_manager_outline_access_menu_callback';
+  $items['node/%node/outline/remove']['access callback'] = '_book_manager_outline_remove_access_menu_callback';
 }
 
 /**
- * This is a helper function used as a replacement callback for book's standard access to
- * 'node/%node/outline' 
+ * Helper function used as a replacement for _book_outline_access().
+ *
  * @see book_manager_menu_alter()
+ * @param object $node
+ * @return boolean
  */
 function _book_manager_outline_access_menu_callback($node) {
   return (_book_manager_outline_access($node) || _book_outline_access($node));
 }
 
 /**
- * This is a helper function used as a replacement callback for book's standard access to
- * 'node/%node/outline/remove' 
+ * Helper function used as a replacement for _book_outline_remove_access().
+ *
  * @see book_manager_menu_alter()
+ * @param object $node
+ * @return boolean
  */
 function _book_manager_outline_remove_access_menu_callback($node) {
   return (_book_manager_outline_remove_access($node) || _book_outline_remove_access($node));
 }
 
 /**
- * Menu item access callback - determine if the user should be able to outline the book the node belongs to
- * @param $node object the node in question 
+ * Menu access callback - is user able to outline the book the node belongs to ?
+ *
+ * @param $node object the node in question
+ *
+ * @FIXME param comment below does not seem to be related to code. Error in code or doc ?
  * @param $noadministeroutlines if TRUE then return TRUE only if the user does not have access to "administer book outlines"
+ *
+ * @return boolean
  */
 function _book_manager_outline_access($node) {
   global $user;
   // if this is in a personal book AND this is their node
-  if (book_type_is_allowed($node->type) && (($node->book_manager & BOOK_MANAGER_IS_IN_PERSONAL) && 
-      ((isset($node->book) && 
-      _book_manager_get_book_owner($node->book['bid']) && $node->uid == $user->uid)) ||
-      (!isset($node->book) && $node->uid == $user->uid && user_access('add content to personal books')))) {
-      return TRUE;
-  }
-  else {
-    return FALSE;
-  }
+  return (book_type_is_allowed($node->type) && (($node->book_manager & BOOK_MANAGER_IS_IN_PERSONAL) &&
+    ((isset($node->book) && _book_manager_get_book_owner($node->book['bid']) && $node->uid == $user->uid)) ||
+    (!isset($node->book) && $node->uid == $user->uid && user_access('add content to personal books'))
+    ));
 }
 
 /**
  * Menu item access callback - determine if the user can remove nodes from the outline.
+ *
+ * @return boolean
  */
 function _book_manager_outline_remove_access($node) {
   return isset($node->book) && ($node->book['bid'] != $node->nid) && _book_manager_outline_access($node);
 }
 
 /**
- * This function returns the uid of the author of the book corresponding to $bid
+ * Returns the uid of the author of the book corresponding to $bid.
+ *
  * @param int $bid The book id to find the author for
  * @return int The uid of the bid author or 0 if this is not a book
  */
 function _book_manager_get_book_owner($bid) {
-  if ($result = db_query("SELECT n.uid FROM {book} b LEFT JOIN {node} n ON n.nid = b.bid WHERE n.nid = %d GROUP BY n.nid", $bid)) {
-    $row = db_fetch_array($result);
-    return $row['uid'];
-  }
-  return 0;
+  $q = db_select('book', 'b');
+  $q->join('node', 'n', 'n.nid = b.bid');
+  $q->addField('n', 'uid');
+  $q->condition('n.nid', $bid);
+  $result = $q->execute();
+  return $result ? $result->fetchField() : 0;
 }
 
 /**
- * implementation of hook_nodeapi()
+ * Implement hook_node_view().
  */
-function book_manager_nodeapi(&$node, $op, $teaser, $page) {
+function book_manager_node_view($node = NULL, $view_mode) {
   global $user;
-  switch ($op) {
-  case 'load':
-    $node->book_manager = book_manager_status($node->nid);
-    break;
-  case 'prepare':
-    // this is necessary because book_nodeapi checks for book permissions and the parent select widget population 
-    // fails without this pre-loaded data
-    // Prepare defaults for the add/edit form.
-    if (empty($node->book) && (user_access('add content to personal books'))) {
-      $node->book = array();
-      if (empty($node->nid) && isset($_GET['parent']) && is_numeric($_GET['parent'])) {
-        // Handle "Add child page" links:
-        $parent = book_link_load($_GET['parent']);
-        if ($parent && $parent['access']) {
-          $node->book['bid'] = $parent['bid'];
-          $node->book['plid'] = $parent['mlid'];
-          $node->book['menu_name'] = $parent['menu_name'];
-        }
-      }
-      // Set defaults.
-      $node->book += _book_link_defaults(!empty($node->nid) ? $node->nid : 'new');
+  $links = array();
+
+  if (isset($node->book) && $view_mode != 'teaser') {
+    $book = node_load($node->book['bid']);
+
+    if (_book_manager_outline_access($book)) {
+      $links['book_manager_outline'] = array(
+        'title' => t('Customize Book'),
+        'href' => "personal-outline/". $book->nid,
+        'attributes' => array('title' => t("You are allowed to customize.")),
+      );
+      $node->content['links']['book-manager'] = array(
+        '#theme' => 'links',
+        '#links' => $links,
+        '#attributes' => array('class' => array('links', 'inline')),
+      );
     }
-    else {
-      if (isset($node->book['bid']) && !isset($node->book['original_bid'])) {
-        $node->book['original_bid'] = $node->book['bid'];
+  }
+
+  return $links;
+}
+
+/**
+ * Implement hook_node_load().
+ *
+ * @param array $nodes
+ * @param array $types
+ * @return void
+ */
+function book_manager_node_load($nodes, $types) {
+  foreach ($nodes as $node) {
+    $node->book_manager = book_manager_status($node->nid);
+  }
+}
+
+/**
+ * Implement hook_node_prepare().
+ *
+ * This is necessary because book_nodeapi checks for book permissions and the
+ * parent select widget population fails without this pre-loaded data
+ *
+ * @param object $node
+ * @return void
+ */
+function book_manager_node_prepare($node) {
+  // Prepare defaults for the add/edit form.
+  if (empty($node->book) && (user_access('add content to personal books'))) {
+    $node->book = array();
+    if (empty($node->nid) && isset($_GET['parent']) && is_numeric($_GET['parent'])) {
+      // Handle "Add child page" links:
+      $parent = book_link_load($_GET['parent']);
+      if ($parent && $parent['access']) {
+        $node->book['bid'] = $parent['bid'];
+        $node->book['plid'] = $parent['mlid'];
+        $node->book['menu_name'] = $parent['menu_name'];
       }
     }
-    // Find the depth limit for the parent select.
-    if (isset($node->book['bid']) && !isset($node->book['parent_depth_limit'])) {
-      $node->book['parent_depth_limit'] = _book_parent_depth_limit($node->book);
-    }
-    break;
-  case 'insert':
-  case 'update':
-    if ((!user_access('create new books') && user_access('create personal books')) || $node->book['ispersonal']) {
-      // TODO: I think this should cascade as well (think about the implications [if any] for users without book privileges.)
-      book_manager_make_personal($node, $user);
-    }
-    elseif (user_access('create new books') && !$form_state['values']['book']['ispersonal']) {
-      book_manager_make_personal($node, $user, FALSE);
+    // Set defaults.
+    $node->book += _book_link_defaults(!empty($node->nid) ? $node->nid : 'new');
+  }
+  else {
+    if (isset($node->book['bid']) && !isset($node->book['original_bid'])) {
+      $node->book['original_bid'] = $node->book['bid'];
     }
-    break;
-  case 'delete':
-    // we only need to do stuff if this was a top-level node in a personal book
-    // code partially taken from book module to determine where our top-level bid goes to.
-    if (!empty($node->book['bid']) && $node->book['bid'] = $node->nid && $node->book_manager & BOOK_MANAGER_IS_IN_PERSONAL) {
-      db_query("DELETE FROM {book_manager} WHERE bid = %d", $node->book['bid']);
-      $result = db_query("SELECT b.nid FROM {menu_links} ml INNER JOIN {book} b on b.mlid = ml.mlid WHERE ml.plid = %d", $node->book['mlid']);
-      while ($child = db_fetch_array($result)) {
-        $child_node = node_load($child['nid']);
-        book_manager_make_personal($child_node, $user);
-      }
+  }
+  // Find the depth limit for the parent select.
+  if (isset($node->book['bid']) && !isset($node->book['parent_depth_limit'])) {
+    $node->book['parent_depth_limit'] = _book_parent_depth_limit($node->book);
+  }
+}
+
+/**
+ * Implement hook_node_insert().
+ *
+ * @param object $node
+ * @return void
+ */
+function book_manager_node_insert($node) {
+  global $user;
+  if ((!user_access('create new books') && user_access('create personal books')) || $node->book['is-personal']) {
+    // TODO: I think this should cascade as well (think about the implications [if any] for users without book privileges.)
+    book_manager_make_personal($node, $user);
+  }
+  elseif (user_access('create new books') && !$node->book['is-personal']) {
+    book_manager_make_personal($node, $user, FALSE);
+  }
+}
+
+/**
+ * Implement hook_node_update().
+ *
+ * Our implementation uses the same processing for insert and update.
+ *
+ * @see book_manager_make_personal()
+ *
+ * @param object $node
+ * @return void
+ */
+function book_manager_node_update($node) {
+  book_manager_node_insert($node);
+}
+
+/**
+ * Implement hook_node_delete().
+ *
+ * We only need to do stuff if this was a top-level node in a personal book.
+ *
+ * Code partially taken from book module to determine where our top-level bid
+ * goes to.
+ *
+ * @param object $node
+ * @return void
+ */
+function book_manager_node_delete($node) {
+  global $user;
+
+  if (!empty($node->book['bid']) && $node->book['bid'] = $node->nid && $node->book_manager & BOOK_MANAGER_IS_IN_PERSONAL) {
+    db_delete('book_manager')
+      ->condition('bid', $node->book['bid'])
+      ->execute();
+
+    $result = db_query("SELECT b.nid FROM {menu_links} ml INNER JOIN {book} b on b.mlid = ml.mlid WHERE ml.plid = :mlid",
+      array(':mlid' => $node->book['mlid']), array(), array('fetch' => PDO::FETCH_ASSOC))
+      ->execute();
+
+    foreach ($result as $child) {
+      $child_node = node_load($child['nid']);
+      book_manager_make_personal($child_node, $user);
     }
   }
 }
 
 /**
- * Implementation of hook_theme()
+ * Implement hook_theme().
+ *
+ * @return array
  */
 function book_manager_theme() {
-  return array('book_manager_outline_table' => array(
-    'arguments' => array('form' => NULL),
-    ), 
+  return array(
+    'book_manager_outline_table' => array(
+      'render element' => 'form',
+    ),
   );
 }
 
 /**
- * This function heavily copied from theme_book_admin_table
+ * This function heavily copied from theme_book_admin_table.
+ *
+ * @TODO restructure to take advantage of node_load_multiple().
+ * @TODO restructure to use renderable array format for the output instead of a string
+ *
  * @see theme_book_admin_table
 */
 function theme_book_manager_outline_table($form) {
@@ -275,7 +372,7 @@ function theme_book_manager_outline_tabl
 
       // add links for users with administer nodes permission
       if (!$access) {
-        $node = node_load(array('nid' => $nid));
+        $node = node_load($nid);
         $edit =  user_access('edit own '. $node->type .' content');
         $delete = user_access('delete own '. $node->type .' content');
       }
@@ -290,17 +387,17 @@ function theme_book_manager_outline_tabl
       $form[$key]['weight']['#attributes']['class'] = 'book-weight';
 
       $data = array(
-        theme('indentation', $form[$key]['depth']['#value'] - 2) . drupal_render($form[$key]['title']),
+        theme('indentation', array('size' => $form[$key]['depth']['#value'] - 2) . drupal_render($form[$key]['title'])),
         drupal_render($form[$key]['weight']),
         drupal_render($form[$key]['plid']) . drupal_render($form[$key]['mlid']),
         l(t('view'), $href),
-        $edit ? l(t('edit'), 'node/'. $nid .'/edit', array('query' => $destination)) : '&nbsp',
-        $delete ? l(t('delete'), 'node/'. $nid .'/delete', array('query' => $destination) )  : '&nbsp',
+        $edit   ? l(t('edit'),   'node/'. $nid .'/edit',   array('query' => $destination)) : '&nbsp;',
+        $delete ? l(t('delete'), 'node/'. $nid .'/delete', array('query' => $destination)) : '&nbsp;',
       );
-      
+
       $edit = FALSE;
       $delete = FALSE;
-      
+
       $row = array('data' => $data);
       if (isset($form[$key]['#attributes'])) {
         $row = array_merge($row, $form[$key]['#attributes']);
@@ -309,16 +406,20 @@ function theme_book_manager_outline_tabl
       $rows[] = $row;
     }
 
-    return theme('table', $header, $rows, array('id' => 'book-outline'));
+    return theme('table', array(
+      'header' => $header,
+      'rows'   => $rows,
+      'attributes' => array('id' => 'book-outline'),
+    ));
   }
 
 /**
-  * Implementation of hook_form_alter()
+  * Implement hook_form_alter()
   */
 function book_manager_form_alter(&$form, $form_state, $form_id) {
-  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
+  if (!empty($form['#node_edit_form'])) {
     $node = $form['#node'];
-    
+
     // if the book module did not already add our widgets
     if (!isset($form['book']) && user_access('add content to personal books') && ((!empty($node->book['mlid']) && !empty($node->nid)) || book_type_is_allowed($node->type))) {
       _book_add_form_elements($form, $node);
@@ -337,7 +438,7 @@ function book_manager_form_alter(&$form,
           $form['table'][$key]['title']['#prefix'] = $form['table'][$key]['title']['#default_value'];
         }
       }
-      
+
       $form['table']['#theme'] = 'book_manager_outline_table';
       // ensure the user didn't change any values (needs to run before book_admin_edit_submit)
       array_unshift($form['#submit'], 'book_manager_admin_edit_submit');
@@ -356,18 +457,25 @@ function _book_manager_outline_alter(&$f
 }
 
 /**
- * This function does the appropriate checking to; 
- *    ensure this is a personal book and display appropriate output (either the outline form or "empty book")
- *    if this is in a book, but not the top-level it redirects the user to the top-level
- *    if the above criteria is not met, the user is notified it is not in a personal book
- * @param object $node the node the user is attempting to access
- * @return mixed text to be rendered, or user is redirected
+ * This function does the appropriate checking to:
+ * - ensure this is a personal book and display appropriate output (either the
+ *   outline form or "empty book")
+ * - if this is in a book, but not the top-level it redirects the user to the
+ *   top-level
+ * - if the above criteria is not met, the user is notified it is not in a
+ *   personal book
+ *
+ * @param object $node
+ *   The node the user is attempting to access
+ * @return mixed
+ *   Text to be rendered, or user is redirected
  */
 function book_manager_outline_edit($node) {
   if ($node->book_manager & BOOK_MANAGER_IS_BOOK && $node->book_manager & BOOK_MANAGER_IS_IN_PERSONAL) {
-    $result = db_query("SELECT count(*) AS pages FROM {book} WHERE bid = %d", $node->book['bid']);
-    $row = db_fetch_array($result);
-    if ($row['pages'] > 1) {
+    $result = db_query("SELECT count(*) AS pages FROM {book} WHERE bid = :bid",
+      array(':bid' => $node->book['bid']))->execute();
+    $pages = $result->fetchField();
+    if ($pages > 1) {
       module_load_include('inc', 'book', 'book.admin');
       $output = drupal_get_form('book_admin_edit', $node);
     }
@@ -375,7 +483,7 @@ function book_manager_outline_edit($node
       $output = t('This book is currently empty. You may add content by editing other nodes and adding them to this book via the book fieldset.');
     }
   }
-  else if ($node->book_manager & BOOK_MANAGER_IS_IN_PERSONAL) {
+  elseif ($node->book_manager & BOOK_MANAGER_IS_IN_PERSONAL) {
     // redirect user to top-level of the book
     drupal_goto('personal-outline/'. $node->book['bid']);
   }
@@ -383,17 +491,26 @@ function book_manager_outline_edit($node
     // book is not a personal book or in one (should we differentiate whether or not it is in A book?)
     $output = t('This node is not in a personal book.');
   }
-  
+
   return $output;
 }
 
 /**
- * Implementation of hook_submit()
+ * Submit handler inserted by book_manager_form_alter.
+ *
+ * This function ensures the user did not change any values via the
+ * book_admin_edit (outline form).
  *
- * This function ensures the user did not change any values via the book_admin_edit (outline form)
- * This is necessary because we are leveraging a large amount of book code, and this required using hidden fields,
- * a crafty user could hack the form to change the hidden field values potentially changing the node titles.
- * This "pre-process" submit handler will revert any changes the user made if they shouldn't be allowed to
+ * This is necessary because we are leveraging a large amount of book code, and
+ * this required using hidden fields, A crafty user could hack the form to
+ * change the hidden field values potentially changing the node titles.
+ *
+ * This "pre-process" submit handler will revert any changes the user made if
+ * they shouldn't be allowed.
+ *
+ * @param array $form
+ * @param array $form_state
+ * @return void
  */
 function book_manager_admin_edit_submit($form, &$form_state) {
   // this function ensures the user did not change any values via the book_admin_edit (outline form);
@@ -411,94 +528,124 @@ function book_manager_admin_edit_submit(
 /**
  * Turn an existing book into a personal book, or remove
  * the personal designation.
- * 
+ *
+ * Books are designated as personal by having a row in the {book_manager} table
+ * with their top-level nid as bid.
+ *
+ * If a book is already designated as personal, the INSERT will fail, but there
+ * is no need to report anything.
+ *
  * @param $node
  *   Node that is at the top level of a book
  * @param $user
  *   The new owner of the book.
  * @param $make_personal
- *   If true, then make the book personal, if false, remove
- *   the "personality" so that the book reverts to being a
- *   normal book.
+ *   If true, then make the book personal, if false, remove the "personality" so
+ *   that the book reverts to being a normal book.
  * @param $cascade
- *   If true, all pages in the book change ownership to the
- *   book's owner.
+ *   If true, all pages in the book change ownership to the book's owner.
+ * @return void
  */
 function book_manager_make_personal($node, $user, $make_personal = TRUE, $cascade = FALSE) {
-  // Books are designated as personal by having a row in the
-  // {book_manager} table with their top-level nid as bid.
-  //
-  // If a book is already designated as personal, the INSERT
-  // will fail, but there is no need to report anything.
+  dsm(func_get_args());
 //  $row = db_fetch_array(db_query("SELECT bid FROM {book} WHERE bid = %d", $node->nid));
   if ($node->book['bid'] == $node->nid) {
     if ($make_personal) {
-      @db_query('INSERT INTO {book_manager} (bid) VALUES (%d)', $node->nid);
-      drupal_set_message(t('The book <a href="@book_url">%book_name</a> is now a personal book.', array('@book_url' => url("node/$node->nid"), '%book_name' => $node->title)));
+      $q = db_insert('book_manager')->fields(array('bid' => $node->nid));
+      @$q->execute();
+      drupal_set_message(t('The book <a href="!book_url">%book_name</a> is now a personal book.', array(
+        '!book_url' => url("node/$node->nid"),
+        '%book_name' => $node->title,
+      )));
     }
     else {
-      db_query('DELETE FROM {book_manager} WHERE bid = %d', $node->nid);
-      drupal_set_message(t('The book <a href="@book_url">%book_name</a> is now a regular book', array('@book_url' => url("node/$node->nid"), '%book_name' => $node->title)));
+      db_delete('book_manager')->condition('bid', $node->nid);
+      drupal_set_message(t('The book <a href="!book_url">%book_name</a> is now a regular book', array(
+        '!book_url' => url("node/$node->nid"),
+        '%book_name' => $node->title,
+      )));
     }
   }
 }
 
+/**
+ * Change the owner of a book.
+ *
+ * @TODO: seems unused. Remove ?
+ *
+ * @param object $node
+ * @param object $user
+ * @return void
+ */
 function book_manager_change_node_ownership(&$node, $user) {
   if ($node->nid) {
     $node->uid = $user->uid;
     node_save($node);
   }
   else {
-    watchdog('error', t('book_manager_change_node_ownership() called with unpopulated node'), array(), WATCHDOG_ERROR);
+    watchdog('error', t('book_manager_change_node_ownership() called with unpopulated node'),
+      array(), WATCHDOG_ERROR);
   }
 }
 
 /**
- * Returns the status of the given node
+ * Returns the status of the given node.
  *
  * This is used to populate $node->book_manager for easy access later
- *  
- *  BOOK_MANAGER_IS_IN_PERSONAL: The node in question is IN a personal book
- *  BOOK_MANAGER_IS_BOOK: The node in question is the root of a book
- *  BOOK_MANAGER_IS_IN_BOOK: The node in question is in a book
  *
- * @param int $nid the node id to load book status for
+ * - BOOK_MANAGER_IS_IN_PERSONAL: The node in question is IN a personal book
+ * - BOOK_MANAGER_IS_BOOK: The node in question is the root of a book
+ * - BOOK_MANAGER_IS_IN_BOOK: The node in question is in a book
+ *
+ * @param int $nid
+ *   The node id to load book status for
  */
 function book_manager_status($nid) {
   $return = 0;
-  if ($result = db_query("SELECT nid, bid FROM {book} WHERE nid = %d", $nid)) {
-    $book = db_fetch_object($result);
+  $q = db_select('book', 'b')
+    ->fields('b', array('nid', 'bid'))
+    ->condition('b.nid', $nid);
+  $result = $q->execute();
+  if ($result && $book = $result->fetchObject()) {
     if ($book->nid == $book->bid) {
       $return += BOOK_MANAGER_IS_BOOK;
     }
     $return += BOOK_MANAGER_IS_IN_BOOK;
-    
-    if ($result = db_query("SELECT bid FROM {book_manager} WHERE bid = %d", $book->bid)) {
-      $book = db_fetch_object($result);
-      if ($book->bid) {
+
+    $q2 = db_select('book_manager', 'bm');
+    $q2->addField('bm', 'bid');
+    $q2->condition('bm.bid', $book->bid);
+    if ($result = $q2->execute()) {
+      $bid = $result->fetchField();
+      if ($bid) {
         $return += BOOK_MANAGER_IS_IN_PERSONAL;
       }
     }
-  } 
+  }
+
   return $return;
 }
 
 /**
- * returns an array keyed by node pointing to the bid that the nid belongs to 
- * @param bool $personalonly if TRUE only personal books are included, if FALSE all books are included
+ * Returns an array keyed by node pointing to the bid that the nid belongs to
+ *
+ * @FIXME convert to dynamic db_select query
+ *
+ * @param bool $personalonly
+ *   If TRUE only personal books are included, if FALSE all books are included.
 */
-function _book_manager_nodes_to_books($personalonly = TRUE) {
-  if ($personalonly) {
-    if ($result = db_query('SELECT bid FROM {book_manager}')) {
+function _book_manager_nodes_to_books($personal_only = TRUE) {
+  if ($personal_only) {
+    if ($result = db_query('SELECT bm.bid FROM {book_manager} bm', array(), array('fetch' => PDO::FETCH_ASSOC))->execute()) {
       $books = array();
-      while ($row = db_fetch_array($result)) {
+      foreach ($result as $row) {
         $books[$row['bid']] = $row['bid'];
       }
       if (!empty($books)) {
         $in = "WHERE bid IN (". implode(', ', $books) .")";
       }
     }
-    
+
     if (!isset($in)) {
       // something strange happened (let's ensure the next query will return no results)
       $in = "WHERE 1 = -1";
@@ -507,44 +654,51 @@ function _book_manager_nodes_to_books($p
   else {
     $in = '';
   }
-  
-  $nidstobooks = array();
-  $query = "SELECT nid, bid FROM {book}";
+
+  $nids_to_books = array();
+  $query = "SELECT b.nid, b.bid FROM {book} b ";
   if (!empty($in)) {
     $query .= ' '. $in;
   }
-  $result = db_query($query);
-  while ($nidbid = db_fetch_array($result)) {
-    $nidstobooks[$nidbid['nid']] = $nidbid['bid'];
+  $result = db_query($query, array(), array('fetch' => PDO::FETCH_ASSOC))->execute();
+  if ($result) {
+    foreach ($result as $nid_bid) {
+      $nids_to_books[$nid_bid['nid']] = $nid_bid['bid'];
+    }
   }
-  
-  return $nidstobooks;
+
+  return $nids_to_books;
 }
 
 
 /**
- * Given an array of book options this function prunes those entries the user should not be allowed to add
- * This also adds the create option if the user should be able to crete books
+ * Given an array of book options, prunes entries the user should not be allowed to add.
  *
- * options matching any of the following criteria are removed:
- *      1. node is in a personal book and node->author does not match current nid author
- *      2. user does not have permission to add content from other authors (add_content_to_books)
- *      3. node in question is in a personal book and the author does not match the current node author 
- * @param $bid array form element for the book
- * @param $node node object the user is editing
+ * This also adds the create option if the user should be able to create books
+ *
+ * Options matching any of the following criteria are removed:
+ * 1. node is in a personal book and node->author does not match current nid author
+ * 2. user does not have permission to add content from other authors (add_content_to_books)
+ * 3. node in question is in a personal book and the author does not match the current node author
+ *
+ * @param $bid array
+ *   Form element for the book
+ * @param $node
+ *   Node object the user is editing
+ * @return void
  */
 function _book_manager_limit_books(&$bid, $node) {
   $nidstobids = _book_manager_nodes_to_books(TRUE);
   $addtobooks = user_access('add content to books');
   foreach ($bid['#options'] as $nid => $title) {
-    if ((($node->book_manager & BOOK_MANAGER_IS_IN_PERSONAL) && !_book_manager_author_matches($nid, $node->uid)) || // node is in a personal book and node->author does not match current nid author
+    if (((isset($node->book_manager) & BOOK_MANAGER_IS_IN_PERSONAL) && !_book_manager_author_matches($nid, $node->uid)) || // node is in a personal book and node->author does not match current nid author
         (!user_access('add content to books') && !_book_manager_author_matches($nid)) ||                            // user does not have permission to add content from other authors
         (in_array($nid, $nidstobids) && !_book_manager_author_matches($nid, $node->uid))) {                         // node in question is in a personal book and the author does not match the current node author
       unset($bid['#options'][$nid]);
     }
   }
 
-  $nid = isset($node->nid) ? $node->nid : 'new';  
+  $nid = isset($node->nid) ? $node->nid : 'new';
   if (!user_access('create new books') && user_access('create personal books')  && ($nid == 'new' || ($nid != $node->book['original_bid']))) {
     $bid['#options'] = array($nid  => '<'. t('create new book') .'>') + $bid['#options'];
   }
@@ -552,135 +706,159 @@ function _book_manager_limit_books(&$bid
 }
 
 /**
- * Returns whether or not the node author for the supplied nid matches the given uid (or the currently logged in user)
+ * Does the node author for the supplied nid match the given uid (or the currently logged in user) ?
  *
- * @param int $nid the node it in question
- * @param int $uid optional, if not supplied then the logged in user's id is used.
- * @return bool whether or not the author for nid matches the given ui
+ * This query is cheaper than a node_load().
+ *
+ * @TODO: should this apply a node_access tag or not ?
+ *
+ * @param int $nid
+ *   The node in question
+ * @param int $uid
+ *   Optional, if not supplied then the logged in user's id is used.
+ * @return bool
+ *   Whether or not the author for nid matches the given uid
  */
 function _book_manager_author_matches($nid, $uid = 0) {
-  if ($uid == 0) {
+  if ($uid === 0) {
     global $user;
     $uid = $user->uid;
   }
 
-  $result = db_query("SELECT uid FROM {node} WHERE nid = %d", $nid);
-  $dbuid = db_fetch_object($result);
-  if ($uid != $dbuid->uid) {
-    return FALSE;
-  }
-  else {
-    return TRUE;
-  }
+  $result = db_query("SELECT uid FROM {node} WHERE nid = :nid", array(':nid' => $nid))->execute();
+  $dbuid = $result->fetchField();
+  return $uid == $dbuid;
 }
 
 /**
  * Menu callback; show the outline form for a single node.
+ *
+ * @return array
  */
 function book_manager_outline($node) {
   module_load_include('inc', 'book', 'book.pages');
-  
-  drupal_set_title(check_plain($node->title));
+
+  drupal_set_title($node->title); // adds a check_plain by default in D7
+  // @FIXME: shouldn't this be 'book_manager_outline_form' ?
   return drupal_get_form('book_outline_form', $node);
 }
 
 /**
- * This function relies on book_outline_form to do the major form building
- * However we need to restrict the books the user can choose to those they are
+ * This function relies on book_outline_form() to do the major form building;
+ * however we need to restrict the books the user can choose to those they are
  * the author for.
 */
 function book_manager_outline_form(&$form_state, $node) {
   module_load_include('inc', 'book', 'book.pages');
   $form = book_outline_form(&$form_state, $node);
-  
-  _book_manager_limit_books($form['book']['bid'], $node);
 
+  _book_manager_limit_books($form['book']['bid'], $node);
   _book_manager_set_personal($form);
 
-  return drupal_render($form);
+  return $form;
 }
 
 /**
- * This function handles the form processing for our modified book_outline_form
- * again we rely on book code to do our processing, checking to see if the user will 
- * be redirected to outline and adjusting to personal-outline
+ * This function handles the form processing for our modified book_outline_form.
+ *
+ * Again we rely on book code to do our processing, checking to see if the user
+ * will be redirected to outline and adjusting to personal-outline
+ *
+ * @param array $form
+ * @param array $form_state
+ * @return void
  */
 function book_manager_outline_form_submit($form, &$form_state) {
+  dsm(func_get_args());
   $node = $form['#node'];
   book_outline_form_submit($form, &$form_state);
-  
-  if ($form_state['redirect'] == "node/". $node->nid ."/outline") {
-    $form_state['redirect'] = "node/". $node->nid ."/personal-outline";
+
+  if ($form_state['redirect'] == "node/" . $node->nid . "/outline") {
+    $form_state['redirect'] = "node/" . $node->nid . "/personal-outline";
   }
 }
 
-
-
 /**
- * This function adds a checkbox if the user has both "create new books" and "create personal books"
- * That allows the user to check the box and create a personal book rather than a global book
+ * Adds a checkbox if the user has both "create new books" and "create personal books".
+ *
+ * This allows the user to check the box and create a personal book rather than
+ * a global book.
  */
 function _book_manager_set_personal(&$form) {
   if (user_access('create new books') && user_access('create personal books')) {
     $node = $form['#node'];
-    if ($node->nid) {
-      $bid = db_fetch_array(db_query('SELECT bid FROM {book_manager} WHERE bid = %d', $node->nid));
-      if ($bid['bid']) {
-        $ispersonal = TRUE;
-      }
-      else {
-        $ispersonal = FALSE;
-      }
+    if (isset($node->nid)) {
+      $q = db_select('book_manager', 'bm');
+      $q->addField('bm', 'bid');
+      $q->condition('bm.bid', $node->nid);
+      $bid = $q->execute()->fetchField();
+      $is_personal = !empty($bid);
     }
     else {
-      $ispersonal = FALSE;
+      $is_personal = FALSE;
     }
 
-    // prepare some jquery to hide the ispersonal box unless it makes sense
-    if ($node->nid) {
-      $jsnid = " || $('#edit-book-bid').val() == ". $node->nid;
-    }
-    else {
-      $jsnid = '';
-    }
-    
-    $jscript = "if (Drupal.jsEnabled) { 
-                  function ispersonaljs() {
-                    if ($('#edit-book-bid').val() == 'new'". $jsnid .") {
-                      $('#edit-book-ispersonal-wrapper').css('display', 'inline');
-                    }
-                    else {
-                      $('#edit-book-ispersonal-wrapper').css('display', 'none'); 
-                    }
-                  }
-                
-                  $(document).ready(function() { 
-                    ispersonaljs();
-                    $('#edit-book-bid').change(ispersonaljs);
-                  }); 
-                }";
-    
-      drupal_add_js($jscript, 'inline');
-    
-    $form['book']['ispersonal'] = array(
-      '#type' => 'checkbox',
-      '#default_value' => $ispersonal,
-      '#title' => t('Make Personal'),
-      '#description' => t('If checked this book will be a personal book, and will only be able to contain nodes from a single author'),
+    // prepare some jQuery to hide the ispersonal box unless it makes sense
+    $js_nid = isset($node->nid)
+      ? " || $('#edit-book-bid').val() == ". $node->nid
+      : '';
+
+    $jscript = <<<EOT
+      if (Drupal.jsEnabled) {
+        function ispersonaljs() {
+          if ($('#edit-book-bid').val() == 'new' $js_nid) {
+            $('#edit-book-ispersonal-wrapper').css('display', 'inline');
+          }
+          else {
+            $('#edit-book-ispersonal-wrapper').css('display', 'none');
+          }
+        }
+
+        $(document).ready(function() {
+          ispersonaljs();
+          $('#edit-book-bid').change(ispersonaljs);
+          }
+        );
+      }
+EOT;
+
+    drupal_add_js($jscript, 'inline');
+
+    $form['book']['is-personal'] = array(
+      '#type'          => 'checkbox',
+      '#default_value' => $is_personal,
+      '#title'         => t('Make personal'),
+      '#description'   => t('If checked this book will be a personal book, and will only be able to contain nodes from a single author'),
     );
+    array_unshift($form['#submit'], 'book_manager_outline_form_submit');
   }
 }
 
 /**
  * This function just sets whether a book is personal or not
+ *
+ * @param array $form
+ * @param array $form_state
+ * @return void
  */
 function book_manager_set_personal_submit($form, &$form_state) {
   $node = $form['#node'];
-  if ($form_state['values']['book']['ispersonal']) {
+  if ($form_state['values']['book']['is-personal']) {
     book_manager_make_personal($node, $user);
   }
 }
 
+/**
+ * Implement hook_views_data().
+ *
+ * @TODO Finish the D7 port of Views
+ * @TODO Check whether a hook_views_api() implementation is needed too.
+ *
+ * This hook belongs to Views module.
+ * @link http://drupal.org/project/views @endlink
+ *
+ * @return array
+ */
 function book_manager_views_data() {
   $data = array();
 
@@ -688,25 +866,31 @@ function book_manager_views_data() {
   $data['book_manager']['table']['join'] = array(
     'node' => array(
       'left_field' => 'nid',
-      'field' => 'bid'),
+      'field'      => 'bid',
+    ),
   );
-  
+
   $data['book_manager']['bid'] = array(
-    'title' => t('Personal Book'),
-    'help' => t('Thie personal book the node is in.'),
+    'title'        => t('Personal Book'),
+    'help'         => t('The personal book the node is in.'),
     'relationship' => array(
-      'base' => 'node',
-      'field' => 'bid',
-      'handler' => 'views_handler_relationship',
-      'label' => t('Personal Book'),
+      'base'         => 'node',
+      'field'        => 'bid',
+      'handler'      => 'views_handler_relationship',
+      'label'        => t('Personal Book'),
     ),
   );
+
   return $data;
 }
 
 /**
-* Implementation of hook_book_copy_alter()
-*/
+ * Implement hook_book_copy_alter()
+ *
+ * This hook belongs to book_copy module.
+ * @link http://drupal.org/project/book_copy @endlink
+ * @see book_copy_copy_book()
+ */
 function book_manager_book_copy_alter(&$node, $oldbid, $newbid) {
   $oldbook = node_load(array('nid' => $oldbid));
   if ($oldbook->book_manager & BOOK_MANAGER_IS_IN_PERSONAL) {
@@ -715,9 +899,12 @@ function book_manager_book_copy_alter(&$
 }
 
 /**
- * Implementation of hook_book_copy_goto_alter()
- *  change $node->bookcopydata['url'] to goto location
- *  change $node->bookcopydata['message'] to message to send via drupal_set_message()
+ * Implement hook_book_copy_goto_alter().
+ * - change $node->bookcopydata['url'] to goto location
+ * - change $node->bookcopydata['message'] to message to send via drupal_set_message()
+ *
+ * This hook belongs to book_copy module.
+ * @link http://drupal.org/project/book_copy @endlink
  */
 function book_manager_book_copy_goto_alter(&$node) {
   if (_book_manager_outline_access_menu_callback($node)) {
@@ -729,8 +916,10 @@ function book_manager_book_copy_goto_alt
 }
 
 /**
+ * Theme override for theme_help().
+ *
  * Since we overrode the book module's access control to /node/%/outline
- * we need to remove the help as it displays links specific to users 
+ * we need to remove the help as it displays links specific to users
  * with 'administer book outlines'
  */
 function phptemplate_help() {
@@ -744,4 +933,4 @@ function phptemplate_help() {
       return '<div class="help">'. $help .'</div>';
     }
   }
-}
\ No newline at end of file
+}
