A client wanted a workflow that included automated transitioning between states as content aged. For example Draft -> Live -> Expiring -> Archived where the last two transitions occur automatically after 6 and 1 months respectively.

I was suprised to find no Workflow rules action to schedule a transition. I found a few related issues and patches for earlier versions of Workflow but it seems that it never made it into 7.2.

I could get my automation done using scheduled rules but that seems so clunky when Workflow already has a transition scheduling mechanism. And what happens when manual intervention changes the content's current state such that the scheduled rule no longer applies?

Currently busy coding and testing a 'Schedule a Workflow transition' action myself. Patch to follow soon.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

RogerB’s picture

Here is the patch to add the 'Schedule a Workflow transition' to the available actions in workflow_rules.

Use it in conjuction with the 'Workflow state has changed' event to create a rule that will, on a particular state change, schedule a subsequent change.

{ "rules_when_snippet_moves_to_live_" : {
    "LABEL" : "When snippet moves to \u0027Live\u0027",
    "PLUGIN" : "reaction rule",
    "WEIGHT" : "1",
    "OWNER" : "rules",
    "TAGS" : [ "Workflow" ],
    "REQUIRES" : [ "workflow_rules" ],
    "ON" : { "workflow_state_changed" : [] },
    "IF" : [
      { "workflow_check_state" : { "node" : [ "node" ], "workflow_state" : { "value" : { "6" : "6" } } } }
    ],
    "DO" : [
      { "workflow_rules_schedule_transition" : {
          "node" : [ "node" ],
          "workflow_state" : { "value" : { "7" : "7" } },
          "workflow_comment" : "System scheduled transition after being in \u0027Live\u0027 for 6 months.",
          "workflow_time_offset" : "6 months"
        }
      }
    ]
  }
}
johnv’s picture

Looks good.
I'm not sure if it is useful for your use case, but workflow-extensions contains a token [node:workflow-state-age].
It needs to be ported to this module. See #2148933-8: Check compatibility of 'workflow extensions' with 'workflow' module, version 7.x-2.0 (where this token is not listed ...)

johnv’s picture

(Using this as a dump for my thoughts)
The following feature request asks the same for the CaseTracker module:
#2220875: Rules integration for casetracker

johnv’s picture

Status: Active » Needs work

The patch in 31 needs to be adapted for workflow_field, and tested on another entity_type, e.g, a taxonomy term.

RogerB’s picture

Patch not working for 7.x-2.2

I applied the patch to version 7.x-2.2 and it no longer works. User recieved the notice of a future transition being scheduled but no row is created in the workflow_scheduled_transition database table.

RogerB’s picture

Looking at a database log I see that the workflow_scheduled_transition row is actually inserted but then gets deleted again a bit later.

Not quite sure what is going on but it looks as if the workflow_state_changed event is triggered and the rules processed before the node and its new workflow state is itself saved. When the new state is saved there seems to be a clean-up of any existing scheduled transitions, which makes sense.

Investigating further.

johnv’s picture

Yes, in a recent commit the workflowtransition-execute() function was changed. I think removing the scheduled transition was moved up some lines. It was changed because with multiple workflows per node, too many scheduled transitions were deleted.
This is in 7.x-2.2+dev.

RogerB’s picture

This rule action will not work as currently implemented if the transaction that triggers it includes a workflow state transition. It seems that the rule always executes and creates the scheduled transition before the new transition is executed which then deletes the scheduled transition created by the rule. This occurs whatever event is chosen to trigger the rule.

I have implemented a work around for my client which defers the creation of the scheduled transition untill the request has completed and all database updates involved in the transaction has been written. In the action handler I cache the properties of the scheduled transaction and then process them later in a hook_exit().

The action handler:

/**
 * Action implementation: schedule transition ignoring current user permission.
 *
 * Note that we defer the actual creation of the scheduled transition until
 * the end of the page request by statically caching its details here and
 * processing them within a hook_exit(). If the scheduled transition were
 * created here it would get deleted if the page request also included executing
 * a regular, immediate transition. Executing a transition always deletes any
 * existing scheduled transitions.
 *
 * @see cruk_workflow_exit().
 */
function cruk_workflow_schedule_transition(array $parameters, RulesAction $action) {

  $entity = $parameters['node'];
  $entity_type = 'node';

  $field_name = isset($parameters['settings']['field:select']) ? _workflow_rules_token_replace($parameters['settings']['field:select']) : '';
  $new_sid = array_pop($parameters['workflow_state']);

  // Apply time offset to current datetime to get required transition datetime.
  $date = new DateTime();
  $date->setTimestamp(REQUEST_TIME);
  $date->add(DateInterval::createFromDateString($parameters['workflow_time_offset']));

  // Add transition to cache for committing later.
  $transitions = &drupal_static('cruk_workflow_scheduled_transitions', array());
  $transitions[] = array(
    'entity_type' => $entity_type,
    'entity_id' => $entity->nid,
    'field_name' => $field_name,
    'new_sid' => $new_sid,
    'timestamp' => $date->getTimestamp(),
    'comment' => $parameters['workflow_comment'],
  );
}

The hoo_exit() function:


/**
 * Implements hook_exit().
 *
 * This hook is called after all processing of the request has finished, all
 * updates are done. It is now safe to write any workflow scheduled transitions
 * that were cached by the schedule a workflow transition action handler.
 */
