Hi
Worklist is sent to multiple approvers and each approver can fill their comments and say approve or reject. if there are multiple approvers(destination users), how to pass the comments from each approver to K2 Process?
Â
Â
Â
Hi
Worklist is sent to multiple approvers and each approver can fill their comments and say approve or reject. if there are multiple approvers(destination users), how to pass the comments from each approver to K2 Process?
Â
Â
Â
There are several ways to do this:
1. Use a SmartObject. I typically create one that has an autonumber key, userid, date/time, and comment, and a field used as a foreign key to the process ID or some other field that is relevant to your process. Then on the ASPX page or InfoPath form I insert the comment into the SmartObject. Using this approach is also really handy if you need to report on comments later.
2. Create an ActivityInstanceDestination datafield. Right-click on the activity and add a new data field, but don't click the Shared checkbox. Each slot will get its own copy of the datafield. It will be logged (if you check the box) but if you want to do something else with it you will have to do it before the activity ends.
I ususally use approach #1.Â
David
David: Thanks for your reply. I tried second approach ( Create an ActivityInstanceDestination datafield).
I need to insert each approvers comments(ASPX Page) to sharepoint list after he/she say approve or reject, but ActivityInstanceDestination datafield value becomes empty after executing ApprovedAction.Execute().
Could you please give me more detailed explanation on approach # 2?Â
Â
Arduk: Thanks for your reply.I am not able to get how you are doing it. Could you please give me some more explanation?Â
Hi Arduk,
IÂ need to capture the comments of several approvers of an aspx page and pass them to K2 process. Once all the approvers approve it then I need to go with the next step.
I wanted to know how exactly you are using the xml datafield. How you are adding the comments for each user and passing them to the K2 process and storing it until all the approvers have finished with the approval. Where is the code activity written and under which event?
Could you kindly explain me with a very small example.
Thanks in advance!!!!!!!!!Â
Â
OK, so just to ensure we are using the same terminology, an Activity is the container in your K2 workspace, and it can contain multiple events....
First off, I create a process level xml data field "tempApprovalStatus", which has the following structure:
<approval>
 <history>
   <username></username>
   <statusSelected></statusSelected>
   <comments></comments>
 </history>
