Hi,

this is a bit complicated, I hope I can explain what happens.

I have the problem that pathauto doesn't generate a URL alias for my nodes when they are created. I use [menu-link-title] in the pattern for pages, and on node creation, [menu-link-title] is undefined. But if I edit the node and save it again, the alias ist created fine.

Here's why:
- node_save first calls node_invoke_nodeapi($node, 'insert');
After this it calls node_access_acquire_grants($node);

- node_invoke_api calls pathauto_nodeapi which executes pathauto_get_placeholders

- pathauto_get_placeholders calls token_get_values, this calls node_token_value.

- node_token_value calls menu_link_load to get the menu relevant stuff.

- menu_link_load calls _menu_link_translate which executes _menu_check_access
on the menu item. Only if the check returns TRUE, _menu_item_localize is called and will define $menu_link['title'] so that $values['menu-link-title'] could be defined in node_token_values.

- however, _menu_check_access fails! That's because the access_callback is node_access with parameter 'view', and so _menu_check_access finally calls node_access('view', $node). Now, if the user doesn't have the 'administer nodes" permissions, node_access executes
SELECT COUNT(*) FROM {node_access} WHERE (nid = 0 OR nid = %d) $grants_sql AND grant_$op >= 1
And here things go wrong: At this time there are no entries for the new node in the node_acess table, because (see first step), node_access_acquire_grants($node); is called *after* node_invoke_nodeapi.

So, menu_check_access fails, and $menu_link['title'] stays empty.

On editing and saving the node again, the grants in the node_access table are there

I wonder why no one else is seeing this. Maybe it only happens with node access modules installed? I've node_privacy_by_role which grants view access to all user created nodes for all roles. The problem is just that it writes the grants after pathauto tries to read them.

Does anyone have any idea what to do? (Except giving users 'administer nodes' permissions?

cu,
Frank

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Frank Steiner’s picture

I needed a quick fix so I put a node_access_acquire_grants($node) into pathauto_nodeapi for case 'insert' so that the grants are written to the db before the alias is created.

This fixes the problem. I wonder if this would be an acceptable patch for pathauto?

greggles’s picture

That doesn't strike me as being an acceptable solution. Pathauto runs with a weight of 10 by default (I think it's 10) which you can check by looking in the system table in the database. I feel like having a heavy weight should be enough to fix this, but maybe not.

Frank Steiner’s picture

Hi greggles,

sorry for my bad english, but I don't understand the meaning of "That doesn't strike me as" :-) Does it mean you consider the solution acceptable or not acceptable?

The module weight won't help here, because node_save() has the following code:

  node_invoke($node, $op);
  node_invoke_nodeapi($node, $op);

  // Update the node access table for this node.
  node_access_acquire_grants($node);

So whatever weight pathauto has, the grants are always written only after all the nodeapi hooks have run.

greggles’s picture

Frank - Your English is very good. I must apologize for using an idiomatic phrase!

I don't think the solution is acceptable.

It does raise a good question about whether the token should be created regardless of whether the user has access to the item. In some cases (like this one) I think the token should be rendered properly regardless of the user access. But, if the token will be used in a message displayed to the user then it would not be appropriate to show them tokens which they are denied access to.

For this specific issue - I wonder if it would be acceptable to move the node_access_acquire_grants above the node_invoke_nodeapi. That function was added in #75395: Incorporate node access arbitrator into node.module. I don't see any discussion about exactly where it should be relative to other functions.

Frank Steiner’s picture

Ok, got it! I don't have any hope getting code in node.module changed thinking of the latest discussion about patches on the devel list, but I will try and open an issue asking for a discussion about it. Maybe it helps!

greggles’s picture

Well, first thing is to see if it works ;) If so, post back here and I will try to help get it committed.

It's not impossible to get patches committed to core - just requires a well researched patch and some effort.

Frank Steiner’s picture

