Welcome to K2 Underground Sign In | Join | Help
K2 process errors after submitting an InfoPath form used by a Client Event

By default, when an InfoPath form is submitted, it will ask the user if he/she would like to save the form.  InfoPath also will then give a "form was submitted successfully" message.  This can potentially create problems for a form that is being used by a K2 client event.

When an InfoPath client event is used, the K2 process will automatically attempt to clean up (e.g. delete) the XML forms that it created for the client event.  However, it can't do that if the specific XML file is locked within SharePoint.  When a user takes too long to respond to these previously mentioned prompts, the lock on the file in SharePoint is still in place when the K2 process attempts to delete it.  Thus an error is generated and the process moves into an error state. 

It is very easy to correct the actual processes in error state as a result of this condition, just navigate to the Error node within K2 Workspace, select the process(es) and click Retry.  However this is an instance by instance remedy, it does nothing to prevent it from happening again in the future.

To avoid this situation, you can disable the prompting of these dialog boxes (and thus cause the file to be locked as soon as the form is submitted).  This can be done by opening the form within Design Mode:

To Turn off Submit Messages:
- Tools -> Submit Options
- Tick the “Allow users to submit this form” checkbox
- Click Advanced
- Untick the “Show success and failure messages” checkbox

To Turn off Save Option:
- Tools -> Form Options
- Select the Open and Save category
- Untick the “Save and Save As” checkbox

by Bob | 0 Comments

Creating a quick AD utilities service object with the Dynamic Assembly Service

David Loomis really outdid himself when he posted the Dynamic Assembly Service to Blackmarket. Within the project location you will find an installer which is basically all that you need to get things rolling.  The Dynamic Assembly Service is a service object that you can use to call methods from any class library dll that you can access on the BP server. 

In my case, I needed the ability to create an AD user.  The out of the box AD service provides many read and list methods with Active Directory, but at the time of this writing doesn't provide everything you would need for handling user on-boarding or off-boarding.

Some time ago, a custom event template called the Active Directory Event Template was written for K2 2003.  While it continues to be a work in progress and the gui looses it's values during an edit (making it difficult to use in a real development scenario), it does generate beautiful AD code which saves us from having to weed through countless lines of horrid AD code available from various sources.  In the case of this example, I opened K2 2003 Studio, ran the Active Directory custom even template, generated my AD code, and copy/pasted into a class library for use by David's service object.  I did have to add/tweak some method arguments and follow them through the code, but spent less than 30 minutes adapting the code to suit my scenario.  (tip:  when running the Active Directory Template you'll be able to click various AD properties and provide either a datafield or plain text to populate them.  I populated each of them with plain text that I could find easily in the code once it was generated.  for example:  for the cn property I used the value  strVariablecn;  for the samaccountname I used the value strVariablesamaccountname -- it was a snap adapting the code to my method by looking for the prefix strVariable everywhere)

Within the attached zip file you will find two visual studio solutions:
-  ADToolsExample contains the class library that I built for a simplistic user creation method.  It takes in several of the possible arguments available when creating a user and returns a boolean value noting the success or failure of the user creation.   In my case I took the compiled dll out of the solution's debug directory and copied it into the "c:\program files\k2 blackpearl\host server\bin" directory.   Then within Workspace > Management Console > SmartObjects > Services, I created a new service instance of the Dynamic Assembly Service and pointed it to the path\filename where the dll now resided.   (tip: You shouldn't have to open this solution or recompile, you should be able to simply copy the dll from the solution's obj/Debug directory to C:\program files\k2 blackpearl\host server\bin )
- ADToolsExample-K2Project contains 2 files:
        1)  A smartobject called CreateADUser.sodx:  This smartobject surfaces the newly created Dynamic Assembly Service ADTools instance's create method.  Note that Service Instance GUIDs are embedded into already created SmartObjects, so you will need to delete/recreate the service method within the included SmartObject before deploying.  If you don't, you will receive a "parent object" error during deployment.
        2)  A test workflow called TestCreateADUser.kprx: This workflow contains one primary activity which essentially runs the SmartObject call to create the AD User.   If the AD user creation fails, the workflow loops back to the client event allowing you to adjust your values.  (**tip:  when you start the process instance - presumably from workspace) you will need to provide the LDAP path in the following format:  LDAP://CN=Users,DC=Denallix,DC=com  substituting Denallix and com for your particular domain) (**another tip: when creating your first user, if you are unsure what cn and samaccountname refer to, add that as a text suffix onto your value, for instance  JosephCN and JosephSAM)

