Index: book_access.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/book_access/book_access.module,v
retrieving revision 1.15
diff -u -r1.15 book_access.module
--- book_access.module	26 Jan 2009 05:56:15 -0000	1.15
+++ book_access.module	7 Feb 2009 00:39:06 -0000
@@ -21,7 +21,7 @@
   switch ($path) {
     case 'admin/content/book/access':
       return t('
-        <p>Configure access control per book based on user roles. Settings
+        <p>Configure access control per book based on users or roles. Settings
         affect all pages within the given book. If a page is moved into a
         different book, it will assume that book\'s access control settings.</p>
         <p><em>Important:</em> If you are going to manage access control here,
@@ -98,10 +98,11 @@
 
 /**
  * This function supplies the book access grants. book_access simply uses
- * roles as grant IDs.
+ * roles and user IDs as grant IDs.
  */
 function book_access_node_grants($user, $op) {
   $grants['book_access'] = array_keys($user->roles);
+  $grants['book_access_user'] = array($user->uid);
   return $grants;
 }
 
@@ -128,6 +129,19 @@
       );
     }
 
+    // Add the per-user grants.
+    $result = db_query("SELECT * FROM {book_access_user} WHERE nid = %d", $book_nid);
+    while ($grant = db_fetch_object($result)) {
+      $grants[] = array(
+        'realm'        => 'book_access_user',
+        'gid'          => $grant->uid,
+        'grant_view'   => $grant->grant_view,
+        'grant_update' => $grant->grant_update,
+        'grant_delete' => $grant->grant_delete,
+        'priority'     => BOOK_ACCESS_GRANT_PRIORITY,
+      );
+    }
+
     return $grants;
   }
 }
@@ -241,6 +255,62 @@
       '#title'         => t('Delete pages in this book'),
       '#default_value' => $delete
     );
+
+    // Note that the autocomplete widget will only enable for users with the
+    // 'access profiles' permission. Other users will have to specify the name
+    // manually.
+    $form['access'][$book_nid]['search_user'] = array(
+      '#type'              => 'textfield',
+      '#prefix'            => '<div class="book-access-clearer"></div><div class="book-user-div">',
+      '#suffix'            => '</div>',
+      '#title'             => t('Add a User'),
+      '#description'       => t('Enter a user name to set permissions for that user on this book.'),
+      '#size'              => 40,
+      '#autocomplete_path' => 'user/autocomplete',    
+    );
+    $form['access'][$book_nid]['user_view'] = array(
+      '#type'          => 'checkbox',
+      '#title'         => t('View this book'),
+      '#default_value' => TRUE,
+    );
+    $form['access'][$book_nid]['user_update'] = array(
+      '#type'  => 'checkbox',
+      '#title' => t('Edit pages in this book'),
+    );
+    $form['access'][$book_nid]['user_delete'] = array(
+      '#type'  => 'checkbox',
+      '#title' => t('Delete pages in this book'),
+    );
+
+    // Show a table of existing user grants.
+    $result = db_query("SELECT * FROM {book_access_user} where nid = %d", $book_nid);
+    $form['access'][$book_nid]['users'] = array();
+    while ($book_access = db_fetch_object($result)) {
+      $user = user_load($book_access->uid);
+      $defaults = array();
+      if ($book_access->grant_view) {
+        $defaults[] = 'view';
+      }
+      if ($book_access->grant_update) {
+        $defaults[] = 'update';
+      }
+      if ($book_access->grant_delete) {
+        $defaults[] = 'delete';
+      }
+      $form['access'][$book_nid]['users'][$user->uid] = array(
+        '#type'   => 'checkboxes',
+        '#title'  => t('Permissions for %user', array('%user' => $user->name)),
+        '#prefix' => '<div class="book-access-div">',
+        '#suffix' => '</div>',
+        '#options' => array(
+          'view' => t('View pages in this book'),
+          'update' => t('Edit pages in this book'),
+          'delete' => t('Delete pages in this book'),
+          'remove' => t('Reset permissions for this user to defaults'),
+        ),
+        '#default_value' => $defaults,
+      );
+    }
   }
 
   $form['clearer'] = array(
@@ -259,8 +329,31 @@
   return $form;
 }
 
