How to properly use user_access($string, $account) in Drupal? - drupal

I would like to limit the use of some url's. Let's say node/add and node/7 (just random examples). I'm thinking the best way to do this is to use the user_access function.
But as we are used to it, the Drupal documentation doesn't help much. When I just use the function, I get the message the function is already in use. So my best guess is to use this existing function with my own arguments in my custom function in my custom module.
But in this way I need to catch the page before loading it. Or I'm I missing something here?
EDIT:
I've set this
global $user;
$items['node/add/%']['access callback'] = array('_mymodule_node_access');
$items['node/add/%']['access arguments'] = array(0,2, $user);
But for some reason, Drupal isn't picking up the % card for all types. It's just working for one type (script). Other terms like page or fiche aren't getting picked up... % is a Drupal wildcard right?
EDIT:
I just found out there are already some paths in the database. How can I overwrite them? What I need is one selector which can select all four content types (fiche, page, script and news-item).

The way to define a particular access function for a path is to set the access callback for the path's menu item in hook_menu(). This is slightly different for existing paths, in that you need to implement hook_menu_alter() to edit the existing access callback for that path:
function mymodule_menu_alter(&$items) {
$items['node/add']['access callback'] = 'mymodule_node_add_access_callback';
}
function mymodule_node_add_access_callback() {
// return TRUE to allow access, FALSE to deny
}
This gets a bit more fun when we're talking about node pages as their menu items is defined using a wildcard node/%. This means that using hook_menu_alter() you can only change the access callback for all nodes.
Fortunately Drupal has a hook_node_access hook to come to the rescue:
function mymodule_node_access($node, $op, $account) {
$restricted_nids = array(7, 10, 12);
if (in_array($node->nid, $restricted_nids) && $op == 'view') {
if ($some_condition_is_true) {
return NODE_ACCESS_ALLOW;
}
return NODE_ACCESS_DENY;
}
return NODE_ACCESS_IGNORE;
}
Hope that helps
EDIT
If that all seems like a bit much hassle you might get some joy installing the Path Access module, I think it has the functionality you're after.
ANOTHER EDIT
I think the reason overriding the wildcard isn't working in this case is because the node module explicitly defines a path for each node type, e.g. node/add/page, node/add/article, etc. Because Drupal will take an exact match (node/add/page) over a wildcard match (node/add/%) you're actually overriding the wrong menu item.
Try specifying the path explicitly in your hook_menu_alter() function (note that the access callback should be a string and not an array as you currently have):
$items['node/add/page']['access callback'] = '_mymodule_node_access';
$items['node/add/page']['access arguments'] = array(0,2, $user);
It's also worth noting that the $user object you're passing will always be the user object of the logged in user who cleared Drupal's caches (since menu items are rebuilt when the cache is rebuilt). If you're looking to pass the current logged in user (i.e. the one logged in at the time the page is accessed) that's a different thing altogether...I'd advise asking another question on it as it can be a tricky bugger and you want to get as much input as possible from people on here.

Related

How to use hook_menu_alter() to manipulate path access control

/**
* Implementation of hook_menu_alter().
*/
function joke_menu_alter(&$callbacks) {
// If the user does not have 'administer nodes' permission,
// disable the joke menu item by setting its access callback to FALSE.
if (!user_access('administer nodes')) {
$callbacks['node/add/joke']['access callback'] = FALSE;
// Must unset access arguments or Drupal will use user_access()
// as a default access callback.
unset($callbacks['node/add/joke']['access arguments']);
}
}
The above function is from the pro development drupal. I can't understand it well. Why must I unset the access arguments (unset($callbacks['node/add/joke']['access arguments']);)?
Thank you.
That entire example seems broken and bad. In short, a joke. First, let me answer your question, then I'll go on to explain why you shouldn't follow that example in practice.
From includes/menu.inc:
if (!isset($item['access callback']) && isset($item['access arguments'])) {
// Default callback.
$item['access callback'] = 'user_access';
}
Unsetting the access callbacks when you no longer need them (relying on a boolean now, after all) prevents the over-clever logic in Drupal's routing system from slapping in user_access() just so it has something to do.
Now, on to why that's bad code.
hook_menu() and hook_menu_alter() are both run on cache clear (more specifically when the menu routing system is rebuilt). This means that the permissions of whichever user hits the site to rebuild the menus will be hard-coded into menu routing behaviors. This is a very bad and inconsistent arrangement.
If you want to block access to a path based on a permission, you need to change the callback to something that will test for that permission. Then when the menu is rebuilt, it will check the new callback function per page load to see if the current user should be granted permission.
A simple example of this might look like:
/**
* Implementation of hook_menu_alter().
*/
function joke_menu_alter(&$items) {
$items['node/add/joke']['access callback'] = 'user_access';
$items['node/add/joke']['access arguments'] = array('administer nodes');
}
Now we have a function which takes the node/add/joke path and declares that the only thing that matters is whether or not the user has administer nodes permission. Of course, that's a little more limited than the apparent intentions of the example, which were to preserve the existing access controls, but also require the user to have administer nodes permission.
That is also fixable, but is more complicated. To borrow some concepts from the Spaces project:
/**
* Implementation of hook_menu_alter().
*/
function joke_menu_alter(&$items) {
$path = 'node/add/joke';
$items[$path]['access arguments'][] = $items[$path]['access callback'];
$items[$path]['access callback'] = 'joke_menu_access';
}
function joke_menu_access() {
$args = func_get_args();
$access_callback = array_pop($args);
$original_access = call_user_func_array($access_callback, $args);
return $original_access && user_access('administer nodes');
}
We have successfully wrapped the original access callback in a new access callback, to which we can add whatever additional logic we need.
Note that in the last two function examples, I used the $path variable to keep the code simple. I also separated $original_access to it's own line and had it checked first, in practice I would check user_access() first as it would almost certainly be more performant than whatever happens in the original access callback.
The comment directly above that line explains it?
access callback is the function that is called (or TRUE/FALSE) and arguments is what is passed to that function. You are setting the callback to false and therefore always deny access to that router item.
And now, as the comment is saying, you also needto unset the arguments or Drupal will still use user_access() (The default access callback).

