Custom History Form in Skelta

The main feature of any workflow engine is tracking the histories of transactions for its process flows, normally all workflow engines will support for this feature but when you want track the custom history for a process flow like Approval history (approver, result, date, role, department and comments) then you need to dig your own logic based on your workflow engine. Normally history is collection log user or actor action of activities of a process flow, before displaying it we need to consolidate as single unit before it displaying it. Here some possible methods for showing custom history for a Skelta process flow.

  1. Maintaining consolidated custom history into database and Showing with Custom ASPX page.
  2. Maintaining consolidated custom history into database and Showing with Skelta Form.
  3. Maintaining consolidated custom history into List and showing with form which created on List.

Let us see merits and demerits about all above methods. First method gives us full customization on displaying and formatting data, here we don’t have limitation of Skelta form but it will take extra time to design and develop ASPX form, next things we have to replicate same thing for each process flow. In second method same like first one except custom ASPX page, here we need to design the form instead of ASPX page, but it requires special form rendering method because it is required to convert db data to Skelta form xml format and other thing is we may encounter with Skelta form limitations, more over nobody will choose this method.

Third method fully depends on Skelta List. History form should be created on list and this form not updating on anything on list table, instead of it is going to read the value from list tables, so we need to prepare the data for history list table using forms or Skelta API or both (in most case) here consolidating data little complex work. Skelta List can be stored in the repository database and it won’t support for custom database. Comparing with first two solutions this is good solution but still it has own drawbacks, so we need a generic solution following which fulfill following needs.

  1. Need a generic ASPX page for rendering History form for all process flows, so it will reduce development time.
  2. Consolidated History data should not depend on database.
  3. System should provide the option to Map the History form(s) for each Process flow otherwise every time we need to create or modify the history form render ASPX form.

Now we are going to have new solution with above requirements. Initially we need to do the following prerequisites.

  1. History Form Custom Property for Start Activity.
  2. History Skelta Form Design (for each work flow).
  3. SKHistory variable to workflow (for each work flow).
  4. Generic History Form Render ASPX page.

First and last steps are single time setups and we need to do other two steps for each work flows because history data and display format will vary.

History Form Property for Start Activity:

Skelta provides option to create our own custom property for custom activity, with this feature we going to create new custom property for mapping the history form for current process flow. Here we can use the FormDetails type property to specify the history form.

If you are familiar with creating custom activities for Skelta then you may breathe easy, don’t worry if you haven’t any prior experience on this, you just follow the following steps.

  1. Take backup this folder C:\Program Files\Skelta\BPM.NET\WorkflowElements\Default\en-US\Actions\XML. Path may vary according to your Skelta environment. Make sure that you copied on different drive if you copied on same Skelta BPM.Net folder then you may encountered by some problem, so you must consider the above point.
  2. Open the Actions.xml from above specified folder and find out the start activity section, refer the following image.
  3. Navigate to properties tag of Start activity and paste the following property node.
    
    
    
    Actually this property is modified version of Skelta Invoke form activity’s Form Details property. Here I changed the name of property; display name and mandatory field, rest of things are optional.
  4. Save the modified Actions.xml then close it.
  5. Stop IIS, Skelta Services and clear all required temporary files then start all services.
  6. Login as admin on your custom application or enterprise console then navigate Workflow list and create a new workflow. Now you can able to see the custom property which we added few moments before on process designer. Refer the following image.

  7. Click on History Form Details button, it will pop ups the Form Details window here you can select your History Form.
  8. Now publish process flow, actually step 7 not required now, it just for understanding the History Form details property.

We successfully completed first step, now we will move to next step.

SKHistory XML Variable:

SKHistory is a user defined XML variable (normally in Skelta process flow system variable start with SK) for storing custom history of the current process flow, schema of this variable is depends on History Form (the Skelta Form for showing history), it means SKHistory variable should be created based on History Form definition. If you want to show the custom history for a process flow then you should add this variable on your process flow and update it according to your requirements, so now we are not worry about database or duplicating the data.

Generic ASPX Form for Showing History Form:

Since we are using a custom property on start activity we need a special Form rendering method for Web form to access this custom property and render the specified form along with history data. But we need to consider the generic web form because we need to avoid creating new form for each process or application. Let us step into our business.

For preparing a generic render form we need to follow the following steps.

  1. Get History Form Id.
  2. Get metadata information for History Form.
  3. Get value SKHistory xml variables.
  4. Get the form definition.
  5. Render the form renderer.

