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