Wednesday, November 28, 2007

Voyages with BlackPearl - 4. Replace InfoPath Attachments

I am using InfoPath as the user interface to a K2 BlackPearl workflow. The form allows attachments to be added and they will need to be accessible to other users as the workflow progresses. Since the attachments are quite large, I wanted to strip the attachments off and replace them with a link. This workflow allows mutliple attachments and there are several different files that can be attached. The workflow itself makes no refrence to the attachments. I would have used the K2 wizard to store the attachment to a SharePoint document library but it doesn't work for repeating nodes of file attachments and I would need extra code to find the file name and store this in the InfoPath form as a hyperlink, and then delete the original attachment.
My solution was to build a web service that does exactly what I want. Remove the attachment, store it in SharePoint, put the path to the document on another field and repeat this for all occurrences in the repeating node.
The challenge then is to forward the now amended InfoPath form onto the K2 InfoPathService.asmx.
Here is how its done. You'll need a class defined for an XmlNode Array because that is how the InfoPath form is submitted to the InfoPathService.asmx.
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.42")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "")] [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public class nodeArray { [System.Xml.Serialization.XmlArray()] [System.Xml.Serialization.XmlAnyElement()] public System.Xml.XmlNode[] node; }

Then the web service method looks like this:
public void InfoPathReplaceAttachment(Object infoPathFormXml) {
nodeArray nodes = new nodeArray();
nodes.node = (System.Xml.XmlNode[]) infoPathFormXml;
string s = System.Web.HttpUtility.HtmlDecode(nodes.node[0].OuterXml);
// load the string into an XML dom called myDoc and replace the attachments
//Finally submit the XML to the InfoPathService.asmx
// do this to use escaped XML
XmlNode escNode = myDoc.CreateTextNode(myDoc.OuterXml);
// replace node with the amended version
nodes.node[0] = escNode;
infoPathFormXml = nodes.node;
K2submit.InfoPathService ws = new K2submit.InfoPathService();
ws.Credentials = System.Net.CredentialCache.DefaultCredentials;
myDoc = null;

Voyages with BlackPearl - 3.Team member becomes owner of a task

I have a task in the workflow that is assigned to a group of users. I want the person that actions the task to then 'own' subsequent tasks which are assigned only to them.

I'm using a workflow that is integrated to InfoPath and there are two InfoPath client activities. Let's call the first activity 'TEAM Activity' and then the subsequent activity 'USER Activity'.

One of the good things about BlackPearl is the ability to assign a Destination User not just to a user or a role but to a data field.
This solution uses that ability, so I created a DataField called 'DestinationUser' and an Environment variable called 'Domain'.

It also uses a field from the InfoPath form which stores the current user. Just use the userName() function as the default value and you have the name of the current user. See this post

1. TEAM Activity is assigned to the Role of the 'A Team' where membership of this role is determined through K2.
2. So when you run through the InfoPath client wizard, select the 'A Team' role in the usual way.
3. Now on the TEAM Activity create a Server Code event and make sure it is underneath the InfoPath client event.
4. In the Server Event add the following code:

System.Xml.XmlDocument doc = new System.Xml.XmlDocument();

// call custom utility that uses SelectSingleNode to retrieve value of

//node using namespace
string CurrentOwner = Utils.GetXMLValue(doc, "my:CurrentOwner");

K2.ProcessInstance.DataFields["NewOwner"].Value = CurrentOwner;
// Set DestinationUser datafield
K2.ProcessInstance.DataFields["DestinationUser"].Value = K2.StringTable["Domain"] + @"\" + K2.ProcessInstance.DataFields["NewOwner "].Value.ToString();

Note the use of K2.ActivityInstanceDestination.XmlFields["Root"] on the second line. Using ProcessInstatance won't work because it hasn't been updated yet.

On the USER Activity when you run through the InfoPath client wizard, select the Destination User to be the 'DestinationUser' data field.

A word of caution. Bad things happen if you don't set the DataField to a value. K2 will understandably throw a runtime error as it doesn't know who to assign the client activity to. Also think through the implications of redirecting a task. You may need to reset the current user on other activities. But I've found this trick really useful.

Monday, November 26, 2007

Voyages with BlackPearl - 2. SharePoint permissions

I have a K2 BlackPearl workflow that is integrated to an InfoPath form. The workflow creates a number of InfoPath client events each with a different view.
Users will initiate the workflow from SharePoint, clicking on a link on the home page which opens the initial view of the form. As the workflow advances to the next InfoPath client event it adds a copy of the InfoPath form to the form library that I specified when I attached the form to the workflow.
So I was wondering what are the minimum permissions I need to give users to this site in order for them to create workflows but not edit anything else?
I created a SharePoint group called 'WorkflowUsers' and added my users (or AD groups if you're smart) as members. I gave the group WorkflowUsers 'Read' permission to the site. The lists, libraries and other SharePoint objects inherit this permission. But Read permission is not enough to initiate workflows in this situation. WorkflowUsers also have to have Contribute permissions on the K2 BlackPearl Data Connection library and on the Form library where the workflow stores the forms. This is achieved by navigating to each, change the settings and using the Edit Permissions option to break the inheritance form the site. You can then change permission to Contribute for the WorkflowUser group.
I give the K2 Service account full permission to the site, which is probably more than is necessary but then it's just a service account.

Voyages with BlackPearl - 1. KPRX size

I've create a process in K2 BlackPearl that is quite large. The file size of the KPRX is about 8MB. The trouble is that every time I save it, even if I make no changes, the file size increases - this is with Hotfix 2.01 applied.
I started investigating and found that some redundant nodes are created when a KPRX file is saved. These nodes are used to define the layout of lines and activities but ghost copies are created every time you save.
These nodes are:
/Process/Views/DocumentView/LinesLayoutData/DocumentViewLineLayoutData and
Each time I save the KPRX I found that it created another copy of the nodes, each has a different Guid but the Name element (amongst others is blank).
This may sound trivial but with some 25 activities and over 50 lines each time I saved the KPRX it would increase the file size by 100KB.
So I wrote a utility that iterates through all these redundant nodes and removes them from the KPRX. I've used it many times and it has no adverse effect on the KPRX file, but the benefit is that it loads quicker and is less likely to give me an out of memory error.
If you want a copy of this KPRX cleaner ping me an email and I'll send it to you. It's a simple .Net Windows application that will prompt for the location of your KPRX file and creates a copy of the cleansed version.
So I finally got around to creating a blog. I'm going to find it hard to keep it updated. I spend most of the day in front of a computer screen so blogging is not one of my favourite past times. But it is time I shared some of the knowledge I've gained on a K2 BlackPearl project and on other software projects.
I'm determined not to fill it with the the minutia and trivia of every day life that so many other bloggers seem compelled to share. I'm sure that you really don't care how I spent the weekend or to know my opinion on some TV show.
If you find the contents useful well that's great. If not, then there is another blog just a click away.
By the way, 'No Intelligent Life' comes from an entry in a New Scientist competition of a text message that an alien might send home having arrived on earth. It wasn't the winning entry, but it appealed to me.