A while back one of my colleagues received this error deploying the process from visual studio to a SharePoint 2010 site. Which was quite simple to fix if you know what you’re looking for.

Server was unable to process the request —> Activation could not be completed because the InfoPath Form Services support feature is not present.

Server was unable to process the request ---> Activation could not be completed because the InfoPath Form Services support feature is not present.

This is easily resolved by activating the relevant features to the site and site collection at the Site Collection level activate the following features:

Enable Enterpirse Site Collection Features

Also enable the following at site level:

Enable enterprise site features

Something I have been asked a lot lately is how to persist items from a xml repeating group into some database.

Although this focuses around persisting data in a repeating group stored in an InfoPath form. The concepts can be applied to generic xml. There is 2 ways to actually do this the one is use smartobject integration. Which has its own set of quirks. This post will deal with persisting data in the K2 workflow I will do a post later that will use integrated smartobjects for this.

First we should set up an InfoPath main datasource with a repeating group that we’re going to persist:

Lay out the workflow as the image below the first event in the only activity is a Server Event (code). The second event is a SmartObject event that we will use to persist the data.

K2 Persist InfoPath Repeating Group

The approach I’m going to take is going to leverage the destination rule functionality more importantly the use of advanced destination rules. I will post something on advanced destination rules shortly as I believe a lot of people out there aren’t aware of this functionality. However for the purposes of this post I’ve recorded a short video. Accessing advanced destination rules is done by navigating to the destination rule wizard and then clicking the back button as in the following video : Advanced Destination Rule.

The video shows us how to access the advanced destination rules, and how to create slots based on the number of items that have been created in the InfoPath repeating group. The important thing to note is that the value of the item instance in the group is then stored in the activity instance data, by ensuring that the at least one item within the repeating group is unique we can use it to identify a given row within the xml. The snippet below shows us how to access the xml field and perform an xpath query on the InfoPath xml that will allow us to retrieve the current row.

// Load the XMl from the XML field into xml document
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(
K2.ProcessInstance.XmlFields["RepeatingList"].Value
);

// Access the instance data at this point
// this is the unique identifier we used to create the slots.
string instanceData =
K2.ActivityInstanceDestination.InstanceData.ToString();

XmlNode xmlNode = null;

// Retrieve the my prefix namespace.
XmlNamespaceManager nsm =
new XmlNamespaceManager(xmlDocument.NameTable);

nsm.AddNamespace(
"my",
xmlDocument.DocumentElement.GetNamespaceOfPrefix("my")
);

// use XPath to identify the node we're looking for.
XmlNode node = xmlDocument.SelectSingleNode(
string.Format(
"/my:myFields/my:InvoiceLineItem[my:UniqueLineId={0}]",
instanceData
),
nsm
);

// Set the various activity level datafields which
// will map up to the relevant values in the InfoPath form.
// Note that they datafields need to be defined first.
// The lines below don't actually create the datafields.

K2.ActivityInstanceDestination.DataFields["LineId"].Value =
node["my:UniqueLineId"].InnerText;
K2.ActivityInstanceDestination.DataFields["Description"].Value =
node["my:Description"].InnerText;
K2.ActivityInstanceDestination.DataFields["Value"].Value =
node["my:Value"].InnerText;

Finally we use a create type method on a smartobject to persist the data to a repository. In this post we simply used a SmartBox smartobject.
K2 Smart Object defintion of the InfoPath repeating list.

The smartobject event we placed in the workflow will persist the data to the correct repository. The smartobject event is configured as follows:
The smartobject event wizard definition.

Finally the step most people ( including myself ) miss, setting up a suceeding rule. Basically we check whether all of the activity instance status values are set to completed. This will prevent the workflow from completing until all of the repeating items are persisted to the repository.

That’s all folks. Thanks for reading :).

Fairly quick turn around as update 1290 hasn’t been in the wild too long.

Please note that there are some outstanding “known” issues that which have not been resolved. Most of the fixes appear to be around the SmartObject Services functionality introduced with the 1230 udpate.

Head over to the K2 site to get the latest goodies.

http://www.k2.com

K2 blackpearl has the ability to hold configurable values that target different environments, during design time this data is stored in the K2 environment library. Upon deployment the Environment Library will used to create the string table entries required at runtime.

During the development life cycle you may find yourself creating a substantial amount of environment fields. However when creating these K2 will set a value for the current environment however the value will not be set for any of the other environments configured on the K2 Environment Library. Resulting in a deployment that may inject empty string table entries into another environment. This can be easily corrected depending on the approach being taken for deployment. Generally speaking I prefer deployment package creation, purely due to the fact that this is how I would provide the solution to the client. I would not expect them to have a visual studio workstation where I could simply deploy from ( which in itself poses another issue that this post attempt to resolve ).

Although the code here is pretty basic, it satisfied my immediate need and I’m sure it will help many of you too.

So let’s get dirty, and the first warning: This API does not appear to be documented in K2 4.5 with update 1230., although I have tested this functionality.

First thing we need to do is reference SourceCode.EnvironmentSettings.Client.dll this should appear in the GAC if it doesn’t just navigate to the bin directory of your blackpearl installation folder and reference it from there. You should see it is marked as Global rather than Copy Local indicating that it is being pulled from the GAC.

To connect we create an instance of the EnvironmentSettingsManager. We then specify the connection string for the K2 Environment Server, which is effectively the same connection string for connecting to the K2 workflow management server (note port 5555 as opposed to 5252 which is used by the workflow server). It is important to note that the EnvironmentSettingsManager class does implement IDisposable so you could actually use a using statement when you instantiate the class to ensure that the class is disposed of correctly, and all of the connections are closed.

// This will set up the class with out actually opening the connection.
// The first argument indicates that the connection should not be opened,
// and the second that we shouldn't use the caching mechanism.
EnvironmentSettingsManager manager =
   new EnvironmentSettingsManager(false, false);

// specify the connection string. Impersonate the calling user rather than provide a user name and password.
manager.ConnectionString = "Integrated=True;IsPrimaryLogin=True;Authenticate=True;EncryptedPassword=False;Host=localhost;Port=5555";

// initialize the manager, and specify true as the argument to establish the connection to the environment library.
manager.InitializeSettingsManager(true);

The next thing we need to do is select an environment. One thing to consider is the environment template being used this however is out of scope for this particular post as we’re dealing a standard k2 installation where little customisation has taken place. I will therefore always use the first available template, and to keep the demo short I’ve selected the first environment in that template.

// Retrieve available environments
EnvironmentInstanceCollection environments = manager.EnvironmentTemplates[0].Environments;

// Change the environment
manager.ChangeEnvironment(environments[0].EnvironmentName);
manager.Refresh()

Now given that we’re copying values from one environment to another on the same server we can assume that environment fields will already exist, but their values will be blank.

Setting the value is as simple as executing the following code we will use the first available environment field and set its value to test.
You have the option of using the actual field name or the internal field id. It is important to note that the SaveUpdate() method is also available on the EnvironmentFields property this however does not seem to persist the data back to the K2 server, and I would recommend that you would execute the SaveUpdate() method on the individual EnvironmentField to guarantee success.

// Set new value
manager.CurrentEnvironment.EnvironmentFields[0].Value = "Some Value";

// Persist to K2
manager.CurrentEnvironment.EnvironmentFields[0].SaveUpdate();

In cleaning up if the code has not been wrapped in a using block, be sure to invoke Disconnect() method on the manager instance in this document.

manager.Disconnect();