Hello,

First off, I suspect that this issue is related to #1229178: Access denied when trying to view a revision with Display Suite module enabled, though I'm opening up a new issue thread as the dependencies of the specific problem I have found are a bit different.

We have been using Revisioning for a long time now with numerous projects (primarily nonpofit portals that have a variety of editors), and it has become a "core" module for us. On a very recent project we have been exploring the case-specific use of Domain Access (which operates by manipulating Drupal's node access records) and noticed that using it with Revisioning created some issues. Essentially, as long as Domain Access is installed, we loose the ability to save existing nodes into a "moderated" state. As soon as we do so, we loose access to the node that has a moderated revision. More specifically, the publicly accessible revision of the node that has a parallel moderated revision becomes inaccessible to everyone except admins.

Initially I though this was a Domain Access problem, but a little trial and error (and a lot of dpm()) points more and more toward issues with the way Revisioning sets the $node->status variable during certain operations.

It looks like Revisioning sets the status of a node to 0 during hook_node_presave() when it's going into a moderated state (around line 510 in revisioning/module):

    // This is not required for correct operation, as a revision becomes
    // pending based on vid > current_revision_id. But it looks less confusing,
    // if the "Published" box is in sync with the moderation radio buttos.
    $node->status = $auto_publish ? NODE_PUBLISHED : NODE_NOT_PUBLISHED;

Then, it makes note of the "real" status that's important to it, the status of the current revision, several lines later:

$node->current_status  = $current_revision->status;

Then, later on hook_node_update(), it makes sure the proper status gets recorded in the database:

   // Have to do this due to D7's "sick" denormalisation of node revision data.
    // Resetting the fields duplicated from new {node_revision} back to their
    // originial values to match the current revision as opposed to the latest
    // revision. The latter is done by node_save() just before it calls this
    // function.
    // By resetting {node.vid} {node.vid} < {node_revision.vid}, which makes
    // the newly created revision a pending revision in Revisioning's books.
    // Note: cannot use $node->old_vid as set by node_save(), as this refers to
    // the revision edited, which may not be the current, which is what we are
    // after here.
    db_update('node')
      ->fields(array(
        'vid'     => $node->current_revision_id,
        'status'  => $node->current_status,
        'title'   => $node->current_title,
        'comment' => $node->current_comment,
        'promote' => $node->current_promote,
        'sticky'  => $node->current_sticky))
      ->condition('nid', $node->nid)
      ->execute();

The problem I see is that I think that hook_node_access_records() may be called between hook_node_presave() and hook_node_update(), and modules that implement it (such as Domain Access) may make use of $node->status when setting access records. The result seems to be that node access permissions get set incorrectly as $node->status is not set correctly by Revisioning.

I can get around this problem by either commenting out this line (about 514 of revisioning.module)

$node->status = $auto_publish ? NODE_PUBLISHED : NODE_NOT_PUBLISHED;

OR by manually rebuilding the content permissions after saving a node into a moderated state. This seems to further indicate that Revisioning might be confusing other modules that set node access permissions upon save.

Again, I have not fully connected all the dots, or fully digested just what Revisioning is doing, but I just wanted to share what I found in hopes that it will lead to a quick solution to the issue we have found.

Mainly, I'm wondering what the consequence would be of commenting out line 514 (noted above) as it seems like the "quick-win" way to solve this.

Thanks!

Comments

rjacobs’s picture

Title: Revisioning may not be compatible with other modules that hook_node_access_records() (e.g. Domain Access) » Revisioning may not be compatible with certain modules that use hook_node_access_records() (e.g. Domain Access)

Edit: update title

rdeboer’s picture

Assigned: Unassigned » rdeboer

Thanks for your elaborate analysis Ryan and great to hear you consider Revisioning "core"!
I'll set some time apart soon to look into this deeper.
Will get back.

rjacobs’s picture

Thanks Rik,

Basis revisioning workflows have become such a common interest/demand that I've needed to find a nice scalable solution. Recent projects have not leveraged nearly the full potential of Revisioning, but knowing that the flexibility is there makes it seem like the general way to go.

Note that my analysis may not be accurate, as I was just stepping through a bunch of trial-and-error in a specific use case, but I have noticed that commenting out line 514 in revisioning.module gets things back on track. I also noticed several comments in the revisioning code about the D7 "flawed" handling of internal revisions, that required some workarounds in this module. Perhaps those workarounds are inadvertently leading to this unwanted behavior?

rjacobs’s picture

Humm, I just discovered that commenting-out line 514 may indeed have some functional consequences. Though it does solve the problem that I noted at the start, it seems to remove the ability for a user to override the default publishing status for a content type upon creation (e.g. choosing "create a new revision and moderate" will not force the revision to be unpublished if the default publishing status for the type is "published").

komlenic’s picture

I have observed nearly this same behavior when using Revisioning with Taxonomy Access Control under D7 - essentially, anytime a user saves a revision as "moderated" (and therefore unpublished!) it appears that the entire node in question disappears from the node_access table, thus making it inaccessible to all but roles with the "Bypass content access control" permission. (Possibly of interest: http://drupal.org/node/408816#appendix )

Rebuilding permissions after creating a moderated revision fixes the errors in the node_access table, but I am unsure if this is a viable solution.

I have observed that adding "node_access_acquire_grants($node);" into revisioning_node_load() in revisioning.module AT LEAST seems to keep the current revision available. (not a full solution but perhaps a step towards).

rdeboer’s picture

Thanks rjacobs and komlenic for the pointers. I'm hoping to investigate this deeper soon. In the mean time: patches welcome!

rjacobs’s picture

Hummm, I think I may have figured this out, as I just needed to pay more attention to what was happening when a node is being "auto published". From what I can see, whenever a "revision operation" is happening, revisioning is always making a binary decision on what to set the node's status ($node->status) to upon save, and is not considering what it was before the save. So it seems that there are cases when $node->status will be set to "unpublished", when it was previously "published", and should in fact not be changed at all.

This seems to play-out later in the node access stuff as some of the node access operations kick-in while this $node->status variable is set incorrectly.

Anyway, it's looking like simply changing line 514 in revisioning.module from:

$node->status = $auto_publish ? NODE_PUBLISHED : NODE_NOT_PUBLISHED;

to:

if ($node->status == $auto_publish) {
  $node->status = NODE_PUBLISHED;
}

Might take care of this. I'll make that into a patch in a moment, but it should be a quick thing to test either way.

rjacobs’s picture

Status: Active » Needs review
StatusFileSize
new630 bytes

Here is #7 in patch form. It's a tiny patch.

I'm hoping others can review with setups that use node access modules besides domain access. Also, I have yet to really try all the different revisioning options (types of revisioning actions, etc.) under this patch, but so far so good. I'm also hoping Rik can confirm things on a code level.

Cheers

rjacobs’s picture

Sorry, I made a syntax mistake in my comment in #7 which carried over into my patch. Please check out this one instead.

komlenic’s picture

The patch in #9 seems to make sense, and in my limited testing with Revisioning + Taxonomy Access Control, it appears to solve the issue. More thorough testing is needed, but essentially I can confirm that it fixes it.

rdeboer’s picture

Thanks for the feedback. Will apply to next release. In the mean time, anyone who can confirm that this patch works for them, let us know.

rjacobs’s picture

Glad to hear this might be a simple fix. I've tested it out in all the different editorial workflows that we use in one of my active projects (that relies on revisioning), and things appear to be working as expected under this patch. Hoping others experience the same with some more testing.

Also, if this does get committed, I'd certainly be honored if I was able to get git commit attribution (assuming it's justified here). I just realized that I didn't roll this one with git format-patch.

I'm happy to investigate further if issues come up (and it's something within my own range of knowledge). This is certainly a module we want to keep robust in D7.

