Commit 56277e00 authored by good_man's avatar good_man

removing everything from master branch, and keeping the README file

parent a87776da
Content Access Module
-----------------------
by Wolfgang Ziegler, nuppla@zites.net
<<<<<<< HEAD
Yet another node access module.
This module allows you to manage permissions for content types by role. It allows you to specifiy
custom view, view own, edit, edit own, delete and delete own permissions for each content type.
Optionally you can enable per content access settings, so you can customize the access for each
content node.
In particular
* it comes with sensible defaults, so you need not configure anything and everything stays working
* it is as flexible as you want. It can work with per content type settings, per content node settings
as well as with flexible Access Control Lists (with the help of the ACL module).
* it trys to reuse existing functionality instead of reimplementing it. So one can install the ACL
module and set per user access control settings per content node.
Furthermore the module provides conditions and actions for the rules module, which allows one
to configure even rule-based access permissions.
* it optimizes the written content node grants, so that only the really necessary grants are written.
This is important for the performance of your site.
* it takes access control as important as it is. E.g. the module has a bunch of simpletests to ensure
everything is working right.
* it respects and makes use of drupal's built in permissions as far as possible. Which means the
access control tab provided by this module takes them into account and provides you a good overview
about the really applied access control settings. [1]
So the module is simple to use, but can be configured to provide really fine-grained permissions!
Installation
------------
* Copy the content access module's directory to your modules directory and activate the module.
* Optionally download and install the ACL module too.
* Edit a content type at admin/content/types. There will be a new tab "Access Control".
ACL Module
-----------
You can find the ACL module at http://drupal.org/project/acl. To make use of Access Control Lists
you'll need to enable per content node access control settings for a content type. At the access
control tab of such a content node you are able to grant view, edit or delete permission for specific
users.
Running multiple node access modules on a site (Advanced!)
-----------------------------------------------------------
A drupal node access module can only grant access to content nodes, but not deny it. So if you
are using multiple node access modules, access will be granted to a node as soon as one of the
module grants access to it.
However you can influence the behaviour by changing the priority of the content access module as
drupal applies *only* the grants with the highest priority. So if content access has the highest
priority *alone*, only its grants will be applied.
By default node access modules use priority 0.
Footnotes
----------
[1] Note that this overview can't take other modules into account, which might also alter node access.
If you have multiple modules installed that alter node access, read the paragraph about "Running
multiple node access modules on a site".
=======
For the code have a look at the branches DRUPAL-5, ..
>>>>>>> 12d1e2400e3d4936cac12671420c0e4fd1175ba1
This is a plain master branch, don't use it, use the other versions branches,
Upgrade from 5.x
----------------
You can easily upgrade from drupal 5 installations. After upgrading your drupal installation
just install the latest module and run update.php - it will automatically run the upgrade routine
which is update 6001.
After that the content access permissions for your site needs to be rebuilt. Just go to your
site and follow the instructions.
Notes for workflow-ng users
----------------------------
The workflow-ng integration has been properly upgraded to its 6.x version, the rules module.
However it has been a bit refactored: The 'Set content permissions' action has been removed in
favor of the 'Grant content permissions by role' and 'Revoke content permissions by role' as
this gives us more flexibility.
So the automatic upgrade of your configured rules converts the 'Set content permissions' action
to an 'Grant content permissions by role' action. If this doesn't fit, you'll have to edit it
manually.
All other conditions and actions have their equivalent in rules and will be automatically converted.
<?php
/**
* @file Content access administration UI.
*/
/**
* Specifies the threshold until we try to mass update node grants immediately.
*/
define('CONTENT_ACCESS_MASS_UPDATE_THRESHOLD', 1000);
/**
* Per node settings page.
*/
function content_access_page($form, &$form_state, $node) {
drupal_set_title(t('Access control for @title', array('@title' => $node->title)));
foreach (_content_access_get_operations() as $op => $label) {
$defaults[$op] = content_access_per_node_setting($op, $node);
}
// Get roles form
content_access_role_based_form($form, $defaults);
// Add an after_build handler that disables checkboxes, which are enforced by permissions.
$form['per_role']['#after_build'] = array('content_access_force_permissions');
// ACL form
if (module_exists('acl')) {
// This is disabled when there is no node passed.
$form['acl'] = array(
'#type' => 'fieldset',
'#title' => t('User access control lists'),
'#description' => t('These settings allow you to grant access to specific users.'),
'#collapsible' => TRUE,
'#tree' => TRUE,
);
foreach (array('view', 'update', 'delete') as $op) {
$acl_id = content_access_get_acl_id($node, $op);
acl_node_add_acl($node->nid, $acl_id, (int) ($op == 'view'), (int) ($op == 'update'), (int) ($op == 'delete'), content_access_get_settings('priority', $node->type));
$form['acl'][$op] = acl_edit_form($form_state, $acl_id, t('Grant !op access', array('!op' => $op)));
$form['acl'][$op]['#collapsed'] = !isset($_POST['acl_' . $acl_id]) && !unserialize($form['acl'][$op]['user_list']['#default_value']);
}
}
$form_state['node'] = $node;
$form['reset'] = array(
'#type' => 'submit',
'#value' => t('Reset to defaults'),
'#weight' => 10,
'#submit' => array('content_access_page_reset'),
'#access' => count(content_access_get_per_node_settings($node)) > 0,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#weight' => 10,
);
// @todo not true anymore?
// http://drupal.org/update/modules/6/7#hook_node_access_records
if (!$node->status) {
drupal_set_message(t("Warning: Your content is not published, so this settings are not taken into account as long as the content remains unpublished."), 'error');
}
return $form;
}
/**
* Submit callback for content_access_page().
*/
function content_access_page_submit($form, &$form_state) {
$settings = array();
$node = $form_state['node'];
foreach (_content_access_get_operations() as $op => $label) {
// Set the settings so that further calls will return this settings.
$settings[$op] = array_keys(array_filter($form_state['values'][$op]));
}
// Save per-node settings.
content_access_save_per_node_settings($node, $settings);
if (module_exists('acl')) {
foreach (array('view', 'update', 'delete') as $op) {
acl_save_form($form_state['values']['acl'][$op]);
}
}
// Apply new settings.
node_access_acquire_grants($node);
cache_clear_all();
drupal_set_message(t('Your changes have been saved.'));
}
/**
* Submit callback for reset on content_access_page().
*/
function content_access_page_reset($form, &$form_state) {
content_access_delete_per_node_settings($form_state['node']);
node_access_acquire_grants($form_state['node']);
cache_clear_all();
drupal_set_message(t('The permissions have been reseted to the content type defaults.'));
}
/**
* Per content type settings form.
*/
function content_access_admin_settings($form, $form_state, $content_type) {
$type = $content_type->type;
$form_state['type'] = $type;
// Add role based per content type settings
$defaults = array();
foreach (_content_access_get_operations() as $op => $label) {
$defaults[$op] = content_access_get_settings($op, $type);
}
content_access_role_based_form($form, $defaults);
// Per node:
$form['node'] = array(
'#type' => 'fieldset',
'#title' => t('Per content node access control settings'),
'#collapsible' => TRUE,
'#description' => t('Optionally you can enable per content node access control settings. If enabled, a new tab for the content access settings appears when viewing content. You have to configure permission to access these settings at the !permissions page.', array('!permissions' => l(t('permissions'), 'admin/people/permissions'))),
);
$form['node']['per_node'] = array(
'#type' => 'checkbox',
'#title' => t('Enable per content node access control settings'),
'#default_value' => content_access_get_settings('per_node', $type),
);
$form['advanced'] = array(
'#type' => 'fieldset',
'#title' => t('Advanced'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['advanced']['priority'] = array(
'#type' => 'weight',
'#title' => t('Give content node grants priority'),
'#default_value' => content_access_get_settings('priority', $type),
'#description' => t('If you are only using this access control module, you can safely ignore this. If you are using multiple access control modules you can adjust the priority of this module.'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#weight' => 10,
);
return $form;
}
/**
* Submit handler for per content type settings form.
*/
function content_access_admin_settings_submit($form, &$form_state) {
$roles_permissions = user_role_permissions(user_roles());
$permissions = user_permission_get_modules();
// Remove disabled modules permissions, so they can't raise exception
// in content_access_save_permissions()
foreach ($roles_permissions as $rid => $role_permissions) {
foreach ($role_permissions as $permission => $value) {
if (!array_key_exists($permission, $permissions)) {
unset($roles_permissions[$rid][$permission]);
}
}
}
foreach (array('update', 'update_own', 'delete', 'delete_own') as $op) {
foreach ($form_state['values'][$op] as $rid => $value) {
$permission = content_access_get_permission_by_op($op, $form_state['type']);
if ($value) {
$roles_permissions[$rid][$permission] = TRUE;
}
else {
$roles_permissions[$rid][$permission] = FALSE;
}
}
// Don't save the setting, so its default value (get permission) is applied always.
unset($form_state['values'][$op]);
}
content_access_save_permissions($roles_permissions);
// Update content access settings
$settings = content_access_get_settings();
foreach (content_access_available_settings() as $setting) {
unset($settings[$setting][$form_state['type']]);
if (isset($form_state['values'][$setting])) {
$settings[$setting][$form_state['type']] = is_array($form_state['values'][$setting]) ? array_keys(array_filter($form_state['values'][$setting])) : $form_state['values'][$setting];
}
}
content_access_set_settings($settings);
$type = $form_state['type'];
// Mass update the nodes, but only if necessary.
if (content_access_get_settings('per_node', $type) ||
content_access_get_settings('view', $type) != $form['per_role']['view']['#default_value'] ||
content_access_get_settings('view_own', $type) != $form['per_role']['view_own']['#default_value'] ||
content_access_get_settings('priority', $type) != $form['advanced']['priority']['#default_value'] ||
content_access_get_settings('per_node', $type) != $form['node']['per_node']['#default_value']
) {
// If per node has been disabled and we use the ACL integration, we have to remove possible ACLs now.
if (!content_access_get_settings('per_node', $type) && $form['node']['per_node']['#default_value'] && module_exists('acl')) {
_content_access_remove_acls($type);
}
if (content_access_mass_update(array($type))) {
drupal_set_message(t('Permissions have been successfully rebuilt for the content type @types.', array('@types' => node_type_get_name($type))));
}
}
drupal_set_message(t('Your changes have been saved.'));
}
/**
* Mass updates node access records for nodes of the given types.
* @param $types
* An array of content type names.
* @return
* Whether the operation has been processed successfully (TRUE) or postponed (FALSE).
*/
function content_access_mass_update($types) {
$q = db_select('node', 'n')
->fields('n', array('nid'))
->condition('n.type', $types, 'IN');
$count = $q->countQuery()->execute()->fetchField();
node_access_needs_rebuild(TRUE);
// If there not too much nodes affected, try to do it.
if ($count <= CONTENT_ACCESS_MASS_UPDATE_THRESHOLD) {
$records = $q->execute();
foreach ($records as $node) {
node_access_acquire_grants(node_load($node->nid));
}
cache_clear_all();
node_access_needs_rebuild(FALSE);
return TRUE;
}
return FALSE;
}
/**
* Saves the given permissions by role to the database.
*/
function content_access_save_permissions($roles_permissions) {
foreach ($roles_permissions as $rid => $permissions) {
user_role_change_permissions($rid, $permissions);
}
}
/**
* Builds the role based permission form for the given defaults.
*
* @param $defaults
* Array of defaults for all operations.
*/
function content_access_role_based_form(&$form, $defaults = array()) {
$form['per_role'] = array(
'#type' => 'fieldset',
'#title' => t('Role based access control settings'),
'#collapsible' => TRUE,
'#description' => t('Note that users need at least the %access_content permission to be able to deal in any way with content.', array('%access_content' => t('access content'))) .
' ' . t('Furthermore note that content which is not @published is treated in a different way by drupal: It can be viewed only by its author or users with the %administer_nodes permission.', array('@published' => t('published'), '%administer_nodes' => t('administer nodes'))),
);
$operations = _content_access_get_operations();
$roles = array_map('filter_xss_admin', user_roles());
foreach ($operations as $op => $label) {
// Make sure defaults are set properly
$defaults += array($op => array());
$form['per_role'][$op] = array('#type' => 'checkboxes',
'#prefix' => '<div class="content_access-div">',
'#suffix' => '</div>',
'#options' => $roles,
'#title' => $label,
'#default_value' => $defaults[$op],
'#process' => array('form_process_checkboxes', 'content_access_disable_checkboxes'),
);
}
$form['per_role']['clearer'] = array(
'#value' => '<br clear="all" />',
);
drupal_add_css(drupal_get_path('module', 'content_access') . '/content_access.css');
return $form;
}
/**
* Formapi #process callback, that disables checkboxes for roles without access to content
*/
function content_access_disable_checkboxes($element) {
$access_roles = content_access_get_permission_access('access content');
$admin_roles = content_access_get_permission_access('administer nodes');
foreach (element_children($element) as $key) {
if (!in_array($key, $access_roles) &&
!($key != DRUPAL_ANONYMOUS_RID &&
in_array(DRUPAL_AUTHENTICATED_RID, $access_roles))) {
$element[$key]['#disabled'] = TRUE;
$element[$key]['#default_value'] = FALSE;
$element[$key]['#prefix'] = '<span title="' . t("This role is lacking the permission '@perm', so it has no access.", array('@perm' => t('access content'))) . '">';
$element[$key]['#suffix'] = "</span>";
}
elseif (in_array($key, $admin_roles) ||
($key != DRUPAL_ANONYMOUS_RID && in_array(DRUPAL_AUTHENTICATED_RID, $admin_roles))) {
// Fix the checkbox to be enabled for users with administer node privileges
$element[$key]['#disabled'] = TRUE;
$element[$key]['#default_value'] = TRUE;
$element[$key]['#prefix'] = '<span title="' . t("This role has '@perm' permission, so access is granted.", array('@perm' => t('administer nodes'))) . '">';
$element[$key]['#suffix'] = "</span>";
}
}
return $element;
}
/**
* Formapi #after_build callback, that disables checkboxes for roles without access to content.
*/
function content_access_force_permissions($element, &$form_state) {
foreach (array('update', 'update_own', 'delete', 'delete_own') as $op) {
foreach (content_access_get_settings($op, $form_state['node']->type) as $rid) {
$element[$op][$rid]['#disabled'] = TRUE;
$element[$op][$rid]['#attributes']['disabled'] = 'disabled';
$element[$op][$rid]['#value'] = TRUE;
$element[$op][$rid]['#checked'] = TRUE;
$element[$op][$rid]['#prefix'] = '<span title="' . t("Permission is granted due to the content type's access control settings.") . '">';
$element[$op][$rid]['#suffix'] = "</span>";
}
}
return $element;
}
/**
* Submit callback for the user permissions form.
* Trigger changes to node permissions to rebuild our grants.
*/
function content_access_user_admin_perm_submit($form, $form_state) {
// Check for each content type, which has per node access activated
// whether permissions have been changed.
$types = array();
foreach (array_filter(content_access_get_settings('per_node')) as $type => $value) {
foreach (_content_access_get_node_permissions($type) as $perm) {
foreach (user_roles() as $rid => $role) {
if (isset($form_state['values'][$rid]) && in_array($perm, $form['checkboxes'][$rid]['#default_value']) != in_array($perm, $form_state['values'][$rid])) {
//permission changed!
$types[$type] = node_get_types('name', $type);
continue 2;
}
}
}
}
if ($types && content_access_mass_update(array_keys($types))) {
drupal_set_message(format_plural(count($types),
'Permissions have been successfully rebuilt for the content type @types.',
'Permissions have been successfully rebuilt for the content types @types.',
array('@types' => implode(', ', $types))
));
}
}
function _content_access_get_node_permissions($type) {
return array_filter(array_map('content_access_get_permission_by_op', array_flip(_content_access_get_operations()), array_fill(0, 6, $type)));
}
/**
* 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) {
$acl_id = acl_create_new_acl('content_access', $op . '_' . $node->nid);
}
return $acl_id;
}
/**
* Detaches all our ACLs for the nodes of the given type.
*/
function _content_access_remove_acls($type) {
$result = db_query("SELECT n.nid FROM {node} n WHERE type = :type", array('type' => $type));
foreach ($result as $node) {
acl_node_clear_acls($node->nid, 'content_access');
}
}
\ No newline at end of file
name = Content Access
description = Provides flexible content access control.
core = 7.x
package = Access control
files[] = content_access.rules.inc
files[] = tests/content_access.test
files[] = tests/content_access_acl.test
\ No newline at end of file
<?php
/**
* @file
* Install Content Access DB schema.
*
*/
/**
* Implementation of hook_uninstall().
*/
function content_access_uninstall() {
variable_del('content_access_settings');
}
/**
* Implementation of hook_schema().
*/
function content_access_schema() {
$schema['content_access'] = array(
'fields' => array(
'nid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0
),
'settings' => array(
'type' => 'text',
'not null' => FALSE,
'size' => 'medium'
),
),
'primary key' => array('nid')
);
return $schema;
}
//@todo: do we need an upgrade function from D6 to D7?
\ No newline at end of file
<?php
/**
* @file Content access module file.
*/
/**
* Implements hook_help().
*/
function content_access_help($path, $arg) {
switch ($path) {
case 'admin/help#content_access':
$output = '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Content Access module provides flexible way to control how and who should read or control your site content. Content Access can define custom access control rules for content types and even for every piece of content.') . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Default and custom settings') . '</dt>';
$output .= '<dd>' . t("Each <a href='@content-type'>content type</a> can have its own default content access settings configured as: <em>View any content</em> to allow anyone to view content from this content type, <em>View own content</em> to allow only content creators to see their own content, <em>Edit any content</em> to allow anyone to edit content from this content type, <em>Edit own content</em> to allow only content creators to edit their own content, <em>Delete any content</em> to allow anyone to delete content from this content type, <em>Delete own content </em> to allow content creators to delete their own content. This default settings for each content type can be further customized per every piece of content per user if you have <a href='@acl'>ACL</a> module enabled.", array('@content-type' => url('admin/structure/types'), '@acl' => 'http://drupal.org/project/acl/')) . '</dd>';
$output .= '</dl>';
return $output;
}
}
/**
* Implements hook_admin_paths().
*/
function content_access_admin_paths() {
$paths = array(
'node/*/access' => TRUE,
);
return $paths;
}
/**
* Implements 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',
'theme callback' => '_node_custom_theme',
'type' => MENU_LOCAL_TASK,
'weight' => 3,
);
$items['admin/structure/types/manage/%node_type/access'] = array(
'title' => 'Access control',
'description' => 'Configure content access control.',
'page callback' => 'drupal_get_form',
'page arguments' => array('content_access_admin_settings', 4),
'access callback' => 'content_access_admin_settings_access',
'access arguments' => array(),
'type' => MENU_LOCAL_TASK,
'file' => 'content_access.admin.inc',
'theme callback' => '_node_custom_theme',
'weight' => 1,
);
return $items;
}
/**
* Get access tab page for the viewed node.
*/
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));
}
/**
* Content access settings for content type.
*/
function content_access_admin_settings_access() {
return user_access('administer nodes') && user_access('administer content types');
}
/**
* Implements hook_perm().
*/
function content_access_permission() {
return array(
'grant content access' => array(
'title' => t('Grant content access'),
'description' => t('View and modify content access for any nodes'),
),
'grant own content access' => array(
'title' => t('Grant own content access'),
'description' => t('View and modify content access for own nodes'),
),
);
}
/**
* Implements 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),
);
}
/**
* Implements hook_node_access_records().
*/
function content_access_node_access_records($node) {
if (content_access_disabling() || !$node->status) {
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 = $any_roles ? $any_roles : array();
$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;
}
/**
* Implements hook_node_delete().
*/
function content_access_node_delete($node) {
db_delete('content_access')
->condition('nid', $node->nid)
->execute();
}
/**
* Used by the ACL module.
*/
function content_access_enabled() {
return !content_access_disabling();
}
/**
* Implements 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;
}