book_access.module in Book access 6.2
Same filename and directory in other branches
Allows to set the 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 to set the access control for book nodes on a per book basis.
* It is based on forum_access.module and tac_lite.module.
*/
final class BookAccess {
/**
* The API version implemented by the module.
*/
const API_VERSION = '2.4';
/**
* The default priority used by the access grants implemented by the module.
*/
const GRANT_PRIORITY = 0;
/**
* Adds author grants to book pages.
*
* @param $bid
* The book ID.
* @param $uid
* The user ID of the book author.
* @param $grants
* An array of grants, in the format @code $grants[$grant] @endcode,
* where @code $grant @endcode is a string between 'grant_view',
* 'grant_update', 'grant_delete', 'grant_admin_access', 'grant_add_child',
* 'grant_edit_outline'.
*/
public static function addAuthorGrants($bid, $uid, array $grants) {
$row = new stdClass();
$row->nid = $bid;
$row->uid = $uid;
$bool = db_result(db_query_range("SELECT 1 FROM {book_access_author} WHERE nid = %d AND uid = %d", $bid, $uid, 0, 1));
foreach (self::grantIDs() as $id) {
$row->{$id} = !empty($grants[$id]);
}
drupal_write_record('book_access_author', $row, $bool ? array(
'nid',
'uid',
) : array());
}
/**
* Adds role grants to book pages.
*
* @param $bid
* The book ID.
* @param $rids
* An array of role IDs for which to add the book grants.
* @param $grants
* An array of grants, in the format @code $grants[$grant][$rid] @endcode,
* where @code $grant @endcode is a string between 'grant_view',
* 'grant_update', 'grant_delete', 'grant_admin_access', 'grant_add_child',
* 'grant_edit_outline', and @code $rid @endcode is the role ID.
*/
public static function addRoleGrants($bid, array $rids, array $grants) {
$row = new stdClass();
$row->nid = $bid;
foreach ($rids as $rid) {
$row->rid = $rid;
$bool = db_result(db_query_range("SELECT 1 FROM {book_access_role} WHERE nid = %d AND rid = %d", $bid, $rid, 0, 1));
foreach (self::grantIDs() as $id) {
$row->{$id} = !empty($grants[$id][$rid]);
}
drupal_write_record('book_access_role', $row, $bool ? array(
'nid',
'rid',
) : array());
}
}
/**
* Adds user grants to book pages.
*
* @param $bid
* The book ID.
* @param $uids
* An array of user IDs for which to add the book grants.
* @param $grants
* An array of grants, in the format @code $grants[$grant][$uid] @endcode,
* where @code $grant @endcode is a string between 'grant_view',
* 'grant_update', 'grant_delete', 'grant_admin_access', 'grant_add_child',
* 'grant_edit_outline', and @code $uid @endcode is the user ID.
*/
public static function addUserGrants($bid, array $uids, array $grants) {
foreach ($uids as $uid) {
$bool = db_result(db_query_range("SELECT 1 FROM {book_access_user} WHERE nid = %d AND uid = %d", $bid, $uid, 0, 1));
$row = new stdClass();
$row->nid = $bid;
$row->uid = $uid;
foreach (self::grantIDs() as $id) {
$row->{$id} = !empty($grants[$id][$uid]);
}
drupal_write_record('book_access_user', $row, $bool ? array(
'nid',
'uid',
) : array());
}
}
/**
* Verifies the current API version is included between two values passed as
* arguments.
*
* @param $minimum
* The minimum API version required.
* @param $maximum
* The maximum version required. This argument is optional; the current API
* will be checked against this value only if it is passed to the function.
*
* @return
* TRUE, if the current API version is included between the passed values.
*/
public static function api($minimum, $maximum = NULL) {
if (version_compare(self::API_VERSION, $minimum, '<')) {
return FALSE;
}
if (isset($maximum) && version_compare(self::API_VERSION, $maximum, '>')) {
return FALSE;
}
return TRUE;
}
/**
* 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.
*/
public static function checkGrant($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_author} WHERE nid = %d AND uid = %d AND grant_{$grant} > 0", $bid, $account->uid, 0, 1)) || 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;
}
/**
* Deletes the book access grants from the database.
*
* @param $value
* The value to look for.
* param $field
* The database field where to look the value. The currently accepted
* values are 'bid', 'nid', 'uid', 'rid'.
* @param $types
* An array of grants types for which the function deletes records; the
* currently used values are 'author', 'role', 'user'. When this parameter
* is not passed, the method
*/
public static function deleteGrants($value, $field = 'nid', array $types = NULL) {
if (isset($types)) {
$tables = array();
foreach ($types as $type) {
switch ($type) {
case 'author':
case 'role':
case 'user':
$tables[] = "book_access_{$type}";
break;
}
}
}
else {
switch ($field) {
case 'bid':
case 'nid':
$tables = array(
'book_access_author',
'book_access_role',
'book_access_user',
);
break;
case 'uid':
$tables = array(
'book_access_author',
'book_access_user',
);
break;
case 'rid':
$tables = array(
'book_access_role',
);
break;
default:
$tables = array();
}
}
foreach ($tables as $table) {
db_query("DELETE FROM {" . $table . "} WHERE {$field} = %d", $value);
}
}
/**
* Returns the book author grants.
*
* @param $bid
* The book ID.
* @param $uid
* The user ID for the book author.
* @param $defaults
* An array containing the default values for the grants.
*
* @return
* An array of grants, in the format @code $grants[$grant] @endcode,
* where @code $grant @endcode is a string between 'grant_view',
* 'grant_update', 'grant_delete', 'grant_admin_access', 'grant_add_child',
* 'grant_edit_outline'.
*/
public static function getAuthorGrants($bid, $uid, array $defaults = array()) {
$grants = db_fetch_array(db_query_range('SELECT * FROM {book_access_author} WHERE nid = %d AND uid = %d', $bid, $uid, 0, 1));
if (!$grants) {
$defaults = array_filter($defaults);
foreach (self::grantIDs() as $id) {
$grants[$id] = !empty($defaults[$id]);
}
}
return $grants;
}
/**
* Lists all the books to which the current user has access.
*
* @param $account
* The user account to use; if NULL, the currently logged in user account
* will be used.
* @param $grants
* An array containing one or more values between 'view', 'update',
* 'delete', 'admin_access', 'add_child', and 'edit_outline'.
*
* @return
* An array containing the node ID of the books to which the user has access.
*/
public static function getBookList(array $grants = array(
'update',
), $account = NULL) {
$permitted_bids = array();
if (!isset($account)) {
$account = $GLOBALS['user'];
}
foreach ($grants as $grant) {
$query = db_query("SELECT nid FROM {book_access_author} WHERE uid = %d AND grant_{$grant} > 0", $account->uid);
while ($result = db_fetch_object($query)) {
$permitted_bids[$result->nid] = $result->nid;
}
$roles = array_keys($account->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", $account->uid);
while ($result = db_fetch_object($query)) {
$permitted_bids[$result->nid] = $result->nid;
}
}
return $permitted_bids;
}
/**
* Gets the list of grant records assigned to a book.
*
* @param $bid
* The ID of the book for which the function returns the grant records.
* @param $types
* An array of grants types for which the function returns the records; the
* currently used values are 'author', 'role', 'user'.
*
* @return
* The array of grant records for the specified book.
*/
public static function getGrantRecords($bid, array $types = array(
'author',
'role',
'user',
)) {
$grants = array();
$info = array();
foreach ($types as $type) {
switch ($type) {
case 'author':
$info[] = array(
'table' => 'book_access_author',
'gid' => 'uid',
);
break;
case 'role':
$info[] = array(
'table' => 'book_access_role',
'gid' => 'rid',
);
break;
case 'user':
$info[] = array(
'table' => 'book_access_user',
'gid' => 'uid',
);
break;
}
}
foreach ($info as $data) {
$result = db_query("SELECT * FROM {" . db_escape_table($data['table']) . "} WHERE nid = %d", $bid);
while ($grant = db_fetch_object($result)) {
$grants[] = array(
'realm' => $data['table'],
'gid' => $grant->{$data['gid']},
'grant_view' => $grant->grant_view,
'grant_update' => $grant->grant_update,
'grant_delete' => $grant->grant_delete,
'priority' => self::GRANT_PRIORITY,
);
}
}
return $grants;
}
/**
* Returns the role book grants.
*
* @param $bid
* The book ID.
* @param $roles
* The variables where to store the value returned by user_roles().
* @param $defaults
* An array containing the default values for the grants.
*
* @return
* An array of grants, in the format @code $grants[$grant][$rid] @endcode,
* where @code $grant @endcode is a string between 'grant_view',
* 'grant_update', 'grant_delete', 'grant_admin_access', 'grant_add_child',
* 'grant_edit_outline', and @code $rid @endcode is the role ID.
*/
public static function getRoleGrants($bid, &$roles, array $defaults = array()) {
$grants = array(
'grant_view' => array(),
'grant_update' => array(),
'grant_delete' => array(),
'grant_admin_access' => array(),
'grant_add_child' => array(),
'grant_edit_outline' => array(),
);
$roles = user_roles();
$rcopy = $roles;
$rids = array_keys($roles);
$result = db_query("SELECT * FROM {book_access_role} WHERE nid = %d AND rid IN (" . db_placeholders($rids) . ")", array_merge(array(
$bid,
), $rids));
// Build the role access permissions for the book.
while ($access = db_fetch_object($result)) {
unset($rcopy[$access->rid]);
foreach (self::grantIDs() as $id) {
$grants[$id][$access->rid] = !empty($access->{$id});
}
}
$defaults = array_filter($defaults);
// Set the default role access permissions for the roles that don't have
// access permissions already set.
foreach (self::grantIDs() as $id) {
foreach ($rcopy as $rid => $name) {
$grants[$id][$rid] = !empty($defaults[$id]);
}
}
return $grants;
}
/**
* Returns the user book grants.
*
* @param $bid
* The book ID.
* @param $uids
* The variables where to store the user IDs.
* @param $defaults
* An array containing the default values for the grants.
*
* @return
* An array of grants, in the format @code $grants[$grant][$uid] @endcode,
* where @code $grant @endcode is a string between 'grant_view',
* 'grant_update', 'grant_delete', 'grant_admin_access', 'grant_add_child',
* 'grant_edit_outline', and @code $uid @endcode is the user ID.
*/
public static function getUserGrants($bid, &$uids) {
$grants = array(
'grant_view' => array(),
'grant_update' => array(),
'grant_delete' => array(),
'grant_admin_access' => array(),
'grant_add_child' => array(),
'grant_edit_outline' => array(),
);
$uids = array();
$result = db_query("SELECT * FROM {book_access_user} bau INNER JOIN {users} u ON u.uid = bau.uid WHERE bau.nid = %d", $bid);
while ($access = db_fetch_object($result)) {
$uid = $access->uid;
$uids[$uid] = $uid;
foreach (self::grantIDs() as $id) {
$grants[$id][$uid] = !empty($access->{$id});
}
}
return $grants;
}
/**
* Returns the grant IDs implemented by the module.
*/
public static function grantIDs() {
return array(
'grant_view',
'grant_update',
'grant_delete',
'grant_admin_access',
'grant_add_child',
'grant_edit_outline',
);
}
/**
* Restricts the options available to who moves book pages between books.
*
* @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.
* @param $account
* The user account to use; if NULL, the currently logged in user account
* will be used.
*/
public static function restrictOptions(&$options, $account = NULL) {
if (!isset($account)) {
$account = $GLOBALS['user'];
}
if (user_access('administer nodes', $account)) {
return;
}
$permitted_bids = self::getBookList(array(
'update',
), $account);
foreach ($options as $bid => $value) {
if ($bid > 0 && !isset($permitted_bids[$bid])) {
unset($options[$bid]);
}
}
}
/**
* Sets the author grants for book pages.
*
* The method delete the author grants before to set the new ones.
*
* @param $bid
* The book ID.
* @param $uid
* The user ID of the book author.
* @param $grants
* An array of grants, in the format @code $grants[$grant] @endcode,
* where @code $grant @endcode is a string between 'grant_view',
* 'grant_update', 'grant_delete', 'grant_admin_access', 'grant_add_child',
* 'grant_edit_outline'.
*/
public static function setAuthorGrants($bid, $uid, array $grants) {
db_query("DELETE FROM {book_access_author} WHERE nid = %d", $bid);
self::addAuthorGrants($bid, $uid, $grants);
}
/**
* Sets the role grants for book pages.
*
* The method delete the role grants before to set the new ones.
*
* @param $bid
* The book ID.
* @param $rids
* An array of role IDs for which to add the book grants.
* @param $grants
* An array of grants, in the format @code $grants[$grant][$rid] @endcode,
* where @code $grant @endcode is a string between 'grant_view',
* 'grant_update', 'grant_delete', 'grant_admin_access', 'grant_add_child',
* 'grant_edit_outline', and @code $rid @endcode is the role ID.
*/
public static function setRoleGrants($bid, array $rids, array $grants) {
db_query("DELETE FROM {book_access_role} WHERE nid = %d", $bid);
self::addRoleGrants($bid, $rids, $grants);
}
/**
* Sets the user grants for book pages.
*
* The method delete the user grants before to set the new ones.
*
* @param $bid
* The book ID.
* @param $uids
* An array of user IDs for which to add the book grants.
* @param $grants
* An array of grants, in the format @code $grants[$grant][$uid] @endcode,
* where @code $grant @endcode is a string between 'grant_view',
* 'grant_update', 'grant_delete', 'grant_admin_access', 'grant_add_child',
* 'grant_edit_outline', and @code $uid @endcode is the user ID.
*/
public static function setUserGrants($bid, array $uids, array $grants) {
db_query("DELETE FROM {book_access_user} WHERE nid = %d", $bid);
self::addUserGrants($bid, $uids, $grants);
}
/**
* Updates the grants for a book.
*
* @param $bid
* The book ID.
* @param $field
* The field to update.
* @param $value
* The value to set for the field.
* @param $types
* An array containing the grant types; the currently used values are
* 'author', 'role', 'user'. It is responsibility of the caller to use
* values for $types for which $field is valid.
*/
public static function updateGrants($bid, $field, $value, array $types = array(
'author',
'role',
'user',
)) {
foreach ($types as $type) {
switch ($type) {
case 'author':
case 'role':
case 'user':
$table = "book_access_{$type}";
break;
default:
$table = '';
}
if ($table) {
db_query("UPDATE {" . $table . "} SET {$field} = %d WHERE nid = %d", $value, $bid);
}
}
}
/*
* Writes the grant records to the Drupal node access table.
*
* @param $node
* The node object for the book page.
* @param $types
* An array of grants types that are written; the currently used values are
* 'author', 'role', 'user'.
*/
public static function writeGrantRecords($node, array $types = array(
'author',
'role',
'user',
)) {
if (!empty($node->nid) && !empty($node->book['bid'])) {
foreach ($types as $type) {
switch ($type) {
case 'author':
case 'role':
case 'user':
$table = "book_access_{$type}";
break;
default:
$table = '';
}
if ($table && ($grants = self::getGrantRecords($node->book['bid'], array(
$type,
)))) {
node_access_write_grants($node, $grants, $table, TRUE);
}
}
}
}
}
/**
* Allows to access the Drupal variables used by the module.
*/
class BookAccessVars extends Vars {
/**
* Constructs a BookAccessVars object.
*/
public function __construct() {
parent::__construct('book_access');
}
/**
* Implements Vars::getDefaults().
*/
protected function getDefaults() {
return array(
'book_access_default_authors_access' => array(
'value' => array(),
),
'book_access_default_roles_access' => array(
'value' => array(),
),
'book_access_default_users_access' => array(
'value' => array(),
),
);
}
}
/**
* 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'])) {
BookAccess::restrictOptions($form['book']['bid']['#options']);
}
$form['#submit'][] = 'book_access_edit_form_submit';
}
elseif ($form_id == 'book_outline_form') {
if (isset($form['book']['bid']['#options'])) {
BookAccess::restrictOptions($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_link_alter().
*
* Enables the link "Add child page" only for users who have the right
* permission.
*/
function book_access_link_alter(&$links, $node) {
if (user_access('administer nodes')) {
return;
}
if (!empty($node->book['bid']) && !empty($links['book_add_child'])) {
$vars = new BookAccessVars();
$bool = (user_access('add content to books') && BookAccess::checkGrant($node->book['bid'], 'add_child') || user_access('administer book outlines')) && node_access('create', $vars['book_child_type']) && $node->status == 1 && $node->book['depth'] < MENU_MAX_DEPTH;
if (!$bool) {
unset($links['book_add_child']);
}
}
}
/**
* 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_author') {
$result = array(
t('Grants for book authors'),
);
}
elseif ($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'])) {
$grants = BookAccess::getGrantRecords($node->book['bid']);
}
return $grants;
}
/**
* Implements hook_node_grants().
*/
function book_access_node_grants($account, $op) {
$grants = array();
$grants['book_access_author'] = array(
$account->uid,
);
$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) {
switch ($op) {
case 'delete':
BookAccess::deleteGrants($node->nid);
break;
case 'update':
BookAccess::updateGrants($node->nid, 'uid', $node->uid, array(
'author',
));
break;
}
}
/**
* Implements hook_perm().
*/
function book_access_perm() {
return array(
'administer book access',
'administer access of any books',
'administer access of own books',
);
}
/**
* Implements hook_user().
*/
function book_access_user($op, &$edit, &$account, $category = NULL) {
if ($op == 'delete') {
BookAccess::deleteGrants($account->uid, '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 BookAccess::checkGrant($node->book['bid'], 'edit_outline') && $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;
}
/**
* Form submission callback for node_form().
*
* @see node_form()
* @see book_outline_form()
* @see book_access_form_alter()
*/
function book_access_edit_form_submit($form, &$form_state) {
BookAccess::writeGrantRecords($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) {
if (isset($form['plid']) && $form['plid'] != $form_state['values']['plid']) {
BookAccess::writeGrantRecords($form['#node']);
}
}
Functions
Name | Description |
---|---|
book_access_edit_form_submit | Form submission callback for node_form(). |
book_access_form_alter | Implements hook_form_alter(). |
book_access_link_alter | Implements hook_link_alter(). |
book_access_menu_alter | Implements hook_menu_alter(). |
book_access_nodeapi | Implements hook_nodeapi(). |
book_access_node_access_explain | Implements hook_node_access_explain(). |
book_access_node_access_records | Implements hook_node_access_records(). |
book_access_node_grants | Implements hook_node_grants(). |
book_access_outline_access | Determines if the outline tab is accessible. |
book_access_outline_form_submit | Form submission callback for book_outline_form(). |
book_access_outline_remove_access | Determines if the user can remove nodes from the outline. |
book_access_perm | Implements hook_perm(). |
book_access_user | Implements hook_user(). |
Classes
Name | Description |
---|---|
BookAccess | @file |
BookAccessVars | Allows to access the Drupal variables used by the module. |