</approval>
NOTE: the History node is a repeating node.
Now I have an activity which has 2 events in it:
1. An infopath client event in it - this is the event that actually collects the information from the user
2. A Default Server Event (Code) - this activity takes the information that is collected in the form by each user, and adds a history node, including the subnodes to the xml process field. Each user that the activity is assigned to will then have a task assigned to them. When they complete the task, the code event saves their values to the process level xml field. The result is that there will be a "history" node structure for each person who completes the task
I then have a second activity which has a single code event in it. The code event reads through the "tempApprovalStatus" process xml field, and creates an xml fragment which is then inserted into my infopath form to form a repeating table.
The result is that I end up with an infopath form that stores all of the comments history within itself.
Hope this helps.
Hi Arduk,
When there are several approvers, the comments are updated for the last user only.
The tempApprovalStatus should be a Process level or Activity level field.
Can you explain how the history node is appended from each user.
Is it in the second server code activity, the  "tempApprovalStatus" field is looped for each user and the comments are extracted.
I'm able to get the comments but only for the last user.
More explanation is needed in the second point.
Kindly comment.
Thanks in advance!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Â
Please read through my previous post carefully, as it answers most of your questions - I will answer them again below, and provide a bit more detail where I can...
Q. The tempApprovalStatus should be a Process level or Activity level field
A. tempApprovalStatus is a PROCESS LEVEL xml field
Q. Can you explain how the history node is appended from each user
A. The Default Server Event (code) event that is in the first activity will run for each destination user. It is in this event that I am creating the <history> nodeset, and appending it to the tempApprovalStatus PROCESS LEVEL xml field
The basics of the code I use to do this is below:
Hope this helps.
Â
// This section reads in the data from the infopath form which I am using to collect the information
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlDoc.LoadXml(K2.ActivityInstanceDestination.XmlFieldsd"MyIPForm"].Value); // this loads the xml from my infopath form
// Get the status that the user has selected
System.Xml.
XmlNamespaceManager nameSpaceMgr = new System.Xml.XmlNamespaceManager(xmlDoc.NameTable);nameSpaceMgr.AddNamespace(
"my", xmlDoc.DocumentElement.GetNamespaceOfPrefix("my"));System.Xml.
XmlNode node = xmlDoc.SelectSingleNode("//my:myfields/my:approval/my:tmpApprovalStatus", nameSpaceMgr);node.InnerXml =
""; // clear out the temporary status field. on the IP formnode = xmlDoc.SelectSingleNode(
"//my:myfields/my:approval/my:tmpComments", nameSpaceMgr);node.InnerXml =
"";node = xmlDoc.SelectSingleNode(
"//my:myfields/my:approval/my:tmpUserName", nameSpaceMgr);node.InnerXml =
"";// I now have all the values from the Infopath form for the user who has just completed the form, and so I can load in the process level xml field, and save the values to it
newHistory +=
"<username>" + actionedBy + "</username>";newHistory +=
"<date>" + System.DateTime.Now.ToString("dd MMM yyyy hh:mm tt") + "</date>";newHistory +=
"<statusSelected>" + selectedStatus + "</statusSelected>";newHistory +=
"<comments>" + comments + "</comments>";newHistory +=
"</history>";processStatus.LoadXml(docRS);
newHistory = processStatus.SelectSingleNode(
"/approval").InnerXml + newHistory;}
newHistory =
"<approval>" + newHistory + "</approval>";K2.ProcessInstance.XmlFieldsl
"tempApprovalStatus"].Value = newHistory;Hi arduk,
Can you tell us how to get this repeating xml process field into a repeating table in infopath??
to get the comments back into the infopath form in a repeating table, I have another activity (lets call this activity "CollateComments") after the user comments have been collected.
In the CollateComments activity, there is a server code event, which reads the values in from the process level xml field "tempApprovalStatus", and loads them into an xml document.
I then loop through each of the comments that have been added and build an xml fragment that is in the same format as the nodes of the repeating table in the infopath form.
I retrieve the innerxml of the repeating table, and add the new list of comments at the start (so that comments appear with the latest at the top)
I then save xml of the infopath document back to the process level field.
The key is getting the xml correct for the repeating table. The easiest way I have found to do this has been either to debug your code, and examine the xml, or to fill in a form (using Infopath to populate the repeating table) and then save it, and examine the xml format. Of course, if you have a good understanding of how infopath builds its forms, then you will probably be able to look at the structure, and know what you need to build.
Hope this helps....
i tried the same thing but i got schema errors. i'm trying to get the comments as well the user names into the repeating table.
process xml field                               Infopath xmlfield
Tracking Comments                         Trackingcomments(node)
History(repeating node)Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â History(repeating node)
user(field)Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â user(field)
comments(field)Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â comments(field)
Â
I take it that the schema errors are in your IP form?
I found the key to working this out was to open the form in Infopath, and actually manually add some rows, and then save the form. I was then able to examine the source xml, and work out the correct string that I needed to reproduce, and then build that string within the code.
Here is my full code that saves the info back to the infopath form. I do not claim that this is the best way or the only way to do it - there may be other methods which are more efficient, but this does work - clearly you will have to change the names of the nodes to represent your schema etc...Hope this is helpful
     // load the infopath document into an XML document so we can add new history nodes
     XmlDocument xmlDoc = new System.Xml.XmlDocument();
     xmlDoc.LoadXml(K2.ProcessInstance.XmlFieldst"FormSignoff"].Value); //.ProcessInstance
     // get the namespace for the infopath form
     XmlNamespaceManager nameSpaceMgr = new System.Xml.XmlNamespaceManager(xmlDoc.NameTable);
     nameSpaceMgr.AddNamespace("my", xmlDoc.DocumentElement.GetNamespaceOfPrefix("my"));
     // Get a history node from the infopath form so we can create a duplicate of it later, and add new nodes before it.
     System.Xml.XmlNode historyNode = xmlDoc.SelectSingleNode("//my:myFields/my:approval/my:history", nameSpaceMgr);
     // load the temporary status xml field which is used so that each activity instance doesn't get overwritten by the last one.
     XmlDocument tempStatus = new XmlDocument();
     tempStatus.LoadXml(K2.ProcessInstance.XmlFieldsÂ"tempApprovalStatus"].Value.ToString());
     XmlNodeList approvalNodes = tempStatus.SelectNodes("/approval/history");
     foreach (XmlNode node in approvalNodes) {
      // create a new history node to be added to the infopath form
      System.Xml.XmlNode newHistoryNode = historyNode.Clone();
      // Set the values of each of the fields within this node...
      System.Xml.XmlNode tmp = newHistoryNode.SelectSingleNode("my:statusSelected", nameSpaceMgr);
      tmp.InnerText = node.SelectSingleNode("statusSelected").InnerText;
      // If not Accepted as is, set the minor changes required flag. This ensures we take the worst case.
      if (tmp.InnerText.ToLower() != "accepted as is") {
       K2.ProcessInstance.DataFields "minorChangesRequired"].Value = true;
      }
      tmp = newHistoryNode.SelectSingleNode("my:comments", nameSpaceMgr);
      tmp.InnerText = node.SelectSingleNode("comments").InnerText; ;
      tmp = newHistoryNode.SelectSingleNode("my:username", nameSpaceMgr);
      tmp.InnerText = node.SelectSingleNode("username").InnerText; ;
      tmp = newHistoryNode.SelectSingleNode("my:date", nameSpaceMgr);
      tmp.InnerText = node.SelectSingleNode("date").InnerText; ;
      tmp = newHistoryNode.SelectSingleNode("my:stepName", nameSpaceMgr);
      tmp.InnerText = node.SelectSingleNode("stepName").InnerText;
      // Insert the new history node before all other history nodes so it appears at the top.
      xmlDoc.SelectSingleNode("//my:myFields/my:approval", nameSpaceMgr).InsertBefore(newHistoryNode, historyNode);
      // move the history node to the one just inserted so we can keep adding nodes in reverse chronological order
      historyNode = newHistoryNode;
     }
     // clear the temporary status, comments and username fields
     // clear the status field
     System.Xml.XmlNode clearnode = xmlDoc.SelectSingleNode("//my:myFields/my:approval/my:tmpApprovalStatus", nameSpaceMgr);
     clearnode.InnerText = ""; // clear out the temporary status field.
     // clear the comments field
     clearnode = xmlDoc.SelectSingleNode("//my:myFields/my:approval/my:tmpComments", nameSpaceMgr);
     clearnode.InnerText = "";
     // Clear the user who actioned it.
     clearnode = xmlDoc.SelectSingleNode("//my:myFields/my:approval/my:tmpUserName", nameSpaceMgr);
     clearnode.InnerText = "";
     // Set the "Email me my comments" tick box to false
     clearnode = xmlDoc.SelectSingleNode("//my:myFields/my:emailMeMyComments", nameSpaceMgr);
     clearnode.InnerText = "false";
     // Save the changed xml back to the process field
     K2.ProcessInstance.XmlFields "FormSignoff"].Value = xmlDoc.OuterXml.ToString();
     // clear the tempApprovalStatus field
     K2.ProcessInstance.XmlFieldse"tempApprovalStatus"].Value = "";
