Problem/Motivation

Unexpected behaviour occurs when attempting to create a new "draft" revision of a node that has already been published within a workflow that includes users with different permitted transitions.

Steps to recreate

  1. Create a workflow with at least 4 transition states, e.g. 'In draft', 'For review', 'To publish' and 'Live'.
  2. For the purposes of this example, create two user roles: 'author' and 'moderator'. Configure workflow transitions:
    • Grant 'moderator' the ability to perform all transitions.
    • Grant 'author' the ability to perform (at least) the following transitions:
      • (creation) -> 'In draft'
      • 'In draft' -> 'For review'
      • 'Live' -> 'In draft'
  3. Add a workflow flow field to a content type and configure the available states to be displayed as a list of radio buttons.
  4. Configure the publishing options for the same content type as follows:
    • Not published - unchecked
    • Create new revision - checked
    • New revision in draft, pending moderation - checked
    • New revision in draft: Create new revision: Every time revision content is updated, even when saving content in draft/pending moderation - checked
  5. As a 'moderator', create a new node and publish it, i.e. set the workflow state to 'Live'.
  6. As an 'author', edit the published node and change the target state from 'Live' to 'In draft' and save the revision.
  7. Continuing as an 'author', edit the new revision and change the target state from 'In draft' to 'For review' and save.
  8. Also as an 'author', edit the latest revision and check the options for target state: 'Live' and 'In draft' will be displayed again with 'Live' selected, rather than 'In draft' and 'For review' with 'For review' selected.

At this point, 'authors' get stuck in a loop, as it's not possible to transition a revision from 'In draft' to 'For review' without it seemingly reverting to the 'Live' state.

Notes

I've created a module that implements hook_workflow() to try and do some debugging. At the point of changing the workflow state from 'In draft' to 'For review', the transition object indicated that the current state ID (2 - 'In draft') and the target state ID (3 - 'For review') were as expected. However, two errors were produced during the process of saving:

Attempt to go to nonexistent transition (from 5 to 3)
User matt_author not allowed to go from state 5 to 3

The transition from 5 to 3 ('Live' to 'For review') does exist in the configuration of transitions, but only for users with the 'moderator' role. The second error message is correct as 'authors' aren't able to perform this transition. I don't know why the 'Live' to 'For review' transition is in the mix, as it's nothing to do with the transition I was attempting to perform!

Has anyone experienced anything similar to this?! Any advice or suggestions would be greatly appreciated!

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Anonymous’s picture

Issue summary: View changes
johnv’s picture

Priority: Major » Normal

- Please try latest dev (or version 2.5 when it is available already). It has some corrections for working with revisions.
- Setting this to normal, since this seems a special use case including a 3rd module. (Editing revisions is not a standard permission, and provided by the Revisioning module).
- The Revisioning module is developed with Workflow Node in mind (Workflow version 1.2). Do you use Workflow Field or Workflow Node?

Anonymous’s picture

Status: Active » Postponed

Thanks for your reply, and my apologies for taking a while to respond. Unfortunately, I've been able to recreate the problem in version 2.5, but I'm using Workflow Field. I'll try using Workflow Node to see if that plays better with revisions and report back.

Anonymous’s picture

Version: 7.x-2.4 » 7.x-2.5
Status: Postponed » Active

I changed my configuration to use Workflow Node instead of Workflow Field but I didn't get very far unfortunately. After saving a node of a content type with an associated workflow, I got a nasty EntityMetadataWrapperException, which pretty much ended my exploration of that option!

I've updated the module to version 2.5, but the problem I described in my original post is still occurring. I'm keen to find a solution to this, as Workflow combined with Revisioning and Rules has the potential to produce a great content authoring experience. I've done a bit more digging, and I've identified something that might be related to the cause of the problem. If I describe my findings, hopefully someone who knows the module a little better than me will be able to suggest a solution.

I decided to start by looking at the widget provided by Workflow Field first. I assumed that the problem is being caused by incorrect entity information (i.e. not using the correct/latest revision for state transitions), so I added a watchdog call to dump out the contents of $entity in the constructor of the WorkflowD7Base class at various points during the workflow. I've included a summary of the contents of $entity at the various stages below:

1. (creation) -> 'For review' (Skipped the 'In draft' state to get to the problem faster!)

stdClass Object
(
    [nid] => 1
    [vid] => 1

    [revision] => 1
    [revision_moderation] => 1

    [is_new] => 1

    [status] => 0
    [current_status] => 0

    [timestamp] => 1421401510
    [created] => 1421401510
    [changed] => 1421401510
    
    ...

    [field_workflow] => Array
        (
            [und] => Array
                (
                    [0] => Array
                        (
                            [value] => 3    // 'For review'
                        )

                )

        )
)

