Tag Archives: K2 blackpearl

Use SmartObject upload file to SQL

K2 SmartObject can read and store files into SQL table via its File property. Files to be stored into SQL table will be automatically converted to base64 encoded string and when retrieved from SQL, it will be automatically converted back to binary.

In this article, we will see how to setup and use SmartObject upload file to SQL and test the function in  K2 smartforms.

Use SmartObject upload file to SQL

  1. We start by setting up the SQL table. The column that will be storing the file content will be created as a nvarchar(max) type.Create table and file content column
  2. If the Service Instance to the SQL database (in this case “FileUploadTest”) has not been created yet, create it. If the Service Instance already exists, refresh the Service Instance. Make sure the new table and its properties are available.Create or refresh Service Instance
  3. Right-click on the [dbo].[FileUpload] Service Object and select Create SmartObject.Create Smart Object function
  4. Give the new SmartObject an appropriate Name and click on Publish SmartObjectCreate the file upload SmartObject
  5. You should find the SmartObject at the Category it is created above.
    The created FileUpload SmartObject
  6. Next, find the SmartObject in K2 Designer and Edit it. Edit the SmartObject in K2 Designer
  7. Select the property that is supposed to hold the file content (i.e. fileContent in this case) and click on the Edit button.

    306-step7
    Edit the SmartObject’s fileContent property
  8. Now, this is the most important step. Change the Type to File and click OKChange the fileContent property type to File
  9. Lastly, Click Finish to complete editing the SmartObject and you are done.Click Finish to save

Testing

  1. Now, to test the SmartObject, we will use the Generate View feature. Right-click on the SmartObject and select Generate Views.
    Generate the View from the FileUpload SmartObject
  2. Select the checkbox under Item and click OK. This will generate an Item View. Select to create an Item View
  3. Select the newly created Item View and click on RunRun the generated View
  4. In the form, fill in the file Name, select a file in file Content field and click Create. This will create the record in SQL. Try create a record
  5. Take note of the generated ID field value and check against the SQL table created earlier. You will find the newly created record with the file content. Verify that the record is created in SQL
  6. To verify that the file was indeed saved successfully into the table, refresh the View used earlier, fill in the ID field value and click on Read button to load the file. Click on the file to download it and make sure it can be open in the appropriate application. Use the View to download the file to verify that it's working as expected

And that’s all on how to use SmartObject to upload file to SQL.

Have fun!

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!!

 

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!

 

Claims Auth with K2 Windows STS

There are times when you need to provided a dedicated login page to your K2 smartforms Forms or even embed the K2 smartforms Form into your custom ASP.NET website (iFrame). The main issue with these approaches is that K2 smartforms uses Claims Authentication with its K2 Windows STS and thus your users may need to log into your custom ASP.NET website, then log into K2 again, which is not a pretty solution.

This article will show you how to configure your ASP.NET website to authenticate against K2 Windows STS for a seamless login experience.

Note: This procedure is for integration with K2 Windows STS Issuer only. To integrate with K2 Forms STS requires additional work, which will be discussed in a future post.