jp.stacey’s picture

@rjacobs thanks for your patch. I think this is an improvement on the one I submitted on #1246364: Revisioning auto-publish checking on hook_node_presave breaks "Publish selected content" on admin/content so I'm marking that one as a duplicate and giving a +1 here. My colleague @lazysoundsystem is having a look at using this patch instead so hopefully he'll report back if he gets a sec.

rjacobs’s picture

Hi again,

I'm seeing another place where revisioning may not be playing nice with content access permissions. It seems that the fix I mentioned above gets things working correctly when choosing various moderation options via the edit page, but there still seems to be an issue when later publishing a moderated revision via the "publish" tab (that is part of the revisioning options).

Basically if I create a node that's subject to revisioning, and I chose not to publish it immediately (moderate instead), then all works fine... my node goes into a moderated state as expected and access is controlled as expected. However, if I later go to my revision and choose to publish it via the publish tab, I ultimately end up getting an access denied message for all users who do not have the node "bypass access control" permission. So it's looking like hook_node_access_records() may also not be getting invoked correctly within this workflow.

Of course, this is really only an issue when content access permissions are at play. In my case I'm needing to work with Domain Access and I'm wondering if people with other content access modules are also seeing this new problem?

rjacobs’s picture

StatusFileSize
new1.57 KB

