FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

View File

@@ -0,0 +1,22 @@
Content Access 7.x-1.2-beta1, 2011-07-24
--------------------------------------
#1058526 by BenK, good_man: Rules 2.x integration.
#572812 by NikLP, good_man: Add content type to permission title.
#1135466 by Firewolf, videographics, good_man: Renaming a content type results
in notices.
#1144510 by good_man: Fix roles selection in test case.
#1110860 by FiNeX, eosrei, good_man: Content access rules integration -
missing function.
#1159402 by FiNeX, dcmouyard, jjs, good_man: Wrong operations on rules
integration.
#1057400 by xaverx, dooug, jhayzhon, cnolle, Firewolf, Bitnetix, maciej.zgadzaj,
karilu_ec, ChoY, BenK, good_man: Taxonomy access.
#1097248 by ordermind, Itangalo, good_man: PDOException error 1062 when using
rules on create node.
#1209004 by Itangalo, good_man: "Reset content permissions" action doesn't go
all the way.
#1147526 by tmm360, good_man, Akaoni, danielb, Crusher, mithman, BenK, fago:
Unpublished nodes displayed to anonymous users.
#1115794 by rorydflynn, Waldknoblauch, aocoder, good_man, SNaKeMe, cgross,
commanderflash, bryrock, enrikito, BenK, karilu_ec: Conflict with views
module.

View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -0,0 +1,65 @@
Content Access Module
-----------------------
by Wolfgang Ziegler, nuppla@zites.net
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".

View File

@@ -0,0 +1,24 @@
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.

View File

@@ -0,0 +1,347 @@
<?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, $node->type);
// 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]);
module_invoke_all('user_acl', $settings);
}
}
// Apply new settings.
node_access_acquire_grants($node);
module_invoke_all('per_node', $settings);
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']);
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, $type);
// 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();
$type = $form_state['type'];
// 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('all', $type);
foreach (content_access_available_settings() as $setting) {
if (isset($form_state['values'][$setting])) {
$settings[$setting] = is_array($form_state['values'][$setting]) ? array_keys(array_filter($form_state['values'][$setting])) : $form_state['values'][$setting];
}
}
content_access_set_settings($settings, $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(), $type = NULL) {
$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($type);
$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 #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' . drupal_attributes(array('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))
));
}
}

View File

@@ -0,0 +1,7 @@
.content_access-div {
width: 25%;
margin: 0;
padding: 0;
float: left;
overflow: hide;
}

View File

@@ -0,0 +1,13 @@
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
; Information added by drupal.org packaging script on 2013-01-22
version = "7.x-1.2-beta1+10-dev"
core = "7.x"
project = "content_access"
datestamp = "1358858714"

View File

@@ -0,0 +1,54 @@
<?php
/**
* @file
* Content access install file.
*/
/**
* Implements hook_uninstall().
*/
function content_access_uninstall() {
foreach (node_type_get_types() as $type_name => $type) {
variable_del('content_access_' . $type_name);
}
}
/**
* Implements 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;
}
/**
* Convert content type settings to a new features-friendly storage format.
*/
function content_access_update_7101() {
$settings = variable_get('content_access_settings', array());
foreach ($settings as $setting => $data) {
foreach ($data as $type_name => $value) {
$settings_new[$type_name][$setting] = $value;
}
}
foreach ($settings_new as $type_name => $data) {
variable_set('content_access_' . $type_name, $data);
}
variable_del('content_access_settings');
}

View File

