AEM 65 - Content Copy using Workflow Dialog Participant Step

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));
    }
}




No comments:

Post a Comment