Skip to content

March 10, 2013

CQ Workflow Tutorial: Selection Dialog Revision 2

by Andreas Schaefer

In this article we revisit the previously discussed Dialog Selection problem. In my opinion the Dialog is easier to deal with from a code editor than from within CQ Dialog Editor or CRXDE Light. When I am working on Workflows the only piece I edit in CQ are the Models. Everything else I code by hand based on examples. For that purpose I have the libs folder extracted from the **cq-content-<version>.zip (CQ Package).

Creating a Dialog

Based on the previous project CQ Workflow Tutorial Basic Project Setup we are going to add the Dialog, create a new version of the Selection Data Provider and adjust our Basic Workflow. Finally we are going to install it in CQ.

In case you did not through the previous article or in case it is messed up go back to that article and download the solution.

First we need to decide where to place the dialog component. I am not sure if that is the best place but I always but my dialogs in the /apps/<project>/components/workflow/dialog folder within the View module.

  1. Create this folder in the View module: /src/main/content/jcr_root/apps/cq-workflow-tutorial-basic/components/workflow/dialog.

CQ Workflow Tutorial Basic Dialog Folder Structure

Now we create the same dialog again but now files rather than JCR nodes. First we create a folder for the dialog called test-selection-dialog. In there is the .content.xml file for the component:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Component"
    jcr:title="Test Selection Dialog"/>

The other file is the actual dialog definition named dialog.xml:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Dialog"
    title="Test Selection Dialog"
    xtype="dialog">
    <items jcr:primaryType="cq:Panel">
        <items jcr:primaryType="cq:WidgetCollection">
            <test
                    jcr:primaryType="cq:Widget"
                    fieldDescription="Test Selection Dialog"
                    fieldLabel="Test"
                    name="./jcr:content/test-value"
                    options="/apps/geometrixx/components/workflow/test.jsp"
                    type="select"
                    xtype="selection"/>
        </items>
    </items>
<jcr:root>

CQ Workflow Tutorial Basic Dialog Definition Structure

This will generated more or less the same dialog if we would install it now.

More Stable Selection Data Provider

One of the biggest problems with Workflows are bugs that lurk around and only come to light under certain conditions. In order to avoid too many moving targets I like to have data provider as OSGi services / Servlets rather than more volatile features like JSPs. For example in our case bad data might prevent the dialog from coming up or the selection is empty if one of the property names is wrong.

In this Servlet we do the same thing but inside a Servlet that is deployed as OSGi service. First we go to the Services module and create this package (under /src/main/java): com.madplanet.cq.workflow.tutorial.basic.services.osgi.servlet. There we create a Java class name TestSelectionDataProviderServlet.java which that code:

package com.madplanet.cq.workflow.tutorial.basic.services.osgi.servlet;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.io.JSONWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import java.io.IOException;

@Component( immediate = true, metatype = true )
@Service
@Properties(
    {
        @Property(
            name = "sling.servlet.paths",
            value = {
                "/bin/tutorial/workflow/test/selection"
            },
            propertyPrivate = true
        ),
        @Property(
            name = "sling.servlet.methods",
            value = { "GET", "POST" },
            propertyPrivate = true
        ),
        @Property(
            name = "service.description",
            value = "Provides Test Selection Data",
            propertyPrivate = true
        )
    }
)
public class TestSelectionDataProviderServlet
    extends SlingSafeMethodsServlet
{

    private static final Logger LOGGER = LoggerFactory.getLogger( TestSelectionDataProviderServlet.class );

    @Override
    protected void doGet( SlingHttpServletRequest pSlingHttpServletRequest, SlingHttpServletResponse pSlingHttpServletResponse )
        throws ServletException, IOException
    {

        LOGGER.debug( "Got request on path: '{}'", pSlingHttpServletRequest.getPathInfo() );

        pSlingHttpServletResponse.setContentType( "application/json" );
        pSlingHttpServletResponse.setCharacterEncoding( "utf-8" );

        JSONWriter lJSONWriter = new JSONWriter( pSlingHttpServletResponse.getWriter() );

        try {
            lJSONWriter.array();
            writeLine( lJSONWriter, "First Selection", "First Value", "First Tip" );
            writeLine( lJSONWriter, "Second Selection", "Second Value", "Second Tip" );
            writeLine( lJSONWriter, "Third Selection", "Third Value", "Third Tip" );
            lJSONWriter.endArray();
        } catch( JSONException e ) {
            LOGGER.warn( "Failed to write JSon Response", e );
        }

        pSlingHttpServletResponse.getWriter().flush();
    }

    private void writeLine( JSONWriter pWriter, String pText, String pValue, String pTip )
        throws JSONException
    {
        pWriter.object();
        pWriter.key( "text").value( pText );
        pWriter.key( "value" ).value( pValue );
        pWriter.key( "qtip" ).value( pTip );
        pWriter.endObject();
    }
}

This might now look like a lot of overhead but on the other hand it is harder to change and easier to track changes than with a JSP page that can be easily changed in CRXDE Light. In addition an OSGi service can be managed through the OSGi Web Console (/system/console) and can be configured through Runmode Configuration.
That said a data provider service is more complex that this one because you want to provide dynamic data and so the differences won’t be that dramatic as here.

