K2’s 4.6.11 – For Each Wizard

With the release of K2 4.6.11 a new wizard has been added to the k2 blackpearl software. It’s the For Each wizard, which might surprise you. How can a workflow engine like K2 not have a For Each option? It’s quite simple once you understand how the K2 workflow engine works.

Activities and line rules determine the flow of the process. Questions like ‘Does your workflow have branch and merge options?’ never really applied to K2 due to the easy structure of Activities and Line Rules. Every line rule is evaluated after the activity is completed and the line is followed once the line rule evaluates to True. A loop becomes a simple line back to the same activity with a line rule evaluating to True. A branch is just an activity with two lines that evaluate to True and a Merge is an activity with two lines rules going into it that both evaluate to True, with a preceding rule that checks if the activity should start. If a loop is just a line back to the same activity with some line rules, why add a For Each wizard?

The reason is simple, continuous improvements on ease of use and usability.

Before the For Each wizard we had to use a difficult-to-explain option called a destination set configured as “Plan per slot (no destination)” to loop over a set of items. It’s a destination with no destination – yeah, not very clear. It also didn’t show up very well in View Flow, which is the live graphical report for what’s going on with a particular instance of a workflow. If you wanted to do more advanced activities in that loop, you would have to use an IPC event. This means a sub-process per item in the loop which is also not always the best approach visually as well as for performance.

The For Each wizards solves these with an easy way to loop through a list of items.

How does it work?

K2 Studio and K2 for Visual Studio

Let’s first see how it works in the K2 Studio and/or Visual Studio designers:

For Each event in K2 for Visual Studio

Drag and drop the For Each wizard onto the canvas and you’ll need to specify a list of items to loop through in the Source field. You can loop over various items, like:

  • List Item Reference
  • SmartObject List method
  • Comma separated list

ForEach - Object browser

The wizard also asks for the field Reference and Index. The reference becomes the name of the Item Reference for the items in the list. The index is used for two Data Fields. In the example, the Data Fields are ItemIndex and ItemIndex Result. The Item Index is simply the count of the loop, while the ItemIndex Result tells you if there is a next item or not. You will most likely use the item reference created. The item reference refers to the current item in the loop. If you’ve used a list item reference (created with the Create List Reference), then this would be the full reference to that item and not just a reference with a single variable in it. The For Each wizard add its own item reference, so if you use an item reference in the For Each wizard, you will have two item references, which technically might be a consideration as References are stored as a bit of XML and this impacts performance and database growth slightly. If you’ve used a SmartObject than it would contain the property that you selected in the Source field of the For Each loop. If performance really is key, we would use a SmartObject directly in the For Each wizard and not create an Item Reference first. Obviously working with an item reference (for example, looping over list items) can make life a lot easier for you when designing your workflow.

Once you’ve placed the For Each wizard, you get two lines out of the activity. You must create the activities to which these lines need to go yourself, and when you’re done you create a line back to the For Each activity. The final result would looks something like this:

K2 Designer

Within the K2 Designer, it all works similar to the full client, except that it looks a little different:

What can be confusing is that the K2 Designer most likely already has an item reference associated with the process. The reason is because this designer always has context of an item. In the screenshot, I’ve made my workflow on a custom list in SharePoint called Emails. I initially thought that I could use that, but that is just an item reference, so it’s not a list of items. Within the K2 Designer there is a lot more benefit from a design perspective to first create a reference!

The final result would like this:

Conclusion

The For Each wizard is a nice addition to the K2 platform and allows an easier design of the workflow. It’s quite easy to use (especially in combination with the Create Reference wizard), which is exactly the aim of the wizard.

“Syncing” outlook 2013 with Owncloud

Update: Although the below works, it’s not ideal.
I’ve recently discovered https://caldavsynchronizer.org/ which is an active project and works very well!

 

 

As some of you might know, i’m quite fond of my privacy. Together with the fact that i still run my own server at home, this resulted in me investing some time on OwnCloud. I use it to sync my contacts and some files that I use for work. It also makes my MP3’s accessable everywhere in the world. The android app is simple, but the thing i needed the most (backup of photo’s) is in there with a nice auto-upload over wifi option. I recently broke a Nexus 4 and was a bit bummed out because i couldn’t get my photo’s of it anymore.

Anyway, owncloud also has options to do calendar functionality. I used to sync my outlook calendar with google calendar via their Google Calendar Sync. The Calendar Sync is a bit buggy and one wonders why google doesn’t invest a bit more time into that. I decided that i wanted to sync my outlook calendar with OwnCloud so i could also access it from there.

Outlook 201x (in my case 2013) allows you to upload a ICS (iCalendar file) to a WebDav server. OwnCloud is a WebDav server and it would mean my ICS file is now backed up in owncloud. This is how i configured it:

 

You’ll now get this dialog:

Enter the URL of your owncloud WebDav link. This normally is https://servername/remote.php/webdav/

Go for advanced as well, and configure it like so:

 

After hitting OK, the first ICS file will be uploaded. That ICS file is just a file to OwnCloud. So those entries are not in your calendar yet. However, if you click on the file the OwnCloud web UI, you get this:

 

This is a *great* feature, as we know can create calendar entries from our ICS file.

It does a single web requirest to the owncloud server that imports the file, you can automate this with CURL:

 

curl --user username:password --data "progresskey=0&method=old&overwrite=1&calname=&path=%2F&file=FILENAME.ICS&id=4&calcolor="  https://servername/index.php/apps/calendar/ajax/import/import.php

I’m assuming the id=4 identifies the calendar you use. So you might need to change it. Important is also the filename.ICS
All that’s left is to crontab that and we’re done!

Add AD-Group to SP2010 via CSOM

Unfortunatly, projects aren’t always that fancy. In this case I’m still doing a bit of SP2010. Luckaly we do use CSOM to make it a bit interesting.
CSOM has a lot of functionality, and theoretically you can say “it does everything the old-fashioned SPSite model does”. However, it can be a struggle.

In this case, we needed to add a AD Group/User to sharepoint directly. Don’t ask, it’s a customer requirement. So, no SharePoint groups used.

In the end, this MSDN article was hugely useful.

Also note the usage of EnsureUsers which seems to work for groups as well!?

The permission parameter are the “Full Control” or “Read” permissions which are called “Permission Levels” in SharePoint itself. There’s a “level” below that called BasePermissions, for if you want to make it more complex for yourself :)

Here’s the bit of code that works for me:

        public static void AddSecurityGroup(string url, string loginName, string permission)
        {
            using (ClientContext context = new ClientContext(url))
            {
                context.Load(context.Web, w => w.RoleAssignments);
                context.Load(context.Web, w => w.HasUniqueRoleAssignments);
                context.ExecuteQuery();
 
                if (!context.Web.HasUniqueRoleAssignments)
                {
                    throw new ArgumentException("URL provided is not root for permissions. Did you break inheritence correctly? Because that's needed.");
                }
 
 
                User u = context.Web.EnsureUser(loginName); //This also seems to work for groups.
                context.Load(u); // important as this actually loads the properties etc.
                context.ExecuteQuery();
 
 
                RoleDefinition oRoleDefinition = context.Web.RoleDefinitions.GetByName(permission); 
                RoleDefinitionBindingCollection collRoleDefinitionBinding = new RoleDefinitionBindingCollection(context);
                collRoleDefinitionBinding.Add(oRoleDefinition);
 
                RoleAssignment oRoleAssignment = context.Web.RoleAssignments.Add(u, collRoleDefinitionBinding);
                context.ExecuteQuery();
 
            }
        }

Happy coding!