cancel
Showing results for 
Search instead for 
Did you mean: 

Diagrammatically representing the steps in a workflow

nitinkcv
Champ in-the-making
Champ in-the-making
Hi,

We have a requirement where in say we have a workflow which has five steps to it. Each of the steps of the workflow needs to be represented diagrammatically. Say we at the start of the workflow there should be some pointer which indicates we are in the first step and as we move on the pointer would indicate which step we are at.

Has anyone come across such thing or implemented one/

Thanks.
8 REPLIES 8

jayjayecl
Confirmed Champ
Confirmed Champ
There is such a feature in Alfresco, but I think it's not effective.

Easiest way to do this, is for you to create 5 image files, representing each state of the diagram you want to display, and bind each image file to each task Type as a property (in your workflowModel). Then, edit the web-client-config-custom.xml so that each task form will display the image file in the format you need.

nitinkcv
Champ in-the-making
Champ in-the-making
I'm currently following the Alfresco 3 Enterprise Content Management Implementation by Munwar Sheriff. It contans a sample workflow model as follows:


<type name="global:reviewTask">
        <parent>bpm:startTask</parent>
         <properties>
          <property name="global:nurse_id">
              <title>Nurse No.</title>
            <type>d:int</type>
          </property>….

Now say if wanted to add an image as a property here what would the type that i would have to use. I checked the Data Dictionary and could find the type any only suitable, which corresponds to java.lang.object.

Could you please help out as to how i could bind the image here as a property?

jayjayecl
Confirmed Champ
Confirmed Champ
you could indeed choose the d:any datatype.
when you enter this task of the workflow, run a java action that will populate this property with the suitable file.

<task-node name="ReviewTask">
        <task name="global:reviewTask" swimlane="toto">
       
           <event type="task-create">
                 <action class="org.alfresco.yourpackage.bpm.actions.WorkflowActionManageFiles" />
            </event>
           
        </task>
     
        <transition name="" to="next_task" />
    </task-node>

In the java class WorkflowActionManageFiles execute() method, find your image file (either in the repository , like in the data dictionary,… or in the file system etc …), and assign it to the property, using :
executionContext.setVariable("global:nurse_id", File);

nitinkcv
Champ in-the-making
Champ in-the-making
Is it actually possible to do that in the Serve Side Javascript code.

Something like this:


<task-node name="review">
      <event type="node-enter">
         <script>System.out.println("———- Node Enter of Content review Task ————");</script>
      </event>

      <task name="ppdwf:contentReviewerTask"  swimlane="content_reviewer" />
      <event type="task-create">
            <action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
            <script>
               System.out.println("———-Inside task create of ppdwf:contentReviewerTask ————");
               var imageFile=companyhome.childByNamePath("WorkflowImage/Step1.png");
               executionContext.setVariable("ppdwf_wfstatusimage",imageFile);
            </script>
          </action>            
        </event>…

where my workflowcontentmodel file looks like


<types>
  <type name="ppdwf:baseTask">
  <parent>bpm:workflowTask</parent>
  <properties>
  <property name="ppdwf:wfstatusimage">
  <title>WorkFlow Status Image</title>
  <type>d:any</type>
  <mandatory>true</mandatory>
  </property>
  </properties>
  </type>
  <type name="ppdwf:privilegedBaseTask">
  <parent>ppdwf:baseTask</parent>
  </type>
  <type name="ppdwf:contentAuthorTask">
  <parent>ppdwf:privilegedBaseTask</parent>
  </type>…
 

and part of the web-client-custom-config.xml looks like:


<config evaluator="node-type" condition="ppdwf:contentAuthorTask" replace="true">
- <property-sheet>
  <show-property name="bpm:taskId" />
  <show-property name="bpm:description" component-generator="TextAreaGenerator" read-only="true" />
  <show-property name="bpm:dueDate" read-only="true" />
  <show-property name="bpm:priority" read-only="true" />
  <show-property name="bpm:status" />
  <show-property name="ppdwf:wfstatusimage" display-label="Workflow Status Image" />
  <show-property name="bpm:comment" component-generator="TextAreaGenerator" />
  </property-sheet>
  </config>

