book_access.module in Book access 6
Same filename and directory in other branches
Allows access control for book nodes on a per book basis. It is based on forum_access.module and tac_lite.module.
File
book_access.moduleView source
<?php
/**
* @file
*
* Allows access control for book nodes on a per book basis.
* It is based on forum_access.module and tac_lite.module.
*/
define('BOOK_ACCESS_GRANT_PRIORITY', 0);
/**
* Implements hook_form_alter().
*
* @see book_outline_form()
* @see book_access_outline_form_submit()
* @see book_access_edit_form_submit()
*
*/
function book_access_form_alter(&$form, &$form_state, $form_id) {
if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] . '_node_form' == $form_id) {
if (isset($form['book']['bid']['#options'])) {
_book_access_options_restrict($form['book']['bid']['#options']);
}
$form['#submit'][] = 'book_access_edit_form_submit';
}
elseif ($form_id == 'book_outline_form') {
if (isset($form['book']['bid']['#options'])) {
_book_access_options_restrict($form['book']['bid']['#options']);
}
$form['#submit'][] = 'book_access_outline_form_submit';
if (isset($form['remove'])) {
$form['remove']['#submit'][] = 'book_access_edit_form_submit';
}
}
}
/**
* Implements hook_form_FORM_ID_alter() for book_admin_settings().
*/
function book_access_form_book_admin_settings_alter(&$form, &$form_state) {
$form['book_access_default_roles_access'] = array(
'#type' => 'checkboxes',
'#title' => t('Default roles access'),
'#default_value' => variable_get('book_access_default_roles_access', array()),
'#options' => array(
'view' => t('View book'),
'update' => t('Edit pages'),
'delete' => t('Delete pages'),
),
'#description' => t('The default access values for the new roles.'),
);
$form['book_access_default_users_access'] = array(
'#type' => 'checkboxes',
'#title' => t('Default users access'),
'#default_value' => variable_get('book_access_default_users_access', array()),
'#options' => array(
'view' => t('View book'),
'update' => t('Edit pages'),
'delete' => t('Delete pages'),
),
'#description' => t('The default access values for the new users.'),
);
$form['buttons']['#weight'] = 100;
}
/**
* Implements hook_help().
*/
function book_access_help($path, $arg) {
switch ($path) {
case 'admin/help#book_access':
$help = '<p>' . t('Allow access control for book nodes on a per book basis.</p>
<p><a href="@permissions">Permissions enabled</a> will override the
module access settings. For example, if you would like a role to be able
to edit all book pages, enable <q>edit any book content</q> in
<a href="@permissions">Permissions</a>.
If you would like to control edit permission on a per book basis,
disable that permission in <a href="@permissions">Permissions</a> and
configure the module accordingly.', array(
'@permissions' => url('admin/user/permissions'),
)) . '</p>' . '<p>' . t('Certain access control modules can impact functionality of this
module. Broad reaching modules such as "taxonomy access" and "content
access" can override the values set in the <em>Book access</em> settings page.
You must turn off all enabled access controls in such modules.</p>
<p>If you are using additional access control modules, be
certain that none of them are allowing access to book nodes. The simplest
way to do this is to limit the types of pages that a book may contain to
a single node type (such as <q>book page</q>) and unset any access grants
provided by other modules on that node type configuration page.') . '</p>';
break;
case 'node/%/book_access':
$help = '<p>' . t('Configure access control per book based on users or roles. Settings
affect all pages within the given book.
These settings will have no effect for roles with permission to administer nodes.') . '</p>';
break;
default:
$help = '';
}
return $help;
}
/**
* Implements hook_link_alter().
*
* Enables the link "Add child page" only for users who have the right
* permission.
*/
function book_access_link_alter(&$links, $node) {
global $user;
if (user_access('administer nodes')) {
return;
}
if (!empty($node->book['bid']) && !empty($links['book_add_child'])) {
$bool = (user_access('add content to books') && book_access_grant_check($node->book['bid'], 'update') || user_access('administer book outlines')) && node_access('create', variable_get('book_child_type', 'book')) && $node->status == 1 && $node->book['depth'] < MENU_MAX_DEPTH;
if (!$bool) {
unset($links['book_add_child']);
}
}
}
/**
* Implements hook_menu().
*/
function book_access_menu() {
$item = array();
// We create an additional tab in each top-level book view page.
$items['node/%node/book_access'] = array(
'title' => 'Book access',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'book_access_permissions_form',
1,
),
'access callback' => 'book_access_permissions_form_access',
'access arguments' => array(
1,
),
'type' => MENU_LOCAL_TASK,
'weight' => 2,
'file' => 'book_access.admin.inc',
);
$items['book_access/delete/user_permission/%node/%user'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array(
'book_access_remove_user_permissions',
3,
4,
),
'access arguments' => array(
'administer book access',
),
'type' => MENU_CALLBACK,
'file' => 'book_access.admin.inc',
);
return $items;
}
/**
* Implements hook_menu_alter().
*
* @see book_access_outline_access()
* @see book_access_outline_remove_access()
*/
function book_access_menu_alter(&$items) {
if (isset($items['node/%node/outline'])) {
$items['node/%node/outline']['access callback'] = 'book_access_outline_access';
}
if (isset($items['node/%node/outline/remove'])) {
$items['node/%node/outline/remove']['access callback'] = 'book_access_outline_remove_access';
}
}
/**
* Implements hook_node_access_explain().
*
* hook_node_access_explain() is defined in devel_node_access.module, which
* helps you to figure out how node access works and what permissions are
* currently granted.
*/
function book_access_node_access_explain($row) {
static $roles = NULL;
$result = array();
if ($row->realm == 'book_access_role') {
if (!isset($roles)) {
$roles = user_roles();
}
if (isset($roles[$row->gid])) {
$result = array(
t('Grants for users of role %role', array(
'%role' => $roles[$row->gid],
)),
);
}
else {
$result = array(
t('Unknown group ID %gid', array(
'%gid' => $row->gid,
)),
);
}
}
elseif ($row->realm == 'book_access_user') {
if ($user = user_load(array(
'uid' => $row->gid,
))) {
$result = array(
t('Grants for user %username', array(
'%username' => $user->name,
)),
);
}
else {
$result = array(
t('Unknown user ID %gid', array(
'%gid' => $row->gid,
)),
);
}
}
return $result;
}
/**
* Implements hook_node_access_records().
*
* Returns a list of grant records for the book node object passed as argument.
* If we have a book child page, we return the access settings of the top level
* parent book page node.
*/
function book_access_node_access_records($node) {
$grants = array();
if (!empty($node->book['bid'])) {
$bid = $node->book['bid'];
_book_access_node_access_records_roles($grants, $bid);
_book_access_node_access_records_users($grants, $bid);
}
return $grants;
}
/**
* Implements hook_node_grants().
*/
function book_access_node_grants($account, $op) {
$grants['book_access_role'] = array_keys($account->roles);
$grants['book_access_user'] = array(
$account->uid,
);
return $grants;
}
/**
* Implements hook_nodeapi().
*/
function book_access_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
if ($op == 'delete') {
db_query("DELETE FROM {book_access_role} WHERE nid = %d", $node->nid);
db_query("DELETE FROM {book_access_user} WHERE nid = %d", $node->nid);
}
}
/**
* Implements hook_perm().
*/
function book_access_perm() {
return array(
'administer book access',
);
}
/**
* Implements hook_theme().
*/
function book_access_theme() {
return array(
'book_access_permissions_form' => array(
'arguments' => array(
'form' => array(),
),
'file' => 'book_access.admin.inc',
),
);
}
/**
* Implements hook_user().
*/
function book_access_user($op, &$edit, &$account, $category = NULL) {
if ($op == 'delete') {
db_query("DELETE FROM {book_access_user} WHERE uid = %d", $account->uid);
}
}
/**
* Determines if the outline tab is accessible.
*
* @see book_access_menu_alter()
*/
function book_access_outline_access($node) {
if (empty($node->book['bid'])) {
return FALSE;
}
$view_access = node_access('view', $node);
if (user_access('administer book outlines') && $view_access) {
return TRUE;
}
return book_access_grant_check($node->book['bid'], 'update') && $view_access;
}
/**
* Determines if the user can remove nodes from the outline.
*
* @see book_access_menu_alter()
*/
function book_access_outline_remove_access($node) {
$bool = !empty($node->book['bid']) && $node->book['bid'] != $node->nid && book_access_outline_access($node);
return $bool;
}
/**
* Determines when the book access tab can be shown in the node edit page.
*
* @see book_access_menu()
*/
function book_access_permissions_form_access($node) {
$bool = !empty($node->book['bid']) && user_access('administer book access');
return $bool;
}
/**
* Form submission callback for node_form(), and book_outline_form().
*
* @see node_form()
* @see book_outline_form()
* @see book_access_form_alter()
*/
function book_access_edit_form_submit($form, &$form_state) {
_book_access_build_node_grants($form['#node']);
}
/**
* Form submission callback for book_outline_form().
*
* @see book_outline_form()
* @see book_access_form_alter()
*/
function book_access_outline_form_submit($form, &$form_state) {
$node = $form['#node'];
if (isset($form['plid']) && $form['plid'] != $form_state['values']['plid']) {
_book_access_build_node_grants($node);
}
}
/**
* Rebuilds the book page grants for the node passed as argument.
*
* @param $node
* The node for which the grants needs to be rebuilt.
*/
function _book_access_build_node_grants($node) {
if (!empty($node->nid) && !empty($node->book['bid'])) {
$grants = array();
_book_access_node_access_records_roles($grants, $node->book['bid']);
node_access_write_grants($node, $grants, 'book_access_role');
$grants = array();
_book_access_node_access_records_users($grants, $node->book['bid']);
node_access_write_grants($node, $grants, 'book_access_user');
}
}
/**
* Checks if a user has access to the book passed as argument.
*
* @param $bid
* The ID of the book to check.
* @param $grant
* The permission to check for.
* @param $account
* The user account for which the permission is checked; if it is not passed,
* the permission is checked against the current logged in user.
*
* @return
* TRUE if the user has the permission, FALSE otherwise.
*/
function book_access_grant_check($bid, $grant, $account = NULL) {
if (!isset($account)) {
$account = $GLOBALS['user'];
}
$roles = array_keys($account->roles);
$result = db_result(db_query_range("SELECT 1 FROM {book_access_role} WHERE nid = %d AND rid IN (" . db_placeholders($roles) . ") AND grant_{$grant} > 0", array_merge(array(
$bid,
), $roles), 0, 1)) || db_result(db_query_range("SELECT 1 FROM {book_access_user} WHERE nid = %d AND uid = %d AND grant_{$grant} > 0", $bid, $account->uid, 0, 1));
return $result;
}
/**
* Gets the list of grant records assigned to user roles for a book.
*
* @param $grants
* An array of grants.
* @param $bid
* The ID of the book for which the function returns the grant records.
*/
function _book_access_node_access_records_roles(&$grants, $bid) {
$result = db_query('SELECT * FROM {book_access_role} WHERE nid = %d', $bid);
while ($grant = db_fetch_object($result)) {
$grants[] = array(
'realm' => 'book_access_role',
'gid' => $grant->rid,
'grant_view' => $grant->grant_view,
'grant_update' => $grant->grant_update,
'grant_delete' => $grant->grant_delete,
'priority' => BOOK_ACCESS_GRANT_PRIORITY,
);
}
}
/**
* Gets the list of grant records assigned to users for a book.
*
* @param $grants
* An array of grants.
* @param $bid
* The ID of the book for which the function returns the grant records.
*/
function _book_access_node_access_records_users(&$grants, $bid) {
$result = db_query("SELECT * FROM {book_access_user} WHERE nid = %d", $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,
);
}
}
/**
* Restricts the options available to who moves book pages between books.
*
* We don't want users to be able to add child pages to pages they do not
* have update grants for; therefore, we remove select options which point
* to book pages user does not have that grant.
*
* @param $options
* The options array used from book_outline_form() and the book edit form for
* the list of books to which the page can be moved to.
*
* @see book_access_form_alter()
*/
function _book_access_options_restrict(&$options) {
if (user_access('administer nodes')) {
return;
}
$permitted_bids = _book_access_user_books_list('update');
if (isset($options)) {
foreach ($options as $bid => $value) {
if ($bid > 0 && !isset($permitted_bids[$bid])) {
unset($options[$bid]);
}
}
}
}
function _book_access_user_books_list($grant) {
global $user;
$permitted_bids = array();
$roles = array_keys($user->roles);
$query = db_query("SELECT nid FROM {book_access_role} WHERE rid IN (" . db_placeholders($roles) . ") AND grant_{$grant} > 0", $roles);
while ($result = db_fetch_object($query)) {
$permitted_bids[$result->nid] = $result->nid;
}
$query = db_query("SELECT nid FROM {book_access_user} WHERE uid = %d AND grant_{$grant} > 0", $user->uid);
while ($result = db_fetch_object($query)) {
$permitted_bids[$result->nid] = $result->nid;
}
return $permitted_bids;
}
Functions
Constants
Name | Description |
---|---|
BOOK_ACCESS_GRANT_PRIORITY | @file |