Getting History Form Id:

Previously we seen how to add the custom property called History Form on Skelta process flow start activity, now we are going to use that property to get the form Id. Refer the following code snippet.

/// 
        /// Get the history form for current workflow
        /// 
        /// Workflow context/// History form details
        public static FormItem GetHistoryForm(this Workflow.NET.Engine.Context wfContext)
        {
            string[] strHistorForm = new string[2];
            FormItem frmItem = default(FormItem);
            if(wfContext != null && wfContext.ProcessDefinition.Actions["Start"].Properties.Contains("HistoryForm"))
            {
                string strValue = Convert.ToString(wfContext.ProcessDefinition.Actions["Start"].Properties["HistoryForm"].Value);
                if (!string.IsNullOrEmpty(strValue))
                {
                    string[] strValues = strValue.Split('?');
                    frmItem.FormName = strValues[0];
                    frmItem.Id = strValues[2];
                    frmItem.Version = Convert.ToInt32(strValues[1]);
                }
            }
            return frmItem;
        }
Template Class Form Item:
public struct FormItem
    {
        public string FormName { get; set; }
        public string Id { get; set; }
        public int Version { get; set; }
        public bool IsEmpty()
        {
            return string.IsNullOrEmpty(this.Id);
        }
    }
Metadata for Form:

Normally action forms are triggered from Skelta work list with a secured parameter, this parameter contains all required metadata about current work item and form but this parameter encrypted to share it across pages so we decrypt first then we can use it, here Skelta provides a adapter class to help us on this process, that adapter is WebWorkItemAdapter.

Code snippet for processing secured parameter.

WebWorkItemAdapter webAdapter = new WebWorkItemAdapter();
webAdapter.ProcessWebChannelRequest();

Now we decrypted and initialized the WebWorkItemAdapter object from that we can get the instance for current work item and form name other details, the following code snippet help us to get the work item and form.

Get SKHistory Variable Value:

SKHistory contains collection of log data to specific to process flow, it is like input xml variable for Invoke form activity, so we need to get the SKHistory variable Raw xml to fill the Skelta History form state. Find the code snippet to get the SKHistory variable value.

Workflow.NET.Engine.Context ctxContext = Workflow.NET.Engine.Context.GetContext(intInstanceID, strApplication);
inputInstanceXML = ctxContext.XmlVariables["SKHistory"].RawXml;
Form Definition:

Before rendering the given form on Form Renderer we need to get form definition object otherwise form render can’t render the given form. We can get form definition object by using below code snippet.

ListDefinition lstDef = ListDefinition.GetList(this.appObject, this.listParams.ListName);
    this.frmItem = new Skelta.Repository.List.ListItem(lstDef, this.listParams.ListItemId, this.listParams.VersionStamp);
    ListTableForm frmTable = (ListTableForm)this.frmItem.ListForm.FindById("_sys_fmdef_new");
    string strDef = Convert.ToString(((ListTextDataItem)frmTable.Records[0].FindControlByID("_sys_def_xml")).Value);
    if (string.IsNullOrEmpty(strDef))
    {
        //Show error message or thorw exception here if you required
    throw new Exception("Blank Form Definition");
        Response.End();
    }
    BaseForm frmBase = BaseForm.LoadDefinitionXml(strDef);
    frmBase.TopLevelForm.FormAssociatedItemID = this.frmItem.Id.ToString();
    return frmBase;

First we need to get the Skelta List definition for Forms List (Skelta is a generic master list to access all created Skelta elements for a repository each Skelta elements have their own list definition based on this definition Skelta List will display the master list). Based on this Forms List definition we can get ListItem object for given Form, by digging this list item we can get Form definition raw xml from _sys_def_xml system property. Finally convert this raw xml to BaseForm instance; this is Skelta API representation for Form Renderer.

Consolidated code snippet for Form Definition as follows,

/// 
    /// Get Form definition for given Skelta form
    /// 
    /// Form Definition object
    private BaseForm GetFormDefinitionXml()
    {
        try
        {
            bool isFlag = false;
            ListDefinition lstDef = ListDefinition.GetList(this.appObject, this.listParams.ListName);
            this.frmItem = new Skelta.Repository.List.ListItem(lstDef, this.listParams.ListItemId, this.listParams.VersionStamp);
            ListTableForm frmTable = (ListTableForm)this.frmItem.ListForm.FindById("_sys_fmdef_new");
            string strDef = Convert.ToString(((ListTextDataItem)frmTable.Records[0].FindControlByID("_sys_def_xml")).Value);
            if (string.IsNullOrEmpty(strDef))
            {
                //Show error message or thorw exception here if you required
                throw new Exception("Blank Form Definition");
                Response.End();
            }
            BaseForm frmBase = BaseForm.LoadDefinitionXml(strDef);
            frmBase.TopLevelForm.FormAssociatedItemID = this.frmItem.Id.ToString();
            return frmBase;
        }
        catch
        {
            return null;
        }
    }
