Example: Create new story by CodeWorkflow

Build 1501 on 14/Nov/2017  This topic last edited on: 5/Aug/2014, at 15:34

We want to write a code workflow which receives a picture and creates a gn4:story object having the text “Hello World!” and containing the input picture.

First we have to open and setup the testing environment; then we try to compile the CodeWorkflowTestExpress project to check all the referenced dlls. When all is ready, we can start to write the code workflow.

The first thing to do is to pass the correct input data to the workflow. The workflow needs a picture file, so we can modify the RunSequential method of Program.vb module this way:

      ' Sequential workflow test call

      hasLogError = WFTest.RunSequential(

        GetType(SequentialWorkflow),

        WFTest.Ids(),

        WFTest.DataFiles("c:\temp\myPicture.jpg”),

        WFTest.Pars(),

        executionLog

      )

myPicture.jpg is the image file that we want to insert into the new gn4:story object.

Let’s see all the pieces of the code workflow, one by one.

At the beginning of the workflow we want to test if all the needed input data are correctly set or not.

'check the input parameter

If Context.Data.Count = 0 Then

  Throw New TeraDP.GN4.Common.WorkflowException("Missing input picture file")

End If

If no picture is passed to the workflow, then we throw an exception (see ‘Error management’ section).

When we are sure that the workflow has received picture files, then we can process them one-by-one.

In old WindowsWorkflows we need a DataReplicator activity to loop through a list of files. In CodeWorkflows we don’t need the …Replicator activities anymore because VB.NET has better statements, like For Each:

For Each data As TeraDP.GN4.Workflow.IActivityData In Context.Data

Next

First, into the loop, we want to save the input picture into the GN4 database. So, let’s write the gn4:image xml to pass to the ImportXml activity (like we do in the old WindowsWorkflows).

For Each data As TeraDP.GN4.Workflow.IActivityData In Context.Data

  'create the picture xml

  Dim imageXml As XElement =

  <gn4:image name=<%= data.Info.SrcNameNoExtension & "_" & Guid.NewGuid.ToString("N") %> xmlns:gn4="urn:schemas-teradp-com:gn4tera">

    <gn4:folderRef><gn4:keyVal><%= Context.LoginContext.HomeFolderPath %></gn4:keyVal></gn4:folderRef>

    <gn4:data mime="image/jpeg"><gn4:url><%= data.LocalPath %></gn4:url></gn4:data>

  </gn4:image>

Next

Note that in VB.NET we use the XElement and XDocument classes to write/read the xml (here more info about their syntax: http://msdn.microsoft.com/en-us/library/bb384460.aspx).

While in the old WidowsWorkflows we have to write the xml pieces out of the workflow at the bottom of the ‘wf_...’ file, in CodeWorkflows the XElement class allow us to write xml tags directly into the code.

Moreover, we can embed the value of variables into the xml using the “<%= … %>” syntax: very easy.

The name of the gn4:image is the name of the input file (without extension) and a unique suffix.

We can access the data of the current user via the Context.LoginContext property. In this example, we store the picture into the home folder of the current user (for the testing environment, it is “system/users/Administrator”).

Note the mime-type of the image data: for the moment, we assume that the input file is a .jpeg picture.

To create the image into the database, let’s pass the xml to the ImportXml activity.

Dim importImageAct As ImportXml = New ImportXml(Context) With {.Name = "import picture", .Description = "importing picture..."}

importImageAct.XmlIn = New XDocument(imageXml)

Dim importImageRes As ImportXmlResult = importImageAct.Do()

If importImageRes.CreatedIdsOut.Count = 0 Then

  Throw New TeraDP.GN4.Common.WorkflowException(String.Format("Cannot create picture: '{0}'", data.LocalPath))

End If

Running this partial workflow, we should see the new image created into the GN4 database, but without ‘thumbnail’ and ‘preview’, because no one has created them.

Note that the id of the newly created gn4:image is stored into the importImageRes object of type ImportXmlResult.

Once the picture is created, we have to create the story. Again, we write the gn4:story xml and pass it to the ImportXml activity.

Dim storyXml As XElement =

<gn4:story name=<%= Guid.NewGuid.ToString("N") %> xmlns:gn4="urn:schemas-teradp-com:gn4tera">

  <gn4:folderRef><gn4:keyVal><%= Context.LoginContext.HomeFolderPath %></gn4:keyVal></gn4:folderRef>

  <gn4:objs><gn4:ref idref=<%= Context.XsltExtensions.objectIdToString(importImageRes.CreatedIdsOut(0)) %>></gn4:ref></gn4:objs>

  <gn4:xmlText><b>Hello World!</b></gn4:xmlText>

  <gn4:xmlFormatRef><gn4:keyVal>XHTML</gn4:keyVal></gn4:xmlFormatRef>

</gn4:story>

importImageAct.XmlIn = New XDocument(storyXml)

importImageRes = importImageAct.Do()

If importImageRes.CreatedIdsOut.Count = 0 Then

  Throw New TeraDP.GN4.Common.WorkflowException("Cannot create story")

End If

There is an important thing to note here: the id of the newly created gn4:image is stored into the importImageRes.CreatedIdsOut list as integer (for example, 12345). To correctly write the gn4:story xml, we have to provide it into the string format, as “obj12345”.

In old WindowsWorkflows, this conversion (and other similar) is applied into the xslt stylesheet by the extension methods (namespace xmlns:fn="http://www.teradp.com/schemas/GN4/1/Xslt").

These utility methods are available also into the CodeWorkflows: they are in the Context.XsltExtensions property. The call to Context.XsltExtensions.objectIdToString(12345) returns “obj12345”.

Running this partial workflow, we should see both the new image and the new story created into the GN4 database.

The ‘thumbnail’ and ‘preview’ of a picture are created by the Parse activity (ParseImg plugin). In order to use the GN4 plugins, we need the parser dlls, so we have to copy the content of the Parsers_64.zip daily archive into the bin64 folder of the CodeWorkflowTestExpress installation.

Let’s process the input picture with the Parse activity:

Dim parseDataAct As Parse = New Parse(Context) With {.Name = "parse picture"}

parseDataAct.Data = data

parseDataAct.Options.PreviewSize = 200

parseDataAct.Options.ThumbnailSize = 80

Dim parseDataRes As ParseResult = parseDataAct.Do()

We set the preview and thumbnail size properties, so to have the binary of the generated images embedded into the output xmp (stored into the parseDataRes.XmlOut property) in Base64 encoding.

So, in output xmp we have two <xapGImg:image> tags: the first contains the binary of the thumbnail, the second contains the binary of the preview.

We can read the images binary from the output xmp as strings, using the XElement.Descendants() method (which navigates the xml to find the tag name) and setting the correct xapGImg namespace.

Dim xapGImgNS As XNamespace = "http://ns.adobe.com/xap/1.0/g/img/"

Dim thumbnail As String = parseDataRes.XmlOut.Descendants(xapGImgNS + "image").FirstOrDefault.Value

Dim preview As String = parseDataRes.XmlOut.Descendants(xapGImgNS + "image")(1).Value

Moreover, we have the mime type of the input picture stored into the parseDataRes.MimeOut property.

Now we can modify the gn4:image xml to contain also the thumbnail and preview images.

Dim imageXml As XElement =

<gn4:image name=<%= data.Info.SrcNameNoExtension & "_" & Guid.NewGuid.ToString("N") %> xmlns:gn4="urn:schemas-teradp-com:gn4tera">

  <gn4:folderRef><gn4:keyVal><%= Context.LoginContext.HomeFolderPath %></gn4:keyVal></gn4:folderRef>

  <gn4:data mime=<%= parseDataRes.MimeOut %>><gn4:url><%= data.LocalPath %></gn4:url></gn4:data>

  <gn4:thumbnail mime="image/jpeg"><gn4:data><%= thumbnail %></gn4:data></gn4:thumbnail>

  <gn4:preview mime="image/jpeg"><gn4:data><%= preview %></gn4:data></gn4:preview>

</gn4:image>

Now the workflow is ready. Running it into the testing environment, we should see both the new image and the new story created into the GN4 database: the story should contain the image and the image should have both the thumbnail and the preview correctly set.

Here is the final workflow:

'check the input parameter

If Context.Data.Count = 0 Then

  Throw New TeraDP.GN4.Common.WorkflowException("Missing input picture file")

End If

 

For Each data As TeraDP.GN4.Workflow.IActivityData In Context.Data

  Dim parseDataAct As Parse = New Parse(Context) With {.Name = "parse picture"}

  parseDataAct.Data = data

  parseDataAct.Options.PreviewSize = 200

  parseDataAct.Options.ThumbnailSize = 80

  Dim parseDataRes As ParseResult = parseDataAct.Do()

 

  Dim xapGImgNS As XNamespace = "http://ns.adobe.com/xap/1.0/g/img/"

  Dim thumbnail As String = parseDataRes.XmlOut.Descendants(xapGImgNS + "image").FirstOrDefault.Value

  Dim preview As String = parseDataRes.XmlOut.Descendants(xapGImgNS + "image")(1).Value

 

  'create the picture xml

  Dim imageXml As XElement =

  <gn4:image name=<%= data.Info.SrcNameNoExtension & "_" & Guid.NewGuid.ToString("N") %> xmlns:gn4="urn:schemas-teradp-com:gn4tera">

    <gn4:folderRef><gn4:keyVal><%= Context.LoginContext.HomeFolderPath %></gn4:keyVal></gn4:folderRef>

    <gn4:data mime=<%= parseDataRes.MimeOut %>><gn4:url><%= data.LocalPath %></gn4:url></gn4:data>

    <gn4:thumbnail mime="image/jpeg"><gn4:data><%= thumbnail %></gn4:data></gn4:thumbnail>

    <gn4:preview mime="image/jpeg"><gn4:data><%= Preview %></gn4:data></gn4:preview>

  </gn4:image>

 

  Dim importImageAct As ImportXml = New ImportXml(Context) With {.Name = "import picture", .Description = "importing picture..."}

  importImageAct.XmlIn = New XDocument(imageXml)

  Dim importImageRes As ImportXmlResult = importImageAct.Do()

  If importImageRes.CreatedIdsOut.Count = 0 Then

    Throw New TeraDP.GN4.Common.WorkflowException(String.Format("Cannot create picture: '{0}'", data.LocalPath))

  End If

 

  Dim storyXml As XElement =

  <gn4:story name=<%= Guid.NewGuid.ToString("N") %> xmlns:gn4="urn:schemas-teradp-com:gn4tera">

    <gn4:folderRef><gn4:keyVal><%= Context.LoginContext.HomeFolderPath %></gn4:keyVal></gn4:folderRef>

    <gn4:objs><gn4:ref idref=<%= Context.XsltExtensions.objectIdToString(importImageRes.CreatedIdsOut(0)) %>></gn4:ref></gn4:objs>

    <gn4:xmlText><b>Hello World!</b></gn4:xmlText>

    <gn4:xmlFormatRef><gn4:keyVal>XHTML</gn4:keyVal></gn4:xmlFormatRef>

  </gn4:story>

 

  importImageAct.XmlIn = New XDocument(storyXml)

  importImageRes = importImageAct.Do()

  If importImageRes.CreatedIdsOut.Count = 0 Then

    Throw New TeraDP.GN4.Common.WorkflowException("Cannot create story")

  End If

Next

The final step is to insert the workflow into a ‘wf_...’ configuration file, load the file into the GN4 database and run the workflow in GN4.

1.Create a xml file named ‘wf_CreateStoryAndImage.xml’ and write the empty structure of the CodeWorkflow.

 

<codeWorkflow

  xmlns="http://www.teradp.com/schemas/GN4/1/WFRes.xsd">

 

  <References>

    <!-- Add here references to additional assemblies (DLL) -->

    <!-- <Reference>XXXX</Reference>-->

  </References>

 

  <Imports>

    <!-- Add here additional namespaces to import -->

    <!-- <Import>XXXX</Import> -->

  </Imports>

 

  <Members>

    <![CDATA[

    ]]>    

  </Members>

  

  <Sequential>

    <![CDATA[

      'empty workflow

    ]]>

  </Sequential>

 

</codeWorkflow>

2.Copy the workflow into the CDATA section inside the <Sequential> tag.

3.Save the file

4.Load the workflow into the database using Cmd4.exe:

cmd4 config -in wf_CreateStoryAndImage.xml

5.Run the workflow in GN4. For example using Cmd4.exe:

cmd4 wf -name CreateStoryAndImage -in c:\temp\myPicture.jpg