I may have a solution to #14 now. It looks like publishing (via the tab) happens in _revisioning_publish_revision(), which primarily updates node and node_revision tables directly with db_update. The problem seems to be that it does not call anything which invokes hook_node_access_records(), so the node access permissions are not updated via this publishing workflow.

Adding the following at the end of _revisioning_publish_revision() seems to ensure that hook_node_access_records() is invoked (via the node_save()).

// We also want to give various hooks a chance to be invoked upon making these
// changes (such as hook_node_access_records()), which can be done with a
// simple node_save on the actual node.
$node = node_load($node_revision->nid);
node_save($node);

The attached patch captures this change along with the original change in #9. This one was made with git format-patch.

I'm not sure if there is a better way to do this? Maybe Rik will be able to weight-in on that?

rdeboer’s picture

@rjacobs, patch in #15
All looks good. Thanks so much!
Will include in next batch of improvements once I find time.
Rik

rjacobs’s picture

Thanks Rik!

therainmakor’s picture

I'm also having access problems when using organic groups with the group content visibility field enabled. I tried the patches but that did not work. I also tried using rules to rebuild the node access table by calling node_access_acquire_grants() after a node is saved, but it seems to be called after revisioning takes its turn.

joevansteen’s picture

I was having a number of issues, including something similar to this. I've been trying to slowly work my way though the issue queue for D7 and see how each one affects my environment.

After applying the patch in http://drupal.org/node/1243018 (After unpublish & publish revisions, update node_access records) this problem seems to go away for me.

I noticed that both issues have patches which attempt to force the node access records to get updated in module revisioning_api.inc, function _revisioning_publish_revision at the same point. The strategy here is to reload the node after it has been written to the DB, then to re-save it using node_save(). The strategy in the other patch is to call node_access_acquire_grants() in both the _revisioning_publish_revision and _revisioning_unpublish_node functions.

I was concerned about the different approaches hitting a common function for what appeared to be similar reasons. However, I first tested the other patch having affects on visibility of publication of new nodes, and it fixed that issue for me. I thought that this issue might be different since it talks about putting existing nodes into revision. However, when I tested with an existing node, with only the patch from 1243018 and not the patch here, I was able to get the proper effect: I maintained visibility for the 'current' published node for users who were not privy to the in-process revision, and I had recognition of the fact that there was a new revision pending for users who were privy.

Rik, I don't understand the full details, so I'll leave it to you to look at both in detail and compare the logic and see what makes more sense. Avoiding the re-read and re-write seems advantageous to me. Also, I don't know if the change made here to the revisioning.module code overlaps or is different from the changes made to that module in the other patch. The code is different, but the overall effect may be the same.

BTW - Thanks for this great facility!

rdeboer’s picture

@joevansteen,
Thanks so much for your analysis and feedback.
Based on your experiences it looks like #1243018: After unpublish & publish revisions, update node_access records is the way to go.
Rik

rdeboer’s picture

Having just studied and applied c.d.a's patch from #1243018: After unpublish & publish revisions, update node_access records, I'm revisiting @rjacobs patch (#15). As @joevansteen has pointed out there is overlap in the solutions as far as updating of the node access records goes.
However @rjacobs patch adds the auto-publish logic, which is what I've just committed.

From #12: Also, if this does get committed, I'd certainly be honored if I was able to get git commit attribution
Done: http://drupal.org/node/407994/committers

Thanks again for the detective work by all in this thread and for @rjacobs for the patch.

rdeboer’s picture

Status: Needs review » Fixed

Status: Fixed » Closed (fixed)

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

monotaga’s picture

I'm encountering what might be the same issue with Domain Access and Revisioning in D6 (ie. nodes are being auto-published, even if the user doesn't have "publish revisions" permission). Has anyone else encountered this? Ideas?

rdeboer’s picture

@monotaga #24:
6.x-3.14 or 6.x-3.x-dev (8 Feb 2012) ?
If you haven't tried the -dev version, please do so and let us know if the problem persists.

monotaga’s picture

RdeBoer,

I'd tried both 6.x-3.14 and 6.x-3.x-dev (8 Feb 2012) of Revisioning (with Domain Access 6.x-2.13+2-dev (2012-Jan-21)). I'm updating to Revisioning 6.x-3.x-dev (2012-Mar-03), and I'll let you know what I find.

monotaga’s picture

RdeBoer, as it turns out, there was a silly permissions situation. My bad!