Essentially however, you can probably start from scratch following the general logic of my approach and achieve success within a few hours.  The point here is that with David's Dynamic Assembly Server and Tim's Active Directory Event Template, you have everything you need to create a functioning and tested ActiveDirectory Service Object in very short order.

by Joseph | 0 Comments


Attachment(s): ADToolsExample.zip

How to use the SmartObject .NET Data Provider on a Server that Does Not have K2 Installed on it

To use the SmartObjects with the .NET Data Provider, you need to register the following .NET DLLs in the GAC (Global Assembly Cache): 

-SourceCode.Data.SmartObjectsClient.dll
-SourceCode.Categories.Client.dll
-SourceCode.HostClientAPI.dll
-SourceCode.Framework.dll
-SourceCode.SmartObjects.Client.dll
-SourceCode.SmartObjects.Management.dll

You must also add the following entries to the machine.config:
(C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\machine.config - for example)

<configuration>
   <configSections>
... 
      <section name="SourceCode.Data.SmartObjectsClient" type="System.Data.Common.DbProviderConfigurationHandler, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
... 
   <configSections>
<system.data>
<DbProviderFactories>
...
<add name="K2 SmartObjects Data Provider" invariant="SourceCode.Data.SmartObjectsClient" description=".NET Framework Data Provider for K2 SmartObjects" type="SourceCode.Data.SmartObjectsClient.SOClientFactory, SourceCode.Data.SmartObjectsClient, Version=4.0.0.0, Culture=neutral, PublicKeyToken=16a2c5aaaa1b130d" />
...
</DbProviderFactories>
</system.data>
....
</configuration>

by Bob | 0 Comments

How to delegate/undelegate a worklistitem via API

K2 blackpearl provides the ability to delegate a task to another user.  This differs from a Redirect.  A Redirect will reassign a task that was assigned to me to another user.  A Delegate will permit someone else, in addition to myself, to action my task.  This is built into out of box K2 worklists, but if you need to build this into your own application it can be implemented via the SourceCode.Workflow.Management API. 

Below is some code providing some guidance on how it can be done:

        private void AddOrDeleteUserFromWorklistItem(int nProcInstID, 
                        int nActID, 
                        int nActInstID, 
                        int nActDestID, 
                        int nEventID, 
                        string strUserName, // must include the appropriate security label ('K2:' or 'K2SQL:')
                        bool bAddUser) // TRUE = add the user rights for this task; FALSE = delete the user rights this task
        {  
            // connect to K2 Management.  K2 server admin permistions are required.
           // this sample assumes the current user has such permissions.  if not, force the connection
           // via a connection string, to the appropriate user
            WorkflowManagementServer oK2Conn = new WorkflowManagementServer("localhost", 5555);
            oK2Conn.Open();

            // create an Actioner object for this user
            Actioner oActioner = new Actioner();
            oActioner.ActionerType = ActionerType.User;
            oActioner.Name = strUserName;

            // locate the currently assigned rights for this worklist item
            ActionInstanceRights arRights = oK2Conn.GetActionInstanceRights(nProcInstID,
                                                                                nActID,
                                                                                nActInstID,
                                                                                nActDestID,
                                                                                nEventID);

            // check to see if this user already exists for this task
            ActionInstanceRight oExistingRight = null;
            foreach (ActionInstanceRight oRight in arRights)
            {
                if (oRight.Actioner.Name.ToUpper() == strUserName.ToUpper())
                {
                    oExistingRight = oRight;
                    break;
                }
            }

            // now attempt to add or delete user rights for this task

            if (bAddUser)
            {
                // create a new user for this task if one doesn't alreday exist
                if (oExistingRight == null)
                {
                    ActionInstanceRight oActInstRight = new ActionInstanceRight();
                    oActInstRight.ProcInstID = nProcInstID;
                    oActInstRight.ActID = nActID;
                    oActInstRight.ActInstID = nActInstID;
                    oActInstRight.ActInstDestID = nActDestID;
                    oActInstRight.EventID = nEventID;
                    oActInstRight.Actioner = oActioner;
                    oK2Conn.CreateActionInstanceRight(oActInstRight);
                }
            }
            else
            {
                // delete a user for this task if it already exists
                if (oExistingRight != null)
                {
                    ActionInstanceRights arTmpActInstRights = new ActionInstanceRights();
                    arTmpActInstRights.Add(oExistingRight);
                    oK2Conn.DeleteActionInstanceRights(arTmpActInstRights);
                }
            }
        }

 

 

 