function cruk_workflow_exit($destination = NULL) {
  global $user;

  // Get the statically stored scheduled transitions requests.
  $transitions = &drupal_static('cruk_workflow_scheduled_transitions');
  if (!empty($transitions)) {

    // Process each transition request.
    foreach ($transitions as $data) {

      // Load the entity to be transitioned and get it's current state.
      $entity = array_pop(entity_load($data['entity_type'], array($data['entity_id'])));
      if (empty($entity)) {
        continue;
      }
      $old_sid = workflow_node_current_state($entity, $data['entity_type'], $data['field_name']);

      // Create a scheduled transition for the entity.
      $transition = new WorkflowScheduledTransition();
      $transition->setValues(
        $data['entity_type'],
        $entity,
        $data['field_name'],
        $old_sid,
        $data['new_sid'],
        $user->uid,
        $data['timestamp'],
        $data['comment']);
      $transition->force(TRUE); // Ignore current user permissions.
      $transition->save();
    }
  }
}
johnv’s picture

Isn't the problem that your rule runs too early?
I think your rule is triggered while changing the state (i.e. the new state hasn't been saved, yet.)
I you choose the event "after node/entity save", it should be working.
With workflow_node you could also choose the event that is triggered by workflow-rules upon 'post transition'.
Your cruk_workflow_exit() shouldn't be necessary.

See also this project page using Rules to invoke actions after a period of inactivity

johnv’s picture

rpsu’s picture

Status: Needs work » Needs review
FileSize
5.33 KB

I added a new workflow scheduling rule which can be use to schedule transitions (which Workflow is taking care of after that). Leanign on patch in #1 and other comments.

This patch allows use of this Rule where date is taken from field field_state_dates (starting date) and workflow is update in field field_workflow_state:

{ "rules_node_state_update" : {
    "LABEL" : "Confirmed nodes to locked",
    "PLUGIN" : "reaction rule",
    "OWNER" : "rules",
    "REQUIRES" : [ "rules", "workflow_rules" ],
    "ON" : { "node_presave--booking" : { "bundle" : "article" } },
    "IF" : [
      { "AND" : [
          { "entity_has_field" : { "entity" : [ "node" ], "field" : "field_workflow_state" } },
          { "data_is" : { "data" : [ "node:field-workflow-state" ], "value" : "2" } }
        ]
      }
    ],
    "DO" : [
      { "workflowfield_field_schedule_state_change" : {
          "node" : [ "node" ],
          "workflow" : [ "node:field-workflow-state" ],
          "workflow_state" : { "value" : { "4" : "4" } },
          "date" : {
            "select" : "node:field-state-dates:value",
            "date_offset" : { "value" : -2592000 }
          },
          "allow_past" : 0,
          "workflow_comment" : "Automated state change 1 month before give date."
        }
      }
    ]
  }
}
rpsu’s picture

phillipclarke29’s picture

I have successfully added the patch in #11 - but I cannot figure out how to write the rule. I am trying to automatically change the state of a node from "live" to "archived" 1 month after the node's state was changed to "live". Any help would be appreciated.

rpsu’s picture

I see that you might have two different scenarios:
Either you have to track publishing date with perhaps a new date field and act on that date - when "Node is updated". On node update and with that field you should be able to schedule workflow state change.

Or maybe you have automated publishing based on alredy existing date field? If so, then you should be able to use the same date field for archiving, since rule "State change date (field)" has a (collapsed?) fieldset "Add offset" which includes and offset. You may set it to "+1 months" or similar in your case.

rpsu’s picture

Title: Rules action to schedule transition » Rules action to check workflow field state and to schedule transition
FileSize
7.89 KB

This patch allows admin to
- ACTION to schedule workflow field state change based on node published et al. datestamp or a date field value (optionally with time offset)
- (added) CONDITION to verify that workflow field is at required state

With this admin can schedule for example "Published" nodes to change to "Locked" or "Archived" based on node publishing date or separate date field.

NOTE: patch #11 contains by mistake dpm() -calls so do not use that.

gstout’s picture

I'm testing this patch in #15 now. I'll report back.

rpsu’s picture

Status: Needs review » Needs work

Re-assigning Needs review for testbot.

rpsu’s picture

Status: Needs work » Needs review
g33kg1rl’s picture

I need to automatically transition from one workflow state, 'Available' to another workflow state, 'Needs To Be Assigned', if the workflow state is not changed to, 'Claimed / Assigned', in 24 hours. Is that possible without the workflow extensions module (as mentioned above)?

johnv’s picture

You don't need that. Please apply and test patch 15 or before.

g33kg1rl’s picture

Do we have to add a date field that stores the workflow transition date or can we use a token?

johnv’s picture

We have a token 'how long is the entity in this state'.

g33kg1rl’s picture

How do I get that token to be available?

g33kg1rl’s picture

bump

johnv’s picture

Issue tags: +Documentation

Some remarks for this request, and the patch #15

The token '[workflow-state-age]' (that shows the time in seconds since last state change) from Workflow Extensions is now named as [node:last-transition:created:seconds].

#2237125: Adopt tokens from workflow_extensions

Patch 15 adds a workflow_field_check_state. IMO this is not necessary, and is explicitly removed, (in comparison with workflow_node) since you can simply check the field value.

Neeraj420’s picture

I am getting following error message when adding a task Schedule a workflow state change with comment.

Unknown action workflowfield_field_schedule_state_change.

If I add a condition field has a workflow state I get the error message : Unknown condition workflow_field_check_state.

Rule is triggered and content is updated.

Neeraj420’s picture

In my installation Cache was not enabled.
So I thought clearing the cache is not required. However after clearing the cache did the trick and it was working fine.

Is there any way of deleting the the schedule task?

johnv’s picture

Status: Needs review » Closed (outdated)

Please check the src/Event directory in version 8.x for alternatives and example code.

johnv’s picture