Cloning a K2 workflow process from InfoPath

I encountered a situation at a client that had a requirement for cloning an InfoPath form by the click of a button. They wanted the ability to kick off a new work item and retain part of the forms information. The reason being that they might fill out anywhere from 1 – 200 form with some of the information always remaining the same for that particular instance. One thing to note, the InfoPath form was browser-enabled. I was able to design a solution using code-behind on the button click event on the form.

Blank Slate

As I mentioned earlier, one of the requirements was to retain a “portion” of the form. In order to do so, I needed to get a blank copy of the form’s XML structure before it’s loaded. I obtained this by adding code to the FormEvents_Loading method of the InfoPath form. Additionally, I was working with a large portion of views, so I needed to make sure I retained the XML throughout the process and also that it was not overwritten. To achieve this, I added the following nodes to the InfoPath form’s data source:

  • Name: emptyFormXml
  • Type: Field (element)
  • Data Type: string
  • Value:

  • emptyFormXml is used for storing the form’s empty xml structure.
  • Name: formHasLoaded
  • Type: Field (element)
  • Data Type: string
  • Value: “no”

  • formHasLoaded is used to check if the form has loaded for the first time or not.

     

    After creating these nodes, I added the following code to the FormEvents_Loading method:

       1: public void FormEvents_Loading(object sender, LoadingEventArgs e)

       2:         {

       3:             XPathNavigator formNav = MainDataSource.CreateNavigator();

       4:             if (formNav.SelectSingleNode("/my:myFields/my:Support/my:formHasLoaded", NamespaceManager).Value == "no")

       5:             {

       6:                 XPathNavigator navBlankXML = MainDataSource.CreateNavigator();

       7:                 XmlDocument blankXML = new XmlDocument();

       8:                 navBlankXML.SelectSingleNode("/my:myFields/my:Support/my:emptyFormXml", NamespaceManager).SetValue(navBlankXML.OuterXml.ToString());

       9:                 navBlankXML.SelectSingleNode("/my:myFields/my:Support/my:formHasLoaded", NamespaceManager).SetValue("yes");

      10:             }

      11:         }

    The purpose of this code is to check to see if this is the first time the form has loaded (formHasLoaded node). If this is the first time, then you need to grab the OuterXml by creating a XPathNavigator from the form’s MainDataSource. Then you store the OuterXml into the emptyFormXml field for later use and set the value of the formHasLoaded field to “yes”.

    Now that you have captured the empty XML, you are ready to move to the next step in the process.

     

    InfoPathService Web Service

    In order to be able to submit the form into a new process, you will need to add a web reference to your project. The web URL you will need to add is: http://SERVERNAME:81/RuntimeServices/InfoPathService.asmx. The reason your adding this web reference is to be able to access the SubmitInfoPathData method. This method will submit the data of the InfoPath form to the workflow server to either start a new workflow or action a worklist item (task). For my example, I called it InfoPathWebService_Dev.

     

    Submit Clone

    Now for the “meat” of the solution! I added a button to the form called btnSubmitClone, went to the button’s properties and selected Edit Form Code. In the first part of the code, I grabbed the fields I wanted to keep from the form and stored them in a variable accordingly.

    The next step is to obtain the empty form xml that we captured earlier (emptyFormXml field). Once we retrieve that value, load it into a XmlDocument and create an InfoPath navigator off of the XML document. With the new Navigator, set the values you want to display on the new form into the new XML. One thing to take into consideration is that if the field you are trying to pass in is not a string data type, you will receive an error if you don’t delete the “nil” attribute. In order to do so, call the following method (DeleteNil) to remove the attribute:

       1: public void DeleteNil(XPathNavigator node)

       2:         {

       3:             if (node.MoveToAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance"))

       4:             node.DeleteSelf();

       5:         }

     

    Now for the IMPORTANT step in the process. Set the value of the K2 serial number (/my:myFields/my:_K2/my:SN) to string.Empty. If you don’t clear the SN value, it will try to use the existing workitem’s number and will cause an error when referencing the cloned workitem.

    For my K2 workflow process, I check at the beginning of the process for a cloned form or not. In order to do this, I also created an additional field on the InfoPath form called cloneForm with a default value of “no”. Therefore, if the value is no, the process is handled normally, else, the user has to complete the form with the additional info needed after it has been cloned and then it resumes the normal process.

    k2_clone

    I set the value of this cloneForm field at this point in the process to mark the document as cloned. This only is applied with the new blank xml form I’m building, and will not change the existing form’s field value.

    The final step in the cloning process is to load the navigator’s OuterXml into a new XML document and instantiate the web service(InfoPathService) that I had referenced earlier. I then call the SubmitInfoPath method to pass the new XML document I had cloned. In order for this to submit successfully, it NEEDS to be in the following format:

       1: ipService.SubmitInfoPathData(new XmlText[] { xmlDoc2.CreateTextNode(xmlDoc2.InnerXml) });

     

    At this point, you are done with the code. Obviously, you don’t have to set any values and just submit the XML empty to have a new blank form, but it comes in handy if you want to manipulate values. Also, you can add logic to let the user choose how many clones and then loops through the process  of submitting the form the number of times the user has chosen.

    For a complete example of the btnSubmitClone_Clicked event, see below:

       1: public void btnSubmitClone_Clicked(object sender, ClickedEventArgs e)

       2:        { 

       3:             try

       4:            {

       5:                //get info to retain from form

       6:                string s_Name = e.Source.SelectSingleNode("/my:myFields/my:name", NamespaceManager).Value;

       7:                string s_Address = e.Source.SelectSingleNode("/my:myFields/my:address", NamespaceManager).Value;

       8:                string i_Zip = e.Source.SelectSingleNode("/my:myFields/my:zip", NamespaceManager).Value;

       9:                

      10:                //get empty form xml & load the xml

      11:                string cachedFullXml = e.Source.SelectSingleNode("/my:myFields/my:Support/my:emptyFormXml", NamespaceManager).Value;

      12:                XmlDocument formXDoc = new XmlDocument();

      13:                formXDoc.LoadXml(cachedFullXml);

      14:                XPathNavigator navXML = formXDoc.CreateNavigator();

      15:  

      16:                //set info to retain into empty xml

      17:                navXML.SelectSingleNode("/my:myFields/my:name", NamespaceManager).SetValue(s_Name);

      18:                navXML.SelectSingleNode("/my:myFields/my:address", NamespaceManager).SetValue(s_Address);

      19:                

      20:                //integer field

      21:                XPathNavigator xndateField = navXML.SelectSingleNode("/my:myFields/my:zip", this.NamespaceManager);

      22:                DeleteNil(xndateField);

      23:                navXML.SelectSingleNode("/my:myFields/my:zip", NamespaceManager).SetValue(i_Zip);

      24:  

      25:                //set form submission values

      26:                navXML.SelectSingleNode("/my:myFields/my:_K2/my:SN", NamespaceManager).SetValue(string.Empty); //clear K2 Serial Number

      27:                navXML.SelectSingleNode("/my:myFields/my:Support/my:cloneForm", NamespaceManager).SetValue("yes"); //field to mark item as cloned form

      28:  

      29:                //submit xml as new workflow item

      30:                XmlDocument xmlDoc2 = new XmlDocument();

      31:                xmlDoc2.LoadXml(navXML.OuterXml.ToString());

      32:                InfoPathWebService_Dev.InfoPathService ipService = new rtoq2.InfoPathWebService_Dev.InfoPathService();

      33:                ipService.Credentials = System.Net.CredentialCache.DefaultCredentials;

      34:                ipService.SubmitInfoPathData(new XmlText[] { xmlDoc2.CreateTextNode(xmlDoc2.InnerXml) });

      35:                ipService.Dispose();

      36:  

      37:            }

      38:            catch (Exception ex)

      39:            {

      40:                throw new System.Exception(ex.Message);

      41:            }

      42:        }

    Happy Cloning!!

    Leave a Reply

    Your email address will not be published. Required fields are marked *