Changing background image of a Drupal page based on user's selection...?

I'm trying to give my users the functionality to change what the background image used on a page is.
The list of background images will be a small number that won't really change.
I thought I could add a few Taxonomy terms...one for each background type...then apply a class to the body tag when the page is viewed.
Does this sound feasible and if so how would I go about doing it?
Thanks
Sam
EDIT: revised answer after clarification of my misunderstanding of the question
If the background image is to be defined per (node) page, your approach via a taxonomy vocabulary sounds like the right way to go. To make the terms available for CSS, the easiest way would be to just output/use them as classes in the node.tpl.php file(s), where you have direct access to the $node variable. But in that case, they are somewhat buried in the middle of the resulting markup, which makes it a bit difficult to use them properly.
In order to add them to the $body_classes variable in the page.tpl.php, you'd have to either manipulate the zen_preprocess_page() function to add them as well, or (better approach) add them to your own modules/themes preprocess_page() function, using the zen function as an example:
function yourModuleOrTheme_preprocess_page(&$vars) {
// Add classes for body element based on node taxonomy
// Is this a node page?
if ('node' == arg(0) && is_numeric(arg(1))) {
// Yes, extract wanted taxonomy term(s) and add as additional class(es)
$node = node_load(arg(1));
$background_vid = yourFuntionToGetTheBackgroundVocabularyId(); // Could be hardcoded, but better to define as variable
$terms = $node['taxonomy'][$background_vid];
foreach ($terms as $tid => $term) {
// NOTE: The following assumes that the term names can be used directly as classes.
// You might want to safeguard this against e.g. spaces or other invalid characters first.
// Check the zen_id_safe() function for an example (or just use that, if zen is always available)
$vars['body_classes'] .= ' ' . $term;
}
}
}
NOTE: Untested code, might contain typos and other oversights.
(Original answer before edit - based on a misunderstanding of the OPs intent, left her in case other misunderstand it as well :)
The basic idea sounds feasible, but I'd suggest a minor variation:
Since you want the setting to be adjustable per user, you would have to jump through some hoops to allow users to 'tag' themselves with a taxonomy term. I think it would be far easier to just enable the (core, but optional) profile module and configure a 'background' field there (with type 'list selection'). The field will show up on the user page (or a separate tab on that page, if you give it a category), and the user selection will be available from code later on quite easily, e.g. to derive a class for the page template:
global $user;
// NOTE: The following call would be the explicit way,
// but usually the profile fields get added to the $user object
// automatically on user_load(), so you might not need to call it at all,
// extracting the values directly from the $user object instead
$profile = profile_load_profile($user);
$background = $user->profile_background

Problem saving imagefield using hooks in Drupal 6

Here's my custom module; it basically fetches a file from a particular URL, saves it on temporary folder and then i want it to modify a cck field of type 'file' (field name being : field_video_thumb) :
function mymodule_nodeapi(&$node, $op) {
switch ($op) {
case "update":
$node->field_video_thumb[0] =
field_file_save_file ($filename, array(),
$files_path, FILE_EXISTS_REPLACE);
// node_save($node);
break;
}
}
The problem i have here is that when i ucomment the 'node_save($node)' it works (but calls recursively of course) and removing it does not do anything.
I must be missing something really obvious but can't figure it out.
I have answered a similar question a while ago. There are some additional steps involved, but the most important difference to your attempt is to use the 'presave' operation of hook_nodeapi() instead of 'update', as update happens after the node has been updated.
(The code in the answer was taken from a utility class, so you would need to adjust it a bit to work from within a function.)

