Hi there,

Say you have a group's home page displaying a teaser list of nodes. You have a module installed that provides a link (i.e. hook_link()), but only if a user_access() call returns true. This link won't get displayed within the group using OG User Roles, as the call to user_access() returns false.

I think the reason behind this is the cacheing that user_access() does. The first time you call user_access() it will remember the permissions that user has (if it has some permissions; if it has none, I don't think the caching actually happens). Adding roles later therefore doesn't have any influence on the results of user_access(). As the caching is done with a static variable, local to user_access(), I don't see an easy way around this, other than perhaps trying to load the roles provided by the group context even earlier than init() (you need to catch it before the first permission gets queried, which is administer menus, menu.inc:873 where I am).

CommentFileSizeAuthor
#3 ogur.patch590 bytesdmhouse

Comments

somebodysysop’s picture

Say you have a group's home page displaying a teaser list of nodes. You have a module installed that provides a link (i.e. hook_link()), but only if a user_access() call returns true. This link won't get displayed within the group using OG User Roles, as the call to user_access() returns false.

This is exactly what the OG module does: calls user_access to determine which "create" links are displayed on the group menu for a user. Are you saying that og_user_roles is not displaying the correct group menu links, or just the link from your module?

What module is it that wants to create a link that should be displayed on the group home page, but is not? Have you tried weighting OG User Roles lighter than this module?

Same thing happens when sitewide caching disabled?

What is an example url in the case where the link in question is not displayed? Have you looked at the og_user_test table to see what permissions are being returned by og_user_roles at this url?

I don't see an easy way around this, other than perhaps trying to load the roles provided by the group context even earlier than init() (you need to catch it before the first permission gets queried, which is administer menus, menu.inc:873 where I am)

Do you have any suggestions for loading roles earlier than init()? Right now, it's done when (if) the $user object is loaded and in hook_init().

dmhouse’s picture

This is exactly what the OG module does: calls user_access to determine which "create" links are displayed on the group menu for a user. Are you saying that og_user_roles is not displaying the correct group menu links, or just the link from your module?

By "create" links do you mean the 'Create <content type>' links within the Group details block? That's not what I'm talking about. When you have a teaser list of nodes, each node can have a list of links associated with it. For example, I've hacked modr8 to display 'Approve' or 'Delete' links alongside each node that's pending moderation (I also had to write a menu callback handler for this to work). Links can be added with hook_link().

Have you tried weighting OG User Roles lighter than this module?

How do you weight modules?

Same thing happens when sitewide caching disabled?

It will do, yes: if you look at the code for user_access(), it doesn't check to see if caching is enabled.

Have you looked at the og_user_test table to see what permissions are being returned by og_user_roles at this url?

I've var_dump()'d the $user object in user_access() when the appropriate call goes through. $user->roles has been correctly set to array('authenticated user', 'contributor', 'editor') at this point. However, this isn't looked at, as the caching that user_access() does (inside its local and static $perm variable) holds the permissions that were fetched the very first time that user_access() was called in this request, before og_user_roles had a change to add the extra items to $user->roles. These cached permissions are the ones that are used.

Do you have any suggestions for loading roles earlier than init()? Right now, it's done when (if) the $user object is loaded and in hook_init().

I would have thought that doing it when the $user object is loaded would be early enough, as this should be earlier than any user_access() calls. What do you mean be "(if)" here? Under what circumstances would $user not get loaded?

dmhouse’s picture

StatusFileSize
new590 bytes

I would have thought that doing it when the $user object is loaded would be early enough, as this should be earlier than any user_access() calls. What do you mean be "(if)" here? Under what circumstances would $user not get loaded?

After digging a bit more, I found the problem was that the code executed in og_user_roles_user('load') wasn't grabbing the group ID successfully. Adding the following code in og_user_roles_getgid(), before the 'return $gid' line, fixed the issue for me:

  $node = 
    db_fetch_object(
      db_query('SELECT nid, type FROM {node} WHERE nid = ' . $nid));
  if (og_is_group_type($node->type)) {
    $gid = $node->nid;
  } elseif (is_array($groups = og_get_node_groups($node))) {
    foreach ($groups as $id => $group) {
      if ($group) $gid = $id;
    }
  }

I'm wondering whether this code will be enough to grab the gid every time, or whether we still need the two other queries in that function.

I've attached a patch which contains my fix.

somebodysysop’s picture

Thanks for the code! Why didn't I think of that!

I am concerned about this section:

  } elseif (is_array($groups = og_get_node_groups($node))) {
    foreach ($groups as $id => $group) {
      if ($group) $gid = $id;
    }
  }