@@ -0,0 +1,640 @@
<?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,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
);
$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;
}
/**
* 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'),
),
);
}
/**
* 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_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;
}
/**
* Return content_access' settings.
*
* @param $setting
* One of the content_access_available_settings(), e.g. 'view' or 'per_node'.
* If 'all' is passed, all available settings are returned.
* @param $type_name
* The name of the content type to return settings for.
*
* @return
* The value of the given setting or an array of all settings.
*/
function content_access_get_settings($setting, $type_name) {
$settings = variable_get('content_access_' . $type_name, array());
$settings += content_access_get_setting_defaults($type_name);
if ($setting == 'all') {
return $settings;
}
return isset($settings[$setting]) ? $settings[$setting] : NULL;
}
/**
* Save content_access settings of a content type.
*/
function content_access_set_settings($settings, $type_name) {
// Do not store default values so we do not have to care about synching our
// settings with the permissions.
foreach (content_access_get_setting_defaults($type_name) as $setting => $default_value) {
if (isset($settings[$setting]) && $settings[$setting] == $default_value) {
unset($settings[$setting]);
}
}
variable_set('content_access_' . $type_name, $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($type) {
$defaults = array();
$defaults['view'] = $defaults['view_own'] = array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID);
foreach (array('update', 'delete') as $op) {
$defaults[$op] = content_access_get_permission_access(content_access_get_permission_by_op($op, $type));
$defaults[$op . '_own'] = content_access_get_permission_access(content_access_get_permission_by_op($op . '_own', $type));
}
$defaults['priority'] = 0;
$defaults['per_node'] = FALSE;
return $defaults;
}
/**
* Returns an array of role ids that contain the given permission.
*/
function content_access_get_permission_access($perm, $reset = FALSE) {
$roles = &drupal_static(__FUNCTION__, 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 = :uid', array(':uid' => $node->uid));
foreach ($result as $role) {
$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);
}
/**
* 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) {
foreach (db_query("SELECT settings FROM {content_access} WHERE nid = :nid", array(':nid' => $node->nid)) as $record) {
$settings = $record->settings;
if (!$settings) {
return array();
}
return unserialize($settings);
}
}
/**
* Saves custom per node settings in the own content_access table.
*/
function content_access_save_per_node_settings($node, $settings) {
$count = db_select('content_access')
->condition('nid', $node->nid)
->countQuery()->execute()->fetchField();
if ($count > 0) {
db_update('content_access')
->condition('nid', $node->nid)
->fields(array('settings' => serialize($settings)))
->execute();
}
else {
db_insert('content_access')
->fields(array('nid' => $node->nid, 'settings' => serialize($settings)))
->execute();
}
// 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_delete('content_access')
->condition('nid', $node->nid)
->execute();
// Clear the cache.
content_access_per_node_setting(NULL, $node, FALSE);
// Delete possible acl settings
if (module_exists('acl')) {
// @todo why content_access.admin.inc is not loaded before?
module_load_include('inc', 'content_access', 'content_access.admin');
foreach (array('view', 'update', 'delete') as $op) {
$acl_id = content_access_get_acl_id($node, $op);
acl_delete_acl($acl_id);
}
}
}
/**
* 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]);
}
}
/**
* Implements hook_node_type_delete().
*/
function content_access_node_type_delete($info) {
variable_del('content_access_' . $info->type);
}
/**
* Implements hook_node_type_update().
*
* Updates settings on node type name change.
*/
function content_access_node_type_update($info) {
if (!empty($info->old_type) && $info->old_type != $info->type) {
$settings = content_access_get_settings('all', $info->old_type);
content_access_set_settings($settings, $info->type);
variable_del('content_access_' . $info->old_type);
}
}
/**
* Implements 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]));
}
}
/**
* Implements 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 possible operations on content and their labels.
*/
function _content_access_get_operations($type = NULL) {
$operations = array(
'view' => t('View any @type content', array('@type' => $type)),
'view_own' => t('View own @type content', array('@type' => $type)),
'update' => t('Edit any @type content', array('@type' => $type)),
'update_own' => t('Edit own @type content', array('@type' => $type)),
'delete' => t('Delete any @type content', array('@type' => $type)),
'delete_own' => t('Delete own @type content', array('@type' => $type)),
);
return $operations;
}
/**
* 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' . drupal_attributes(array('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' . drupal_attributes(array('title' => t("This role has '@perm' permission, so access is granted.", array('@perm' => t('administer nodes'))))) . '>';
$element[$key]['#suffix'] = "</span>";
}
}
return $element;
}
/**
* Gets node's access permissions.
*/
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');
}
}

View File

@@ -0,0 +1,14 @@
name = Content Access Rules Integrations
description = Integrates Rules with Content access. Allows to act on access events, conditions, and actions.
package = Access control
core = 7.x
dependencies[] = content_access
dependencies[] = rules
files[] = content_access.rules.inc
; Information added by drupal.org packaging script on 2013-01-22
version = "7.x-1.2-beta1+10-dev"
core = "7.x"
project = "content_access"
datestamp = "1358858714"

View File

@@ -0,0 +1,27 @@
<?php
/**
* @file
* Hooks and callback functions for rules.module integration.
*/
/**
* Implements hook_content_type_access().
*/
function content_access_rules_content_type_access($settings) {
rules_invoke_event('content_access_content_type');
}
/**
* Implements hook_user_acl().
*/
function content_access_rules_user_acl($settings) {
rules_invoke_event('content_access_user_acl');
}
/**
* Implements hook_per_node().
*/
function content_access_rules_per_node($settings) {
rules_invoke_event('content_access_per_node');
}

View File