by Bob | 0 Comments

How to retrieve the value from an XML field element

Sometimes one needs to get the value from an element within a K2 XML field.  This is usually done within a Server Event.  In the below example, I needed to retrieve a URL from a K2 generated XML field.  This specific example should work if you do not expect repeating values in the Items node.  For scenarios where repeating values are selected, then the XPath would need to be changed.


            // get the full XML from the K2 XML field
            string strXML = K2.ProcessInstance.XmlFields["SPEventsField"].Value;
            // set the XPath to the desired node
            string strXPath = "/Items/Item/URL";
            // use the helper class to retrieve the value for this element (as set in the XPath) for this XML
            string strValue = SourceCode.Workflow.Common.XmlHelper.GetXPathValue(strXML, strXPath);

 

 

by Bob | 3 Comments

Super Simples Notes Field with History in Infopath

It is common for an Infopath based process to require the use of a notes field that allows users to add to but not edit the notes history.  This is a snap with  couple of rules and one hidden text field:

1)  Create three text boxes and one button.  All text boxes should be multi-line.  In my case I named them:
      a.  NewNote
      b.  NotesHistory
      c.  CarriageReturn
      d.  SaveNewNote (button)

2)  The NotesHistory textbox should be the largest (perhaps 8 or 10 lines; the full width of the form) as it will display all prior notes.  This text box should be set to read-only in it's control properties.

3)  I typically make the NewNote textbox large enough for two lines of text (the full width of the form)

4)  The SaveNewNote button should be placed after the NewNote textbox and before the NotesHistory text box accompanied by a note to the effect of, "New notes will only be saved by clicking this button"

5)  The CarriageReturn text box should be hidden and have it's default value set in the function builder to:
      a.  Type an open quotation      "
      b.  Hold down the Control key and hit the Enter key
      c.  Type a close quotation       "

6)  Finally configure a rule for the SaveNewNote button including the following Actions:
      a.   Set a Field's Value  >  Field:  NotesHistory  > Value (function builder) : concat(today(), ": ", NewNote, CarriageReturn, NotesHistory)
          (Note that NewNote, CarriageReturn and NotesHistory are inserted using the "Insert Field or Group" button)
      b.   Set a Field's Value >  Field:  NewNote  > Value: 

This button will successfully update the NotesHistory with the new note at the top preceded by the current date.

Happing Infopathing,
Joseph

 

by Joseph | 0 Comments

How to Retrieve Process Instance Reporting Data

Often times the data that appears within the out of box K2 Workspace reports is needed to be accessed directly from some other custom application.  The standard K2 reports are based upon a number of SmartObjects that are built in to the K2 product, thus if one needs access to this data, those exact SmartObjects can be used.

The first thing is to know what built in SmartObjects are available and understand what data you are looking for.

You can see the list of SmartObjects via the K2 Object Browser:

image

So if we wanted to get the data from the Activity Instances Report (as show below):

image

In this example, to retrieve the data used by this report, the SmartObject we want to use is the "Activity Instances".  To code against this SmartObject, the SourceCode.SmartObjects.Client API is used.

Below is a code sample where I have created a method that accepts a SmartObject name and Process Instance ID and retrieves the data.  For the purposes of simplicity of this demo, I simply am doing a Response.Write to the ASPX page instead of doing something fancier with the data.

