content_access.module in Content Access 6
Same filename and directory in other branches
Content access module file.
File
content_access.moduleView source
<?php
/**
* @file Content access module file.
*/
/**
* Implementation of hook_menu().
*/
function content_access_menu() {
$items = array();
$items['node/%node/access'] = array(
'title' => 'Access control',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'content_access_page',
1,
),
'access callback' => 'content_access_node_page_access',
'access arguments' => array(
1,
),
'file' => 'content_access.admin.inc',
'weight' => 3,
'type' => MENU_LOCAL_TASK,
);
foreach (node_get_types('types', NULL, TRUE) as $type) {
$type_url_str = str_replace('_', '-', $type->type);
$items['admin/content/node-type/' . $type_url_str . '/access'] = array(
'title' => 'Access control',
'description' => 'Configure content access control.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'content_access_admin_settings',
$type->type,
),
'access callback' => 'content_access_admin_settings_access',
'access arguments' => array(),
'type' => MENU_LOCAL_TASK,
'file' => 'content_access.admin.inc',
'weight' => 1,
);
}
return $items;
}
/**
* Implementation of hook_init().
*
* Make node access settings page use admin theme if appropriate.
*
* @see system_init()
*/
function content_access_init() {
// Use the administrative theme if the user is looking at a page at node/%/access
if (variable_get('node_admin_theme', '0') && arg(0) == 'node' && arg(2) == 'access') {
global $custom_theme;
$custom_theme = variable_get('admin_theme', '0');
drupal_add_css(drupal_get_path('module', 'system') . '/admin.css', 'module');
}
}
function content_access_node_page_access($node) {
global $user;
return content_access_get_settings('per_node', $node->type) && user_access('grant content access') || content_access_get_settings('per_node', $node->type) && (user_access('grant own content access') && $user->uid == $node->uid);
}
function content_access_admin_settings_access() {
return user_access('administer nodes') && user_access('administer content types');
}
/**
* Implementation of hook_perm().
*/
function content_access_perm() {
return array(
'grant content access',
'grant own content access',
);
}
/**
* Implementation of hook_node_grants().
*/
function content_access_node_grants($account, $op) {
return array(
'content_access_author' => array(
$account->uid,
),
'content_access_rid' => array_keys($account->roles),
);
}
/**
* Implementation of hook_node_access_records()
*/
function content_access_node_access_records($node) {
if (content_access_disabling()) {
return;
}
// Apply per node settings if necessary.
if (content_access_get_settings('per_node', $node->type)) {
$grants = array();
foreach (array(
'view',
'update',
'delete',
) as $op) {
foreach (content_access_get_rids_per_node_op($op, $node) as $rid) {
$grants[$rid]['grant_' . $op] = 1;
}
}
foreach ($grants as $rid => $grant) {
$grants[$rid] = content_access_proccess_grant($grant, $rid, $node);
}
// Care for the author grant.
$grant = array();
foreach (array(
'view',
'update',
'delete',
) as $op) {
// Get all roles that have access to use $op on this node.
$any_roles = drupal_map_assoc(content_access_per_node_setting($op, $node));
$any_roles += $op != 'view' ? content_access_get_settings($op, $node->type) : array();
$grant['grant_' . $op] = content_access_own_op($node, $any_roles, content_access_get_rids_per_node_op($op . '_own', $node));
}
if (array_filter($grant)) {
$grant['realm'] = 'content_access_author';
$grants[] = content_access_proccess_grant($grant, $node->uid, $node);
}
}
else {
// Apply the content type defaults.
$grants = content_access_get_type_grant($node);
}
if (empty($grants)) {
// This means we grant no access.
$grants[] = content_access_proccess_grant(array(), 0, $node);
}
else {
content_access_optimize_grants($grants, $node);
}
return $grants;
}
/**
* Implementation of hook_nodeapi().
*/
function content_access_nodeapi($node, $op, $teaser, $page) {
if ($op == 'delete') {
db_query("DELETE FROM {content_access} WHERE nid = %d", $node->nid);
}
}
/**
* Used by the ACL module.
*/
function content_access_enabled() {
return !content_access_disabling();
}
/**
* Implementation of hook_disable().
*/
function content_access_disable() {
content_access_disabling(TRUE);
}
/**
* Remembers if we have disabled access.
*/
function content_access_disabling($set = NULL) {
static $disabling = FALSE;
if (isset($set)) {
$disabling = $set;
}
return $disabling;
}
/**
* Returns the content_access' settings.
*
* @param $return
* One of the content_access_available_settings(), e.g. 'view' or 'per_node'.
* @param $type
* If not all, return the setting for the specified type.
*/
function content_access_get_settings($return = 'all', $type = NULL) {
if ($return == 'all') {
return variable_get('content_access_settings', array());
}
if (isset($type)) {
$settings = content_access_get_settings($return);
return isset($settings[$type]) ? $settings[$type] : content_access_get_setting_defaults($return, $type);
}
if (!isset($type)) {
$settings = content_access_get_settings();
return isset($settings[$return]) ? $settings[$return] : array();
}
return array();
}
/**
* Saves the content_access settings - needs the complete settings array.
*/
function content_access_set_settings($settings) {
// Cleanup the settings before saving.
foreach (content_access_available_settings() as $setting) {
if (isset($settings[$setting])) {
foreach ($settings[$setting] as $type => $value) {
if (!isset($value)) {
unset($settings[$setting][$type]);
}
}
}
}
variable_set('content_access_settings', $settings);
}
/*
* Return an array containing all available content_access settings.
*/
function content_access_available_settings() {
return array(
'view',
'update',
'delete',
'view_own',
'update_own',
'delete_own',
'per_node',
'priority',
);
}
/**
* Defines default values for settings.
*/
function content_access_get_setting_defaults($setting, $type) {
switch ($setting) {
default:
return array();
case 'view':
case 'view_own':
return array(
DRUPAL_ANONYMOUS_RID,
DRUPAL_AUTHENTICATED_RID,
);
case 'update':
case 'update_own':
case 'delete':
case 'delete_own':
return content_access_get_permission_access(content_access_get_permission_by_op($setting, $type));
case 'priority':
return 0;
}
}
/**
* Returns an array of role ids, that contain the given permission.
*/
function content_access_get_permission_access($perm, $reset = FALSE) {
static $roles = array();
if ($reset) {
$roles = array();
}
if (!isset($roles[$perm]) && $perm) {
$roles[$perm] = array_keys(user_roles(0, $perm));
}
return isset($roles[$perm]) ? $roles[$perm] : array();
}
/**
* Gets the name of a permission for the given operation, if there is a suiting one.
*/
function content_access_get_permission_by_op($op, $type) {
switch ($op) {
default:
return FALSE;
case 'update':
return 'edit any ' . $type . ' content';
case 'update_own':
return 'edit own ' . $type . ' content';
case 'delete':
return 'delete any ' . $type . ' content';
case 'delete_own':
return 'delete own ' . $type . ' content';
}
}
/**
* Returns the default grants for a given node type.
*/
function content_access_get_type_grant($node) {
// Cache per type default grants in a static array
static $defaults = array();
if (!isset($defaults[$node->type])) {
$grants = array();
// Only process the 'view' op as node_access() will take care of edit and delete
foreach (content_access_get_settings('view', $node->type) as $rid) {
$grants[$rid]['grant_view'] = 1;
$grants[$rid] = content_access_proccess_grant($grants[$rid], $rid, $node);
}
$defaults[$node->type] = $grants;
}
// Care for the author grant.
$grant = $grants = array();
$grant['grant_view'] = content_access_own_op($node, content_access_get_settings('view', $node->type), content_access_get_settings('view_own', $node->type));
if ($grant['grant_view']) {
$grant['realm'] = 'content_access_author';
$grants = array(
'author' => content_access_proccess_grant($grant, $node->uid, $node),
);
}
return $defaults[$node->type] + $grants;
}
/**
* Process a grant, which means add priority, realm and other properties.
*/
function content_access_proccess_grant($grant, $gid, $node) {
$grant += array(
'grant_view' => 0,
'grant_update' => 0,
'grant_delete' => 0,
'realm' => 'content_access_rid',
);
$grant['gid'] = $gid;
$grant['priority'] = content_access_get_settings('priority', $node->type);
return $grant;
}
/**
* Determines the grant for the node author and the given allowed roles of a operation.
*
* @param $any_roles
* The roles with which anybody has access (not optimized!)
* @param $own_roles
* The roles with which only the author has acess (optimized!)
*/
function content_access_own_op($node, $any_roles, $own_roles) {
static $roles = array();
if (!isset($roles[$node->uid])) {
$roles[$node->uid] = $node->uid ? array(
DRUPAL_AUTHENTICATED_RID,
) : array(
DRUPAL_ANONYMOUS_RID,
);
$result = db_query('SELECT rid FROM {users_roles} WHERE uid = %d', $node->uid);
while ($role = db_fetch_object($result)) {
$roles[$node->uid][] = $role->rid;
}
}
if (array_intersect($roles[$node->uid], $any_roles)) {
// If there is access due to "any permissions" there is no need to at an author grant.
return 0;
}
return array_intersect($roles[$node->uid], $own_roles) ? 1 : 0;
}
/**
* Returns optimized role ids for the given operation and node to
* grant access for.
*
* If to a role access is granted by permissions, it's not necessary
* to write a grant for it. So it won't be returned.
*
* @param $op
* One of the supported operations.
* @param $node
* The node object.
*/
function content_access_get_rids_per_node_op($op, $node) {
$rids = content_access_per_node_setting($op, $node);
if ($permission = content_access_get_permission_by_op($op, $node->type)) {
$perm_roles = content_access_get_permission_access($permission);
$rids = array_diff($rids, $perm_roles);
if (in_array(DRUPAL_AUTHENTICATED_RID, $perm_roles)) {
return in_array(DRUPAL_ANONYMOUS_RID, $rids) ? array(
DRUPAL_ANONYMOUS_RID,
DRUPAL_AUTHENTICATED_RID,
) : array(
DRUPAL_AUTHENTICATED_RID,
);
}
}
return $rids;
}
/**
* Returns the per node role settings. If no per node settings are available,
* it will return the content type settings.
*
* @param $op
* One of the supported operations.
* @param $node
* The node object.
* @param $settings
* Optional array used to update the settings cache with the given settings.
* @return
* An array of role ids which have access.
*/
function content_access_per_node_setting($op, $node, $settings = NULL) {
static $grants = array();
if (isset($settings)) {
// Update settings cache
$grants[$node->nid] = $settings;
return;
}
if (!isset($grants[$node->nid]) || $grants[$node->nid] === FALSE) {
$grants[$node->nid] = content_access_get_per_node_settings($node);
}
// Return the content type defaults if no per node settings are available
return isset($grants[$node->nid][$op]) ? $grants[$node->nid][$op] : content_access_get_settings($op, $node->type);
}
/**
* Saves custom per node settings in the own content_access table.
*/
function content_access_save_per_node_settings($node, $settings) {
$count = db_result(db_query('SELECT COUNT(nid) FROM {content_access} WHERE nid = %d', $node->nid));
// settings exists
if ($count > 0) {
db_query("UPDATE {content_access} SET settings = '%s' WHERE nid = %d", serialize($settings), $node->nid);
}
else {
db_query("INSERT INTO {content_access} (nid, settings) VALUES(%d, '%s')", $node->nid, serialize($settings));
}
// Make content_access_per_node_setting() use the new settings
content_access_per_node_setting(NULL, $node, $settings);
}
/**
* Deletes all custom per node settings, so that content type defaults are used again.
*/
function content_access_delete_per_node_settings($node) {
db_query("DELETE FROM {content_access} WHERE nid = %d", $node->nid);
// Clear the cache.
content_access_per_node_setting(NULL, $node, FALSE);
// Delete possible acl settings
if (module_exists('acl')) {
foreach (array(
'view',
'update',
'delete',
) as $op) {
$acl_id = content_access_get_acl_id($node, $op);
acl_delete_acl($acl_id);
}
}
}
/**
* Gets the content access acl id of the node.
*/
function content_access_get_acl_id($node, $op) {
$acl_id = acl_get_id_by_name('content_access', $op . '_' . $node->nid);
if (!$acl_id) {
// Create one:
$acl_id = acl_create_new_acl('content_access', $op . '_' . $node->nid);
}
return $acl_id;
}
/**
* Gets the per node settings of a node.
*
* @note
* This function won't apply defaults, so if there are no other settings
* it will return an empty array.
*/
function content_access_get_per_node_settings($node) {
$settings = db_result(db_query("SELECT settings FROM {content_access} WHERE nid = %d", $node->nid));
if (!$settings) {
return array();
}
return unserialize($settings);
}
/**
* Removes grants that doesn't change anything.
*
* @note
* The grants are compared with the normal access control settings.
*/
function content_access_optimize_grants(&$grants, $node) {
$rids = array(
'view' => array(),
'update' => array(),
'delete' => array(),
);
foreach ($grants as $key => $grant) {
foreach (array(
'view',
'update',
'delete',
) as $op) {
if (is_numeric($key) && !empty($grant['grant_' . $op])) {
$rids[$op][] = $key;
}
}
}
// Detect if all are allowed to view
$all = array(
DRUPAL_ANONYMOUS_RID,
DRUPAL_AUTHENTICATED_RID,
);
if (count(array_diff($all, $rids['view'])) == 0) {
//grant view access to all instead of single roles
$rids['view'] = array(
'all',
);
$grants['all'] = array(
'realm' => 'all',
'gid' => 0,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => content_access_get_settings('priority', $node->type),
);
}
// If authenticated users are involved, remove unnecessary other roles.
foreach (array(
'view',
'update',
'delete',
) as $op) {
if (in_array(DRUPAL_AUTHENTICATED_RID, $rids[$op])) {
$rids[$op] = in_array(DRUPAL_ANONYMOUS_RID, $rids[$op]) ? array(
DRUPAL_ANONYMOUS_RID,
DRUPAL_AUTHENTICATED_RID,
) : array(
DRUPAL_AUTHENTICATED_RID,
);
}
}
// Now let's remove unnecessary grants, if any.
foreach ($grants as $key => $grant) {
if (!is_numeric($key)) {
continue;
}
foreach (array(
'view',
'update',
'delete',
) as $op) {
if ($grant['grant_' . $op] && in_array($key, $rids[$op])) {
//it's still here, so we can't remove this grant
continue 2;
}
}
//ok, remove it
unset($grants[$key]);
}
}
/**
* Implementation of hook_node_type():
* Update settings on node type name change.
*/
function content_access_node_type($op, $info) {
switch ($op) {
case 'delete':
$settings = content_access_get_settings();
foreach (content_access_available_settings() as $setting) {
unset($settings[$setting][$info->type]);
}
content_access_set_settings($settings);
break;
case 'update':
if (!empty($info->old_type) && $info->old_type != $info->type) {
$settings = content_access_get_settings();
foreach (content_access_available_settings() as $setting) {
$settings[$setting][$info->type] = $settings[$setting][$info->old_type];
unset($settings[$setting][$info->old_type]);
}
content_access_set_settings($settings);
}
break;
}
}
/**
* Implementation of hook_node_access_explain().
*/
function content_access_node_access_explain($row) {
static $roles;
if (!isset($roles)) {
$roles = user_roles();
}
if (!$row->gid && $row->realm == 'content_access_rid') {
return t('Content access: No access is granted.');
}
switch ($row->realm) {
case 'content_access_author':
return t('Content access: author of the content can access');
case 'content_access_rid':
return t('Content access: %role can access', array(
'%role' => $roles[$row->gid],
));
}
}
/**
* Implementation of hook_form_alter().
*/
function content_access_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'user_admin_perm') {
module_load_include('inc', 'content_access', 'content_access.admin');
$form['#submit'][] = 'content_access_user_admin_perm_submit';
}
}
/**
* Returns an array of operations used by the module.
*/
function _content_access_get_operations() {
return array(
'view',
'view_own',
'update',
'update_own',
'delete',
'delete_own',
);
}
Functions
Name | Description |
---|---|
content_access_admin_settings_access | |
content_access_available_settings | |
content_access_delete_per_node_settings | Deletes all custom per node settings, so that content type defaults are used again. |
content_access_disable | Implementation of hook_disable(). |
content_access_disabling | Remembers if we have disabled access. |
content_access_enabled | Used by the ACL module. |
content_access_form_alter | Implementation of hook_form_alter(). |
content_access_get_acl_id | Gets the content access acl id of the node. |
content_access_get_permission_access | Returns an array of role ids, that contain the given permission. |
content_access_get_permission_by_op | Gets the name of a permission for the given operation, if there is a suiting one. |
content_access_get_per_node_settings | Gets the per node settings of a node. |
content_access_get_rids_per_node_op | Returns optimized role ids for the given operation and node to grant access for. |
content_access_get_settings | Returns the content_access' settings. |
content_access_get_setting_defaults | Defines default values for settings. |
content_access_get_type_grant | Returns the default grants for a given node type. |
content_access_init | Implementation of hook_init(). |
content_access_menu | Implementation of hook_menu(). |
content_access_nodeapi | Implementation of hook_nodeapi(). |
content_access_node_access_explain | Implementation of hook_node_access_explain(). |
content_access_node_access_records | Implementation of hook_node_access_records() |
content_access_node_grants | Implementation of hook_node_grants(). |
content_access_node_page_access | |
content_access_node_type | Implementation of hook_node_type(): Update settings on node type name change. |
content_access_optimize_grants | Removes grants that doesn't change anything. |
content_access_own_op | Determines the grant for the node author and the given allowed roles of a operation. |
content_access_perm | Implementation of hook_perm(). |
content_access_per_node_setting | Returns the per node role settings. If no per node settings are available, it will return the content type settings. |
content_access_proccess_grant | Process a grant, which means add priority, realm and other properties. |
content_access_save_per_node_settings | Saves custom per node settings in the own content_access table. |
content_access_set_settings | Saves the content_access settings - needs the complete settings array. |
_content_access_get_operations | Returns an array of operations used by the module. |