Thursday, 20 October 2016

Send an Email for approval to the admin in a CQ5/AEM page publishing workflow

The Problem

You want to send an email to the admin for approval or rejection and after approval, page should be activate and send an email to the author of activation and workflow should be complete but if rejected than send an email to the initiator of rejection and workflow should not be completed until the page approved using workflow.

In this post, I will show you all the required step and and configurations which we have to do.

For send an email to the admin on the click of activate, Firstly you have to deny the permission of replication of that author. Now when you click on the activate button you will find a pop up like this:


It means your request for activation workflow has been launched.

Note: It is applied till AEM 6.0 version above this version (AEM 6.1 & AEM 6.2) Activate button will be disabled So In that case we can not activate from siteadmin for this, we have to go on that particular page for activate the page.

Now if you will see in the admin inbox,you will find that request for activation workflow is in running condition.

Now we have to change this model according to our requirements.
Go to the workflow by logging in to the admin interface:
http://localhost:4502/libs/cq/workflow/content/console.html

And search for Request for Activation model in model tab.










Double click on this model and delete all steps and drop your Dialog participant step and double click on this step you will find a pop up like this:















To Know about Dialog Participant Step click here:


In this popup, there are three tabs Common, User/Group and Dialog.

In first tab, there are two widgets title and description. Here you can give title and description of your step.
In second tab, there are two widgets User/Group and Email.

Select admin in user and tick on the Email if you want to send the mail to the admin inbox.
Note:
If you want to send the mail to the gmail of admin than you have to give the gmail id to the admin from the useradmin console.








In third tab you have to select the path of your dialog which you have created for the dialog participant step.
In my case I select the path:
/etc/workflow/dialogs/approval/dialog

Now click on OK. your dialog participant step has been created.








Now if we activate the page, an email will send to the admin inbox and gmailId. Admin will select this step and click on the complete tab than a dialog will open with the drop down. Admin will select approve or reject from the drop down than this value will be save into the workitem.















Till now, workflow not complete this was only for our dialog participant step confirmation that it is working fine or not.

So go back our workflow model and drop the process step below the dialog participant step.This step is used for fetch the value of dilaog input from the workitem and set this property on the workflow metedata.To do this we have to write an ECMA script with some code as given below:

var name;
var history = workflowSession.getHistory(workItem.getWorkflow());

for (var index = history.size() - 1; index >= 0; index--) {
  var previous = history.get(index);
    var tempRejectApprove = previous.getWorkItem().getMetaDataMap().get('name');
    if ((tempRejectApprove != '')&&(tempRejectApprove != null)) {
        name = tempRejectApprove;
        break;
    }
}
workItem.getWorkflowData().getMetaData().put('name', name);

In my case I created a script named as relpaceDialogValue.ecma. Location of this file will be
/etc/workflow/scripts/replaceDialogvalue.ecma

Now when we select from the process step drop down, this script will show and select it.
Because of this script, your dialog value will be saved on workflow metadata.

Now below this process step, we have to drop the OR Split process and select the 2 branches. In the first branch script, we have to write the script which is given below:

function check() {
    var match = 'approve';
    if (workflowData.getMetaData().get('name') == match) {
        return true;
    } else {
        return false;
    }


In the Second Branch we have to write this code:

function check() {
    var match = 'rejected';
    if (workflowData.getMetaData().get('name') == match) {
        return true;
    } else {
        return false;
    }
}
If the branch 1 will return true then workflow move on left side. If Branch 1 return false or branch2 return true then workflow will move on right side.

Note:
Before the OR Step I have to use Process step because we can not access the value of the dialog from workItem in to the OR Split process there is an error that "workitem is not defined" because OR Split process does not have workitem object so I have to use process Step.

Step for left split:

Now one more process step drop in to the left split.


 Double click on this step and go to the process tab and select activate page from the drop down.


This process step will activate the page after selection by the admin as approve from the inbox as select the drop down.












Below this step drop one more process step for sending the mail to the initiator.


 Double click on this step and select the custom process step (custom step for approval) for sending the mail to the initiator.




 Code for this step is given below:

package com.havells.services;

import java.util.List;

import javax.jcr.RepositoryException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.framework.Constants;

import com.adobe.granite.workflow.WorkflowException;
import com.adobe.granite.workflow.WorkflowSession;
import com.adobe.granite.workflow.exec.HistoryItem;
import com.adobe.granite.workflow.exec.WorkItem;
import com.adobe.granite.workflow.exec.Workflow;
import com.adobe.granite.workflow.exec.WorkflowProcess;
import com.adobe.granite.workflow.metadata.MetaDataMap;
import com.day.cq.mailer.MessageGateway;
import com.day.cq.mailer.MessageGatewayService;

import org.apache.commons.mail.Email;
import org.apache.commons.mail.SimpleEmail;

//This is a component so it can provide or consume services
@Component
@Service
@Properties({
@Property(name = Constants.SERVICE_DESCRIPTION, value = "Test Email workflow process implementation."),
@Property(name = Constants.SERVICE_VENDOR, value = "Adobe"),
@Property(name = "process.label", value = "Custom Step for approval") })
public class CustomStepForApproval implements WorkflowProcess {

protected final Logger log = LoggerFactory.getLogger(this.getClass());

@Reference
private MessageGatewayService messageGatewayService;

public void execute(WorkItem workitem, WorkflowSession wfsession,
MetaDataMap metaDataMap) throws WorkflowException {

ResourceResolver resolver = wfsession.adaptTo(ResourceResolver.class);
UserManager userManager = resolver.adaptTo(UserManager.class);


Workflow workflow = workitem.getWorkflow();
String payload = (String) workitem.getWorkflowData().getPayload();
String initiator = workitem.getWorkflow().getInitiator();

Authorizable authorizable = null;
String userEmail = null;
try {
authorizable = userManager.getAuthorizable(initiator);
} catch (RepositoryException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
        try {
userEmail = PropertiesUtil.toString(authorizable.getProperty("profile/email"), "");
} catch (RepositoryException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

String temp = null;
List<HistoryItem> list = wfsession.getHistory(workflow);

for(int index = list.size()-1; index >=1; index--){
HistoryItem previous = list.get(index);
temp = (String) previous.getWorkItem().getMetaDataMap().get("name");
}

try {

MessageGateway<Email> messageGateway;

Email email = new SimpleEmail();

String emailToRecipients = userEmail;
//String emailCcRecipients = "abc@gmail.com";

email.addTo(emailToRecipients);
//email.addCc(emailCcRecipients);
email.setSubject("AEM Custom Step");
email.setFrom("gkgauravkumar445@gmail.com");

if(temp .equals("approve")){
email.setMsg("This message is to inform you that the CQ content has been approved and activated which Payload path is = "+ payload);
}else{
email.setMsg("This message is to inform you that the CQ content has been Rejected Please modify it which Payload path is = "+ payload);
}

// Inject a MessageGateway Service and send the message
messageGateway = messageGatewayService.getGateway(Email.class);

// Check the logs to see that messageGateway is not null
messageGateway.send((Email) email);
}

catch (Exception e) {
e.printStackTrace();
}
}

}

Step for Right Split:

In the right split, I will drop a process step for sending the mail of rejection to the initiator.

Double click on this step and select the custom process step (custom step for approval) for sending the mail to the initiator.

Now below this step drop Dynamic Participant Step.

About Dynamic Participant Step, I discussed in my last blog click here.



Double click on this step and select the custom dynamic participant step (workflow participant chooser) for assign to the initiator. 




Don't forget to mark the email. Because of this, the mail will be send to the initiator inbox.

Now click OK. Now because of this step the mail will be send to the initiator inbox and from the inbox the initiator will click on complete tab. 







Now Below this step we will drop GoTo Step. And select the Dialog Participant Step from the drop down for which the next will be execute as a Dialog Participant Step.

To know Goto Step Click here

This process will be continue until the admin will not approve the page.

So the final model will be look like this:





Be Happy

Dynamic Participant Step

In this blog, I will discuss about Dynamic Participant Step:

1. What is the use of Dynamic Participant Step?
2. How can we write Custom Dynamic Participant Step?
3.How can we use Dynamic Participant Step?

Use of Dynamic participant step:

Workflow Dynamic Participant Step is used when the user or a group to perform an activity in that step is selected automatically at run time based on some business logic unlike the user or a group are pre-selected. When we select the dynamic participant step in the workflow, we need to select the respective participant user from the drop down. There are many script/service available out of the box to select from or we can write our own participant chooser service as well which will be discussed further in this post.

Custom Dynamic Participant Step:

For crating the Custom Dynamic Participant Step we have to implement ParticipantStepChooser in our java class which provide us getParticipant method with three parameter Workitem, WorkflowSession and MetaDataMap.

Register this java class as a component and add some properties as enabled, immediate, these property indicates-
That this service will be enabled immediate at the time of deployment of your project.

Now register your class as a service with some configurations-
i.e. add a property named as "chooser.label". This property defines a name of your process i.e. in workflow participant chooser step, you are able to see this process in your participant chooser drag drop menu with a name define using this property.

Our final code will look like this:

package com.havells.services;

import com.adobe.granite.workflow.WorkflowException;
import com.adobe.granite.workflow.WorkflowSession;
import com.adobe.granite.workflow.exec.HistoryItem;
import com.adobe.granite.workflow.exec.ParticipantStepChooser;
import com.adobe.granite.workflow.exec.WorkItem;
import com.adobe.granite.workflow.exec.Workflow;
import com.adobe.granite.workflow.metadata.MetaDataMap;

import java.util.List;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(immediate=true)
@Service
@Properties({@org.apache.felix.scr.annotations.Property(name="service.description", value={"Sample Implementation of dynamic participant chooser."}),
@org.apache.felix.scr.annotations.Property(name="chooser.label", value={"Workflow Participant Chooser"})
})
public class CustomStepForDynamicParticipant
  implements ParticipantStepChooser
{
  private static final Logger logger = LoggerFactory.getLogger(CustomStepForDynamicParticipant.class);
   
  public String getParticipant(WorkItem workitem, WorkflowSession wfSession, MetaDataMap metaDataMap)
    throws WorkflowException
  {
    logger.info("################ Inside the SampleProcessStepChooserImpl GetParticipant ##########################");
    String participant = "admin";
    Workflow workflow = workitem.getWorkflow();
    String initiator = workflow.getInitiator();
    List<HistoryItem> wfHistory = wfSession.getHistory(workflow);
    if (!wfHistory.isEmpty()) {
      participant = initiator;
    } else {
      participant = "admin";
    }
    logger.info("####### Participant : " + participant + " ##############");
    return participant;
  }
}

To know How can we use this step click here:

Be Happy 

Wednesday, 19 October 2016

Dialog Participant Step

In this blog, I will discuss about dialog participant step:

What is the use of dialog participant step?
How can we create the dialog?
How can we save the value of dialog?
Where the value of dialog will be saved?

Use of dialog participant step:

Use of dialog participant Step to collect information from the user who is assigned the work item. This step is useful for collecting the small amounts of data that is used later in the workflow.

Creating the dialog:

Dialogs for participant Step are similar to dialogs that you create for authoring components. The node structure of the dialog which I will use in my dialog Participant Step will be like this:














You can store widget data in the workflow payload or in the work item metadata. The format of the name property of the widget will determines where the data is stored.

Store data with the payload:

To store widget data as a property of the workflow payload, use the following format for the value of the name property of the cq:widget node:

./jcr:content/nodename

The data is stored in the nodename property of the payload node.
When stored with the payload, subsequent uses of the dialog with the same payload overwrites the value of the property.

Store data with the Work Item:

To store widget data as a property of the work item metedata, use the following format for the value of the name property of the cq:widget node:

nodename

The data is stored in the nodename property of the work item metadata.
The data is preserved if the dialog subsequently used with the same payload.

In my case, I stored the data with the work item.





To know How we will use this step click here:


Be Happy

Goto Step

In this blog we will discuss about Goto Step:

The Goto Step specifies the next step in the workflow model to execute, depending on the result of an ECMA script.

If Script returns true, The Goto step completes and the workflow engine executes the specified step.
If Script returns false, The Goto step completes and the normal routing logic determines the next step to executes.

When you drop this step in your model and double click on this step you will find two tab Common and Process.
In common tab you have to give the title and and description and , the following properties are available  in process tab:

1. Step to go to: Select the step to execute.
2. Script path: The path of the ECMA script that determines whether to execute Goto step.
3. Script: The ECMA script that determines whether to execute the Goto step.

Specify the value for either the Script path or the script property. If you specify values for both properties, the step uses Script Path.

In my case there are four step I can switch.

 












And write an ECMA script which return always true.





Click OK. Now after this step Dialog Participant Step will be execute.









Be Happy