Category: Work > Visual Studio

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!

Creating a K2 Deployment Package from code

In our current project, we are using Tean Foundation Server to automate our build process. This also means MSI’s are created for every artifact to deploy, eventually using TFS’s functionality to deploy a new msi when the build quality is changed. MSI’s are build in a way so they can be installed from command line without user actions required (unattended installation). So, when we change the build quality of a build it can fully automatically be deployed to a testing environment.

Most of our software that needs to be deployed are webservices, websites or web parts. K2 however, is somewhat harder to deploy. Since BlackPearl it has become a lot easier because you can use a MSBUILD command to deploy a process. For those of you who don’t know about this. Check out KB000188 at the K2 Knowledge base. In general, it comes down to this command:

MSBUILD DeploymentPackage.msbuild /p: Environment=Development

It’s easy to create a MSI or (PowerShell) script that starts the MSBUILD command and installs the process. But how do we automate the creation of the deployment package? Since this is normally done from VS2005.

Building/deploying using code

The solution is actually quite easy, but we’ll get to that later. First off all k2 developers should have a copy of the developer reference. This documentation is still work in progress but there is a lot of information already in there. It also has an example on how to deploy a process using code. This utilizes the DeploymentPackage and Sourcecode.Framework.ProjectSystem.Project class. So the msbuild command can actually be replaced with a custom-coded application, which is even nicer for a msi than just kicking of a msbuild command.
The deploymentPackage is produced using the Project.CreateDeploymentPackage(). From the function name you’d expect it to do the same as the VS2005 context menu. It however does not.

Creating the deployment package

So if the CreateDeploymentPackage() doesn’t create the deployemtn package, what does? Well, simple, the Save() method of the Project. It takes in two parameters. The directory where to save the deployment package and the name of the deployment package.
The result is the same file/folder structure as it is described in the KB000188 article.

Great! Now why is this article longer?

Well, from what I’ve found, the code in the developer reference does not export the environment variables and so the Project.Save() method does not give you any environment information. This also results in a empty stringtables section when you use the Developer reference example code.

From what I can find out, the environment settings need to be added to the package itself.
Since the K2 api is so nice to code agains, this results in the following method:

private void AddEnvironment(DeploymentPackage package, string SelectedEnvironment) {
  EnvironmentSettingsManager envManager = new EnvironmentSettingsManager(true);
  envManager.ConnectToServer(this.K2ConnectionString);
  envManager.InitializeSettingsManager(true);
  envManager.Refresh();

  //Add environments + environment properties.
  foreach (EnvironmentTemplate envTemp in envManager.EnvironmentTemplates)
  {
    foreach (EnvironmentInstance envInst in envTemp.Environments)
    {
      //Add an environment to the package.
      DeploymentEnvironment env = package.AddEnvironment(envInst.EnvironmentName);
      foreach (EnvironmentField field in envInst.EnvironmentFields)
      {
        env.Properties[field.FieldName] = field.Value;
      }

      // Make sure the environment we select actually exists.
      if (envInst.EnvironmentName == SelectedEnvironment)
      {
        package.SelectedEnvironment = envInst.EnvironmentName;
      }
    }
  }

  //Cleanup
  envManager.Disconnect();
  envManager = null;

  //Final check of the selected environment
  if (package.SelectedEnvironment == null || package.SelectedEnvironment == string.Empty)
  {
    throw new Exception(string.Format("Could not find the environment you wanted to select ({0})", SelectedEnvironment));
  }
}

The above method adds the environment fields to the DeploymentPackage. The DeploymentPackage is part of the Project and if we deploy the project now, the environment settings are exported to the server. This also means when we call the .Save() method, the environment settings are stored inside the .msbuild script that the .Save() method produces.
The environment manager can be located in the SourceCode.EnvironmentSettings.Client assembly and the ConnectionString used is retrieved from the SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder object.

And voila, a deployment package is created using code!

Download!

I’m still working on a final version that creates the deployment package using a MS build task, this MSBuild task will be added to the TFS build so the TFS build on the build server creates the deployment packages. This is done by inheriting from the Task object.
The attached file is a example on how to do that. The code works, but i’m still having small issues to getting it work on the TFS server itself. Nevertheless, you can use the class to create your own command line tool to deploy the process.

Your VS2005 colors

So, i’ve been wanting to optimize the colors in VS2005 since i’ll be coding a little more in the comming months. I have a big 24″ wide screen from Dell and all the white light of VS2005 makes me crazy after 8 hours…

that sad, just check out this post at codinghorror.com!

I’m using Zenburn’s schema, it’s nice and darkish. I did change the contrast between colors a bit. In the default theme it is hard to see the difference between some items in VS2005, and that’s what the highlighting is for right? Well if you change them, don’t make it scream out, that’s what the themes are for, to make it easier on your eyes… After the color change i set my virtual machine to cleartype and added the nice Consolas font, ready to code!

WordPress Themes