K2 Server Event Run As API

In K2 blackpearl workflow, you can specify “Run As” credentials on your Server Events. During workflow execution, the event will be ran using the specified account instead of the K2 Service Account (Note: All Server Events are ran using the service account by default). This feature is available in the K2 Studio and K2 Designer in Visual Studio and it is particularly useful when you need to run critical tasks like create create new AD account, provision Exchange mailbox etc. You do not want the K2 Service Account to have all these critical rights and no Developer should create these critical workflows without the K2 Administrator’s knowledge.

Updating K2 Server Event Run As via K2 Workspace
Updating K2 Server Event Run As via K2 Workspace

Now, the problematic part comes when the Development environment is running on the Development AD whereas the UAT and Production running on the Production AD. It is not possible to embed the credentials in the Server Events now and the only other option is to set it in the K2 Workspace (See article). Setting the credentials in K2 Workspace requires the Administrator to update it on every deployment since it is version specific. Things could get worst when there are multiple events to update in a workflow. To make things simple, we can write a console application that makes use of the workflow management api to update the credentials.

Let’s Code K2 Server Event Run As API

You can find a copy of the working codes at GitHub repository.

The logical steps go like this:

1. Connect to the Workflow Management Server. You need to run using a K2 Administrator account.

2. Get the Process Set for your process. Process Set contains the generic configuration for a Process and is the parent container for all the versions of this process.

3. Find the default Process version. This is required for “Run As” credential setting is version specific.

4. Find the Activity that holds the Server Event which you want to update.

5. Find the required Event.

6a. To set a credential.

6b. To remove the credential and event to service account.

 

And that’s the end of how to code K2 Server Event Run As API.

Enjoy!!

 

K2 smartforms control custom property

In this article, I’m going to show you how to add a property, MaxFileSize, to a K2 smartforms custom control.

Note: The focus of this article is all about adding a property and using it in your control. For the basics on how to create a K2 smartforms control, please visit K2 smartforms Developers Reference .

Adding a K2 smartforms control custom property

1. Add a new property to your control’s xml definition

In your control’s definition xml file, add a new Prop element under Properties. It should look like the following:

ID attribute is the new property’s name and InitialValue sets the default value when the control is first added to a View.

For more information about what each of the property’s attribute does, look at Control Definition XML File.

2. Add a Getter/Setter method in your control class to access the property value

You need this method to create and save the option values into the control so that you can retrieve it in your JavaScript later.

The method name should match the ID value set in the property earlier on. In the GetOption and SetOption method, use small caps for your option name (e.g. maxfilesize) as this will be the data-options attribute registered in the control’s html.

3. Reading the option values via JavaScript

Now, as I mentioned earlier, the options will be surfaced as data-options attributes in the control’s html. Use your browser’s in-built developer tools to look at the control’s html. It should look similar to this:

Notice that the maxfilesize value has been registered in the data-options attribute? This is the value set in the K2 Designer and you have access to it now. =)

So, last step. To get the value, just use the jQuery .data function to retrieve the data-options values. For me, I create a initialise function to handle the values initialization.

 

Good luck!

Form is Initializing or Initialized??

Since K2 smartforms 1.0.6, there is a new event, When the Form is Initialized, which runs after When the Form is Initializing completes. The new event allows the Designer to configure rules that runs after a form has been loaded (as ajax calls). This is an important feature as we can now load the form first (forms becomes interactive), then make X number of ajax calls to load the other controls/Views data (e.g. dropdown, List View, etc), instead of loading everything when the form loads. So what does this mean? Let me show you a simple example.

Firstly, let’s create a SmartObject that simulate a large result set or simply a very slow data source (imagining my Oracle and SAP in dev env…). I’ll be using a stored procedure that simulates a 5 seconds wait before returning my result set.

As the procedure name says, it is a very slow script and the corresponding spVSlowScript SmartObject is generated.

222 - SmO
The very slow data source

Next, I generate a View from spVSlowScript SmartObject and made sure to remove all Rules in the View. I don’t need the When the View is Initializing rule.

222 - View
The generated View.

With the View ready, I created a new Form and added 2 instances of the View into it.

222 - Form
The Form with 2 instance of the View.

Now, move on to the Form Rules configuration. You should see the When the Form is Initializing rule added for you.  Let’s edit this rule and change the views method to List. Change the execution block type to also for efficiency (You should use and block if one action returns much faster than the other).