@@ -0,0 +1,396 @@
<?php
/**
* @file
* Rules specific functions that expose content_access' API.
*
* @todo
* A way to enable per-node settings when a rule created, otherwise no effects
* will be noticed.
* Clean-up function names.
*/
/**
* Implements hook_rules_event_info().
*
* @ingroup rules
*/
function content_access_rules_rules_event_info() {
$events['content_access_content_type'] = array('label' => t('Content type access control was changed'));
$events['content_access_per_node'] = array('label' => t('Per node access control was changed'));
if (module_exists('acl')) {
$events['content_access_user_acl'] = array('label' => t('User was added to ACL'));
}
$items = array();
foreach ($events as $name => $event) {
$items[$name] = array(
'label' => $event['label'],
'group' => t('Content Access'),
);
}
return $items;
}
/**
* Implementation of hook_rules_action_info().
*
* @ingroup rules
*/
function content_access_rules_rules_action_info() {
$role_actions = array(
'content_access_action_grant_node_permissions' => array(
'label' => t('Grant access by role'),
'description' => t('Grant access to the following content'),
),
'content_access_action_revoke_node_permissions' => array(
'label' => t('Revoke access by role'),
'description' => t('Revoke access to the following content'),
),
);
$reset_actions = array(
'content_access_action_reset_node_permissions' => array(
'label' => t('Reset access to content type defaults'),
'description' => t('Reset node permissions to default permissions'),
),
);
$user_actions = array(
'content_access_action_user_grant' => array(
'label' => t('Grant access by user'),
'operation' => t('Grant'),
'description' => t('Grant access to the following content'),
),
'content_access_action_user_revoke' => array(
'label' => t('Revoke access by user'),
'operation' => t('Revoke'),
'description' => t('Revoke access to the following content'),
),
);
$items = array();
foreach ($role_actions as $name => $action) {
$items[$name] = array(
'label' => $action['label'],
'parameter' => array(
'node' => array(
'type' => 'node',
'label' => t('Content'),
'description' => $action['description'],
),
'permissions' => array(
'type' => 'list<text>',
'label' => t('Role-based access control settings'),
'optional' => TRUE,
'options list' => 'content_access_action_roles_permissions_list',
'restriction' => 'input',
),
),
'group' => t('Content Access'),
'callbacks' => array(
'form_alter' => 'content_access_rules_action_form_alter',
),
);
}
foreach ($reset_actions as $name => $action) {
$items[$name] = array(
'label' => $action['label'],
'parameter' => array(
'node' => array(
'type' => 'node',
'label' => t('Content'),
'description' => $action['description'],
),
),
'group' => t('Content Access'),
);
}
if (module_exists('acl')) {
foreach ($user_actions as $name => $action) {
$items[$name] = array(
'label' => $action['label'],
'named parameter' => TRUE,
'parameter' => array(
'node' => array(
'type' => 'node',
'label' => t('Content'),
'description' => $action['description'],
),
'content_access_user_view' => array(
'type' => 'user',
'label' => t('@action view access', array('@action' => $action['operation'])),
'optional' => TRUE,
'description' => t('@action view access to the following user.', array('@action' => $action['operation'])),
),
'content_access_user_update' => array(
'type' => 'user',
'label' => t('@action update access', array('@action' => $action['operation'])),
'optional' => TRUE,
'description' => t('@action edit access to the following user.', array('@action' => $action['operation'])),
),
'content_access_user_delete' => array(
'type' => 'user',
'label' => t('@action delete access', array('@action' => $action['operation'])),
'optional' => TRUE,
'description' => t('@action delete access to the following user.', array('@action' => $action['operation'])),
),
),
'group' => t('Content Access User'),
);
}
}
return $items;
}
/**
* Returns an options list array for the content access permission parameter.
*/
function content_access_action_roles_permissions_list() {
$options = array();
$roles = array_map('filter_xss_admin', user_roles());
foreach (_content_access_get_operations() as $op => $label) {
foreach ($roles as $rid => $role) {
$options[$op][$op . ':' . $rid] = $op . ' ' . $role;
}
}
return $options;
}
/**
* Alter the settings form to render the text<list> as checkboxes.
*/
function content_access_rules_action_form_alter(&$form, &$form_state) {
// Per node access control should be enabled for this type of action to be
// effective.
drupal_set_message(t('Access control settings will not be executed for the affected node unless you enabled \'%per_node\' from the Access Control tab on content type page of the node', array('%per_node' => 'Per content node access control settings')), 'warning');
// Alter the text<list> to make it into checkboxes groups
$elements =& $form['parameter']['permissions']['settings']['permissions'];
$elements = content_access_rules_checkboxes_form((array) $elements['#default_value']);
// Add our own after build callback for fixing the form value afterwards.
$elements['#after_build'][] = 'content_access_rules_action_form_after_build';
// Make sure our include file is loaded when FAPI processes the form.
form_load_include($form_state, 'inc', 'content_access_rules', 'content_access_rules.rules');
}
/**
* Form after build callback.
*
* Get the form settings as checkboxes, convert them back to list<text>.
*/
function content_access_rules_action_form_after_build($element, &$form_state) {
if ($form_state['process_input']) {
$form_value = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
$value = content_access_rules_transform_to_rule_value($form_value);
form_set_value($element, $value, $form_state);
}
return $element;
}
/**
* Returns the form elements for configuring content access per-role permissions.
*/
function content_access_rules_checkboxes_form($value) {
$form = array();
$roles = array_map('filter_xss_admin', user_roles());
$defaults = content_access_rules_transform_rules_value($value);
foreach (_content_access_get_operations() as $op => $label) {
$form[$op] = array(
'#type' => 'checkboxes',
'#prefix' => '<div class="content_access-div">',
'#suffix' => '</div>',
'#options' => $roles,
'#title' => $label,
'#default_value' => isset($defaults[$op]) ? $defaults[$op] : array(),
'#process' => array('form_process_checkboxes', 'content_access_disable_checkboxes'),
);
}
$form['clearer'] = array(
'#value' => '<br clear="all" />',
);
drupal_add_css(drupal_get_path('module', 'content_access') . '/content_access.css');
return $form;
}
/**
* Transforms the array of text values used by Rules to an array keyed by $op and $rid.
*
* @see content_access_rules_transform_to_rule_value()
*/
function content_access_rules_transform_rules_value($value) {
$array = array();
foreach ($value as $op_role) {
$parts = explode(':', $op_role);
// The first item is $op and the second $rid.
$array[$parts[0]][] = $parts[1];
}
return $array;
}
/**
* Transform the form values array keyed by $op and $rid to an array of text values as used by Rules.
*
* @see content_access_rules_transform_rules_value()
*/
function content_access_rules_transform_to_rule_value($form_values) {
$value = array();
foreach ($form_values as $op => $array) {
foreach (array_filter($array) as $rid => $data) {
$value[] = $op . ':' . $rid;
}
}
return $value;
}
/**
* Action implementation: Grant permissions for a node.
*/
function content_access_action_grant_node_permissions($node, $permissions) {
if (!empty($node->nid) && _content_access_rules_check_setting($node)) {
// Transform the value to the content-access format.
$settings = content_access_rules_transform_rules_value($permissions);
$ca_settings = array();
foreach (_content_access_get_operations() as $op => $label) {
// Merge in the array of role-ids for each operation.
$settings += array($op => array());
$ca_settings[$op] = array_keys(array_flip(content_access_per_node_setting($op, $node)) + array_flip($settings[$op]));
}
content_access_save_per_node_settings($node, $ca_settings);
content_access_action_aquire_grants($node);
}
}
/**
* Action implementation: Revoke permissions for a node.
*/
function content_access_action_revoke_node_permissions($node, $permissions) {
if (!empty($node->nid) && _content_access_rules_check_setting($node)) {
// Transform the value to the content-access format.
$settings = content_access_rules_transform_rules_value($permissions);
$ca_settings = array();
foreach (_content_access_get_operations() as $op => $label) {
$settings += array($op => array());
$ca_settings[$op] = array_diff(content_access_per_node_setting($op, $node), $settings[$op]);
}
content_access_save_per_node_settings($node, $ca_settings);
content_access_action_aquire_grants($node);
}
}
/**
* Action implementation: Reset permissions for a node.
*/
function content_access_action_reset_node_permissions($node) {
content_access_delete_per_node_settings($node);
content_access_action_aquire_grants($node);
}
/**
* Verifies that per content settings are activated for the given node.
*/
function _content_access_rules_check_setting($node) {
$type = $node->type;
$settings = variable_get('content_access_' . $type, array());
if (isset($settings['per_node']) && $settings['per_node']) {
return TRUE;
}
// If we didn't find any settings in content access for this type return
// false as we don't want to process it.
rules_log("Can't set per content permissions for content type @type. Make sure to have per content settings activated for content types you want to alter access control for.", array('@type' => $node->type), RulesLog::WARN);
return FALSE;
}
/**
* Split the settings string into array.
*/
function content_access_action_settings($action_settings = array()) {
$roles_ids = array_flip(user_roles());
foreach (_content_access_get_operations() as $op => $label) {
$settings[$op] = array();
}
foreach ($action_settings as $op_role => $role) {
$op = substr($op_role, 0, strpos($op_role, ':'));
$rid = $roles_ids[$role];
$settings[$op][] = $rid;
}
return $settings;
}
/**
* Action implementation: Grant user access.
*/
function content_access_action_user_grant($params) {
content_access_action_user($params, 'grant');
}
/**
* Action implementation: Revoke user access.
*/
function content_access_action_user_revoke($params) {
content_access_action_user($params, 'revoke');
}
/**
* Process Rule's param, and grant by the passed operation.
*/
function content_access_action_user($params, $type) {
$ops = array('view', 'update', 'delete');
$settings = array();
$node = $params['node'];
foreach ($ops as $op) {
if ($params['content_access_user_' . $op]) {
$settings[$op] = $params['content_access_user_' . $op]->uid;
}
}
foreach ($settings as $op => $uid) {
$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));
db_delete('acl_user')
->condition('acl_id', $acl_id)
->condition('uid', $uid)
->execute();
if ($type == 'grant') {
db_insert('acl_user')
->fields(array(
'acl_id' => $acl_id,
'uid' => $uid,
))
->execute();
}
}
content_access_action_aquire_grants($node);
}
/**
* Apply the new grants to the affected node.
*/
function content_access_action_aquire_grants($node) {
// node_save() does implement node_access_acquire_grants() so we don't want
// to execute it again or we'll get a duplicated key exception
if (!isset($node->op) ||
(isset($node->op) && $node->op != 'Save')) {
node_access_acquire_grants($node);
}
}