I've tried using this but it just displays me the label "Workflow Status Image" and a text box besides it.

If this way is not correct could you plet me know which class i should extend to create my custom action. Also if you have any links for the same that would be great.

I get this error log:

Caused by: org.alfresco.scripts.ScriptException: Failed to execute supplied script: couldn't serialize 'Node Type: {http://www.alfresco.org/model/content/1.0
}content, Node Aspects: [{http://www.alfresco.org/model/content/1.0}author, {http://www.alfresco.org/model/content/1.0}titled, {http://www.alfresco.org/model
/system/1.0}referenceable, {http://www.alfresco.org/model/content/1.0}auditable]'
        at org.alfresco.repo.jscript.RhinoScriptProcessor.executeString(RhinoScriptProcessor.java:254)
        at org.alfresco.repo.processor.ScriptServiceImpl.executeScriptString(ScriptServiceImpl.java:314)
        at org.alfresco.repo.processor.ScriptServiceImpl.executeScriptString(ScriptServiceImpl.java:292)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:615)
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:304)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
        at org.alfresco.repo.security.permissions.impl.AlwaysProceedMethodInterceptor.invoke(AlwaysProceedMethodInterceptor.java:40)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
        at org.alfresco.repo.security.permissions.impl.ExceptionTranslatorMethodInterceptor.invoke(ExceptionTranslatorMethodInterceptor.java:49)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
        at org.alfresco.repo.audit.AuditComponentImpl.audit(AuditComponentImpl.java:275)
        at org.alfresco.repo.audit.AuditMethodInterceptor.invoke(AuditMethodInterceptor.java:69)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
        at $Proxy152.executeScriptString(Unknown Source)
        at org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript.executeScript(AlfrescoJavaScript.java:178)
        at org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript.execute(AlfrescoJavaScript.java:132)
        at org.jbpm.graph.def.Action.execute(Action.java:122)
        at org.jbpm.graph.def.GraphElement.executeAction(GraphElement.java:264)
        … 101 more
Caused by: org.jbpm.JbpmException: couldn't serialize 'Node Type: {http://www.alfresco.org/model/content/1.0}content, Node Aspects: [{http://www.alfresco.org
/model/content/1.0}author, {http://www.alfresco.org/model/content/1.0}titled, {http://www.alfresco.org/model/system/1.0}referenceable, {http://www.alfresco.o
rg/model/content/1.0}auditable]'
        at org.jbpm.context.exe.converter.SerializableToByteArrayConverter.convert(SerializableToByteArrayConverter.java:53)
        at org.jbpm.context.exe.VariableInstance.setValue(VariableInstance.java:131)
        at org.jbpm.context.exe.VariableInstance.create(VariableInstance.java:74)
        at org.jbpm.context.exe.VariableContainer.setVariableLocally(VariableContainer.java:173)
        at org.jbpm.context.exe.VariableContainer.setVariable(VariableContainer.java:45)
        at org.jbpm.context.exe.VariableContainer.setVariable(VariableContainer.java:49)
        at org.jbpm.graph.exe.ExecutionContext.setVariable(ExecutionContext.java:97)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:615)
        at org.mozilla.javascript.MemberBox.invoke(MemberBox.java:155)
        at org.mozilla.javascript.NativeJavaMethod.call(NativeJavaMethod.java:243)
        at org.mozilla.javascript.Interpreter.interpretLoop(Interpreter.java:3237)
        at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:2394)
        at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:162)
        at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:393)
        at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:2834)
        at org.mozilla.javascript.InterpretedFunction.exec(InterpretedFunction.java:173)
        at org.alfresco.repo.jscript.RhinoScriptProcessor.executeScriptImpl(RhinoScriptProcessor.java:449)
        at org.alfresco.repo.jscript.RhinoScriptProcessor.executeString(RhinoScriptProcessor.java:250)
        … 125 more