private void GetReportData(string strSmartObjectName, int nProcInstID)
{
    SourceCode.SmartObjects.Client.SmartObjectClientServer serverName = new SourceCode.SmartObjects.Client.SmartObjectClientServer();
    SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder connectionString = new SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder();

    // build a connection string
    connectionString.Authenticate = true;
    connectionString.Host = "localhost";
    connectionString.Integrated = true;
    connectionString.IsPrimaryLogin = true;
    connectionString.Port = 5555;

    // open a K2 Server connection
    serverName.CreateConnection();
    serverName.Connection.Open(connectionString.ToString());
    try
    {
        // get a handle to the SmartObject
        SourceCode.SmartObjects.Client.SmartObject smartObject = serverName.GetSmartObject(strSmartObjectName);
        // specify which method will be called
        smartObject.MethodToExecute = "List";
        // specify input parameters for the method
        smartObject.Properties["ProcessInstanceID"].Value = nProcInstID.ToString();
        // call the method
        SourceCode.SmartObjects.Client.SmartObjectList oSmOList = serverName.ExecuteList(smartObject);

        // iterate each smartobject in the collection and do something with the data
        foreach (SourceCode.SmartObjects.Client.SmartObject oSmO in oSmOList.SmartObjectsList)
        {
            // for the purposes of this example, i'm just dynamically building out a string
            string strRecord = "";
            foreach(SourceCode.SmartObjects.Client.SmartProperty oProp in oSmO.Properties)
            {
                strRecord += oProp.Name + ": " + oProp.Value.ToString() + ",  ";
            }
            Response.Write(strRecord + "<br />");
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
        // close the connection
        serverName.Connection.Close();
    }

}

When I invoke this method in with the following parameters:

    GetReportData("Activity_Instance", 4782);

I see the following (which from a data point of view matches up to the Workspace report):

image

Obviously this is just a simple example showing how to interact with these SmartObjects.  There are other things that one can do with them.  For one, if you don't want to write any code you could use the SmartObject .NET data provider to bind the results to a control (perhaps a topic for a later post).  You can also integrate these objects into InfoPath forms if desired.

Additionally a very useful way to interact with these SmartObjects (actually any SmartObjects) to get a better understanding of what data and functionality they expose is via the 'Amazing SmartObject Tool' which is freely available on the K2 blackmarket (http://k2underground.com/k2/ProjectHome.aspx?ProjectID=47).  I highly recommend this tool for anyone developing or working with SmartObjects.

by Bob | 0 Comments

Customizing Out of the Box K2 Reports and Replacing the Originals

There are multiple articles on exporting SQL Reporting Services “.RDL” files from SQL for customization and re-import using the K2 workspace interface:

http://kb.k2workflow.com/articles/kb000189.aspx

http://k2underground.com/blogs/pitchblack/archive/2008/05/15/extending-a-k2-report-in-visual-studio.aspx

However, you may desire to update all of the OOTB (out of the box) reports with your own look and feel.  If this is the case, using the import feature within workspace may become tedious as opposed to just deploying a project containing 17+ reports.  These tips should help you in addition to the published articles if you plan on getting off of the beaten path covered by the aforementioned articles.

To accomplish updating the OOTB reports there are a few tips to keep in mind:

1)      All of the reports viewed from K2 Workspace are stored in the “SQL Reporting Services root \ Standard Reports \ Hidden”  location.  The Process Overview report that resides in the root of Standard Reports is a simplistic copy that is only used when viewing the reports from http://server/reports and it is NOT used by K2 Workspace.

2)      When you export an rdl file from within http://server/reports the file’s name will include underscores instead of spaces.  Once you have imported the rdl into your Visual Studio project always rename them with spaces instead of underscores.  If you do not rename the file with spaces, deploying the project will create duplicate names on the server containing the underscores and K2 Workspace will not use your new reports.

3)      Within your visual studio project properties (Solution Explorer > Right-click your project name > properties), you will require the following two settings changes

a.       TargetReportFolder should be set to:             Standard Reports/Hidden

b.      TargetServerURL should be set to:                   http://your servername/reportserver

****Danger Will Robinson, don’t forget, before you set that target report folder to overwrite the OOTB reports, have a backup of the original rdl file on hand.

4)      You may find that the reports complain about their datasource missing.  By default you can add a dummy datasource to your Visual Studio reports project and the datasource will not overwrite the one already on the report server.  Here is the info you’ll need to setup a dummy:

a.       Name:          BLACKPEARL

b.      Type:             SOURCECODE

c.       Connection String:                   Server=yourK2servername;Port=5555;AutoAlias=false

d.      Set Credentials to:                   “Use Windows Authentication (Integrated Security)”

5)      Beware before deleting portions of these reports in order to recreate.  You will find object references buried in the charts for everything from data to colors (no you cannot globally change the green K2 colors used in the 3d charts)  I suggest creating any new charts or entries alongside the existing ones before you blow away the stock design.

6)      Probably a common VS report designer question, but I was surprised that depending on the tables within these reports, sometimes it was easier to set a background image for a row and sometimes it was only possible for the whole table.  This is likely because of what table controls were used in the original reports’ design.

I’ve attached a Visual Studio project that contains customized versions of the base K2 reports (not including any of the charts).  I’ve just updated the look and feel here, but this using this project will save you the time of exporting a dozen rdl files.  Although I'm not noting any reporting changes in the recent 803 release, keep in mind that this project is based on the reports available in the K2 BlackPearl SP1 release.

by Joseph | 0 Comments


Attachment(s): Hidden.zip

Activity Destination Users based upon a Repeating XML element

One of the lesser known new features of the expanded Destination Rule functionality of K2 blackpearl is the ability to very easily base Destination Users from a repeating XML node that contains a User ID. In K2 2003 this required a bit of coding in the Destination Rule for each Activity for which you wished to do something like this. With K2 blackpearl this is now a drag and drop experience in the process design canvas.  This can be a very useful feature in a schema driven process development effort.

In this example I will show how an InfoPath form can be leveraged to capture a list of users that will then become the basis for the next step’s Destination Rule.

1. Create an InfoPath form and InfoPath integrated K2 process as normal.

2. Design the form as per normal.

3. Add a new repeating Text (string) element to the Main data source (in this example I’ve named the field “Reviewers”)

clip_image002[8]

4. I then drag out the repeating text field onto the desired InfoPath view:

clip_image004[19]

5. In this example I did the following:

  • a. Selected “Repeating Table” when prompted
  • b. Changed the text box to a Drop Down List
  • c. Added following content to the Drop Down List to allow users to select reviewers from an easily readable drop down list (with actual Domain\userID as the hidden value):

clip_image006[8]

6. Save and close the InfoPath form.

7. You should now be back at the Process design canvas and see the following message:

clip_image008[8]

8. Click OK and then Finish

9. In this example, I then want to change the destination rule for the desired activity:

clip_image010[8]

10. With the Destination Rule window open Navigate to the Destination Users window:

clip_image012[8]

11. Click the Add button

12. Click the ellipse (“…”)button

13. Navigate to the Process/Activity Data tab within the Context Browser then expand the XML Fields node until you find the “Reviewers” node.

14. Either drag and drop the Reviewers node or select and click the Add button.

clip_image014[8]

15. Once this has been selected it should look like:

clip_image016[8]

Now if we deploy the process and then open the form we can select multiple users:

clip_image018[8]

When the process hits the Destination we setup it will create task rights for each user that appears in that reviewer repeating section. If we review the Out of the Box “Activity Instance Destinations Report” for this instance we can see that the three users we selected in the InfoPath form are now destination users for this activity.

clip_image020[8]

by Bob | 1 Comments

Differences between Stop, Start and Sleep

A question that is sometimes asked is "what is the difference between stopping, starting and sleeeping within K2?".

Start and Stop reside at the Process Instance level and accessible via K2 Workspace Management tooling or SourceCode.Workflow.Management API (and thus require Admin permission).  “Stop” will put an active process instance into a status of “Stop” which will prevent it from executing (meaning client events can’t be actioned, esclations won’t fire…).  The process instance is still alive in the sense that it resides in the K2Server/transactgion database (as opposed to just K2ServerLog/reporting database).  You can equate this to a “pause”.  “Start” will essentially resume the process; kind of like pressing play after pausing a movie. 