2. 'For review' -> 'Live'

stdClass Object
(
    [nid] => 1
    [vid] => 2
    [old_vid] => 1

    [revision] => 1
    [revision_moderation] => 1
    [revision_timestamp] => 1421401510
    [num_revisions] => 1
    [current_revision_id] => 1

    [is_new] => 
    [is_current] => 1
    [is_pending] => 1

    [status] => 0
    [current_status] => 0

    [timestamp] => 1421401601
    [created] => 1421401510
    [changed] => 1421401601
    
    ...

    [field_workflow] => Array
        (
            [und] => Array
                (
                    [0] => Array
                        (
                            [value] => 4    // 'Live'
                        )

                )

        )

    ...

    [original] => stdClass Object
        (
            [nid] => 1
            [vid] => 1

            [revision_moderation] => 1
            [revision_timestamp] => 1421401510
            [num_revisions] => 1
            [current_revision_id] => 1

            [is_current] => 1
            [is_pending] => 1
            
            [status] => 0
            
            [created] => 1421401510
            [changed] => 1421401510

            ...

            [field_workflow] => Array
                (
                    [und] => Array
                        (
                            [0] => Array
                                (
                                    [value] => 3    // 'For review'
                                )

                        )

                )
        )
)

3. 'Live' -> 'In draft'

stdClass Object
(
    [nid] => 1
    [vid] => 3
    [old_vid] => 2
    
    [revision] => 1
    [revision_moderation] => 1
    [revision_timestamp] => 1421401601
    [num_revisions] => 2
    [current_revision_id] => 1

    [is_new] => 
    [is_current] => 
    [is_pending] => 1
    
    [status] => 0
    [current_status] => 0
    
    [timestamp] => 1421401849
    [created] => 1421401510
    [changed] => 1421401849

    ...

    [field_workflow] => Array
        (
            [und] => Array
                (
                    [0] => Array
                        (
                            [value] => 2    // 'In draft'
                        )

                )

        )

    ...

    [original] => stdClass Object
        (
            [nid] => 1
            [vid] => 1    // Should be 2!

            [revision_moderation] => 1
            [revision_timestamp] => 1421401510
            [num_revisions] => 2
            [current_revision_id] => 1

            [is_current] => 1
            [is_pending] => 

            [status] => 0

            [created] => 1421401510
            [changed] => 1421401601
            
            ...

            [field_workflow] => Array
                (
                    [und] => Array
                        (
                            [0] => Array
                                (
                                    [value] => 3    // 'For review'
                                )

                        )

                )
        )
)

At this point, the error occurs. From what I can see, the contents of [original] in step 3 is incorrect. [old_vid] refers to the correct revision (vid = 2), but the previous revision is included in [original] - vid = 1. The value of [field_workflow] is therefore 'for review' rather than 'live', and attempting to transition from 'for review' to 'in draft' is not permitted and results in the error.

(NB: The configuration of transition states have changed from my original post, and rules were disabled for this test so the node never actually gets published, which is why [status] => 0.)

Any thoughts? Thanks for your help!

Anonymous’s picture

Status: Active » Postponed

Postponed while I investigate if the Revisioning module is having any effect on the contents of [original].

Anonymous’s picture

Category: Support request » Feature request
Status: Postponed » Active

I've discovered that the source of the problem I've been experiencing is within the workflow_node_previous_state function in workflow.module. It isn't a bug, as without the Revisioning module in the mix, the functionality provided by the Workflow module works as expected.

Due to the way the Revisioning module manages revisions, there is no guarantee that the contents of $entity->original is the latest revision. Depending on how revisioning is configured, and based on the testing I've done, $entity->original appears to contain the current revision, i.e. the revision that is currently published. This means that using $entity->original to retrieve the previous workflow state will cause the error I described above when using the Revisioning module.

In order to get things working (and in an attempt to get back on track with my sprint!), I've implemented a hacky fix which simply provides an alter hook before $sid is returned (approx. line 1015):

// Allow other modules alter $sid.
$context = array(
  'entity' => clone $entity,
  'entity_type' => $entity_type,
  'field_name' => $field_name,
);
drupal_alter('workflow_node_previous_state', $sid, $context);

I've implemented the hook in a custom module, which will then check for the latest revision, and change the value of $sid. Not very pretty to say the least, but it'll let me get on with what I need to do for the time being!

Unfortunately, I don't know the module well enough to answer this question: could there be a more elegant solution for this issue?

Thanks!

johnv’s picture

"$entity->original appears to contain the current revision, i.e. the revision that is currently published."

