colin.longhurst

colin.longhurst

Alfresco Certified Engineer

Scheduling a “nag” email using Activiti Workflow and Alfresco

Sometimes users need to be reminded of outstanding tasks that have been neglected for a specified period of time. A helpful way to do this is to send them a nice email. For a recent project, I cobbled together an Activiti workflow that had various such “nag” steps that would fire off an email if a task became stagnant for too long, and I thought I’d share some code snippets on how I did it.

Workflow

I’ve simplified the workflow to a single step, plus the “nag” task for brevity:

You could easily imagine this as a part of a much more complex workflow, perhaps with multiple steps requiring timed email notifications.

Classify Task

If the Classify task is idle for more than 14 days, we kick off a friendly reminder email. Here’s the Classify Article workflow step.

<userTask id=”classifyArticle” name=”Classify Article” activiti:candidateGroups= “${ATCWF_classifierGroup}” activiti:formKey=”ATCWF:classifyArticle”>
    <extensionElements>
      <activiti:taskListener event=”complete”>
        <activiti:field name=”script”>
          <activiti:string>classification = task.getVariable(‘ATCWF_classification’);
              execution.setVariable(‘ATCWF_classification’,classification);
          </activiti:string>
        </activiti:field>
      </activiti:taskListener>
    </extensionElements>
 </userTask>
 <boundaryEvent id=”classifyTimer” name=”Classify Nag Timer” cancelActivity=”false” attachedToRef=”classifyArticle”>
    <timerEventDefinition>
      <timeCycle>${ATCWF_nagTimerDuration}</timeCycle>
    </timerEventDefinition>
</boundaryEvent>

So when this task is completed, it grabs the ATCWF_classification variable chosen by the user on the workflow’s form, and stores it in an Activiti workflow variable for later use. Not overly useful, but good enough for demonstration.

I’ve attached a boundary event, “classifyTimer”, to this task which will fire after a timeCycle determined by the ATCWF_nagTimerDuration Activiti workflow variable.

For the required 14 days, the value will be R/P14D (“repeat every period of 14 days”, according to ISO 8601 format.) Make sure cancelActivity is set to false so that the workflow won’t be suspended while we’re waiting.

A Brief Aside

I’d like to make a brief aside to comment on a useful “trick” I’ve used in my code snippets that might be useful to others, although it is not necessary for our task of creating a “nag” email.

Both ${ATCWF_nagTimerDuration} and ${ATCWF_classifierGroup} are Activiti workflow variables that I declared and set in a step prior to this Classify Article task.

This let me store those values in a configuration file in the underlying Alfresco repository, allowing them to be changed without having to redeploy the workflow. Which is nice, since deploying a new workflow doesn’t impact in-flight workflows, but it is possible by altering the value of some workflow variables.

This “trick” could be applied to change what group a later workflow step is assigned to, depending on choices made by a user in an earlier step, cutting down on extraneous exclusive gateways and tasks needed to account for all possible choices.


Email Task

Now let’s look at the step that’ll send a reminder email every 14 days.

<serviceTask id=”classifyNagEmail” name=”Nag Email” activiti:class=”org.alfresco.repo.workflow.activiti.script.AlfrescoScriptDelegate” activiti:async=”true” activiti:exclusive=”false”>
    <extensionElements>
      <activiti:field name=”script”>
        <activiti:string>
            var article = bpm_package.children[0];

                   // construct notification email
                  var subject = “An article needs to be classified”;
                  var emailTemplatePath = “”Data Dictionary/Email Templates/Notify Email Templates/ATC”;
                  var emailTemplate = companyhome.childByNamePath(emailTemplatePath+”/notify_reminder.ftl”);
                  var emailFrom = “reminders@abstractive.ca”;

                  var recipient = ATCWF_classifierGroup;
     
                  var mail = actions.create(“mail”);
                  mail.parameters.to_many  = recipient;
                  mail.parameters.subject   = subject;
                  mail.parameters.from = emailFrom;
                  mail.parameters.template = emailTemplate;
                  // placeholder body text; the template will have the message
                  mail.parameters.text = “Task requires action”;

           // pass article properties to the template layer for easy email substitution
           var templateArgs = new Array();
           var articleId = article.properties['sys:node-dbid'];
           templateArgs['articleId'] = articleId + “”;
           templateArgs['articleTitle']    = article.properties['cm:title'];
           templateArgs['reviewPeriod']    = ATCWF_nagTimerValue;

                  var templateModel = new Array();
                  templateModel['args'] = templateArgs;
                  mail.parameters.template_model = templateModel;

           mail.execute(article);
       </activiti:string>
    </activiti:field>
  </extensionElements>
</serviceTask>

Here we see the construction of an Alfresco mail action, the retrieval of a FreeMarker template from the repository for the message body, and passing the Activiti workflow variables and package properties to the template so the email can give more specific information about the article needing attention.

I simplified things a bit in the above snippet, as I normally put static variables such as the email subject, email template path etc. as properties in a configuration file for the same reasons I mentioned earlier.

Hopefully it’s not too difficult to see how you could extend the concepts and “tricks” I’ve shown in these two steps to make an intelligent workflow capable of making decisions and notifying pesky users to take care of neglected tasks.