Index: book_access.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/book_access/book_access.install,v retrieving revision 1.4.2.4 diff -u -p -r1.4.2.4 book_access.install --- book_access.install 13 Dec 2009 03:13:25 -0000 1.4.2.4 +++ book_access.install 13 Dec 2009 04:42:00 -0000 @@ -28,6 +28,8 @@ function book_access_install() { * Implementation of hook_schema(). */ function book_access_schema() { + $schema = array(); + $schema['book_access'] = array( 'description' => 'Table for tracking book access.', 'fields' => array( @@ -44,7 +46,7 @@ function book_access_schema() { 'default' => 0, ), 'grant_view' => array( - 'description' => 'View book is allowed.', + 'description' => 'View book is permission.', 'type' => 'int', 'size' => 'tiny', 'insigned' => TRUE, @@ -52,7 +54,7 @@ function book_access_schema() { 'default' => 0, ), 'grant_update' => array( - 'description' => 'Edit book pages is allowed.', + 'description' => 'Edit book pages permission.', 'type' => 'int', 'size' => 'tiny', 'insigned' => TRUE, @@ -60,7 +62,7 @@ function book_access_schema() { 'default' => 0, ), 'grant_delete' => array( - 'description' => 'Delete book pages is allowed.', + 'description' => 'Delete book pages permission.', 'type' => 'int', 'size' => 'tiny', 'insigned' => TRUE, @@ -71,6 +73,49 @@ function book_access_schema() { 'primary key' => array('nid', 'rid'), ); + $schema['book_access_user'] = array( + 'description' => 'Table for tracking book access by user.', + 'fields' => array( + 'nid' => array( + 'description' => 'Primary key: The node ID of the book.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'uid' => array( + 'description' => 'Primary key: The user ID associated with a book node ID.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'grant_view' => array( + 'description' => 'View book is permission.', + 'type' => 'int', + 'size' => 'tiny', + 'insigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'grant_update' => array( + 'description' => 'Edit book pages permission.', + 'type' => 'int', + 'size' => 'tiny', + 'insigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'grant_delete' => array( + 'description' => 'Delete book pages permission.', + 'type' => 'int', + 'size' => 'tiny', + 'insigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('nid', 'uid'), + ); + return $schema; } @@ -83,7 +128,62 @@ function book_access_update_6100() { return $ret; } - /** +/** + * Implementation of hook_update_N(). + */ +function book_access_update_6101() { + $ret = array(); + $schema = array(); + + $schema['book_access_user'] = array( + 'fields' => array( + 'nid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'uid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'grant_view' => array( + 'type' => 'int', + 'size' => 'tiny', + 'insigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'grant_update' => array( + 'type' => 'int', + 'size' => 'tiny', + 'insigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'grant_delete' => array( + 'type' => 'int', + 'size' => 'tiny', + 'insigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('nid', 'uid'), + ); + + if (!db_table_exists('book_access_user')) { + db_create_table($ret, 'book_access_user', $schema['book_access_user']); + } + + if (!db_table_exists('book_access_role')) { + db_rename_table($ret, 'book_access', 'book_access_role'); + } + + return $ret; +} + +/** * Implementation of hook_uninstall(). */ function book_access_uninstall() { Index: book_access.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/book_access/book_access.module,v retrieving revision 1.15.2.11 diff -u -p -r1.15.2.11 book_access.module --- book_access.module 13 Dec 2009 02:59:41 -0000 1.15.2.11 +++ book_access.module 13 Dec 2009 04:42:01 -0000 @@ -44,10 +44,10 @@ function book_access_form_alter(&$form, _book_access_restrict_options($form['book']['bid']['#options']); } - // when an outline is modified, taxonomy is changed, but the node is not - // saved, so node grants can become broken if a book page is moved into - // another book. so we fix that by adding an additional #submit callback - // that rebuilds the grants when the book outline is modified. + // When an outline is modified, taxonomy is changed but the node is not + // saved; node grants can become broken if a book page is moved into + // another book. We fix that by adding an additional #submit callback + // to rebuild the grants when the book outline is modified. if ($form_id == 'book_outline_form') { // TODO: diff settings before/after and only rebuild if changes were made $form['#submit'][] = 'book_access_build_node_grants'; @@ -61,7 +61,7 @@ function book_access_help($path, $arg) { switch ($path) { case 'admin/content/book/access': $help = t( - '

Configure access control per book based on user roles. Settings + '

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 access control settings.

Important: If you are going to manage access control here, @@ -148,18 +148,21 @@ function book_access_node_access_explain } if (isset($roles[$row->gid])) { - return array( + $result = array( t('grants for users of role: %role', array('%role' => $roles[$row->gid]) ) ); } + else { + $result = array( + t('Unknown group id %gid: It is recommended to rebuild node access', + array('%gid' => $row->gid) + ) + ); + } - return array( - t('Unknown group id %gid: It is recommended to rebuild node access', - array('%gid' => $row->gid) - ) - ); + return $result; } } @@ -175,7 +178,6 @@ function book_access_node_access_explain if (isset($node->book['bid'])) { $result = db_query('SELECT * FROM {book_access} WHERE nid = %d', $node->book['bid']); - while ($grant = db_fetch_object($result)) { $grants[] = array( 'realm' => 'book_access', @@ -187,6 +189,19 @@ function book_access_node_access_explain ); } + // Add the per-user grants. + $result = db_query("SELECT * FROM {book_access_user} WHERE nid = %d", $node->book['bid']); + 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; } } @@ -244,6 +259,8 @@ function book_access_perm() { */ function book_access_node_grants($user, $op) { $grants['book_access'] = array_keys($user->roles); + $grants['book_access_user'] = array($user->uid); + return $grants; } @@ -263,6 +280,11 @@ function book_access_admin_form() { ); $books = array(); $form = array(); + $grants = array( + 'grant_view' => 'view', + 'grant_update' => 'update', + 'grant_delete' => 'delete', + ); $rids = user_roles(); if ($bool) { @@ -281,38 +303,33 @@ function book_access_admin_form() { foreach ($books as $book_nid => $book_name) { // Variables used to store existing grants for this book. - - $view = array(); - $update = array(); - $delete = array(); + $book_grants = array( + 'view' => array(), + 'update' => array(), + 'delete' => array(), + ); $result = db_result( db_query( - "SELECT COUNT(*) FROM {book_access} where nid = %d", $book_nid + "SELECT COUNT(*) FROM {book_access_role} where nid = %d", $book_nid ) ); - // If there are no grants, then use some safe defaults; it only makes + // If there are no grants, then use some safe defaults; it only makes // sense when a new book is created. if (!$result) { - $view = array(1, 2); - $update = array(); - $delete = array(); + $book_grants['view'] = array(1, 2); } else { $result = db_query( - "SELECT * FROM {book_access} where nid = %d", $book_nid + "SELECT * FROM {book_access_role} where nid = %d", $book_nid ); while ($book_access = db_fetch_object($result)) { - if ($book_access->grant_view) { - $view[] = $book_access->rid; - } - if ($book_access->grant_update) { - $update[] = $book_access->rid; - } - if ($book_access->grant_delete) { - $delete[] = $book_access->rid; + foreach ($grants as $grant => $var) { + if ($book_access->$grant) { + $book_grants[$var][] = $book_access->rid; + } } } } @@ -329,7 +346,7 @@ function book_access_admin_form() { '#type' => 'checkboxes', '#title' => t('View this book'), '#options' => $rids, - '#default_value' => $view, + '#default_value' => $book_grants['view'], '#prefix' => '

', '#suffix' => '
', ); @@ -337,17 +354,75 @@ function book_access_admin_form() { $form['access'][$book_nid]['update'] = array( '#type' => 'checkboxes', '#title' => t('Edit pages in this book'), + '#options' => $rids, + '#default_value' => $book_grants['update'], '#prefix' => '
', '#suffix' => '
', - '#options' => $rids, - '#default_value' => $update, ); $form['access'][$book_nid]['delete'] = array( '#type' => 'checkboxes', '#title' => t('Delete pages in this book'), '#options' => $rids, - '#default_value' => $delete, + '#default_value' => $book_grants['delete'], + '#prefix' => '
', + '#suffix' => '
', + ); + } + + // 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', + '#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', + '#prefix' => '
', + '#suffix' => '
', + ); + + $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. + $form['access'][$book_nid]['users'] = array(); + $result = db_query("SELECT * FROM {book_access_user} where nid = %d", $book_nid); + + while ($book_access = db_fetch_object($result)) { + $defaults = array(); + $user = user_load($book_access->uid); + + foreach ($grants as $grant => $var) { + if (!empty($book_access->$grant)) { + $defaults[] = $var; + } + } + + $form['access'][$book_nid]['users'][$user->uid] = array( + '#type' => 'checkboxes', + '#title' => t('Permissions for %user', array('%user' => $user->name)), + '#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, '#prefix' => '
', '#suffix' => '
', ); @@ -372,25 +447,80 @@ function book_access_admin_form() { } /** + * Validation function for the administration settings. + */ +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']))); + } + } +} + +/** * Submission callback for the administration settings. */ function book_access_admin_form_submit($form, &$form_state) { + $grants = array( + 'grant_view' => 'view', + 'grant_update' => 'update', + 'grant_delete' => 'delete', + ); $row = new stdClass(); - // Remove previous book access entries, and keep table clean. - db_query("DELETE FROM {book_access}"); - if (isset($form_state['values']['access'])) { foreach ($form_state['values']['access'] as $nid => $access) { $row->nid = $nid; + // Remove previous book access entries. + db_query("DELETE FROM {book_access_role} WHERE nid = %d", $nid); + foreach ($access['view'] as $rid => $checked) { $row->rid = $rid; - $row->grant_view = (bool) $checked; - $row->grant_update = ($access['update'][$rid] > 0); - $row->grant_delete = ($access['delete'][$rid] > 0); - drupal_write_record('book_access', $row); + foreach ($grants as $grant => $var) { + $row->$grant = (!empty($access[$var][$rid])); + } + + drupal_write_record('book_access_role', $row); + + // 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'])) { + if ($user = user_load(array('name' => $access['search_user']))) { + $row->uid = $user->uid; + + db_query("DELETE FROM {book_access_user} WHERE nid = %d AND uid = %d", $nid, $user->uid); + + foreach ($grants as $grant => $var) { + $row->$grant = (!empty($access["user_$var"])); + } + + drupal_write_record('book_access_user', $row); + + // If an existing user has been changed, update that user. + if (!empty($access['users'])) { + foreach($access['users'] as $uid => $checked) { + if ($user = user_load($uid)) { + $row->uid = $user->uid; + + db_query( + "DELETE FROM {book_access_user} WHERE nid = %d AND uid = %d", + $book_nid, $user->uid + ); + + if (!$checked['remove']) { + foreach ($grants as $grant => $var) { + $row->$grant = !empty($checked[$var]); + } + + drupal_write_record('book_access_user', $row); + } + } + } + } + } + } } } } @@ -436,6 +566,16 @@ function _book_access_restrict_options(& $permitted_bids[$result->nid] = $result->nid; } + // Add in the per-user grants. + $results = db_query( + "SELECT nid FROM {node_access} WHERE realm = 'book_access_user' AND gid = %d AND grant_update > 0", + $user->uid + ); + + while ($result = db_fetch_object($results)) { + $permitted_bids[$result->nid] = $result->nid; + } + if (isset($options)) { foreach ($options as $bid => $value) { if ($bid > 0 && !isset($permitted_bids[$bid])) {