Drupal - Panels - use variants according to NID

I'm using Panels to overwrite node template (node/%node). I would like the system to use specific variant when a node is loaded. E.g. node 123 should use variant A and node 223 should use variant B. There isn't an option for me to determine that under Selection rules, I'm wondering if I should use PHP Code, and if I do, how should I go about writing the code?
I'm aware of the option of using Panels Node, but by using it, there is no easy way to edit the node thus rendering it a less than desirable choice.
In this case the easiest thing is probably to throw in some PHP code. It would be prettier to make an extension to the Panels selection rules, but this might be a bit overkill in this case.
Anyways something like
return arg(1) == 123;
should do it.
Your problem is probably Drupal/Panel cache. I just tested it, and it works fine.
You'll need to do something like this...
$nid = 11;
if (arg(0) == 'node' && arg(1) == $nid && !arg(2)) {
return true;
}
return false;
Be careful only testing arg(1) as in the previous answer, that will also match users (user/123) as well as any page view that accepts a numeric argument (articles/123).

Drupal: How can one hook exit in a custom module be called in every page request?

How can one hook exit in a custom module be called in every page request of a drupal website?
Here is the hook exit code in a custom module:
<?php
function cec_monitoring_exit() {
if ((arg(0) == 'node') && is_numeric(arg(1)) && arg(2) == '') {
// database inserts here
}
}
?>
Is it because of the if condition or someting else? Because some of the custom modules are calling the hook 'cec_monitoring_exit()' but some other custom modules don't.
Thanks in advance.
Cheers,
Mark
I'm not sure what you mean by it is not called in some of the custom modules either, so just speculating here that you might mean custom pages provided by other modules:
As Jeremy and Googletorp stated already, your if block
if ((arg(0) == 'node') && is_numeric(arg(1)) && arg(2) == '') {
// database inserts here
}
will only evaluate to true if the user is requesting a full node page (e.g. 'node/42'). It will not match any other page like a term view (e.g. 'taxonomy/term/36') or the default frontpage ('node') or a view page provided by the views module (might/have/any/path), etc..
So your database inserts would only take place for node pages, and nothing else.
If you want to log every page request, you can just remove the if block and do your database insertions directly.
Edit after clarification in comments (cec_monitoring_exit() does not get invoked for some pages created by other modules):
I can only see two possible reasons for this.
The first reason would be an error occurring right after the page output, but before the invocation of your hook_exit() implementation (check your server logs for php errors on requests for the failing pages). If you take a look at 'index.php' (top level folder of your Drupal instance):
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
$return = menu_execute_active_handler();
// Menu status constants are integers; page content is a string.
if (is_int($return)) {
switch ($return) {
case MENU_NOT_FOUND:
drupal_not_found();
break;
case MENU_ACCESS_DENIED:
drupal_access_denied();
break;
case MENU_SITE_OFFLINE:
drupal_site_offline();
break;
}
}
elseif (isset($return)) {
// Print any value (including an empty string) except NULL or undefined:
print theme('page', $return);
}
drupal_page_footer();
you can see that the page output is generated by printing the result of theme('page', $return). The invocation of hook_exit() happens right after that in drupal_page_footer().
So you should check (preferably via debugger, but you could also use print statements) if drupal_page_footer() gets executed on the pages in question at all. If it gets called, the error might occur in a hook_exit() implementation of another module that gets called before yours, so you'd need to check those.
The second reason would be if one of the modules would circumvent the standard Drupal execution flow by calling theme('page', ...) itself and stopped the execution afterwards. In this case, drupal_page_footer() would not be called, because the execution would have stopped long before during the call to menu_execute_active_handler(). Note that no established Drupal Module would do this, so it is pretty unlikely.
Besides these options, I have no further idea on what could cause this.
Hooks are called whenever their condition is met. This is done with the use of module_invoke_all(). This means that hooks that are called on every page request, will be invoked in every page request. In your example above, your hook wont do anything in some cases, but it will still be called since Drupal wont know when it actually will do something.
This waste of resource will be limited when pages are cached, since you only will need to run the hooks when page cached page is created. Another example is whenever a node is loaded with node_load, this will result in a lot of hooks being fired, and is thus quite expensive. Thus you usually want to avoid using this whenever possible, when you want to access something on a lot of nodes like their title.
#marknt15 what i think your failing to realize here is its all based on name so that hook being the hook_exit() in your case AKA _exit() is called when it is placed with a prefix of the calling modules name eg cec_monitoring_exit() will only work in the cec_monitoring module but in you other custom module named say marknt15_loves_drupal then it would meed to use hook_exit() like marknt15_loves_drupal_exit()
drupal then calls it and all other hooks with ...
call_user_func_array('modulename'_'hookname') or something very similar

Resources