Well, thinking about it in detail, fetching and writing the grants before calling hook_nodeapi will only work if modules setting node_access_records are not altering the values from the node edit form before saving them.
node_privacy_by_role and forum access would work fine, because they return the settings from the edit form if they were not yet written to their db tables, and those are the same as the values written to the tables.

But the nodeaccess module wouldn't. In nodeaccess_node_access_records it checks if it has values in its own db table or not. If not, it returns the defaults and not the values from the edit form. The values are written in hook_nodeapi('insert'), so when calling node_access_acquire_grants before hook_nodeapi, nodeaccess would return the default set instead of the settings made by the user in the input form.

So I'm not sure how to solve this. We cannot write the node grants before all nodeapi hooks have run as they could alter them. But then we cannot check the grants in any hook_nodeapi.

We could change node_token.inc to do sth. like

	$menu_link = menu_link_load($mlid);
	// If the node was just created, access check for the menu item might fail
	// because node grants are written after nodeapi. Set access and retry menu
	// item translation, otherwise fields like 'title' are not filled.
	if ($node->is_new && !$menu_link['access']) {
	  drupal_set_message('redoing translate');
	  $menu_link['access'] = TRUE;
	  $menu_link['options'] = serialize($menu_link['options']);
	  _menu_link_translate($menu_link);
	}

This would solve the problem for menu-link-title. For menupath/menupath-raw we run into the same problem because menu_tree_all_data performs the access check and menu item translation for every item in the tree. But I guess we could just replace the last element of the menupath[-raw] with the values from $menu_link as this is supposed to be the last element of the path, isn't it?

greggles’s picture

Title: alias creation fails because permissions are checked too early » token creation fails because permissions are checked too early
Project: Pathauto » Token

I thought that might be a problem.

I don't have a good answer to this, but we should at least discuss it in the token queue since that's the main area of the issue.

Frank Steiner’s picture