222 - Form Rule
The Form Initializing rule

With the Rule above, the Form will finish loading when both the actions have been completed. Save the Form now.

Testing the Form Initializing rule

In this test, all loading actions (especially the slow view methods) are executed in the initialising rule.

Run the Form URL now and you will notice that the spinner overlay will cover the entire form and the form will only be usable/interactive after the 2 List methods completed the ajax call, which is after 5 seconds. The main interest point here is that your Form takes more than 5 seconds to load, which will fail most of your performance requirements. =(

222 - Initialising Spinner
Whole form spinner overlay.

Testing the Form Initialized rule

Now, let’s try changing the When the Form is Initialising rule to When the Form is Initialized and see the difference.

222 - Form Initialized Rule
Changed the rule to Form is Initialised.

When you run the form again, you will realise that the form actually loads almost immediately (<1 second) and each View will have a spinner overlay to indicate that it is performing an operation now (ajax call). While the spinner overlays are running, the User can start to interact with any controls that have completed loading (e.g. text boxes, etc). Now, you have passed your performance test. =)

222 - Initialized Spinner
Each View running its ajax call now.

Form is Initializing or Initialized?

So in short, you should place the actions that are required on form load in When the Form is Initializing rule and any other actions that could slow down a form loading or could be loaded at a later stage to the When the Form is Initialized rule.

The following is a list of actions that you can consider for the 2 events.

Some example of actions that could be added to the When the Form is Initializing rule

  • OpenWorklistItem action.
  • SmartObject or View methods that loads the main form content. (e.g. The Leave object in your Leave Application)
  • SmartObject or View methods that returns small number of records and the performance is good.
  • Hide/show controls and views. If you already know what to hide or show at this stage, you should do it here.

Some example of actions that could be added to the When the Form is Initialized rule

  • SmartObject or View methods that does not load the main form content and is slow in performance (e.g. large result set that takes a considerable time to load).
  • Hide/show controls and views. If the consideration logics are finalised after all actions in the initialising and initialised rule are completed. These will be added to the end of the initialised rule.

Have fun!

Global Variable in Objective C

Using Global Variable in Objective C, allow you to store static information or values similar to Microsoft.NET C#.

In this post, we are going to using the Singleton design pattern.

Step 1: Create the class in Xcode, add both the header and method file (.h / .m), call it GlobalVariables.

Step 2: Add the following content into your header file.

Step 3: Add the following content into your method file.

 

At this stage, you will be wondering what does all these do. You will be adding @property for those value that you want to be able to access globally in the header file and synthesize them in the method file.

How do we use them?

To access the singleton, you need to import the header file.

First, saving data to the singleton.

Getting Data from the Singleton

Reachability: Testing Internet Connectivity on IOS APP

Introduction

Any apps that you build today will require some form of internet connectivity.

In today’s mobile applications environment, apps are getting more and more intuitive. Users are constantly looking at the apps to notify them of the lack of connectivity.

Step 1 : Getting Reachability library into your Xcode Project

We will be using the following library, Reachability. https://github.com/tonymillion/Reachability

There are 2 ways to setup the app in your Xcode project.

1. Manual
– Check out a copy from the above github url, add the Reachability.h/.m file to your project

2. Cocoapod
– Add the following line to your pod file, pod ‘Reachability’
– Run pod install on terminal.

Step 2: Setting up the SystemConfiguration Framework.
– Go to the Project -> Targets -> Build Phases tab.
– Add SystemConfiguration.Framework into your project.

Step 3: Codes to set up Reachability to test internet connectivity.

AppDelegate.h

Import the Reachability.h, at the very top of the file.

#import “Reachability.h"

AppDelegate.m

Add the codes below to your application did Finish Launching method.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

Add the following codes,

Reachability *reachability = [ReachabilityreachabilityWithHostname:@"http://localhost:3000/"];

reachability.reachableBlock = ^(Reachability *reachability) {
NSLog(@”Network is reachable.”);
};

reachability.unreachableBlock = ^(Reachability *reachability) {
NSLog(@”Network is unreachable.”);
};

// Start Monitoring
[reachability startNotifier];

Using K2 WorklistCriteria

In my previous post, I’ve discussed on how to use K2 blackpearl’s SourceCode.Worklist.Client API for basic functions like start Process Instance, open Worklist, open Worklist Item and action a Worklist Item. All these functions are straight forward (that’s about all you can do with it) except the open Worklist  function.