Caused by: java.io.NotSerializableException: org.springframework.beans.factory.support.DefaultListableBeanFactory
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1108)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1462)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1434)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1377)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1106)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:324)
        at org.springframework.transaction.interceptor.TransactionInterceptor.writeObject(TransactionInterceptor.java:186)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:615)
        at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:972)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1426)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1377)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1106)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1462)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1434)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1377)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1106)
        at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1338)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1102)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1462)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1434)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1377)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1106)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1462)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1434)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1377)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1106)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1462)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1434)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1377)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1106)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1462)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1434)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1377)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1106)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:324)
        at org.jbpm.context.exe.converter.SerializableToByteArrayConverter.convert(SerializableToByteArrayConverter.java:49)
        … 145 more

Thanks.

jayjayecl
Confirmed Champ
Confirmed Champ
1/ in your web-client-config-custom.xml, you would have to specify a "component-generator". Indeed, the way you did things, Alfresco just knows that the property is of type "any", but then it does not know how to display it.
2/ By the way, I don't think I gave you the best way to do it. The error you have makes me think that you cannot provide a File object to the execution context, because it is not serializable.
So, I think we have to be tricky there. Instead of storing the file, let's store its location.
then :
- the property type becomes => d:text
- if the various image files are on the file server, provide the location to the property via the executionContext, if they are in Alfresco (for example, in the Data dictionary), provide their nodeRef.
- at last, in your web-client-config-custom.xml, you'll have to specify a custom component-generator to tell Alfresco not to simply display the text, but to fine the image file thanks to its location (or Noderef), and display it.

Look for "component generators" in the wiki.
As for the display, you'll have plenty of choices (just display the image in a div, convert it to swf and user the swf viewer etc…)

Hope this helps

nitinkcv
Champ in-the-making
Champ in-the-making
Thanks a lot for the steps. I'm able to get the workflow images in my tasks now. I'm writing the code here for anyone who might want a similar thing.
Actually The Alfresco Developer Guide book has an example of creating custom generators which was really helpful. Page 170 "Step-by-Step: Changing the Status Field on the Details Page to Display as a Stoplight Indicator" has the details for anyone whos interested.

part of the processdefinition file:

<task-node name="review">
      <event type="node-enter">
         <script>System.out.println("———- Node Enter of Content review Task ————");</script>
      </event>

      <task name="ppdwf:contentReviewerTask"  swimlane="content_reviewer" />
      <event type="task-create">
            <action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
            <script>
               executionContext.setVariable("ppdwf_wfstatusimage","/alfresco/images/workflowimages/Step1.png");               
            </script>
          </action>            
        </event>

faces-config-custom

<managed-bean>
      <description>Bean that generates a stoplight control to   display status</description>
      <managed-bean-name>WorkFlowImageGenerator</managed-bean-name>
      <managed-bean-class>
         org.alfresco.web.bean.generator.WorkFlowImageGenerator
      </managed-bean-class>
      <managed-bean-scope>request</managed-bean-scope>
   </managed-bean>
   
   <render-kit>
      <renderer>
         <component-family>javax.faces.Output</component-family>
         <renderer-type>org.alfresco.faces.WorkflowImageDisplayRenderer</renderer-type>
         <renderer-class>org.alfresco.web.ui.extension.renderer.WorkflowImageDisplayRenderer</renderer-class>
      </renderer>
   </render-kit>

WorkflowImageDisplayRenderer.java

package org.alfresco.web.ui.extension.renderer;

import java.io.IOException;

import javax.faces.component.UIComponent;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.renderer.BaseRenderer;

public class WorkflowImageDisplayRenderer extends BaseRenderer {

   public boolean isMultiple(UIComponent component) {
      return false;
   }   

   public boolean getRendersChildren() {
      return false;
   }

   public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
      if (component.isRendered() == false) return;

      ResponseWriter out = context.getResponseWriter();

      // render the field
      if(isMultiple(component)) {
         out.write(Utils.encode("Multiple value property fields are not supported."));
      } else {
         
         //Get the image URL present in the property.
         String workflowImageRef = (String)((ValueHolder)component).getValue();
         
          // display workflow status image tag
           out.write("<div>");
           out.write("<img src=\"" + workflowImageRef + "\" alt=\"" + workflowImageRef + "\"/>");
           out.write("</div>");              
      }      
   }
   
   public void decode(FacesContext context, UIComponent component) {
      if (Utils.isComponentDisabledOrReadOnly(component)) {
         return;
      }
   }
}