View File

@@ -0,0 +1,332 @@
<?php
/**
* @file
* Automatd SimpleTest Case for content access module
*/
require_once(drupal_get_path('module', 'content_access') .'/tests/content_access_test_help.php');
class ContentAccessModuleTestCase extends ContentAccessTestCase {
/**
* Implementation of get_info() for information
*/
public static function getInfo() {
return array(
'name' => t('Content Access Module Tests'),
'description' => t('Various tests to check permission settings on nodes.'),
'group' => t('Content Access'),
);
}
function setUp($module = '') {
parent::setUp();
// Create test nodes
$this->node1 = $this->drupalCreateNode(array('type' => $this->content_type->type));
$this->node2 = $this->drupalCreateNode(array('type' => $this->content_type->type));
}
/**
* Test for viewing nodes
*/
function testViewAccess() {
// Restrict access to the content type (access is only allowed for the author)
$access_permissions = array(
'view[1]' => FALSE,
'view[2]' => FALSE,
);
$this->changeAccessContentType($access_permissions);
// Logout admin and try to access the node anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid);
$this->assertText(t('Access denied'), 'node is not viewable');
// Login test user, view node, access must be denied
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid);
$this->assertText(t('Access denied'), 'node is not viewable');
// Login admin and grant access for viewing to the test user
$this->drupalLogin($this->admin_user);
$this->changeAccessContentTypeKeyword('view');
// Logout admin and try to access the node anonymously
// access must be denied again
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid);
$this->assertText(t('Access denied'), 'node is not viewable');
// Login test user, view node, access must be granted
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid);
$this->assertNoText(t('Access denied'), 'node is viewable');
// Login admin and enable per node access
$this->drupalLogin($this->admin_user);
$this->changeAccessPerNode();
// Restrict access on node2 for the test user role
$this->changeAccessNodeKeyword($this->node2, 'view', FALSE);
// Logout admin and try to access both nodes anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid);
$this->assertText(t('Access denied'), 'node1 is not viewable');
$this->drupalGet('node/'. $this->node2->nid);
$this->assertText(t('Access denied'), 'node2 is not viewable');
// Login test user, view node1, access must be granted
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid);
$this->assertNoText(t('Access denied'), 'node1 is viewable');
// View node2, access must be denied
$this->drupalGet('node/'. $this->node2->nid);
$this->assertText(t('Access denied'), 'node2 is not viewable');
// Login admin, swap permissions between content type and node2
$this->drupalLogin($this->admin_user);
// Restrict access to content type
$this->changeAccessContentTypeKeyword('view', FALSE);
// Grant access to node2
$this->changeAccessNodeKeyword($this->node2, 'view');
// Logout admin and try to access both nodes anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid);
$this->assertText(t('Access denied'), 'node1 is not viewable');
$this->drupalGet('node/'. $this->node2->nid);
$this->assertText(t('Access denied'), 'node2 is not viewable');
// Login test user, view node1, access must be denied
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid);
$this->assertText(t('Access denied'), 'node1 is not viewable');
// View node2, access must be granted
$this->drupalGet('node/'. $this->node2->nid);
$this->assertNoText(t('Access denied'), 'node2 is viewable');
}
/**
* Test for editing nodes
*/
function testEditAccess() {
// Logout admin and try to edit the node anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid .'/edit');
$this->assertText(t('Access denied'), 'edit access denied for anonymous');
// Login test user, edit node, access must be denied
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid .'/edit');
$this->assertText(t('Access denied'), 'edit access denied for test user');
// Login admin and grant access for editing to the test user
$this->drupalLogin($this->admin_user);
$this->changeAccessContentTypeKeyword('update');
// Logout admin and try to edit the node anonymously
// access must be denied again
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid .'/edit');
$this->assertText(t('Access denied'), 'edit access denied for anonymous');
// Login test user, edit node, access must be granted
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid .'/edit');
$this->assertNoText(t('Access denied'), 'node1 is editable');
// Login admin and enable per node access
$this->drupalLogin($this->admin_user);
$this->changeAccessPerNode();
// Restrict access for this content type for the test user
$this->changeAccessContentTypeKeyword('update', FALSE);
// Allow acces for node1 only
$this->changeAccessNodeKeyword($this->node1, 'update');
// Logout admin and try to edit both nodes anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid .'/edit');
$this->assertText(t('Access denied'), 'node1 is not editable');
$this->drupalGet('node/'. $this->node2->nid .'/edit');
$this->assertText(t('Access denied'), 'node2 is not editable');
// Login test user, edit node1, access must be granted
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid .'/edit');
$this->assertNoText(t('Access denied'), 'node1 is editable');
// Edit node2, access must be denied
$this->drupalGet('node/'. $this->node2->nid .'/edit');
$this->assertText(t('Access denied'), 'node2 is not editable');
// Login admin, swap permissions between node1 and node2
$this->drupalLogin($this->admin_user);
// Grant edit access to node2
$this->changeAccessNodeKeyword($this->node2, 'update');
// Restrict edit acces to node1
$this->changeAccessNodeKeyword($this->node1, 'update', FALSE);
// Logout admin and try to edit both nodes anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid .'/edit');
$this->assertText(t('Access denied'), 'node1 is not editable');
$this->drupalGet('node/'. $this->node2->nid .'/edit');
$this->assertText(t('Access denied'), 'node2 is not editable');
// Login test user, edit node1, access must be denied
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid .'/edit');
$this->assertText(t('Access denied'), 'node1 is not editable');
// Edit node2, access must be granted
$this->drupalGet('node/'. $this->node2->nid .'/edit');
$this->assertNoText(t('Access denied'), 'node2 is editable');
}
/**
* Test for deleting nodes
*/
function testDeleteAccess() {
// Logout admin and try to delete the node anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid .'/delete');
$this->assertText(t('Access denied'), 'delete access denied for anonymous');
// Login test user, delete node, access must be denied
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid .'/delete');
$this->assertText(t('Access denied'), 'delete access denied for test user');
// Login admin and grant access for deleting to the test user
$this->drupalLogin($this->admin_user);
$this->changeAccessContentTypeKeyword('delete');
// Logout admin and try to edit the node anonymously
// access must be denied again
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid .'/delete');
$this->assertText(t('Access denied'), 'delete access denied for anonymous');
// Login test user, delete node, access must be granted
$this->drupalLogin($this->test_user);
$this->drupalPost('node/'. $this->node1->nid .'/delete', array(), 'Delete');
$this->assertRaw(t('%node has been deleted', array('%node' => $this->node1->title)), 'Test node was deleted successfully by test user');
// Login admin and recreate test node1
$this->drupalLogin($this->admin_user);
$this->node1 = $this->drupalCreateNode(array('type' => $this->content_type->type));
// Enable per node access
$this->changeAccessPerNode();
// Restrict access for this content type for the test user
$this->changeAccessContentTypeKeyword('delete', FALSE);
// Allow acces for node1 only
$this->changeAccessNodeKeyword($this->node1, 'delete');
// Logout admin and try to delete both nodes anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid .'/delete');
$this->assertText(t('Access denied'), 'node1 is not deletable');
$this->drupalGet('node/'. $this->node2->nid .'/delete');
$this->assertText(t('Access denied'), 'node2 is not deletable');
// Login test user, delete node1, access must be granted
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid .'/delete');
$this->assertNoText(t('Access denied'), 'node1 is deletable');
// Delete node2, access must be denied
$this->drupalGet('node/'. $this->node2->nid .'/delete');
$this->assertText(t('Access denied'), 'node2 is not deletable');
// Login admin, swap permissions between node1 and node2
$this->drupalLogin($this->admin_user);
// Grant delete access to node2
$this->changeAccessNodeKeyword($this->node2, 'delete');
// Restrict delete acces to node1
$this->changeAccessNodeKeyword($this->node1, 'delete', FALSE);
// Logout admin and try to delete both nodes anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid .'/delete');
$this->assertText(t('Access denied'), 'node1 is not deletable');
$this->drupalGet('node/'. $this->node2->nid .'/delete');
$this->assertText(t('Access denied'), 'node2 is not deletable');
// Login test user, delete node1, access must be denied
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node1->nid .'/delete');
$this->assertText(t('Access denied'), 'node1 is not deletable');
// Delete node2, access must be granted
$this->drupalGet('node/'. $this->node2->nid .'/delete');
$this->assertNoText(t('Access denied'), 'node2 is deletable');
}
/**
* Test own view access
*/
function testOwnViewAccess() {
// Setup 2 test users
$test_user1 = $this->test_user;
$test_user2 = $this->drupalCreateUser();
// Change ownership of test nodes to test users
$this->node1->uid = $test_user1->uid;
node_save($this->node1);
$this->node2->uid = $test_user2->uid;
node_save($this->node2);
// Remove all view permissions for this content type
$access_permissions = array(
'view[1]' => FALSE,
'view[2]' => FALSE,
'view_own[1]' => FALSE,
'view_own[2]' => FALSE,
);
$this->changeAccessContentType($access_permissions);
// Allow view own content for test user 1 and 2 roles
$this->changeAccessContentTypeKeyword('view_own', TRUE, $test_user1);
$this->changeAccessContentTypeKeyword('view_own', TRUE, $test_user2);
// Logout admin and try to access both nodes anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node1->nid);
$this->assertText(t('Access denied'), 'node1 is not viewable');
$this->drupalGet('node/'. $this->node2->nid);
$this->assertText(t('Access denied'), 'node2 is not viewable');
// Login test user 1, view node1, access must be granted
$this->drupalLogin($test_user1);
$this->drupalGet('node/'. $this->node1->nid);
$this->assertNoText(t('Access denied'), 'node1 is viewable');
// View node2, access must be denied
$this->drupalGet('node/'. $this->node2->nid);
$this->assertText(t('Access denied'), 'node2 is not viewable');
// Login test user 2, view node1, access must be denied
$this->drupalLogin($test_user2);
$this->drupalGet('node/'. $this->node1->nid);
$this->assertText(t('Access denied'), 'node1 is not viewable');
// View node2, access must be granted
$this->drupalGet('node/'. $this->node2->nid);
$this->assertNoText(t('Access denied'), 'node2 is viewable');
}
}