This seems like a bug in Revisioning: suppose I have 4 versionswith vid 1-4, and I edit the unpublished version vid=2, I'd expect $entity->original to have the value vid=2. Instead, it contains vid=4.
Am i right?

Anonymous’s picture

Status: Active » Postponed

Thanks for your reply. I'm not sure if that behaviour is necessarily a bug in the Revisioning module. I think the value of $entity->original varies depending on exactly how revisions are configured - I'll need to do more exploring to confirm that and possibly post a question in the issue queue for the Revisioning module, so I'll postpone this issue again for the time being.

johnv’s picture

Perhaps it is possible to rewrite workflow_node_previous_state(), without using the original.

Anonymous’s picture

Would using entries stored in the workflow_node_history table be feasible for rewriting workflow_node_previous_state()? I assume an entry is written to the table after a state transition is successfully completed, and as the nid and revision_id is stored, it looks as though all the information needed to determine the previous state is available in that table. If that's the case, I'm happy to have a go at making a patch.

johnv’s picture

Yes, It would.
Be careful: there are always 2 different options: if Fieldname is empty, the WorkflowNode is used, else WorkflowField is used.

A big issue in that function is when the function is called during update. In the update, you have a phase before DB-change, and one after DB-change. That's why the 'original' was so ideal, (and it does not need an extra DB-read.)

Perhaps an exception should be inserted if the VID's are in some state?

sujith.nara’s picture

Beware!! Workflow tab is not syncing with node's revision.
See this http://www.ionsden.com/article/problems-workflow-and-revisioning-drupal (applicable to Drupal 7)

ShaxA’s picture

So i have been struggling with the same issue a whole day. Finally i found how to fix it. We have two case scenarios. The first is when we publish a new version then we have to get the old revision by old_vid which always contains the right vid. This we must do only if we are not publishing an old revision.

The second is when we are publishing an old revision then we must get the sid from the entity->original which contains the previous published revision and the old_vid will contain the new revision.

johnv’s picture

Status: Postponed » Needs work

@ShaxA, is your patch really workfing OK? It has a type here: if (!empty($wrrapper)) {

ShaxA’s picture

I am really sorry i forgot to send the last patch with this fix. Yes it is working after you correct this typo. Thanks for reminding me.

  • johnv committed 491a7f7 on 7.x-2.x authored by ShaxA
    Issue #2373383 by ShaxA: Integration with Revisioning module: Workflow...
johnv’s picture

Version: 7.x-2.5 » 7.x-2.8
Status: Needs work » Fixed

Thanks. This is now fixed.

There is no D8-port of Revisioning module, yet. I'll add a reminder D8-task.

  • johnv committed 8b71515 on 8.x-1.x authored by ShaxA
    Issue #2373383 by ShaxA: Integration with Revisioning module: Workflow...
johnv’s picture

The committed fix is different from your patch, since the code has been changed. Please test the new version and give some feed-back.

Status: Fixed » Closed (fixed)

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

ShaxA’s picture

Hi johnv, I will have a look as soon as i can. Thanks.

carstenG’s picture

Hey.
Patch is working fine as long as a new revision is created on every save.
But when you chose the other option "Only when saving page content that is not already in draft/pending moderation" the problem still remains.
It is taking the $entity->original

I have noticed that workflow_node_previous_state() function we have a entity in place where ne correct workflow status of the previous node is available.
$entity->field_workflow['und'][0]['workflow]['workflow_entity']

So changing this

elseif (isset($entity->original)) {
    $previous_entity = $entity->original;
  }

into

elseif (isset($entity->{$field_name}['und'][0]['workflow']['workflow_entity'])) {
    $previous_entity = $entity->{$field_name}['und'][0]['workflow']['workflow_entity'];
  }
elseif (isset($entity->original)) {
    $previous_entity = $entity->original;
  }

What do you think?
As this is already committed I have added this small patch.

carstenG’s picture

Status: Closed (fixed) » Needs review
daxelrod’s picture

The issue remains as carstenG describes. The patch in #22 works.

A couple other options besides the above patch:

  1. Revisioning clears the $node->revision flag when the above option is selected. It could set $node->old_vid which would allow the existing code to work.
  2. Use some other combination of $entity->revision_moderation and $entity->is_pending to identify revisions instead.

  • johnv committed f14287e on 7.x-2.x authored by carstenG
    Issue #2373383 by ShaxA, carstenG: Integration with Revisioning module:...
johnv’s picture

Status: Needs review » Fixed

Thanks.

  • johnv committed 7f46930 on 7.x-2.x authored by carstenG
    Issue #2373383 by ShaxA, carstenG: Integration with Revisioning module:...

Status: Fixed » Closed (fixed)

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