Stumbling Through

Join me as I stumble, bumble and fumble my way through some new developer technologies. We'll laugh, we'll cry, there may be a mouse tossed through a monitor, but in the end we will all hopefully learn something.
in

Stumbling Through: K2 [blackpearl] - Infopath Integration (Part II)

Previously, I identified a couple of specific technical problems that I'd like to stumble through solutions for.  One of the technical problems I described like this:

'Submitting one order form may spawn multiple processes that can be acted on simultaneously by different users to accomplish one final result.  How will we have one process split into many, passing along the common information necessary to the child processes?'

I suppose I will clarify this description a little bit before I jump into the solution.  One InfoPath form containing some common user-entered data starts the master process.  Submitting this form starts three other processes that have their own InfoPath forms that need the common information pre-populated from the first InfoPath form.  So, to do a proof of concept for this issue, we'll need a total of four InfoPath forms and four processes defined in a K2 workflow project (Master, Child1, Child2, Child3).  Let's start out with the InfoPath forms:

Create an InfoPath form named 'Master', and add two text fields that will represent the common data.  We can just call them 'Id' and 'Name':

image

Create the next three InfoPath forms named 'Child1', 'Child2' and 'Child3'.  You can put any other information you want in them, as long as you include the 'CommonFields' section as it was defined in the Master form:

image

Now that we have our InfoPath templates defined, we need to consume them in K2 Processes.  Start Visual Studio and create a new empty K2 project:

image

Add four processes to the project named 'Master', 'Child1', 'Child2' and 'Child3':

image

image

Let's start with defining each child process.  They will simply create a task list item pointing to their corresponding InfoPath form which will have the header somehow pre-populated with data entered in the Master form.  First things first, though, we need to integrate the process with its InfoPath form.  Drag out an InfoPath Integration process wizard and follow the steps necessary to integrate the InfoPath form and create a simple client event (I've blogged this process before if you get stuck, you can read it here, though that was blogged long before I semi-knew what I was doing).  Duplicate the process for all three child processes so that they look as simple as this:

image

Note that we did nothing with pre-populating the data in the InfoPath form yet, we'll tackle that later.  For now, we can move on to our Master process.  We'll need to integrate the Master InfoPath form with this process similarly to how we did it with the Child processes, the only difference being that we need to ensure that the Master form starts the process, by filling in this portion of the integration wizard:

image

This will make it so that any time this specific form is submitted, it will automatically start this Master process.  So now we come to the heart of the matter - how do we invoke the child processes all at once?  Thankfully, the developers of this product anticipated this very need and supplies a wizard that will make this challenge a breeze.  Drag out the 'IPC Event' wizard:

image

In the wizard, we'll need to specify which process to invoke.  Let's start out invoking our Child1 process.  It also asks us if it is Synchronous or Asynchronous - basically whether or not we need a response from this child process before we can continue.  In our case, we just want to kick off the process and we don't care what it does so we will chose Asynchronous:

image

We also have the ability here to specify a new folio, if we want the child folio to match something that we are gathering in the parent process.  Be warned, though, that this folio definition will be overridden by any folio definition in the Child process (defined in the InfoPath Integration wizard).  Clicking next brings us to a simple security question - who to call the process as.  Now normally, I would expect to use 'Integrated Windows' here so that the process is invoked by whomever invoked the Master process.  However, for reasons I don't yet understand, this never worked successfully for me and I've had to use the 'Impersonate Originator' option:

image

Clicking next again brings us to a screen that is very interesting to us, given the challenge we are trying to overcome.  It is asking us what data we want to pass to the child from the parent!  That is perfect!  Or is it... Let's see how it handles our specific request of passing the 'CommonFields' data from the Master form to the 'CommonFields' data of the child.  Add a new process field mapping:

image

Use the object browser to drill into our Master infopath form XML representation and select the 'CommonFields' section:

image

And drag it onto the Field Mapping form:

image