Add Site Realm and Audience information to K2

  1. Log into K2 Designer and navigate to the Manage Site Realms Form. (All Items > System > Management > Security > Forms > Manage Site Realms)

    140-manage_site_realms
    Path to Manage Site Realms Form
  2. Run the Form.

    140-Run_Form
    Run the Manage Site Realms Form
  3. Click on the New button under the Realms section.

    140-New_Realm
    Add a new Realm
  4. Fill in the URI, Reply URI and select K2 Windows STS for Linked Issuers. Click OK.
    140-New_Realm_Details
    New Realm details
      • URI: This is the IIS website URL for your ASP.NET Web Application. If it is a sub-site, e.g. K2/CustomSite, then you will need to include the full URL path e.g. http://k2blackpearl.somewhere.com/CustomSite/
      • Reply URI: This is the URL that will be called by K2 Issuer. If your site is the root IIS website, then pass in a “/”. If it’s a sub-site, e.g. K2/CustomSite, pass in the sub-site path will do. e.g. “/CustomSite/“.
      • Home Realm: No idea yet. This is something I’ll need to find out more.
      • Linked Issuers: We are authenticating with K2 Windows STS, so obvious choice to choose (=

    IMPORTANT: For both URI and Reply URI, the trailing forward-slash (/) is very important. In earlier versions of K2 blackpearl, the slash is assumed to be always present and thus will throw a “Index and length must refer to a location within the string.” error.

  5. Leave the K2 Designer open for now.

IIS Website Application Pool

Make sure your website’s Application Pool is running .NET Framework v4.0 and Managed Pipeline Mode = Integrated.

Visual Studio Web Application Configuration

Update Web Application’s Web.Config

Download and copy WindowsSTS_web.config to your website’s web.config file. Run through the following sub sections to update the config file.

Update WindowsSTS Thumbprint value
  1. Go back to K2 Designer, navigate to and run the Manager Issuers Form.  (All Items > System > Management > Security > Forms > Manage Issuers).

    140-manage_issuer
    Path to Manage Issuers Form
  2. On the Form, copy the Thumbprint value for K2 WindowsSTS record. The Use For Login value should be True here, since we are going to authenticate with it.

    140-manage_issuer_thumbprint
    Getting K2 WindowsSTS Thumbprint
  3. Open the web.config file updated earlier and change the thumbprint attribute value on the path configuration / system.identityModel / issuerNameRegistry / authorithy / keys / add.

    140-WindowsSTS_Thumbprint
    Updating WindowsSTS thumbprint value
  4. Save the web.config file. Leave the K2 Designer open. You will need to make use of this form again in the next section.
Update Federation Configuration

In the web.config file, go to the section configuration / system.identityModel.services / federationConfiguration.

If your K2 smartforms is configured for HTTPS protocol, you will need to update the requireSsl and requireHttps attributes to true.

140-SSL_Flags
SSL/HTTPS flags

Next, you will need to update the issuer, realm and reply attribute values and save the web.config file.

140-issuer_realm_reply
Issuer, Realm and Reply attributes

 

  • issuer: This is your K2 WindowsSTS issuer URL. Go back to your K2 Designer Manager Issuer Form that was opened earlier to copy the URL.

    140-Issuer_URL
    Edit this record to copy the URL easily
  • realm: This is the website URL used when you add the Realm to K2 in the earlier part of this article. In my example, my URL will be http://K2WindowsSTSLogin.domain.com/.
  • reply: This is the reply uri added earlier in this exercise. Important thing to note here is that the full URL is required. So meaning if it not replying to a sub-site, then the URL will be http://K2WindowsSTSLogin.domain.com/. If it is replying to the sub-site named “/site1”, then the URL will be http://K2WindowsSTSLogin.domain.com/site1/.

Note:

  • It is important that the trailing forward slash (/) is included for both the realm and reply attribute values. If not, you will get the error “Index and length must refer to a location within the string.” when your run the authentication later.
  • There should not be a trailing forward slash (/) for the issuer attribute value.
  • Since the authentication will be looking up the issuer, realm and reply URL, make sure that the web server machine is able to resolve the domain name or NetBios name.

Adding assembly references to your website

You will need to add the following assemble references to your website.

  • System.IdentityModelGAC
  • System.IdentityModel.ServicesGAC
  • SourceCode.Security.WebC:\Program Files (x86)\K2 blackpearl\Host Server\bin\SourceCode.Security.Web.dll
  • SourceCode.Security.Claims.Web –  C:\Program Files (x86)\K2 blackpearl\Host Server\bin\SourceCode.Security.Claims.Web

Add a Global.asax for your website

Download and copy the file content (Global.asax) to your global.asax.cs. This file contains the codes to manage Federation Authentication issues. It is not the perfect set, but solves most of my issues.

Add something to test

Now, to test that the Claims Authentication works and the federation token is recognized by K2, we need a test page.

In our test, let’s create a default.aspx WebForm and add a response.redirect method in the Page_Load method to go to your K2 Designer URL.

140-Page_Load_redirect
Default.aspx Page_Load method

Grand Finale – Testing Claims Auth with K2 Windows STS

Now, to run the test, publish your Web Application to IIS, open your web browser and navigate to your custom website. When the site loads, you will notice that it is redirected to your K2 WindowsSTS for authentication.

140-login-prompt
Redirect to K2 Windows STS for authentication

Go ahead to fill in the user name and password to login. You will notice that the authentication will be successfully and redirected to your reply uri. Your default.aspx page will be loaded and redirect to your K2 Designer site. There is no additional login at your K2 Designer site and it loads your credentials correctly!

140-K2_designer
K2 Designer logged in with your account credentials!

 

Have fun!

 

Troubleshooting

No connection could be made because target machine actively refuse it 127.0.0.1:5555

This error will occur when you have a distributed setup – blackpearl and smartforms server on different boxes.

Error on distributed setup.
Error on distributed setup.

To resolve this, make sure you have the HostName key in your ASP.NET web.config’s appSettings section. This key’s value should be your K2 blackpearl server/cluster’s FQDN.

140-hostnamefqdn
HostName key

Carry out an IIS reset and everything should work now.

Using K2 Workflow Client API

[Updated: 4/6/2015]: Added reference to usage of WorklistCriteria here.

The SourceCode.Workflow.Client assembly provides the access to interact with the K2 blackpearl Server in the context of a User. This means that the API will not be able to query, for example, for all Users who has a Worklist Item from a specific Process. You will need to use SourceCode.Workflow.Management assembly for this. This API, however will allow the current User to impersonate as any other User within K2, if the account has the Impersonate rights on the Workflow Server. We will see more about this. Now down to the basics.


K2 Workflow Client API

Adding a reference to the assembly
  1. SourceCode.Workflow.Client
  2. SourceCode.Hosting.Client

These 2 assemblies can be found in the following location:

  • GAC – This is if you are working from within the K2 sever
  • K2 blackpearl’s bin folder – If you have the client components installed on your machine. The default path is C:\Program Files (x86)\K2 blackpearl\bin.
  • K2 Host Server’s bin folder – If you are working from within the K2 server. The default path is C:\Program Files (x86)\K2 blackpearl\Host Server\bin.

Note: The API call is carried out via RPC, so it means that as long as you have the required DLLs with your application, you will be able to make the call even if you did not install the K2 Client Components on that machine.


 

Open a connection to K2 blackpearl server

To open a connection, you only need the following:

For the Open method, there are a couple of variations:

  • Open(string Server): This requires a server name that can be resolved by the DNS/Host File or an IP.
  • Open(string Server, string ConStr): The 2nd parameter provides a connection string information. See ConnectionSetup.ConnectionString property.
  • Open(ConnectionSetup setup): This requires a ConnectionSetup object. You can provide a different log in credentials here.

 

User Property

Once the connection is opened, the User property will show the current logged on account:

111-User_Property
Connection object’s User property

If you find that the User property does not match the current logged on User in your ASP.NET page, it means that your web.config file is not configured to impersonate the current logged on User. Make sure the following is present in your web.config file:


 

Impersonate another User

If the current logged on User has the Impersonate rights on the Workflow Server:

You can execute the following code to impersonate as any User within the K2 environment:


 

Closing a connection to K2 blackpearl server

When you are done with the connection, always remember to close it by calling on the Close or Dispose method. You should always wrap the connection in a Using block:

Or a try-catch and/or finally block:


 

Start a Process Instance

To start a new process instance (a.k.a new workflow instance),  you will need to create a ProcessInstance object first.

The path to process is a combination of the root project folder name, followed by any folders’ name till the process. So in the following example:

111-Project_process_path
K2 Designer for Visual Studio

111-Workspace_Process_path
K2 Workspace

The path will be “TestProject1\ModuleA\Process1”.

With the ProcessInstance object created, you will be able to update the Folio and process level Data Fields before the process instance starts. This procedure is optional.

When the necessary updates on the ProcessInstance object is completed, you will need the Connection object’s StartProcessInstance method call to kick start the process instance.

Note: The StartProcessInstance method runs asynchronously by default. If you need the method to be executed synchronously, pass a 2nd parameter “true”:


 

Opening a Worklist

A work list (task list) is a collection of work list items (task list items) that is assigned to the current logged on user. You need to call on the OpenWorklist method of the Connection object and it will return a Worklist object, which is a collection of WorklistItems. The following is a sample method call:

Note: The OpenWorklist method without any input parameter will return the entire collection of WorklistItems of the current User. This is not going to be efficient and very time consuming if the current User has thousands of tasks. To overcome this, we should use a Filter with the OpenWorklist method, which we will discuss in a separate article you can find here. [Updated: 4/6/2015]


 

Open WorklistItem

Now, the WorklistItem is a single task assigned to the current User. It has the information of the current process instance and also the Activity Destination Instance. This means that we can draw the following information from it (Just to name a few):

  • WorklistItem.ProcessInstance.Folio: The Folio of the current process instance.
  • WorklistItem.ProcessInstance.DataField[“myField”].Value: Get the process level data field.
  • WorklistItem.Data: The full URL of the task form.
  • WorklistItem.Actions: The configured Actions for this task.

To open a work list item, it means getting K2 to assign a slot to this user. This method will also validate if the current user is the valid Destination User and whether the work list item is still available for actioning.

SN stands for Serial Number, which is an identifier that the K2 server will insert as a query string parameter with the task form URL.

Just for information, the serial number comprises of:

  • Process Instance Id; and
  • Activity Destination Instance Id

The 2 values will be separated by an underscore ‘_’ symbol. For example: SN=123_45


 

Execution the work list item action

And of course, with the WorklistItem, you will be able to execute the configured Action (i.e. when User clicks on the Approve button).

Note: The action name needs to be spelled exactly the same as configured in the process.

Note: The Execute method runs asynchronously by default. If you need the method to be executed synchronously, pass a 2nd parameter “true”.


Samples

Here are some “more complete” sample codes if you are still unclear:

Start a new process instance

Open work list

Open work list Item

Execute an action


 

Have fun!!

 

K2 blackpearl: Why my workflow escalation did not kick off after X working days??

If you have Working Hours configured, for example, Mon-Friday, 8 working hours

102-Working_Hours_Config
Working Hours configured

And in your workflow, you configured an Escalation using the Escalate After template and filled in the Days value. For example, 3 days:

102-Escalation_after_3_days
Escalate After template configured with Days = “3”

 

Your workflow WILL NOT be kicking off the escalation after 3 working days. It seems logical from the configurations, but it is not!

In the back-end, Escalation will convert Days to Hours, meaning 3 days x 24 hours = 72 hours. This will be the number of hours the Escalation is waiting for. When paired with Working Hours (8 hours working day in our example), the final escalation date will be 9 days later ( 72 hours / 8 hours = 9 days). This is how it works!

So, if you are using Escalation with Working Hours, always remember that Days will be converted to Hours to get the final escalation date. Hope it helps!