Sleep on the other hand is functionality available in the K2 Worklists or SourceCode.Workflow.Client API that operates at upon individual worklist item (NOT the process instance). When you sleep a worklist item it simply changes the status for that specific worklist item to a status of “Sleep”.  When you sleep it you must give K2 a duration or specific datetime to awake it at so it doesn’t sleep forever.  Thus, as I understand it Sleep does the following:

1.            Changes status of worklistitem to “Sleep”
2.            Will eventually be automatically be awoken by K2

So what does this mean?  By default, the OOTB worklists like workspace and web part filter out worklist items in a status of sleep so they won’t appear until they are awoken.  However you can change the filter to display these if you’d like.  As for escalations, I don’t think Sleep has any impact on escalations.  In my testing here activity and event escalations still fire for a worklist item I have in sleep. 

by Bob | 0 Comments

One way to retrieve Sharepoint List Attachments through a SmartObject

There are K2 Service Object types for a multitude of resources.  Recently a dynamic web service ServiceObject has been published in beta form that allows K2 to enumerate and access simple web services through SmartObjects.  This document covers setting up a custom web service that provides access to Sharepoint List Attachments for the Dynamic Web Service SmartObject Service to consume.

You will need to download and setup the Dynamic Web Service SmartObject Service available on BlackMarket in order to make use of the web service covered in this document.

This project is limited to one-dimensional arrays of data being returned, but this code could be adapted to return an xml string instead to provide access to record data.

The step by step instructions were developed and tested on the base Training VPC and include instructions for IIS configuration in addition to all code required to create the simple web service.

This code is featured within the attached document, as you can see it isn't hard to write a simple web service:

        public String[] ListAttachments(string strListName, string strListItemID)
        {
            SPWeb spWeb = null;
            SPList spList = null;
            SPListItem spListItem = null;
            int nListItemID = 0;
            int nRowValue = 0;
            String[] strFiles = null;

            //cast the incoming ID to int
            nListItemID = Convert.ToInt32(strListItemID);

            try
            {
                //define the Overall Sharepoint Site URL and the "web" within (often blank)
                //Customize the MOSS Site URL if necessary in the web.config <appSettings> section
                SPSite spSite = new SPSite(System.Configuration.ConfigurationManager.AppSettings["MossServerURL"]);
                spWeb = spSite.OpenWeb();

                if (spWeb != null)
                {
                    //open the sharepoint list
                    spList = spWeb.Lists[strListName];

                    //open the list item
                    spListItem = spList.GetItemById(nListItemID);

                    //populate each attachment into the strFiles array
                    strFiles = new String[spListItem.Attachments.Count];
                    foreach (string strAttachment in spListItem.Attachments)
                    {
                        strFiles[nRowValue] = spListItem.Attachments.UrlPrefix + strAttachment;
                        nRowValue += 1;
                    }
                }               
            }
            catch
            {
                //if the Sharepoint site, List or Attachments cannot be found, empty string array
                strFiles = new String[0];
            }
            return strFiles;
        }

by Joseph | 0 Comments


Attachment(s): List Attachments from Sharepoint with a Simple Custom Web Service.doc

How to generically iterate through associated SmartObjects

I was asked recently how one can load associated SmartObjects for a given SmO (SmartObject).  I worked up the attached Console app as a quick example to provide guidance around this.

In my environment I have an the following SmartObjects:

 

-          Order

o   OrderID

o   CustomerID

o   ProductID

o   Qty

o   Price

-          Customer

o   CustomerID

o   CustomerName

o   Address

-          Product

o   ProductID

o   ProductionName

 

I have setup the following associations within the Order Smo:

1.       Customer SmO : Order.CustomerID -> Customer.CustomerID and

2.       Product SmO : Order.ProductID -> Product.ProductID

 

I then created a simple console app that utilizes our API, to open up a specific Order, then determine what it’s associated with then open up those associated SmO.  This is all done generically…. By that I mean nowhere in my code do you see me hard coding in knowledge that an Order is linked to a Customer or Product.  My console app then simply spits all the data for each SmO back to console window in order to prove it’s working.  NOTE:  my code makes a few assumptions (like only one property makes up the primary key as well as the default Load method).  With a little work it could be made flexible enough to be more robust.