WorkFlowImageGenerator

package org.alfresco.web.bean.generator;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.ui.common.ComponentConstants;
import org.alfresco.web.ui.repo.component.property.PropertySheetItem;
import org.alfresco.web.ui.repo.component.property.UIProperty;
import org.alfresco.web.ui.repo.component.property.UIPropertySheet;


public class WorkFlowImageGenerator extends TextFieldGenerator {   

   public static final String WORKFLOW_IMAGE_STATUS_RENDERER = "org.alfresco.faces.WorkflowImageDisplayRenderer";
   
   @Override
   public UIComponent generate(FacesContext context, String id) {
      UIComponent component = context.getApplication().createComponent(ComponentConstants.JAVAX_FACES_OUTPUT);
      component.setRendererType(WORKFLOW_IMAGE_STATUS_RENDERER);
      FacesHelper.setupComponentId(context, component, id);
      return component;
   }

   @Override
   @SuppressWarnings("unchecked")
   protected UIComponent createComponent(FacesContext context, UIPropertySheet propertySheet, PropertySheetItem item) {
      UIComponent component = null;

      if (item instanceof UIProperty) {
        
         //Call the generate method to invoke the renderer to paint the workflow image.
         component = generate(context, item.getName());
      }
      return component;
   }

}


WorkflowModel


<type name="ppdwf:baseTask">
         <parent>bpm:workflowTask</parent>
                        <properties>
            <property name="ppdwf:wfstatusimage">
               <title>WorkFlow Status Image</title>
               <type>d:text</type>               
            </property>                        
         </properties>
      </type>

web-client-custom-config

<config evaluator="node-type" condition="ppdwf:contentReviewerTask" replace="true">
      <property-sheet>
         <show-property name="bpm:status" />
         <show-property name="bpm:comment" component-generator="TextAreaGenerator" />
         <show-property name="ppdwf:wfstatusimage" display-label="Workflow Status Image"  component-generator="WorkFlowImageGenerator" />
      </property-sheet>
   </config>

Now coming to my question, rt now i am taking my images from file system and referencing that in my renderer code. Say if i want to refer the images from some DataDictionary | WorkFlowImages space how should i give that.
Would something like work :

executionContext.setVariable("ppdwf_wfstatusimage",companyhome.childByNamePath("DataDictionary/WorkFlowImages/Step1.png"););

Thanks.

jayjayecl
Confirmed Champ
Confirmed Champ
I think you'd rather deal with the nodeRef of the file.


var imageFile = companyhome.childByNamePath("/Data Dictionary/WorkflowImages/yourImage.png");
var nodeRef = imageFile.nodeRef;

Then, push the Noderef in the executionContext (it might need to be serialized into a String), and adapt your java classes to use the Noderef instead of the location.

Glad it worked for you ! Smiley Happy

nitinkcv
Champ in-the-making
Champ in-the-making
I tried with the nodeRef however i'm not sure how can i link the nodeRef to the image src. I got nodeRef something like: workspace://SpacesStore/a460d52b-3111-47d4-b007-e0b4bf909979

So for the time being i got the value from url property:


var workflowImageNode = companyhome.childByNamePath("WokflowImage/Step3.PNG");
var workflowImageNodeURL = workflowImageNode.url;
executionContext.setVariable("ppdwf_wfstatusimage",workflowImageNodeURL);

and in the java code:


/Get the image URL present in the property.
String workflowImageRefFromComp = (String)((ValueHolder)component).getValue();
//Append the host and context.
String workflowImageRef = "http://" + context.getExternalContext().getRequestHeaderMap().get("host") +  context.getExternalContext().getRequestContextPath() + workflowImageRefFromComp;

// display workflow status image tag
out.write("<div>");
out.write("<img src=\"" + workflowImageRef + "\" alt=\"" + workflowImageRef + "\"/>");
out.write("</div>");         

Thanks.