Hmm, looks like no one else has any idea for this :-(

Johnny vd Laar’s picture

i'm running into the same problem now while using tac_lite. i've created a separate module to perform node_access_acquire_grants($node); in the nodeapi call.

so this fixes the problem for tac_lite but i assume we would need a better solution. as the problem only happens for the last added item in a menu path I think solution in #7 sounds correct but it feels a bit like a workaround patch

Dave Reid’s picture

Title: token creation fails because permissions are checked too early » Node menu token fails (node grants not saved yet, menu access checking, oh my!)
apemantus’s picture

I'm also getting this. However, it's also happening for users who have "administer nodes" (and User 1) as well.

I'm using several node access modules (Domain Access, Module Grants, Revisioning, a custom module etc).

Based on the fix from #10, I've created this in my custom module as a temporary hack:

function custom_nodeapi(&$node,$op) {
  switch ($op) {
    case "insert":
      node_access_acquire_grants($node);
      break;
  }
}

which seems to fix it. Is this going to cause any other problems?

Dave Reid’s picture

Version: 6.x-1.x-dev » 7.x-1.x-dev
Assigned: Unassigned » Dave Reid

Agreed that we'll need to provide a menu link load helper function in token.module that does not care or lookup access, so we can fix this once and for all.

Something like this:

function token_menu_link_load($mlid) {
  $cache = &drupal_static(__FUNCTION__, array());

  if (!is_numeric($mlid)) {
    return FALSE;
  }
  elseif (isset($cache[$mlid])) {
    return $cache[$mlid];
  }
  else {
    if ($item = db_fetch_array(db_query("SELECT m.*, ml.* FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $mlid))) {
      _token_menu_link_translate($item);
    }
    $cache[$mlid] = $item;
    return $item;
  }
}

function _token_menu_link_translate(&$item) {
  $item['options'] = unserialize($item['options']);
  if ($item['external']) {
    $item['access'] = 1;
    $map = array();
    $item['href'] = $item['link_path'];
    $item['title'] = $item['link_title'];
    $item['localized_options'] = $item['options'];
  }
  else {
    $map = explode('/', $item['link_path']);
    _menu_link_map_translate($map, $item['to_arg_functions']);
    $item['href'] = implode('/', $map);
    _menu_item_localize($item, $map, TRUE);
  }

  // Allow other customizations - e.g. adding a page-specific query string to the
  // options array. For performance reasons we only invoke this hook if the link
  // has the 'alter' flag set in the options array.
  if (!empty($item['options']['alter'])) {
    drupal_alter('translated_menu_link', $item, $map);
  }

  return $map;
}

Working on a patch for D7 (will be backported).

areikiera’s picture

Will be thrilled when this feature is ready. Has anyone found a safe workaround for this problem?

gooddesignusa’s picture

I'm using the [menupath-raw] token along with taxonomy access control. I'm having the same issue and I'm assuming its because of the same reason, _menu_check_access fails. This seems to not effect user 1 like others have already stated. In order for it to work for other users I need to enable 'administer nodes' permissions which defeats the purpose of having a permission module to limit node editing. I can provide a bounty to get this issue fixed if that will help speed things along.

Dave Reid’s picture

Status: Active » Needs review
FileSize
17.6 KB

Woot, working, cleaned-up code and patch for D7 token for review.

Dave Reid’s picture

So summary of #17:
1. Added token_menu_link_load(), token_book_link_load(), and _token_menu_link_translate() as copies of the core functions, but without access checking so we always get the titles of the links. We also can add more caching than core provides to help improve performance.
2. Added extensive tests for the menu link tokens. Can even confirm the bug here is fixed by creating menu links that point to non-existant paths, which would normally fail access checking.
3. Moved the [menu-link:menu] and [menu-link:menu-name] tokens to menu_token_info() since we shouldn't provide them if menu.module is not enabled.
4. Made a 'menu' token type that corresponds to the menu a menu link belongs to. This is also a chainable token.
5. If a menu link is already the root link, don't allow [menu-link:root] to return anything.
6. Did I mention more extensive tests?

Dave Reid’s picture

Status: Needs review » Fixed

Yay! The testbot likes #17 and so do I so I committed it to CVS!
http://drupal.org/cvs?commit=450834

Dave Reid’s picture

Version: 7.x-1.x-dev » 6.x-1.x-dev
Status: Fixed » Patch (to be ported)
gooddesignusa’s picture

@Dave Reid
When will this make it into the drupal 6 version? And when it does make it will this fix the issue with [menupath-raw] not working correctly with taxonomy access control ? Thanks again for all your hard work
I could provide a bounty to speed this along since I need it for a upcoming project.

gooddesignusa’s picture

I just tried out 6.x-1.x-dev and it worked. Thank you so much. perfect timing for the start of this new project I'm working on.

edit:
sorry I was in a rush when I tested it and forgot I had to switch to a different user account. When I created the node with taxonomy access control the menupath-raw seems to be acting weird but this could just be a different issue all together.

gooddesignusa’s picture

I've tried changing the weights around on path auto as well as token and taxonomy access control. No matter what weights they have it doesn't seem to work. It still can not get the last menu item so it results in trying to use a path that is already used.

Dave Reid’s picture

Status: Patch (to be ported) » Needs review
FileSize
20.37 KB

Ok, I think I've got this all backported now.

Dave Reid’s picture

Dave Reid’s picture

Little more docs.

Dave Reid’s picture

Found even more optimizations (we can get rid of the menu tree function and just load menu links by using the pN info in the menu links), so this one should be good to go. Did a performance test and when generating tokens for a node that is 6 levels deep in a menu, and three-levels deep in a book, and it saves about 2 seconds when run 500 times and tokens are cleared inbetween each run.

Just wow, this also fixes a lot of bugs with the tokens. For example, if your node is in a book, but not a menu, somehow the menu tokens have values when they should not.

Dave Reid’s picture

And now we have full book token test coverage as well.

Dave Reid’s picture

Status: Needs review » Fixed

And I ran #27 through all simpletests locally, this cleans up a lot of code and is just generally awesome. Committed it to CVS!
http://drupal.org/cvs?commit=463872

gooddesignusa’s picture

I just tried the latest dev and it fixes the issues I had. I'm now using [menu-link-parent-path-raw]/[menu-link-title-raw] instead of [menupath-raw]. Thanks a lot Dave Reid

gooddesignusa’s picture

There is an issue with [menu-link-parent-path-raw]. The way it builds the path for the parents needs some more logic. Basically if you use a token in front of [menu-link-parent-path-raw] it can cause unexpected results. For example:

I'm using [menu-raw]/[menu-link-parent-path-raw]/[menu-link-title-raw]
This works fine for first level menu items. I create a new page with a title of test and place it inside the parents menu. The path renders as /parents/test as expected. If I make another page that lives under that page and name it 'inside test', is when the issue happens. It renders as /parents/parents/test/inside-test. Making another test page named 'inside test inside' and put that page under the inside-test page. The url renders as: /parents/parents/parents/test/inside-test/inside-test-inside

Looking at the code it seems this is happening because of how [menu-link-parent-path-raw] works. On line 262 inside token_node.inc: $values['menu-link-parent-path-raw'] = drupal_get_path_alias($parent['href']);

Basically because its grabbing the alias it will include any token that would be in front of [menu-link-parent-path-raw] on the pathauto config page. This is as far as I got with debugging. I'm not a php expert but I'm thinking maybe some extra logic needs to be added. Maybe somehow run some type of check that strips out duplicates. I will post back after I do some more testing.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

jstoller’s picture

Status: Closed (fixed) » Active

I've been going crazy for days trying to solve this problem with Pathauto and [menupath-raw]. I just tried installing the latest Token 6.x-1.x-dev to get this fix, but now neither [menupath-raw] or [menu-link-parent-path-raw] work at all. It may have been fixed in December, but it seems to be broken again now.

Dave Reid’s picture

@jstoller: Can you elaborate about the problem you are having?

jstoller’s picture

Lets say I have a page on my website called Features, with the path alias imax/features. I also have a Movie content type. Anytime a Movie node is created, I have a function that automatically adds a menu entry placing it under the Features page. Using the currently released Token module, if I set Pathauto to use [menupath-raw] for Movie node aliases, then I will get an alias like imax/features-0 for my movie, when what I'm expecting is something like imax/features/hubble-3d. Basically Pathauto is only seeing the menu path up to the parent item. I believe this is the same problem being discussed throughout this issue.

Yesterday I tried replacing my version of Token with the latest dev release. After I did that, anything using [menupath-raw] to set an alias didn't return any alias at all. Not even the parent item's path. I also tried using [menu-link-parent-path-raw]/[title], but that acted as if I had just used [title]. [menu-link-parent-path-raw] was also not returning anything. I've now downgraded my system back to the official release of Token and things have returned to the way they were, but in some cases this just won't do. I can't always get around using [menupath-raw] to set my aliases. I even tried taking the last patch from this issue and applying that to the official release, but that just crashed my site. I assume the patch wasn't made to be applied to that version of Token.

I'm running Drupal 6.20 and Pathauto 6.x-1.5, if that matters.

gooddesignusa’s picture

jstoller i'm using Token 6.x-1.x-dev (2010-Dec-14) and it is working fine. I'm using menupath-raw token to set my paths as well. Before I was using the recommended version and I also had the same issue. I have yet to upgrade to the latest dev so idk if its broken again.

jstoller’s picture

@gooddesignusa: Would you mind testing the latest dev with your site and seeing if you have the same problems I'm having? Just be prepared to downgrade back to the Dec. 14 dev if things go badly.

gooddesignusa’s picture

If I have time I will try. The deadline to go live is on the 15th, so time is limited. Thanks for the heads up though.

Dave Reid’s picture

Status: Active » Postponed (maintainer needs more info)
jstoller’s picture

@Dave: What can I do to get you the information you require?