-function book_access_admin_form_submit($form, &$form_state) {
+/**
+ * Implementation of the Form API validation handler.
+ *
+ * @param $form
+ *   The form to validate.
+ * @param $form_state
+ *  The current state of the form as submitted.
+ */
+function book_access_admin_form_validate($form, &$form_state) {
+  foreach ($form_state['values']['access'] as $book_nid => $access) {
+    if (!empty($access['search_user']) && !user_load(array('name' => $access['search_user']))) {
+      form_set_error("access][$book_nid][search_user", t('%user is not a valid user name.', array('%user' => $access['search_user'])));
+    }
+  }
+}
 
+/**
+ * Implementation of the Form API submission handler.
+ *
+ * @param $form
+ *   The form to validate.
+ * @param $form_state
+ *  The current state of the form as submitted.
+ */
+function book_access_admin_form_submit($form, &$form_state) {
   foreach ($form_state['values']['access'] as $book_nid => $access) {
     db_query("DELETE FROM {book_access} WHERE nid = %d", $book_nid);
 
@@ -276,6 +369,38 @@
 
       db_query($sql, $book_nid, $rid, $grant_view, $grant_update, $grant_delete);
     }
+
+    // If a user has been specified, update the permissions for that user. Also
+    // update a user if it currently has existing permissions.
+    if (!empty($access['search_user'])) {
+      $user = user_load(array('name' => $access['search_user']));
+      db_query("DELETE FROM {book_access_user} WHERE nid = %d AND uid = %d", $book_nid, $user->uid);
+      $grant_view = ($access['user_view'] > 0);
+      $grant_update = ($access['user_update'] > 0);
+      $grant_delete = ($access['user_delete'] > 0);
+      $sql = "
+        INSERT INTO {book_access_user} (nid, uid, grant_view, grant_update, grant_delete)
+        VALUES (%d, %d, %d, %d, %d)
+      ";
+      db_query($sql, $book_nid, $user->uid, $grant_view, $grant_update, $grant_delete);
+    }
+    // If an existing user has been changed, update that user.
+    if (!empty($access['users'])) {
+      foreach($access['users'] as $uid => $checked) {
+        $user = user_load($uid);
+        db_query("DELETE FROM {book_access_user} WHERE nid = %d AND uid = %d", $book_nid, $user->uid);
+        if (!$checked['remove']) {
+          $grant_view = !empty($checked['view']);
+          $grant_update = !empty($checked['update']);
+          $grant_delete = !empty($checked['delete']);
+          $sql = "
+            INSERT INTO {book_access_user} (nid, uid, grant_view, grant_update, grant_delete)
+            VALUES (%d, %d, %d, %d, %d)
+          ";
+          db_query($sql, $book_nid, $user->uid, $grant_view, $grant_update, $grant_delete);
+        }
+      }
+    }
   }
   node_access_rebuild();
 }
@@ -331,6 +456,21 @@
     $permitted_bids[$result->nid] = $result->nid;
   }
 
+  // Add in the per-user grants.
+  $sql = "
+    SELECT nid
+    FROM {node_access}
+    WHERE realm = 'book_access_user'
+    AND gid = %d
+    AND grant_update > 0
+  ";
+
+  $results = db_query($sql, $user->uid);
+
+  while ($result = db_fetch_object($results)) {
+    $permitted_bids[$result->nid] = $result->nid;
+  }
+
   if (isset($options)) {
     foreach ($options as $bid => $value) {
       // <create new book> option uses current nid as the key, skip it
@@ -338,7 +478,7 @@
         continue;
       }
       if ($bid > 0 && !isset($permitted_bids[$bid])) {
-        unset($options[$nid]);
+        unset($options[$bid]);
       }
     }
   }
Index: book_access.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/book_access/book_access.install,v
retrieving revision 1.4
diff -u -r1.4 book_access.install
--- book_access.install	5 Sep 2008 23:35:45 -0000	1.4
+++ book_access.install	7 Feb 2009 00:39:06 -0000
@@ -59,6 +59,48 @@
     ),
     'primary key' => array('nid', 'rid'),
   );
+  $schema['book_access_user'] = array(
+    'description' => t('Table for tracking book access by user.'),
+    'fields' => array(
+      'nid' => array(
+        'description' => t('Primary key: The node ID of the book.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'uid' => array(
+        'description' => t('Primary key: A user ID associated with a book node ID.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'grant_view' => array(
+        'description' => t('View book is allowed. 1 = TRUE, 0 = FALSE.'),
+        'type' => 'int',
+        'size' => 'tiny',
+        'insigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'grant_update' => array(
+        'description' => t('Edit book pages is allowed. 1 = TRUE, 0 = FALSE.'),
+        'type' => 'int',
+        'size' => 'tiny',
+        'insigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'grant_delete' => array(
+        'description' => t('Delete book pages is allowed. 1 = TRUE, 0 = FALSE.'),
+        'type' => 'int',
+        'size' => 'tiny',
+        'insigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('nid', 'uid'),
+  );
 
   return $schema;
 }
@@ -69,3 +111,57 @@
 function book_access_uninstall() {
   drupal_uninstall_schema('book_access');
 }
+
+/**
+ * Add a table allowing specific users to be added to book access.
+ * @return
+ *   Array of results from the update queries.
+ */
+function book_access_update_6001() {
+  $ret = array();
+  $schema = array();
+  $schema['book_access_user'] = array(
+    'description' => t('Table for tracking book access by user.'),
+    'fields' => array(
+      'nid' => array(
+        'description' => t('Primary key: The node ID of the book.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'uid' => array(
+        'description' => t('Primary key: A user ID associated with a book node ID.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'grant_view' => array(
+        'description' => t('View book is allowed. 1 = TRUE, 0 = FALSE.'),
+        'type' => 'int',
+        'size' => 'tiny',
+        'insigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'grant_update' => array(
+        'description' => t('Edit book pages is allowed. 1 = TRUE, 0 = FALSE.'),
+        'type' => 'int',
+        'size' => 'tiny',
+        'insigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'grant_delete' => array(
+        'description' => t('Delete book pages is allowed. 1 = TRUE, 0 = FALSE.'),
+        'type' => 'int',
+        'size' => 'tiny',
+        'insigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('nid', 'uid'),
+  );
+  db_create_table($ret, 'book_access_user', $schema['book_access_user']);
+  return $ret;
+}