View File

@@ -0,0 +1,180 @@
<?php
/**
* @file
* Automatd SimpleTest Case for using content access module with acl module
*/
require_once(drupal_get_path('module', 'content_access') .'/tests/content_access_test_help.php');
class ContentAccessACLTestCase extends ContentAccessTestCase {
var $node;
/**
* Implementation of get_info() for information
*/
public static function getInfo() {
return array(
'name' => t('Content Access Module with ACL Module Tests'),
'description' => t('Various tests to check the combination of content access and ACL module.'),
'group' => 'Content Access',
);
}
/**
* Setup configuration before each test
*/
function setUp($module = '') {
parent::setUp('acl');
if (!module_exists('acl')) {
$this->pass('No ACL module present, skipping test');
return;
}
// Create test nodes
$this->node = $this->drupalCreateNode(array('type' => $this->content_type->type));
}
/**
* Test Viewing accessibility with permissions for single users
*/
function testViewAccess() {
// Exit test if ACL module could not be enabled
if (!module_exists('acl')) {
$this->pass('No ACL module present, skipping test');
return;
}
// Restrict access to this content type (access is only allowed for the author)
// Enable per node access control
$access_permissions = array(
'view[1]' => FALSE,
'view[2]' => FALSE,
'per_node' => TRUE,
);
$this->changeAccessContentType($access_permissions);
// Allow access for test user
$edit = array(
'acl[view][add]' => $this->test_user->name,
);
$this->drupalPost('node/'. $this->node->nid .'/access', $edit, t('Add User'));
$this->drupalPost(NULL, array(), t('Submit'));
// Logout admin, try to access the node anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node->nid);
$this->assertText(t('Access denied'), 'node is not viewable');
// Login test user, view access should be allowed now
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node->nid);
$this->assertNoText(t('Access denied'), 'node is viewable');
// Login admin and disable per node access
$this->drupalLogin($this->admin_user);
$this->changeAccessPerNode(FALSE);
// Logout admin, try to access the node anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node->nid);
$this->assertText(t('Access denied'), 'node is not viewable');
// Login test user, view access should be denied now
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node->nid);
$this->assertText(t('Access denied'), 'node is not viewable');
}
/**
* Test Editing accessibility with permissions for single users
*/
function testEditAccess() {
// Exit test if ACL module could not be enabled
if (!module_exists('acl')) {
$this->pass('No ACL module present, skipping test');
return;
}
// Enable per node access control
$this->changeAccessPerNode();
// Allow edit access for test user
$edit = array(
'acl[update][add]' => $this->test_user->name,
);
$this->drupalPost('node/'. $this->node->nid .'/access', $edit, t('Add User'));
$this->drupalPost(NULL, array(), t('Submit'));
// Logout admin, try to edit the node anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node->nid .'/edit');
$this->assertText(t('Access denied'), 'node is not editable');
// Login test user, edit access should be allowed now
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node->nid .'/edit');
$this->assertNoText(t('Access denied'), 'node is editable');
// Login admin and disable per node access
$this->drupalLogin($this->admin_user);
$this->changeAccessPerNode(FALSE);
// Logout admin, try to edit the node anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node->nid .'/edit');
$this->assertText(t('Access denied'), 'node is not editable');
// Login test user, edit access should be denied now
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node->nid .'/edit');
$this->assertText(t('Access denied'), 'node is not editable');
}
/**
* Test Deleting accessibility with permissions for single users
*/
function testDeleteAccess() {
// Exit test if ACL module could not be enabled
if (!module_exists('acl')) {
$this->pass('No ACL module present, skipping test');
return;
}
// Enable per node access control
$this->changeAccessPerNode();
// Allow delete access for test user
$edit = array(
'acl[delete][add]' => $this->test_user->name,
);
$this->drupalPost('node/'. $this->node->nid .'/access', $edit, t('Add User'));
$this->drupalPost(NULL, array(), t('Submit'));
// Logout admin, try to delete the node anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node->nid .'/delete');
$this->assertText(t('Access denied'), 'node is not deletable');
// Login test user, delete access should be allowed now
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node->nid .'/delete');
$this->assertNoText(t('Access denied'), 'node is deletable');
// Login admin and disable per node access
$this->drupalLogin($this->admin_user);
$this->changeAccessPerNode(FALSE);
// Logout admin, try to delete the node anonymously
$this->drupalLogout();
$this->drupalGet('node/'. $this->node->nid .'/delete');
$this->assertText(t('Access denied'), 'node is not deletable');
// Login test user, delete access should be denied now
$this->drupalLogin($this->test_user);
$this->drupalGet('node/'. $this->node->nid .'/delete');
$this->assertText(t('Access denied'), 'node is not deletable');
}
}