Below is the majority of the code in case you don't feel like downloading the attachment:

        private void GetSmartObject(string strSmoName, string strSmoID)
        {
            string _serverName = "localhost";
            string _domain = "k2demo";
            string _user = "administrator";
            string _password = "k2pass";

            SmartObjectClientServer serverName = new SmartObjectClientServer();

            SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder connectionString = new SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder();

            // build a connection string
            connectionString.Authenticate = true;
            connectionString.Host = _serverName;
            connectionString.Integrated = true;
            connectionString.IsPrimaryLogin = true;
            connectionString.Port = 5555;
            connectionString.UserID = _user;
            connectionString.WindowsDomain = _domain;
            connectionString.Password = _password;
            connectionString.SecurityLabelName = "K2"; //the default label

            // open a K2 Server connection
            serverName.CreateConnection();
            serverName.Connection.Open(connectionString.ToString());

            try
            {
                // get a handle to the 'Employee' SmartObject
                SmartObject smartObject = serverName.GetSmartObject(strSmoName);

                // specify which method will be called
                smartObject.MethodToExecute = "Load";

                // specify input parameters for the method
                smartObject.Properties[0].Value = strSmoID;

                // call the method
                smartObject = serverName.ExecuteScalar(smartObject);

                // create a string to hold the data
                string strRecord = "(*** " + strSmoName + " ***) ";

                // iterate all the properties in the SmO and build out the string (which will eventually be displayed in console)
                foreach(SmartProperty oProp in smartObject.Properties)
                {
                    string strPropName = oProp.Name;
                    string strPropVal = oProp.Value;
                    strRecord += ", " + strPropName + ":" + strPropVal;
                }
                // write it to the console
                Console.WriteLine(strRecord);

                // now iterate any associated smartobjects we may have
                foreach (Association oAssoc in smartObject.Associations)
                {
                    // grab the name of the association
                    string strAssociationName = oAssoc.Name;
                    // grabe the name
                    string strAssociatedSmartObjectName = oAssoc.SoName;

                    // create an array to store all the properties that make the association
                    // (e.g. the keys of each SmO that make up the join)
                    ArrayList arAssociatedProperties = new ArrayList();
                    foreach(AssociatedProperty oAsPr in oAssoc.AssociatedProperties)
                    {
                        arAssociatedProperties.Add(oAsPr.Name);
                    }

                    // recursively call this same method to run through the association SmO
                    // NOTE: for this example i am assuming only one property in the key and association
                    // a real project handle an unknown number of properties
                    GetSmartObject(strAssociatedSmartObjectName, smartObject.Properties[arAssociatedProperties[0].ToString()].Value);
                }
            }
            catch (Exception ex)
            {
            }

            finally
            {
                // close the connection
                serverName.Connection.Close();
            }       
        } 

 

 

 

by Bob | 0 Comments


Attachment(s): SmartObject Associations.zip

How to Decrypt a K2Hostserver.config file in Order to Add a New Connection String

In order to keep connection strings secure within the K2 config files, the K2 host server (K2HostServer.exe) will encrypt any plain text connections strings it finds when it starts up.  While this does prevent it from being human readable, it also makes it difficult if you need to manually change one of the parameters in the connection string, or add a new connection string for use within a custom K2 component.

I recently needed to add a custom connection string to the K2 server config file.  After a bit of research and trial and error I was able to figure out how to use the .NET Framework aspnet_regiis utility to decrypt the K2 server config file.  Once decrypted, I then added the connection string.  The K2 server will then automatically encrypt it the next time it starts up.

Below are the steps I performed.  If anyone knows a better way, please share.  The below steps could easily be put into a batch file to make it more repeatable.

1.       Create a new directory on the local sever (in this example C:\Temp\K2Config was created)

2.       Create a backup copy of the K2 config file ‘C:\Program Files\K2 blackpearl\Host Server\Bin\K2HostServer.config’ for safe keeping

3.       Copy the file ‘C:\Program Files\K2 blackpearl\Host Server\Bin\K2HostServer.config’ to this new directory

4.       In this new directory, rename the ‘K2HostServer.config’  to ‘web.config’

5.       Open the Windows command prompt