Form Rendering:

Now we are ready for rendering workflow initiator form on web form. We can able render form by using following code snippet.

Skelta.Repository.List.ListItem item;
    try
    {
        item = new Skelta.Repository.List.ListItem(this.lstDef, fidForm);
    }
    catch
    {
        //Handle exception here
        return;
    }
    if (!item.IsFolder)
    {
        this.listParams.ListItemId = fidForm;
        this.listParams.VersionStamp = item.CurrentVersion.VersionStamp;
        this.listParams.ListName = this.lstDef.Name;
        this.frmRequest = new Skelta.Forms.Web.FormRenderer();
        frmRequest.ID = "frmRequest";
        frmRequest.Width = Unit.Percentage(100);
        frmRequest.Height = Unit.Percentage(100);
        this.frmRequest.FormDefinition = this.GetFormDefinitionXml();
        this.Title = this.frmRequest.FormDefinition.Name;
        frmRequest.FormDefinition.AssociatedListName = "Forms List";
        if (!base.IsPostBack)
            this.frmRequest.FormDefinition.FormInstanceXml = "";
        this.frmRequest.InDesignMode = false;
        this.frmRequest.HeaderHidden = true;
        this.pnlForm.Visible = true;
        this.pnlForm.Controls.Add(this.frmRequest);
        this.frmRequest.OnSaveClick += new Skelta.Forms.Web.SaveClickDelegate(frmRequest_OnSaveClick);
    }

Create Form Renderer control instance and assign the Form Definition which we already generated from previous step. Set design mode false then add the renderer to panel.

Find the consolidated code snippet for Form rendering with full signature,

/// 
/// Render the History form to custom aspx page
/// 
private void RenderForm()
{
    try
    {
        //Get the technician form  id from web.config file
        Guid fidForm = new Guid(strFormGuid);
        Skelta.Repository.List.ListItem item;
        try
        {
            //Try to get the technician form information
            item = new Skelta.Repository.List.ListItem(this.lstDef, fidForm);
        }
        catch
        {
            this.ShowMessage("Cannot find the specified Item.");
            return;
        }
        if (!item.IsFolder)
        {
            this.listParams.ListItemId = fidForm;
            this.listParams.VersionStamp = item.CurrentVersion.VersionStamp;
            this.listParams.ListName = this.lstDef.Name;

            this.frmRender = new Skelta.Forms.Web.FormRenderer();
            frmRender.ID = "frmRender";
            frmRender.Width = Unit.Percentage(100);
            frmRender.Height = Unit.Percentage(100);

            //Assign the form definition to form renderer
            this.frmRender.FormDefinition = this.GetFormDefinitionXml();
            //Get the current form name
            strFormName = this.frmRender.FormDefinition.Name.ToString();
            //frmRender.FormDefinition.FormAssociatedItemID = fidForm.ToString();
            frmRender.FormDefinition.AssociatedListName = "Forms List";
            if (!base.IsPostBack)
                this.frmRender.FormDefinition.FormInstanceXml = inputInstanceXML;

            this.frmRender.InDesignMode = false;
            this.frmRender.HeaderHidden = true;
            this.pnlFormRender.Visible = true;
            this.pnlFormRender.Controls.Add(this.frmRender);
            this.frmRender.OnSaveClick += new Skelta.Forms.Web.SaveClickDelegate(frmRender_OnSaveClick);
        }

    }
    catch (Exception errMsg)
    {
        //Handle the exception
    }
}

Recently I created the Starter Kit for Skelta called as Smart Skelta Starter Kit for 2008, you can download it from http://smartskelta.codeplex.com In this kit you can able to find out History form from Item templates of Web Site.

Conclusion:

In this article we seen how to create Generic History Form for Skelta, I hope this article will help you on reduce your development time on developing custom history form for each skelta process flow. If you feel free please send your valuable feedbacks to eshock.vasan@gmail.com


Powered by Blogger.

About

About

Follow us

Popular Posts