Views provides basic access control by role or permission for each View display. This is usually sufficient. But what if you need finer control? For example, only allow access to the view if the current user has a specific relation to the view argument.
Use-case
- Authenticated Users on my site manage their own node pages - they have update access on the nodes they author, but only view access on pages authored by others.
- Moderators and Administrators have update access on all pages.
- A node page typically contain "attached" views - such as a list of photos or products associated with the page via a Node Reference. (see the wonderful Views Attach module)
- I want to add a tab to the page that displays all the items "attached" to the page, with edit & delete links, AJAX field editing, etc. (a mini CMS for the user's attached content items.)
- BUT, this tab should be visible ONLY to users with update access on the page's node.
Another example would involve adding a tab to the user's profile page that was only visible to the user themselves. (For complete case study, see: http://www.urbaninsight.com/2010/09/20/custom-access-control-drupal-views)
General Solution
Other solutions have been proposed, but Ki Kim's solution is very flexible and can all be coded within the view itself.
The basic idea is simple:
- add an argument to the view
- use PHP validation code to validate the argument
- as part of the validation code, check the access control rule
- select "Hide View" if validation fails
Voila. You can add whatever access control logic you like to the argument validation, and if the request fails to meet those criteria, the view will not be displayed (caution: the menu item or tab linking to the view will still be visible!).
Specific Solution for Use-Case
The view defines:
- its path as: node/%/product-list
- a tab menu (this tab will appear on the node/% page)
- a relationship to a Node (required), based on the Node Reference in the targets
- an argument: Node:nid on that relation, so that only nodes with a reference to page node are listed
- a filter to select the related node type
On the Node:nid argument, a "PHP Code" validator is defined, with "Hide View" as the action to take if the validation fails. This is the code used to validate the Node:nid argument:
// Code taken from views_plugin_argument_validate_node.inc : validate_argument()
// It validates that the argument is a nid for a node of specific type (the page node type)
// What's different is that it validates that the current user has UPDATE rights for this node.
if (!is_numeric($argument)) {
return FALSE;
}
$node = node_load($argument);
if (!$node || $node->type != 'page_node_type') { // NOTE: page_node_type is the node type id.
return FALSE;
}
// does current user have update access to this node?
return node_access('update', $node);
I think this is a very general approach and could be applied to a range of view access control issues.
Again, my thanks to Ki Kim for the original idea!
Comments
you also need to understand
you also need to understand that view arguments restrict the view to the argument provided (node view, uid argument -> display only nodes owned by uid), so if you dont actually want an argument, use one that will not filter anything, so for eg if i already filter my node based view with a filter content type "profile", i would add an argument for content type, specify default when not provided to be again "profile", and enter the php validation like above..
thing is that although this works, its not a true access check, as in if you have a menu link to that view, the menu link will still be shown but the page will return 404, whereas with a true access check, the menu link will be hidden
but you can hide the menu through other means
Views 2 makes your live easier
Views 2 makes your live easier. If you use Views 2 you can use
There many Views 2 hooks, see http://views.doc.logrus.com/ for complete information and samples.
This is a dead link.
This is a dead link.
Great Idea!
I had been wondering how to do this -- I want to let members of a group allow / deny access to various content they have posted for specific (other) group members. I just added a field to the user object, which maintains a serialized array of user ids for each group member. My validation function, as suggested here, checks to see if the user who posted the content has added the uid of the user attempting to view it. If so, no access. This works fine for user profile pages and blogs but I hadn't figured out how to apply it to views.
And the "Improved Multi Select" module makes it real easy to maintain the list of allowed or denied users
:
https://www.drupal.org/project/improved_multi_select
Thanks for the post!
Isn't there a hook to provide
Isn't there a hook to provide a custom access function - without that it is impossible to hide the menu tab on a page like node/%?