6.       Change Directory in the command prompt to ‘C:\Program Files\K2 blackpearl\Host Server\Bin\K2HostServer.config’

7.       Run the following command:

 

aspnet_regiis -pdf "connectionStrings" "C:\Temp\K2Config"

 

8.       You should see:

9.       Rename the web.config file to ‘K2HostServer.config’

10.       Edit this K2HostServer.config file and add a new connection string

11.       Save the config file.

12.       Copy this file back to the original location (‘C:\Program Files\K2 blackpearl\Host Server\Bin’)

13.       Restart the K2 service

14.       The K2 service should encrypt it upon service restart.

 

by Bob | 0 Comments

Misc K2 blackpearl Connections Strings

When creating a programmatic connection to K2 (typically via either the SourceCode.Workflow.Client or SourceCode.Workflow.Management APIs) there are a number of connection options that can be used.  I wanted to highlight a couple of common scenarios here.

First of all, the easiest way to create a K2 connection string is via the SCConnectionStringBuilder (SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder).  I will show 3 common connection string builder scenarios. 

Some basic assumptions before we begin:

   - These examples will assume the default blackpearl security lables are in place, where the 'K2' security label equates to Active Directory and the 'K2SQL' security label works with the SQL User Manager.

  - Connection strings to the workflow server are shown (as denoted by the port = 5252) for use when making a Workflow.Client connection.  If you wish to connect to Workflow.Management or SmartObjects.Client, then you will use port 5555.

1.  Make a connection under the currently Windows Identity that is running this code:

        SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder builder =
                new SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder();

        builder.Authenticate = true;
        builder.Host = "localhost";
        builder.Port = 5252;
        builder.Integrated = true;
        builder.IsPrimaryLogin = true;
        builder.SecurityLabelName = "K2";

2. Force the connection under a specific AD Identity

(Note: a Domain Name, User ID and Password must be provided)

        SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder builder =
                new SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder();

        builder.Authenticate = true;
        builder.Host = "localhost";
        builder.Port = 5252;
        builder.Integrated = true;
        builder.IsPrimaryLogin = true;
        builder.SecurityLabelName = "K2";
        builder.WindowsDomain = "k2demo";
        builder.UserID = "carolm";
        builder.Password = "k2pass";

3.  Make a connection that authenticates against the K2 SQL User Manager

        SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder builder =
                new SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder();

        builder.Authenticate = true;
        builder.Host = "localhost";
        builder.Port = 5252;
        builder.Integrated = false;
        builder.IsPrimaryLogin = true;
        builder.SecurityLabelName = "K2SQL";
        builder.UserID = "jdoe";
        builder.Password = "k2pass";

Once you have the connection string builder populated you can use it in the various API connections.

For example, some common ones:

1. SourceCode.Workflow.Client (for workflow interaction)

        NOTE:  the port used here would be 5252

        SourceCode.Workflow.Client.Connection oConn = new SourceCode.Workflow.Client.Connection();
        oConn.Open("localhost", builder.ConnectionString);

2.  SourceCode.Workflow.Management (for administrative functionality)

        NOTE:  the port used here would be 5555

        SourceCode.Workflow.Management.WorkflowManagementServer oConn = new SourceCode.Workflow.Management.WorkflowManagementServer();
        oConn.CreateConnection();
        oConn.Connection.Open(builder.ConnectionString);

3.  SourceCode.SmartObjects.Client (for SmartObject interaction)

        NOTE:  the port used here would be 5555

        SourceCode.SmartObjects.Client.SmartObjectClientServer oConn = new SourceCode.SmartObjects.Client.SmartObjectClientServer();
        oConn.CreateConnection();
        oConn.Connection.Open(builder.ConnectionString);

by Bob | 0 Comments

How to work with XML fields in K2.net 2003
I found the attached document that I worked up to show how to use K2.net 2003 XML fields in an ASP.NET app as well as within a Server Event within the process.  I still get asked this question a bit in regards to K2.net 2003 so I thought it worth posting.  I have not updated this yet for blackpearl, but the concept should be very similar.

by Bob | 0 Comments


Attachment(s): Example - How to Work with K2 XML Fields.doc

More Posts Next page »
Powered by Community Server (Commercial Edition), by Telligent Systems