View File

@@ -0,0 +1,125 @@
<?php
/**
* @file
* Helper class with auxiliary functions for content access module tests
*/
class ContentAccessTestCase extends DrupalWebTestCase {
var $test_user;
var $rid;
var $admin_user;
var $content_type;
var $url_content_type_name;
var $node1;
var $node2;
/**
* Preparation work that is done before each test.
* Test users, content types, nodes etc. are created.
*/
function setUp($module = '') {
if (empty($module)) {
// Enable content access module
parent::setUp('content_access');
}
else {
// Enable content access module plus another module
parent::setUp('content_access', $module);
// Stop setup when module could not be enabled
if (!module_exists($module)) {
$this->pass('No ' . $module . ' module present, skipping test');
return;
}
}
// Create test user with seperate role
$this->test_user = $this->drupalCreateUser();
// Get the value of the new role
// Needed in D7 because it's by default create two roles for new users
// one role is Authenticated and the second is new default one
// @see drupalCreateUser()
foreach ($this->test_user->roles as $rid => $role) {
if (!in_array($rid, array(DRUPAL_AUTHENTICATED_RID))) {
$this->rid = $rid;
break;
}
}
// Create admin user
$this->admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'grant content access', 'grant own content access', 'administer nodes', 'access administration pages'));
$this->drupalLogin($this->admin_user);
// Rebuild content access permissions
node_access_rebuild();
// Create test content type
$this->content_type = $this->drupalCreateContentType();
}
/**
* Change access permissions for a content type
*/
function changeAccessContentType($access_settings) {
$this->drupalPost('admin/structure/types/manage/'. $this->content_type->type .'/access', $access_settings, t('Submit'));
$this->assertText(t('Your changes have been saved.'), 'access rules of content type were updated successfully');
}
/**
* Change access permissions for a content type by a given keyword (view, update or delete)
* for the role of the user
*/
function changeAccessContentTypeKeyword($keyword, $access = TRUE, $user = NULL) {
if ($user === NULL) {
$user = $this->test_user;
$roles[$this->rid] = $user->roles[$this->rid];
} else {
foreach ($user->roles as $rid => $role) {
if (!in_array($rid, array(DRUPAL_AUTHENTICATED_RID))) {
$roles[$rid] = $user->roles[$rid];
break;
}
}
}
$access_settings = array(
$keyword .'['. key($roles) .']' => $access,
);
$this->changeAccessContentType($access_settings);
}
/**
* Change the per node access setting for a content type
*/
function changeAccessPerNode($access = TRUE) {
$access_permissions = array(
'per_node' => $access,
);
$this->changeAccessContentType($access_permissions);
}
/**
* Change access permissions for a node by a given keyword (view, update or delete)
*/
function changeAccessNodeKeyword($node, $keyword, $access = TRUE) {
$user = $this->test_user;
$roles[$this->rid] = $user->roles[$this->rid];
$access_settings = array(
$keyword .'['. key($roles) .']' => $access,
);
$this->changeAccessNode($node, $access_settings);
}
/**
* Change access permission for a node
*/
function changeAccessNode($node, $access_settings) {
$this->drupalPost('node/'. $node->nid .'/access', $access_settings, t('Submit'));
$this->assertText(t('Your changes have been saved.'), 'access rules of node were updated successfully');
}
}