book_access.module in Book access 5
Same filename and directory in other branches
Allows access control for Drupal book nodes.
Based on forum_access.module and tac_lite.module.
File
book_access.moduleView source
<?php
/**
* @file
*
* Allows access control for Drupal book nodes.
*
* Based on forum_access.module and tac_lite.module.
*
*
*/
define(BOOK_ACCESS_GRANT_PRIORITY, 0);
/**
* Implements hook_help()
*/
function book_access_help($section) {
switch ($section) {
case 'admin/content/book/access':
return '<p>' . t('
Configures the access control per books based on user 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.</p>
<p><em>Important:</em> If you are going to manage access control here,
please disable the <q>edit book pages</q> and <q>edit own book pages</q>
permissions in the <a href="@access-control">access control</a> page or
else you may see unexpected behavior.</p>
<p>These settings will have no effect for roles with <q>administer nodes</q>
permission.</p>
<p>For more information, see the <a href="@book-access-help">Book Access help</a> page.
' . '</p>', array(
'@book-access-help' => url('admin/help/book_access'),
'@access-control' => url('admin/user/access'),
));
break;
case 'admin/help#book_access':
return '<p>' . t('
Allows fine grained access control for books.</p>
<p>Permissions enabled in the <a href="@access-control-settings">access control settings page</a> will
override the <a href="@book-access-settings">book access settings</a>. So, for example, if you would like a role to be
able to edit all book pages, regardless, enable <q>edit pages</q> in
the access control settings page. However, if you would like to control edit
permission on a per book basis, disable that permission in
the access control settings page and configure the book access settings
accordingly.
<p>Certain access control modules can impact functionality of this
module. Broad reaching modules such as <q>taxonomy access</q> and <q>content
access</q> can override the values set in the <a href="@book-access-settings">book access settings</a> page.
You must turn off all enabled access controls in such modules.
' . '</p>', array(
'@book-access-settings' => url('admin/content/book/access'),
'@access-control-settings' => url('admin/user/access'),
));
break;
}
}
/**
* Implements hook_perm().
*/
function book_access_perm() {
return array(
'administer book access',
);
}
/**
* Implements hook_menu().
*/
function book_access_menu($may_cache) {
if ($may_cache) {
// We create an additional tab in the book admin page.
$items[] = array(
'path' => 'admin/content/book/access',
'title' => t('Access control'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'book_access_admin_form',
),
'type' => MENU_LOCAL_TASK,
'weight' => 7,
'access' => user_access('administer book access'),
);
}
return $items;
}
/**
* Implements hook_node_grants().
*
* This function supplies the book access grants. book_access simply uses
* roles as grant IDs.
*/
function book_access_node_grants($user, $op) {
$grants['book_access'] = array_keys($user->roles);
return $grants;
}
/**
* Implements hook_node_access_records().
*
* Returns a list of grant records for the passed in book node object. If we
* have a book child page, we return the access settings of the top level parent.
* Checks to see if maybe we're being disabled.
*/
function book_access_node_access_records($node) {
if (!book_access_enabled()) {
return;
}
if ($node->type == 'book') {
$parent_nid = _book_access_get_book_nid($node->nid);
$result = db_query('SELECT * FROM {book_access} WHERE nid = %d', $parent_nid);
while ($grant = db_fetch_object($result)) {
$grants[] = array(
'realm' => 'book_access',
'gid' => $grant->rid,
'grant_view' => $grant->grant_view,
'grant_update' => $grant->grant_update,
'grant_delete' => $grant->grant_delete,
'priority' => BOOK_ACCESS_GRANT_PRIORITY,
);
}
return $grants;
}
}
/**
* Implements hook_form_alter().
*
*/
function book_access_form_alter($form_id, &$form) {
if ($form_id == 'book_node_form' && isset($form['parent'])) {
_book_access_restrict_options($form['parent']['#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.
if ($form_id == 'book_outline_form') {
$form['#submit'][] = '_book_access_build_node_grants';
}
}
/**
* Book access configuration page.
*
*/
function book_access_admin_form() {
$form = array();
$rids = array();
$books = array();
drupal_add_css(drupal_get_path('module', 'book_access') . '/book_access.css');
// Get a list of roles (which act as grant IDs).
$results = db_query("SELECT r.rid, r.name FROM {role} r ORDER BY r.name");
while ($result = db_fetch_object($results)) {
$rids[$result->rid] = $result->name;
}
// Get listing of books, each of which will have it's own access settings.
$sql = "\n SELECT n.nid, n.title\n FROM {node} n\n LEFT JOIN {book} b ON n.vid = b.vid\n WHERE b.parent = 0\n ORDER BY b.weight ASC\n ";
$book_results = db_query($sql);
while ($book = db_fetch_object($book_results)) {
$books[$book->nid] = $book->title;
}
// Each book has its own access control settings.
foreach ($books as $book_nid => $book_name) {
// Used to store existing grants for this book.
$view = array();
$update = array();
$delete = array();
$result = db_query("SELECT * FROM {book_access} where nid = %d", $book_nid);
// if no existing grants, use some safe defaults
if (db_num_rows($result) == 0) {
$view = array(
1,
2,
);
$update = array();
$delete = array();
}
else {
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;
}
}
}
$form['#tree'] = TRUE;
$form['access'][$book_nid] = array(
'#type' => 'fieldset',
'#title' => $book_name,
'#collapsible' => TRUE,
);
$form['access'][$book_nid]['view'] = array(
'#type' => 'checkboxes',
'#prefix' => '<div class="book-access-div">',
'#suffix' => '</div>',
'#options' => $rids,
'#title' => t('View this book'),
'#default_value' => $view,
);
$form['access'][$book_nid]['update'] = array(
'#type' => 'checkboxes',
'#prefix' => '<div class="book-access-div">',
'#suffix' => '</div>',
'#options' => $rids,
'#title' => t('Edit pages in this book'),
'#default_value' => $update,
);
$form['access'][$book_nid]['delete'] = array(
'#type' => 'checkboxes',
'#prefix' => '<div class="book-access-div">',
'#suffix' => '</div>',
'#options' => $rids,
'#title' => t('Delete pages in this book'),
'#default_value' => $delete,
);
}
$form['clearer'] = array(
'#value' => '<div class="book-access-clearer"></div>',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
$form['notice'] = array(
'#type' => 'markup',
'#value' => '<p>' . t('Node access tables must be rebuilt when these changes are
submitted. This may take a few moments.') . '</p>',
);
return $form;
}
function book_access_admin_form_submit($form_id, $form_values) {
foreach ($form_values['access'] as $book_nid => $form) {
db_query("DELETE FROM {book_access} WHERE nid = %d", $book_nid);
foreach ($form['view'] as $rid => $checked) {
$gid = $rid;
$grant_view = (bool) $checked;
$grant_update = $form['update'][$rid] > 0 ? TRUE : FALSE;
$grant_delete = $form['delete'][$rid] > 0 ? TRUE : FALSE;
$sql = "INSERT INTO {book_access} (nid, rid, grant_view, grant_update, grant_delete)\n VALUES (%d, %d, %d, %d, %d)";
db_query($sql, $book_nid, $rid, $grant_view, $grant_update, $grant_delete);
}
}
node_access_rebuild();
}
/**
* Helper function.
*/
function book_access_enabled($set = NULL) {
static $enabled = TRUE;
if (isset($set)) {
$enabled = $set;
}
return $enabled;
}
/**
* Returns the array of book nodes and their parents.
*/
function _book_access_get_parents() {
static $parents = array();
if (empty($parents)) {
$sql = "SELECT n.nid, b.parent\n FROM {node} n\n LEFT JOIN {book} b ON n.vid = b.vid\n WHERE n.type = 'book'";
$query = db_query($sql);
while ($result = db_fetch_object($query)) {
$parents[$result->nid] = $result->parent;
}
}
return $parents;
}
/**
* Somewhat redundant node grants function to allow us to set a node's grants
* when a book outline is modified.
*/
function _book_access_build_node_grants($form) {
$node = $form['#node'];
$grants = book_access_node_access_records($node);
node_access_write_grants($node, $grants, 'book_access');
}
/**
* Returns the very top level (book) nid for a given book page.
*/
function _book_access_get_book_nid($nid) {
$parents = _book_access_get_parents();
if ($parents[$nid] == 0) {
return $nid;
}
return _book_access_get_book_nid($parents[$nid]);
}
/**
* We don't want users to be able to add child pages to pages they do not
* have 'update' grants for, so we remove select options which point to book
* pages user does not have that grant for.
*/
function _book_access_restrict_options(&$options) {
global $user;
$permitted_nids = NULL;
if ($user->uid == 0 || user_access('administer nodes')) {
return;
}
$sql = "SELECT nid\n FROM {node_access}\n WHERE realm = 'book_access'\n AND gid IN (%s)\n AND grant_update > 0";
$results = db_query($sql, implode(',', array_keys($user->roles)));
while ($result = db_fetch_object($results)) {
$permitted_nids[$result->nid] = $result->nid;
}
if (!empty($options)) {
foreach ($options as $nid => $value) {
if ($nid > 0 && !isset($permitted_nids[$nid])) {
unset($options[$nid]);
}
}
}
}
Functions
Name | Description |
---|---|
book_access_admin_form | Book access configuration page. |
book_access_admin_form_submit | |
book_access_enabled | Helper function. |
book_access_form_alter | Implements hook_form_alter(). |
book_access_help | Implements hook_help() |
book_access_menu | Implements hook_menu(). |
book_access_node_access_records | Implements hook_node_access_records(). |
book_access_node_grants | Implements hook_node_grants(). |
book_access_perm | Implements hook_perm(). |
_book_access_build_node_grants | Somewhat redundant node grants function to allow us to set a node's grants when a book outline is modified. |
_book_access_get_book_nid | Returns the very top level (book) nid for a given book page. |
_book_access_get_parents | Returns the array of book nodes and their parents. |
_book_access_restrict_options | We don't want users to be able to add child pages to pages they do not have 'update' grants for, so we remove select options which point to book pages user does not have that grant for. |