thanks arduk, ill try that and let you know. Thanks for your time
public
void Main(Project_9dc436d796d1427586e196902c623d5e.EventItemContext_fc4b075f85594985a3659d1d44477a2f K2){
InfopathTemplate.LoadXml(K2.ProcessInstance.XmlFields[
"TemplateName"].Value.ToString());nameSpaceMgr.AddNamespace(
"my", InfopathTemplate.DocumentElement.GetNamespaceOfPrefix("my"));ClearCommentsTracker(InfopathTemplate, nameSpaceMgr);
ProcessFieldDocument.LoadXml(K2.ProcessInstance.XmlFields[
"TrackingComments"].Value);InfopathTemplate = CreateCommentsTracker(InfopathTemplate, ProcessFieldDocument, nameSpaceMgr);
DeleteFirstTrackingCommentNode(InfopathTemplate, nameSpaceMgr);
K2.ProcessInstance.XmlFields[
"TemplateName"].Value = InfopathTemplate.InnerXml;}
{
{
{
strArray[iCount] = ListOfXmlNodes[iNumberOfNodes].ChildNodes[iCount].InnerText;
}
InfopathTemplate = AddCommentsHistoryToInfopath(InfopathTemplate, nameSpaceMgr, strArray);
}
}
{
nameSpaceMgr.LookupNamespace(
"my"));nameSpaceMgr.LookupNamespace(
"my"));node.InnerText = strArray[0];
field = doc.CreateElement(
"my:Action",nameSpaceMgr.LookupNamespace(
"my"));node = group.AppendChild(field);
node.InnerText = strArray[1];
field = doc.CreateElement(
"my:DateAndTime",nameSpaceMgr.LookupNamespace(
"my"));node = group.AppendChild(field);
node.InnerText = strArray[2];
field = doc.CreateElement(
"my:ApproverComments",nameSpaceMgr.LookupNamespace(
"my"));node = group.AppendChild(field);
node.InnerText = strArray[3];
doc.AppendChild(group);
InfopathTemplateNavigator.CreateNavigator().SelectSingleNode(
nameSpaceMgr).AppendChild(doc.DocumentElement.CreateNavigator());
}
{
{
{
TrackingCommentsNavigator.DeleteSelf();
}
}
}
{
nameSpaceMgr);
TrackingCommentsNavigator.DeleteSelf();
}
}
Â
Please correct me if u find this piece of code very amateurish.. I'm new to programming..
Hi max,
i red thru this post since i needed to capture the comments of approvers in my workflow. Am confused to follow this post max. Could you please guide me thru steps.
Thx much.
Please correct here if am wrong:::A process level xml field can be created in K2 object browser.
How to make this process field as repeating field?
And also do i need to place the comments textbox in IP form under Repeating Section?
Thanks
ALSOÂ do I need to create these fields (repeating node)Â on IP form same as xml fields? Right now I only have txtcomments field on IP form.
Since there are 2 parts of code snippets are provided in post by yourself & Adruk, its getting tough to map between.
Appreciate if you can you provide me with the Default Server Event (code) event that is in the first activity will run for each destination user to create the xml field nodes for each user.
Thx much.
Enter your E-mail address. We'll send you an e-mail with instructions to reset your password.