If the node belongs to more than one group, and the group in question isn't the one returned, then we have the same problem. Also: Suppose that this node belongs to two groups, and the user belongs to both groups, but has different permissions in each group? How do we determine which gid to bring back?

That's what I need to work on next.

somebodysysop’s picture

Status: Active » Needs review

Would you see if this code works for you?:

  // As per: http://drupal.org/node/167180
  // If gid still not found
  if ($gid == 0) {
    $node = db_fetch_object(db_query('SELECT nid, type FROM {node} WHERE nid = ' . $nid));
    if (og_is_group_type($node->type)) {
      $gid = $node->nid;
    } elseif (is_array($groups = og_get_node_groups($node))) {
      $last_id = 0;
      foreach ($groups as $id => $group) {
        if ($group) {
          // Need some way to determine the group to select in case of
		  // multiple groups.  Going to try using og_last session cookie.
		  // Just not sure if session will be set for current group by now.
		  if ($id == $_SESSION['og_last']->nid) $gid = $id;
		  $last_id = $id;
		}
      }
	  // If there was no session with og_last session cookie, then set gid to last
	  // id located in groups.  Roll of the dice until we can find something more
	  // certain.
	  if ($gid == 0) $gid = $last_id;
    }
  }

This adds some logic to try to address the issue of one node in two groups with a user who also belongs to the two groups.

Thanks.

dmhouse’s picture

If the node belongs to more than one group, and the group in question isn't the one returned, then we have the same problem.

This is a valid concern. I guess, instead of looking for the group ID, we need to find all group IDs which apply to this page.

Also: Suppose that this node belongs to two groups, and the user belongs to both groups, but has different permissions in each group? How do we determine which gid to bring back?

I think the right thing to do is to give the user all roles from all groups which apply to this node. My reasoning is that if you're looking at a node in a given group, you expect to have the roles for that group. This applies to all groups, so you would expect to have the roles for all groups. To do this og_user_roles_getgid() and og_user_roles_all_roles() need to be refactored to deal with more than one gid, and to fetch the roles for all of these gids.

Would you see if this code works for you?:

It works fine. However, I'm not sure it's the right approach, I think my above comments would be a better solution.

Thanks for your attention on this issue.

somebodysysop’s picture

You are right. I reached the same conclusion. og_user_roles_getgid() should return all groups a node belongs to, and og_user_roles_all_roles() should calculate all the permissions a user has for the node in all groups.

However, that's a major undertaking. So, right now, just trying my best to get the *current* group a node is in. The logic being that if the user doesn't have the correct permissions in the *current* group for the node, then deny access (even if he does have the correct permissions for the node in another group that the node belongs to).

I agree it's not the best logic, but at least it's consistent for now.

Thanks for tracking down this issue and testing code.

dmhouse’s picture

OK. It will indeed be a large undertaking. Perhaps you could apply that patch, close this issue, then begin work on multiple gid support. In the mean time consider the issue fixed for me, as in my case a group will never be a member of more than one group.

dmhouse’s picture

Err, sorry, I meant a node will never be a member of more than one group!

somebodysysop’s picture

Status: Needs review » Fixed

Code from #5 will be committed to 2.3 release.

somebodysysop’s picture

Status: Fixed » Closed (fixed)

Changes committed to 2.3 release.

somebodysysop’s picture

As a follow up, finally discovered the issue with user_access caching. Submitted patch to Drupal core here: http://drupal.org/node/170524

They rejected it, so I've made the patch available for download and added OG User Roles settings to be able to use it to "clear the cache". http://drupal.org/node/166566

somebodysysop’s picture

Version: 5.x-2.1 » 5.x-3.1

I've finally addressed the issue of a user belonging to two groups with different permissions here: http://drupal.org/node/258976#comment-850935

OGR now has a new function: og_user_roles_user_access($string, $gid, $uid). It is called during nodeapi('validate') and will make sure a user has permission to post in all groups that are listed in his group audience.