Now use the object browser via ellipsis next to the Child process field name to browse to the 'CommonFields' data in the Child InfoPath form:

image

And drag it onto the Field Mapping form... uh oh, it won't let us!  Looks like we are asking it to do something a little more complex than what it was designed for, which is understandable as moving one piece of xml between documents might not always be very straight forward, particularly when namespaces are involved.  Looks like we'll have to do some of this ourselves via code.  Bring up the Child Process object browser again, and add a new xml field called 'ParentCommonFields'.  We'll use that as the temporary container for the common field data:

image

Drag it onto the Field Mapping form:

image

Something else I learned the hard way about this process is that if you specify a portion of an xml document, as we did when we specified 'CommonFields' in the Master document, it will send the data as a list of field values as opposed to the XML.  As such, we'll just send the whole XML over and parse out the CommonFields section in the Child process logic.  So revisit the Parent field value and browse to the Master xml document:

image

Drag it onto the Field Mapping form:

image

Repeat this IPC event process for all three Child processes so your Master process looks like this in the end:

image

Now we need to revisit each Child process and add some logic to copy the parent common fields into the child common fields InfoPath xml.  The only way to do this is with some server side code, so drag out a new 'Default Server Event (Code)' wizard:

image

And connect it up before the InfoPath client event:

image

View the code of this Server Event (Right-Click - View Code - Event Item) and add a 'using System.Xml' to the top to make our XML references cleaner.  Now, what we need to do is load up a DOM with the Child InfoPath form XML so we can navigate to its 'CommonFields' section and replace it with the 'ParentCommonFields' value.  The caveat here is that the Parent Common Fields XML may have a different namespace associated with it, so we'll need to load that up in a DOM too so we can identify and replace any occurrences of an incorrect namespace:

XmlDocument docChild = new XmlDocument();

docChild.LoadXml(K2.ProcessInstance.XmlFields["Child1"].Value);

XmlNamespaceManager nsMgrChild = new XmlNamespaceManager(docChild.NameTable);

nsMgrChild.AddNamespace("my", docChild.DocumentElement.GetNamespaceOfPrefix("my"));

XmlDocument docParent = new XmlDocument();

docParent.LoadXml(K2.ProcessInstance.XmlFields["ParentCommonFields"].Value);

XmlNamespaceManager nsMgrParent = new XmlNamespaceManager(docParent.NameTable);

nsMgrParent.AddNamespace("my", docParent.DocumentElement.GetNamespaceOfPrefix("my"));

docChild.SelectSingleNode("//my:CommonFields", nsMgrChild).InnerXml = docParent.SelectSingleNode("//my:CommonFields", nsMgrParent).InnerXml;

K2.ProcessInstance.XmlFields["Child1"].Value = docChild.InnerXml.Replace(nsMgrParent.LookupNamespace("my"), nsMgrChild.LookupNamespace("my"));

Let's deploy this thing, and when it is done, we will visit the SharePoint list associated with our 'Master' form, add an item, and see if we get tasks generated for each 'Child' form.  Opening up those tasks should show the Master Common Field data in the header.  Don't forget to give yourself process start rights for the master and child processes via the Workspace!  If it doesn't work as expected or I glossed over an important detail, please leave me a comment and I'll try to help as best i can.

Posted: Feb 29 2008, 10:12 AM by tbyrne | with 2 comment(s)
Filed under:

Comments

Rich said:

Hi,

I am trying to do something very similar to this but using information from a smart object to feed into the child process. Have you done anything on this or know of anywhere where I could find information.

Cheers

# April 1, 2008 5:58 AM

tbyrne said:

While I haven't specifically used Smart Objects to do this, I would imagine it would be a bit easier than passing the InfoPath xml around.  Instead of putting your InfoPath xml in the field mapping dialog, you can probably drag your smart object fields (probably the unique id) into the field.  I'd take a look out at the k2 underground for more information on this:  http://k2underground.com/

# April 2, 2008 8:41 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)