Goal
A sample workflow with dialog participant step for selecting destination folder from a list of options in dropdown. Subfolders under a specific root folder eg. /content/dam are shown in the dropdown
Demo | Package Install | Github
Dialog Path in Participant Step
Dialog in CRX
Dialog in Complete Work Item
Solution
1) Create a datasource /apps/eaem-content-copy-dialog-pariticipant-step/dialogs/folders-ds/folders-ds.jsp for showing the folders in dropdown. Here the root folder was set to /content/dam, but the logic can be adjusted to return a folder list based on usecase
<%@include file="/libs/granite/ui/global.jsp"%>
<%@ page import="com.adobe.granite.ui.components.ds.DataSource" %>
<%@ page import="com.adobe.granite.ui.components.ds.ValueMapResource" %>
<%@ page import="org.apache.sling.api.wrappers.ValueMapDecorator" %>
<%@ page import="com.adobe.granite.ui.components.ds.SimpleDataSource" %>
<%@ page import="org.apache.commons.collections.iterators.TransformIterator" %>
<%@ page import="org.apache.commons.collections.Transformer" %>
<%@ page import="org.apache.sling.api.resource.*" %>
<%@ page import="java.util.*" %>
<%
String ROOT_PATH = "/content/dam";
final ResourceResolver resolver = resourceResolver;
Resource rootPath = resolver.getResource(ROOT_PATH);
List<Resource> qualifiedParents = new ArrayList<Resource>();
rootPath.listChildren().forEachRemaining(r -> {
if(r.getName().equals("jcr:content")){
return;
}
qualifiedParents.add(r);
});
DataSource ds = new SimpleDataSource(new TransformIterator(qualifiedParents.iterator(), new Transformer() {
public Object transform(Object o) {
Resource resource = (Resource) o;
ValueMap vm = new ValueMapDecorator(new HashMap<String, Object>()),
tvm = resource.getValueMap();
vm.put("value", resource.getPath());
vm.put("text", tvm.get("jcr:content/jcr:title", tvm.get("jcr:title", resource.getName())));
return new ValueMapResource(resolver, new ResourceMetadata(), "nt:unstructured", vm);
}
}));
request.setAttribute(DataSource.class.getName(), ds);
%>
2) Create the dialog /apps/eaem-content-copy-dialog-pariticipant-step/dialogs/content-copy-dialog to show in Inbox console work item Complete Workflow Item
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Experience AEM Content Copy"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<parent
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldDescription="Select Parent Folder"
fieldLabel="Parent Folder"
name="parentFolder">
<datasource
jcr:primaryType="nt:unstructured"
sling:resourceType="/apps/eaem-content-copy-dialog-pariticipant-step/dialogs/folders-ds"
addNone="{Boolean}true"/>
</parent>
</items>
</column>
</items>
</content>
</jcr:root>
3) Add the dialog path in Dialog Participant Step of workflow model e.g. /conf/global/settings/workflow/models/experience-aem-content-copy
4) Create a process step e.g. apps.experienceaem.assets.ContentCopyTask to read the parent folder path set in parentFolder of previous step (dialog drop down)
package apps.experienceaem.assets;
import com.adobe.granite.workflow.PayloadMap;
import com.day.cq.commons.jcr.JcrUtil;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.commons.util.DamUtil;
import com.day.cq.workflow.metadata.MetaDataMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.*;
import com.day.cq.workflow.WorkflowException;
import com.day.cq.workflow.WorkflowSession;
import com.day.cq.workflow.exec.WorkItem;
import com.day.cq.workflow.exec.WorkflowProcess;
import org.apache.sling.jcr.resource.api.JcrResourceConstants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Node;
import javax.jcr.Session;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
@Component(
immediate = true,
service = {WorkflowProcess.class},
property = {
"process.label = Experience AEM Content Copy Task"
}
)
public class ContentCopyTask implements WorkflowProcess {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
private static final String DIALOG_PARTICIPANT_NODE_ID = "node1";
private static final String DESTINATION_PATH = "parentFolder";
@Reference
private ResourceResolverFactory resolverFactory;
@Override
public void execute(WorkItem item, WorkflowSession wfSession, MetaDataMap args) throws WorkflowException {
try {
Session session = wfSession.getSession();
ResourceResolver resolver = getResourceResolver(session);
Asset payload = getAssetFromPayload(item, resolver);
if(payload == null){
log.error("Empty payload, nothing to copy");
return;
}
String destinationPath = getDestinationPathForCopy(item, resolver);
if(StringUtils.isEmpty(destinationPath)){
log.error("Destination path empty for copyign content - " + payload.getPath());
return;
}
Node copiedPath = JcrUtil.copy(payload.adaptTo(Node.class), resolver.getResource(destinationPath).adaptTo(Node.class), null);
log.debug("Copied Path - " + copiedPath);
session.save();
} catch (Exception e) {
log.error("Failed to copy content", e);
}
}
private String getDestinationPathForCopy(WorkItem item, ResourceResolver resolver) throws Exception{
String wfHistoryPath = item.getWorkflow().getId() + "/history";
Iterator<Resource> historyItr = resolver.getResource(wfHistoryPath).listChildren();
ValueMap metaVM = null;
Resource resource, itemResource;
String nodeId, destinationPath = "";
while(historyItr.hasNext()){
resource = historyItr.next();
itemResource = resource.getChild("workItem");
nodeId = itemResource.getValueMap().get("nodeId", "");
if(!nodeId.equals(DIALOG_PARTICIPANT_NODE_ID)){
continue;
}
metaVM = itemResource.getChild("metaData").getValueMap();
destinationPath = metaVM.get(DESTINATION_PATH, "");
break;
}
return destinationPath;
}
public Asset getAssetFromPayload(final WorkItem item, final ResourceResolver resolver) throws Exception{
Asset asset = null;
if (!item.getWorkflowData().getPayloadType().equals(PayloadMap.TYPE_JCR_PATH)) {
return null;
}
final String path = item.getWorkflowData().getPayload().toString();
final Resource resource = resolver.getResource(path);
if (null != resource) {
asset = DamUtil.resolveToAsset(resource);
} else {
log.error("getAssetFromPayload: asset [{}] in payload of workflow [{}] does not exist.", path,
item.getWorkflow().getId());
}
return asset;
}
private ResourceResolver getResourceResolver(final Session session) throws LoginException {
return resolverFactory.getResourceResolver( Collections.<String, Object>
singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, session));
}
}