Category: Work

Update a K2 ClientEvent URL using code

In our current project there are 18 processes with each at least 4 client events. There are even processes with 8 or 12 client events. We also have 1 webpart that handles all the client events. In the beginning we decide on the URL passing a parameter to allow the webpart to render correctly. The basis of that decision has (of course) changed and the querystring isn’t used anymore.

This also means, that querystring only contains the SerialNumber. The URL itself is based on a field from the Environment library. As you noticed, we’ve got at least 72 client events to update, this means a lot of manual work, luckily there is the K2 SourceCode.Workflow.Authoring API. If you have a big-ish project like we do, you’re going to use this API more and more….

Show me the code!

The following code allows us to update the Client Event URL:

 DirectoryInfo di = new DirectoryInfo(@"C:\LocationOfK2Solution\");
FileInfo[] fileInfo = di.GetFiles("*.kprx");
 
foreach (FileInfo processFile in fileInfo)
{
    Process proc = Process.Load(processFile.FullName);
    Console.WriteLine("Process: {0}", proc.Name);
    foreach (Activity act in proc.Activities)
    {
        Console.WriteLine("\tActivity: {0}", act.Name);
        foreach (Event evnt in act.Events)
        {
            Console.WriteLine("\t\tEvent name: {0}", evnt.Name);
            Console.WriteLine("\t\tEvent type: {0}", evnt.Type);
            if (evnt.Type == EventTypes.Client)
            {
                ClientEventItem clEvent = (ClientEventItem)evnt.EventItem;
 
                if (clEvent.InternetUrl == null || clEvent.InternetUrl.Parts == null)
                {
                    throw new Exception("client event InternetURl is empty.");
                }
 
                K2FieldPartCollection fieldPartCollection = clEvent.InternetUrl.Parts;
 
                // The first fieldpart should be the envField(from the artifact library)
                K2FieldPart envField = fieldPartCollection[0];
                if (envField.GetType() != typeof(ArtifactLibraryFieldPart))
                {
                    throw new Exception("K2FieldPartCollection's 1st element is not an ArtifactLibraryFieldPart (is not the expected environment setting)");
                }
 
                // Remove everything and build it up again.
                fieldPartCollection.Clear();
 
                // Add the original first element
                fieldPartCollection.Add(envField);
 
                // Add the SerialNumberField.
                SerialNoFieldPart serialField = new SerialNoFieldPart();
                fieldPartCollection.Add(serialField);
 
                // Save it back to the clientEvent.
                clEvent.InternetUrl.Parts = fieldPartCollection;
 
                Console.WriteLine("\t\tClient event URL updated, is now {0}", clEvent.InternetUrl.DesignTimeValue);
            }
        } // foreach act.Events
    } // Foreacht process.activities
 
    proc.Save();
    Console.WriteLine("Saving process");
} //foreach file

Code explained

The code isn’t very hard to understand. We first get all the kprx files (i know, you can load the solution and get the files from there). We load the process using Process.Load(). Loop trough the activities and then loop trough al the events. We simply get all the clientevents by looking at the event type.

This is where it gets interesting. You can cast an event to a ClientEventItem. This allows you to get the InternetUrl as a property. That InternetURL is actually a K2FieldPartCollection. A k2FieldPart can be a lot of things, in this case we check if the first one is an ArtifactLibraryFieldPart which is a value from the ArtifactLibrary (or the Environment Library as most people know it). This check was simply there to check if we are doing the correct thing.

After that we clear the K2FieldPartCollection and add the ArtifactLibrary field we just saved in another variable, then just add the SerialFieldPart which represents the SerialNumber.
You can then simply print out the DesignTimeValue as shown. After that, we save the process and we’re done.

As you can see, the SourceCode.Workflow.Authoring API is very easy to use. We use it a lot to update and do stuff to our processes and the K2 Documentation also provide some examples on how to build processes using code!

K2 BlackPoint 902…

Just a quick update on BlackPoint. Currently you can download it from http://portal.k2workflow.com if you purchased it or if you are a K2 partner….

InfoPath (+K2): The form has been closed

InfoPath forms services is widely discussed product, you either love it or hate it. You mostly hate it because you would want more from the product then you can get, you’re then simply running into the boundaries of InfoPath. IMHO this is a limitation of working with out of the box products.

K2 BlackPearl is able to integrate really well with InfoPath and is able to use InfoPath Forms Server so you don’t need the full client on all the client machines. However, forms services add a few extra limitations.

When you’re using K2 and InfoPath you can finish your K2 tasks by submitting the forms, this kicks of a web service call which helps the process move along and execute your K2 action. After it has been submit, the form will be closed, this is something the K2 integration adds (you can change it, but what’s the point). The user is then prompted with the following screen:

formclosed

Some people think this is an error, because sometimes when forms server isn’t able to open the form correctly, this is shown. In this case it’s a perfectly valid message because infopath is configured like this.

For the end user this isn’t very user friendly and some will press the back button or close the window. The best solution is to navigate back to the K2 tasklist so the end user can pick up the next task.

Getting InfoPath to go back

The ones out there that have experience calling the Forms Server url from your code know that there is a Source querystring parameter. And if your using the normal sharepoint list actions you can alter those.

To fix this in K2, you need know how K2 opens a InfoPath Client Event. And here’s a diagram of that:

OpenTaskFlow

The important part is in the OpenInfoPathTask.aspx, so here’s a diagram of that:

OpenInfoPathTask

The OpenInfoPathTask.aspx gets a lot of querystring parameters which are defined in the Event Item code of the InfoPath Client Event. Because the OpenInfoPathTask.aspx is in between the tasklist and the InfoPath forms services page, you do not have control over the parameters that are passed to the /_layouts/FormServer.aspx query. And, it doesn’t forward/copy the Source parameter.

URL rewrite, the solution

The solution to this was hard to find, but a college (Thijs Kroesbergen) mentioned something called URL Rewriting. It’s pretty weird that I didn’t think of it because I have used it on several sites before. Even this blog uses it.

URL rewriting is available in IIS7 but not in IIS5 or IIS6. There are 3rd party IIS ISAPI filters, we choose for the Ionic’s ISAPI Rewrite Filter which is available from Codeplex.com. Although the configuration parameters might seem the same as Apache’s mod_rewrite, they are not! Read the readme file for the real stuff, it’s a long read but is needed.

Install and Configure the ISAPI filter

To install the ISAPI filter:

  • Place the IsapiRewrite4.dll file in the c:\windows\system32\inetsrv\IIRF
  • Add an IsapiRewrite4.ini file to the same directory
  • Add read permissions for IIS_WPG to the DLL and INI file.
  • If you have logging in the INI file, add that directory and provide ‘Everybody’ with full control (hey, make it easy on yourself J)
  • Go into Internet Information manager (winkey + R; type ‘inetmgr’)
  • Go to the properties of the web site where the InfoPath forms server is hosted (so your sharepoint site)
  • Go to the ISAPI tab
  • Add the ISAPI filter

Now the INI file contains the real ‘magic’. Here’s what we used:

# IsapiRewrite4.ini
#
# ini file for the ISAPI rewriter.
#
# Tue, 11 Jul 2006  01:49
#
 
RewriteLog  c:\temp\iirfLog.txt
 
RewriteLogLevel 3
 
#
# This ini file illustrates the use of a redirect rule.
# Any incoming URL that starts with an uppercase W
# will be redirected to the specified server.
 
RewriteCond %{URL}   ^/_layouts/FormServer.aspx.*$
RewriteRule ^(.*)$     $1&Source=http://gpr:1000/

This simply adds the Source parameter to the querystring. You will NOT see this in the browser address bar, but for IIS it’s there!

The RewriteLogLevel 3 defines a loglevel, and the RewriteLog provides the location, remember to set permissions and disable this in production.

The RewriteCond is a filter to apply the next line or not, so this checks if the “^/_layouts/FormServer.aspx.*$ matches the variable ‘URL’ which is the URL, but for forms services this is without ‘http://domain.tld’.

The RewriteRule then matches the hole query and adds &Source=http://gpr:1000/ to the string. The ^(.*)$ marks what’s set in $1 (everything). These are all common things within regular expressions, hard to learn but worth it.

Because the Source Parameter is now successfully added, the form will close and move back to a defined page (in this case, http://gpr:1000) is where the tasklist is. Please remember that the Source parameter should be filled with a URL within the SAME site collection.

And that’s it. You are now able to specify the Source parameter to the sites, it’s not dynamic (i know) but you can also redirect to some other page which handles other stuff, like close the window, just remember that this page needs to be in the same site collection!

WordPress Themes