Normally, when you use the OpenWorklist method, you will like to have some form of filtering. It could be a filter to show the user his/her tasks based on the current department website listed or some custom prioritization logic to help the user to focus on the tasks at hand. To make this filtering possible, you will need to make use of the WorklistCriteria class.

Using K2 WorklistCriteria

To use the WorklistCriteria class, you will need to carry out the same open/close workflow connection steps in my previous post, which I’m not going to repeat here. Once that’s done, you will need to create an instance of the class.

You will need to add your filters here, which we will discuss in the next section and lastly pass the object into the OpenWorklist method.

So in the full picture, your codes will look like this:

So if you execute the codes now, it will return all Worklist Items of the current user.

202-Result_without_filter
My result without filter

Filter Basics

Now, to add a filter, you will need to call on the AddFilterField which has 3 variations:

WCField is the list of properties you can filter on and they are pretty much self explanatory.

Do take note that WorklistItemStatus comparision uses the following values:

WCCompare lists the comparison operators you can use in the filter:

If you notice, there is no “greater than”, “greater than or equal”, “less than” and “less than or equal” operators here, so you will need to put some thoughts into your mathematical operations (=

WCLogical is the joining operator between 2 filters. This will be discussed in more details in a subsequent section.

SubField is a string value used to reference additional key names like Data Field name during comparison.

Object is a object value for your comparison.

So if you just want to filter and show all Worklist Item with “Available” status, the code will look like this:

When using my test Worklist, I’ll get:

202-Result_only_avail
Result showing all “Available” status only

Using AND and OR Logical Operators

Using the AND and OR filter is pretty straight forward.

In my AND join example, I’m going to create a filter that requires the Process Name to start with “C” and the Worklist Item status to be “Open”.

202-ProcessCAndOpen
Result for Process start with “C” and status = “Open”

In my OR join example, I’m going to create a filter that requires the Worklist Item status to be “Available” or “Open”.

202-StatusAvailOrOpen
Status = “Available” OR “Open”

Using the Other Operators

Other than AND and OR operators, the following shows what the other operators does:

StartBracket: Draws the “(” operator for the given filter.

AndBracket: Adds the AND operator and closes the filter with a “)” operator.

OrBracket: Adds the OR operator and closes the filter with a “)” operator.

EndBracket: Closes the filter with a “)” operator, if you have a StartBracket earlier on

Filtering by Process or Activity Data/Xml Field

If you need to filter by Process or Activity Data or Xml Field, you will need to pass in the field name in the SubField like the following:

 

Well, the above details should get you through majority of your needs to create a custom Worklist. Enjoy!!

Setting Exception Handling

In K2 activity, you can set how K2 should handle any exception that is being thrown by K2 BP.

Activity Exception Property screen
Exception Property Screen

Depending on which checkbox is ticked, it can mean the activity proceed on to the next activity or stop completely and throwing exception.

Let’s take a simple workflow with a Start and another two activity in sequence. The following table shows what will happen depending on the ticked box.

Activity Exception table
Activity Exception table

Hence, with that in mind, if your activity or events to be triggered are not that important to the completion of the tasks, you may use row 2 of just ticking the checkbox of Enable Exception Rule and if the activity should complete but the exception should be log then use row 4.

Of course, try the various combination to determine which suits your requirement the best and note that if you are using VS.NET, then you can always write your own logging mechanism to handle the errors.

How to access and manuipulate ActivityInstanceDestination XML field in Workflow

To access datafields in K2 workflow is pretty straigh forward, but to access the XML fields require a bit more work to get to the text. In the following, the K2 API code is used to access the InfoPath XML data

1) Load the XML field using XmlDocument. If you have more then one XML field used, then loop through the XMLfield, use a IF statement to determine which XML field to load

XmlDocument document = new XmlDocument();
for (int i = 0; i < WLItem.ProcessInstance.XmlFields.Count; i++)
{
    if (WLItem.ProcessInstance.XmlFields[i].Name == “ABC”)
    {  xmlDoc.LoadXml(WLItem.ProcessInstance.XmlFields[i].Value.ToString());
   }
}

2) Once loaded, because K2 uses the namespace “my”. create a XmlNamespaceManager (as shown in the code below). Why the use of XMLNamespaceManager? From MSDN (MSDN URL) XmlNamespaceMaanager explanation:

Resolves, adds, and removes namespaces to a collection and provides scope management for these namespaces.

The namespace manager implements enumeration support in addition to adding and retrieving namespaces. You can loop through the information saved in the namespace manager by using the foreach construct.

Because the namespace manager provides a string comparison with the prefix and namespaces as objects, there is a performance improvement when using the namespace manager over the direct comparison of a string

System.Xml.XmlNamespaceManager xmlNSMgr = new System.Xml.XmlNamespaceManager(xmlDoc.NameTable);
xmlNSMgr.AddNamespace(“my”, xmlDoc.DocumentElement.GetNamespaceOfPrefix(“my”));

XmlNode RemarksNode = xmlDoc.DocumentElement.SelectNodes(<Insert the node structure here>, xmlNSMgr).Item(0);

node structure example: /my:myFields/my:Approver/my:Approver_Remarks

Full example: XmlNode RemarksNode = xmlDoc.DocumentElement.SelectNodes(“/my:myFields/my:Approver/my:Approver_Remarks”, xmlNSMgr).Item(0);

3) Once the node has been selected, you can manipulate the innerText. Example, I am updating the innertext of the node comments

Approver_Remarks.InnerText = tbRemarks.Text;

4) Now this is when some additional steps need to be done:

StringWriter _SW = new StringWriter(); // create a stringwriter
XmlTextWriter _XmlW = new XmlTextWriter(_SW); // create a new xml textwriter, writing to the stringwriter
xmlDoc.WriteTo(_XmlW); // write the xmlDoc to the xmltextwriter
string UpdatedXml = _SW.ToString(); // store the stringwriter value

//pass the XML value back to the XmlField

WLItem.ActivityInstanceDestination.XmlFields[“ApproverComment”].Value = UpdatedXml;

WLItem.Actions[“Approve”].Execute(); // Execute the action to update the field

Providing a friendly error message for your K2 error

5 May 2017: I’ve updated this post for 4.6.10 and above here.

Be it on smartforms or your custom ASP.NET forms, the OpenWorklistItem call will return the following error if the current User did not pass the validation rule on K2 Server:

172-OpenWorklistItemError
OpenWorklistItem error

For the K2 Designer and Admins, it’s a simple fact that the User is not the Destination User of this Activity. To the Users of this System, they will be like “What the heck are you trying to say?!?”. So how can we help the Users to better understand what is going on without calling us every time a cryptic message appears?

Find these cryptic out-of-box messages

These messages are stored as a template in [K2 blackpearl folder]\Host Server\bin\HostServerLogging.config.

172-HostServerLoggingMsg
The error message for ID 24411

In this file, you will find all the message templates used by K2. The first three numbers of the MsgID represents a logical section. So for example, MsgID starting with 244 are relating to security.

Of course, you can just modify this file directly, restart your K2 blackpearl Server services and all messages will be friendlier. But what happens on upgrade? Well, there’s a high chance that it will be replaced by the default copy in the installer, so it will be a hassle to make comparisons and update the entire list every time you upgrade. My take? Don’t touch this file unless you are willing to do all the additional work.

Friendly message via ASP.NET

Once you know the message you are looking out for, the rest will be using your try-catch block to capture the error thrown and look out for the MsgID in your error message body.

Friendly message via K2 smartforms

In smartforms, you will need to make use of the Error Handling condition – Error Occurred and read the message via the System Values > Error node.

  1. Open your Rule Configuration Wizard.

    172-RuleWizardConfig
    Rule Wizard Configuration
  2. Add the Error Occurred condition. This will be executed when an error occurs in this Rule.

    172-ErrorOccurred
    Error Occurred condition
  3. Add the An advanced condition is true condition. This condition should be with Error Occurred to create a AND condition like the following:

    172-advCond
    An advanced condition is true with Error occurred
  4. Click on an advance condition link, followed by the Add button to add a new row.

    172-NewCondition
    Add a new row of condition
  5. From the Context Browser, expand System Values > Error. Drag the Error Message node and drop it to the Left column text box. Select Contains for the Operator and fill in the MsgID code you are looking for in the Right column text box. Click OK to save and close the dialog.

    172-conditionDone
    Fill in the conditions
  6. Add your friendly message and any other rules(e.g. disable form, navigate to another form etc).

    172-showMessage
    Add a message
  7. Lastly, let’s test and see the result. 172-result

 

Have fun!