Now we are ready to deploy that service by going to the Services directory and execute Maven with:

mvm clean install -Pauto-deploy

and voila it fails.

There is a bug in CQ where installing an OSGi bundle for the first time on an empty Apps folder it reports a conflict. It is actually not a conflict but a missing folder. In order to install it we need to create a /appss/cq-workflow-tutorial-basic/install folder in CRXDE Light. Now restarting the Maven script and we are back in business.

CQ Workflow Tutorial Basic Dialog Service Install Folder

Now open the page in a link like this: http://localhost:4502/bin/tutorial/workflow/test/selection if you installed CQ locally with a default port and it should print out this:

[{"text":"First Selection","value":"First Value","qtip":"First Tip"},{"text":"Second Selection","value":"Second Value","qtip":"Second Tip"},{"text":"Third Selection","value":"Third Value","qtip":"Third Tip"}]

Having that in place we can go back to the Dialog definition above and change options from the old value /apps/geometrixx/components/workflow/test.jsp to the new service /bin/tutorial/workflow/test/selection.

Add the Dialog to the Workflow Model

Finally we can add the Dialog to the Workflow. First make sure that you installed the View module with Maven:

maven clean install -Dauto-deploy

so that we have the latest Workflow Model in place.

In order to add the dialog we need to open the Workflow Editor (/workflow) and double-click on our Basic Workflow to bring it up in the Workflow Model Editor. There we do the following:

  1. Select Step 1 and delete it

CQ Workflow Tutorial Basic Dialog Model Delete Step1

  1. Go to the Sidekick, make sure the Components tab is selected and expand the Workflow group
  2. Drag the Dialog Participant Step onto the model right under the Start box

CQ Workflow Tutorial Basic Dialog Model Drag Dialog Step

  1. Double-click on the newly created box to bring up the details dialog box
  2. Set the Title to Dialog Selection Test

CQ Workflow Tutorial Basic Dialog Model Step Common

  1. Click on the User / Group tab
  2. Enter a User / Group you want to be able to see that Dialog. Here I use /home/groups/a/administrators meaning any administrator can see that step and handle it.

CQ Workflow Tutorial Basic Dialog Model Step User

  1. Click on the Dialog tab
  2. Enter the path to our dialog: /apps/cq-workflow-tutorial-basic/components/workflow/dialog/test-selection-dialog/dialog

CQ Workflow Tutorial Basic Dialog Model Step Dialog

  1. Click OK to confirm the changes and the click on Save to save (generate) the Workflow Model

Attention: When you enter the path to the Dialog then you must make sure that you enter the path to the dialog component (here /dialog) even though when you select the path through the search tree you can only select the parent folder. If you forget that then the dialog will not show up.

Last step is to export the workflow model and then incorporate into the project before going on:

  1. Go to the Package Manager (/crx/packmgr)
  2. Search for the tutorial-workflow-export package
  3. Click on Edit and bump up the Version number

CQ Workflow Tutorial Basic Dialog Model Package Export

  1. Save, build and download the Package
  2. Copy the workflow model into our project

CQ Workflow Tutorial Basic Dialog Model Export Copy

  1. Build and Install the View module again with:

mvm clean install -Pauto-deploy

Testing the Workflow

The Selection Data Service (Services) and the Workflow Model / Dialog (View) should be built and installed onto your CQ instance.

Now go back to the Workflow Model, select the Basic Workflow model, right-click and select Start.

CQ Workflow Tutorial Basic Dialog Model Start

In the dialog select a page, optionally enter a title and comment and then click on OK to start it.

CQ Workflow Tutorial Basic Dialog Model Start Dialog

Then open the Workflow Inbox (/Inbox), refresh the list of workflows (if it does not come up), select the just started workflow, right-click and select Complete. It should bring up this dialog:

CQ Workflow Tutorial Basic Dialog Model Inbox

CQ Workflow Tutorial Basic Dialog Model Inbox Complete

CQ Workflow Tutorial Basic Dialog Model Inbox Complete Dialog

Conclusion

In this article we discussed how to create an OSGi service / Servlet that provides the Selection Data for the dialog. This has several advantages over the JSP approach:

  1. Less volatile because it is harder to change / deploy
  2. Changes can be reviewed / tracked through the usages of a VCS (Subversion, Git etc)
  3. The Java Compiler and the OSGi service make sure that the dependencies are resolved when the service is deployed
  4. OSGi Service can be configured through Runmodes which allows a configuration based on the Environment
  5. OSGi Services can be managed / maintained through the OSGi Web Console in CQ (/system/console)

Then we also discussed how to create / change a dialog much easier through the file system than through CRXDE light or the Dialog Editor. Later when we discuss the Workflow Steps dialogs this comes handy.

Final Project

If you had problems following the project or need it as a base of the next article this is a ZIP file of the project at the end of this article:

cq-workflow-tutorial-basic.selection.dialog.r2.zip

Have fun – Andy

Read more from Adobe CQ

Leave a comment