Testing a workflow inside a Smartform

So I have posted a few articles on testing in K2, we have looked at Unit Testing,  Testing Smartforms and then just recently more examples of testing a workflow.  After writing that article I realised that there was something missing. What about building a workflow testing tool inside of a Smartform. As it all well and good that we can write unit tests for testing a workflow, but that relies on the workflow developer being able to write code and being that the whole idea of K2 is to build low code , no code solutions. Then there should be away of doing it without the developer having to write code.

There are a number of tools out in the market and they do absolutely fantastic job of testing workflows without having to write code. But I wanted to see if it could be done inside a Smartform.

So my challenge is to build a Smartform App that would test a workflow, with out the tester having to write any code.

The app had to do the following

  1. Start any workflow and check that it has started
  2. Check that tasks have been created and the correct number
  3. Action a task
  4. Checking the task has been completed
  5. Check that the workflow has finished
  6. Check the workflow has gone down the direct path.

The app should all be in Smartforms and should be easy to use.

So lets see how I got on.  So i am going to start with the finished tool and then i will take us through it.

Testing Dashboard

The dashboard shows all the current tests that have been performed and shows the overall percentage of pass and failures.

dashboard

From here you can also click on ‘Create Test’ button to create a new test. Selecting an existing test and clicking on ‘Run Test’ will  run an existing test. Finally double clicking on a test will open up the details about the test.

Test Details

So the test details shows the following information

test details

  1. Workflow being tested
  2. If has already been tested
  3. When it was tested
  4. Overall Pass or Fail
  5. What percentage passed and failed
  6. What was tested and whether each test passed or failed.
  7. It also shows any other tests relating to the workflow being tested.

From the test details you also have the ability to retest the test as well.

Creating a new test

From the dashboard click on ‘Create Test’ button.

test builder.PNG

  1. In the new test form, give the test a name
  2. Select a workflow that you are going to test. Once selected you will then be able to see all the activities of that workflow beneath.test-builder1
  3. Select the activities that the workflow will go throughtest-builder2
  4. The last section is where we build up the actual the tests and what results we expect from those tests.test-builder3

Building a simple test script

The workflow we have selected is a very simple one. It has the following activities

  1. Setup
  2. Task
  3. Approve
  4. Reject
  5. End

test-workflow

Task is where there is a client event which has one slot and goes to the originator.

Now that we have got the basic test details and what route we expect the test to go. We can now build are test script.

  1. Select ‘Start Process’ from the test type drop down list
  2. You will notice that the other text boxes in the row, will be pre populated with information and some will be disabled depending on what information the particular test type requires.
  3. Now we choose comparison sign we are going to use it can be either
    1.  =
    2. >
    3. >=
    4. <
    5. <=
  4. We are going to choose >
  5. In the expected result / comparison box lets enter 0, this is because if workflow starts successfully we expect a value greater than 0.
  6. So ‘Start Process’, needs to know the ‘Folio’ to give the workflow, you can either enter in something. Or if you leave it blank it will take the name we have given earlier to the test.
  7. The next parameter we need to give the test type is the process name. You can either copy the full workflow name and paste into this box or leave it and it will automatically get the workflow name.
  8. The next box is called milliseconds, you can either leave it at 0. Or you can enter in a value. This is useful as it may be your workflow is taking a while to perform an event. So you can enter in a delay to the tests to compensate for this.
  9.  Click on ‘Add’ again , we now what to check the status of the workflow, to make sure that once we started it. It is now active.
  10. So select ‘Get Workflow Status’ from the drop down list
  11. Choose ‘=’ from Sign
  12. Enter ‘Active’ in expected result
  13. We don’t need to enter in anything else
  14. Click on ‘Add’
  15. We now want to check to see if has generated some tasks
  16. Select ‘Get Task Count’
  17. Select ‘=’ in Sign
  18. Expected Result should be 1, as we know it’s only one slot and going to the originator.
  19. ProcessInstanceId, leave that how it is, as the testing framework will replace that with the actual process instance id as run time of the test
  20. Process name either enter in the full name or leave it and it will get automatically populated
  21. Next we can action the task
  22. Select ‘=’ for the sign
  23. In the expected result box enter ‘True’
  24. Where it’s says ‘Action’ we need to enter in the action we expect the test to use. So lets enter ‘Approve’
  25. Where it says ‘Serial Number’ we leave that and like the process instance id, it will be populated by the testing framework at run time.
  26. For Destination user, enter in the destination user that the task is supposed to be for.
  27. When this test type runs it will try and action the task based on the information we have given it (Task Action and Destination User) if the test is successful the actual result will be true.
  28. The last test we are going to do is to check that workflow has gone down the correct path and the correct activities were used. The test will compare the path we expect it to be. which was defined in the ‘Workflow Route’ section to the actual path taken.
  29. Select ‘Compare Activities’ from ‘Test Type’
  30. Select ‘=’ from Sign
  31. Enter in ‘True’ in expected result
  32. Leave ProcessInstanceId and Activities how it is
  33. In the Millisecond box enter ‘4000’ this will make the ‘Compare Activities’ test to wait 4000 milliseconds before starting the test. This will give the workflow time to perform it’s other activities before completing. If this test fails then increase the time. It will then pass.
  34. The test script should now look like thistest-builder4
  35. Now click on ‘Save’
  36. You will now see a read only version of the test
  37. With two buttons ‘Edit Test’ and ‘Run Test’

Running a Test

To run a test, either select a test from the test list and click on ‘Run Test’ or from the test details click on the button ‘Run Test’

Once the test has been completed, you will be displayed the results for each individual test and also the overall test.

resuts

Each individual test will show whether it has passed or failed. If it has passed the status will be true and in green and if had failed it will be false and in red.

For each test that we create we also get the overall test result.

A failed test would be when any of the individual tests has failed

fail

It will show a pie chart showing the % of pass and fail with big black x cross above it.

pass

For an overall Pass we get a tick and of course a 100%

These test results will remain until, you click on ‘Run Test’ again and this will overwrite the previous test results with the new ones.

Continue reading “Testing a workflow inside a Smartform”

Testing Smartforms with the Smartform tester in K2 4.7

There are a number of Smartform testing tools currently on the market to allow developers to test their Smartforms. But in 4.7 there is something hidden away that is just dying to be played with. Like the developer’s best friend SmartObject Service Tester, there is now a tool that will allow you to automate testing of your Smartforms.

How to access it

To access this hidden gem of a tool, go to the K2 Designer and click on ‘show’ link at the bottom of the page and in the pop up click on System.

show-all

How you will notice in the context browser for the designer there is now some additional categories.

This slideshow requires JavaScript.

  1. Expand System
  2. Expand Forms
  3. Expand Smartform tester
  4. Click on ‘SmartForms Tester – Landing Page’
  5. Click on the ‘Runtime URL’ in the properties page
  6. The Smartform Tester Portal will now load up and we can start to build a test case

 

SmartForms Tester Portal

This is where we can manage the automating of the forms from creating new tests to running existing ones.

smartform-tester-form

K2 do give us some examples of how to use the tester portal in the ‘Examples’ folder or what they like to call ‘Suites’

So lets create a ‘Suite’

 

Creating a Test Suite

  1. Click on ‘Test Suites’
  2. A list of options will appear at the top
  3. Click on ‘New Suite’
  4. A pop up window will appear enter in the name of  what your suite is going to be called. I am going to call mine ‘Annual Leave’
  5. Click on ‘Accept’
  6. Click on ‘Refresh List’ button
  7. You will now see the suite you just created in the list

This slideshow requires JavaScript.

 

Creating  a new test

  1. Click on the suite you just created
  2. The list options above will change
  3. Click on ‘New Test’
  4. This will open a new window, from where we can build a new test case
  5. Click on smartform-tester-form25 in the Smartform section and choose the View or Smartform you want to test. In this case i am going to create a simple test to test a view
  6. When you select a view/form it will then appear in the in  top section of the testing  form
  7. Below the name of the select artifact
  8. Is the actions this where will build the test script that the portal will execute for us

 

This slideshow requires JavaScript.

Creating the automated test script

  1. In the actions section, you will see there part of a action already selected
  2. Because i have chosen a view to test. The first drop down list has ‘View’ selected. The 2nd drop down list has ‘Control’ selected.
  3. From the 3rd drop down list we can now choose a control on the view
  4. I am going to choose  the picker control ‘pkrUsername’.
  5. A 4th drop down list appears and should have ‘Change Value’ selected
  6. Text box is also available, where a value can be entered. I am going to enter in ‘sallport’smartform-tester-form8
  7. When we execute this action  the tester, will put ‘sallport’ into the picker control for you.
  8. Click on ‘Add’ to add another action smartform-tester-form26
  9. I am going to select the ‘calStartDate’ control and enter in a date and i am going to the same with for ‘calEndDate’
  10. I am going to add another action so it can ‘select’ a leave type. I am going to enter in ‘Paid leave’
  11. Last of all i am going to select the ‘Button’ control and tell it to click the button.
  12. Now that my simple test script of filling out the view and clicking on the button to submit the data. I am ready to get the tester to run it for me.
  13. But before I do that just click on the smartform-tester-form27 in the top left hand corner and select ‘Save’ from the context menu
  14. ‘Save Test’ pop window will appear, enter in a name for test and make sure the correct suite is selected and click on ‘Ok’

This slideshow requires JavaScript.

Running the test

From the test designer, we can either run the whole script or by clicking one of the actions we can test a particular line.

  1. I am going to run the whole script, so click on ‘Run All’ button. The designer will then carry out the actions line by line.
  2. A report will then be displayed telling you if it was successful or not.
  3. The actions line will also all be green and of course the data inserted into the view will be displayed and of course you can check the SmartObject that was used to save the data.
  4. You can also run the test from main testing portal , by selecting the test from the suite and then clicking on ‘Run Test’ option

This slideshow requires JavaScript.

 

So this is a basic how to use the Smartform tester, and there are other options that I have not covered such as delayed actions and modifying variables and assertions such total count of items in a list etc..

Demo forms can be downloaded from here

Conclusion

What is good about this tool it’s all built using Smartforms, so not only are we seeing some new controls that are not made available yet to in the Smartform designer, such as the context menu, the action list with is probably a more advanced list view. I hope these will made available soon.

The 2nd good thing about this is that the test data is available as SmartObjects which mean we can then use these to build rich testing reports that also take into account business data.

It’s great start to something that will hopefully be fleshed out with further updates in the future. So it would be possible to fill out the form, get a task and action it, all in one test case.

 

 

 

Unchaining Workflows with Framework Part 1

Introduction

Building workflows in K2 is great, we can drag and drop events onto the canvas , click through wizards that tell us what to do. We can setup line rules, tasks etc.. With just using a mouse. Which is fantastic as its quick and easy.

We can also deploy a workflow following a simple wizard too.

But when we want to change something in a email, or change the form that the task is pointing too etc.. It involves opening up the solution and making those changes and redeploying and then only new instances of that workflow picking up those changes.

So what do we do? Well we do what i tell all my clients, we make everything in the workflow that could dynamic. We store the url of a form in a data store somewhere and the same for email content or the business rules like escalation values. We basically unchain the workflow from the data. We free it so we can make these type of changes from outside the workflow. So we don’t have to go back into the workflow and make the change and then redeploy. We just go to a simple form  and make the change and then any workflow which is pointing to this value will automatically receives the change.   Sounds brilliant doesn’t it?

So over the years myself  and my colleagues have been slowly putting  a framework/ pattern together.

It doesn’t have a fancy name so throughout this article i am just going to call it framework.

Framework

The framework designed to make the workflows  adaptable to change without having to redeploy the workflow each time. Its also designed to make building a workflow quicker and giving the people the involved from developers, administrators and users a complete overview of what is happening.

Some of the features of framework are

  1. Headless forms, with framework, we can make the task forms dynamic so at run time we can choose what form the task will be get.
  2. Externalize process data,
    1. External  data fields are created outside of the workflow, allowing workflows to be more efficient and for the data data to be deleted after the instance has finished instead of persisting after the instance has finished.
    2. Process data, key data for the process type is externalized allowing the data to be changed without redeployment of the workflow
  3. Audit trail, every workflow has a audit trail allowing users to keep track of important changes  within the flow of the instance of a workflow
  4. Workflow registration, every workflow is registered allowing users, developers and admin to keep track of what workflows are running , if they are related to another workflow and how complete an instance of a workflow type is.

Components of framework

  1. Process Management
    1. Externalized Data fields
    2. Configuration data
    3. Time slots
  2. Swagger Definition Library
  3. Script Library
  4. Form Library

 

To use the framework within your workflow, we need to setup a few things inside the workflow and also outside the workflow.

 

Setting up a workflow to use the framework

Create Key Data Fields

First thing we need to do is create the three main data fields that every workflow using the framework should have.

Data field name Type
DataFieldId String
K2FID String
ProcessTypeId String

datafields

  1. DataFieldId, will old the external data field Key
  2. K2FID, will hold the key value for the registered process istance
  3. ProcessTypeId, holds the key for type of process

There will probably be fourth data field that is not mentioned above, which will be the primary key for the main business data.

Create References

To make life easier when it comes to designing \ developing workflows the framework recommends we use ‘References’ to create short cut to a SmartObject method and its properties of data.

To create a reference, go to the data tab (3rd) and right click on ‘References’ and follow the instructions.

The references that we need to create are as follows

Reference Name SmartObject Method Parameter
Configuration Data K2F.SMO.ProCon GetProcessConfig ProcessTypeId (Data field)
External Datafields K2F.SMO.Datafields Get Datafields DataFieldId (Data field)

Configuration data, contains values for the process type

External Data fields will contain data for the instance of the process

references

Like the Data fields, you may also have a third one for  your main business data.

 

Setup Activity

Every workflow that uses the framework must have an activity called ‘Setup’ This activity has a number of different things to do.

  1. It needs to register the instance of its self, to help keep a track of the instance
  2. Create the data field key, so we can use external data fields
  3. Start auditing the key stages of the workflow

1.Within your workflow create an activity called ‘Setup’

setupact

Getting the Process Type Id

In the ‘Setup’ Activity, drag a ‘Data Event’ into the activity, give it the name ‘Get Process TypeId’

Reference Name SmartObject Method Parameter Return Property
Source K2F.SMO.ProcType GetTypeByProcessName Process Name ProcessTypeId
Destination DataField: ProcessTypeId

Now we have the process type id, it will allow the workflow to get all related framework information relating to this workflow type.

Creating a Data Field Key

In the ‘Setup’ Activity drag the ‘Data Event’ into the activity and give it the name ‘Create DataField Key’

Reference Name SmartObject Method Return Property
Source K2F.SMO.DataFields Create Key DataFieldKey
Destination DataField: DataFieldId

DataFieldId is what we will use to group the process instance data fields together.

Creating a Folio

In the ‘Setup’ Activity drag the ‘Data Event’ into the activity and give it the name ‘Set Folio’

Reference Name  
Source Free Text
Destination [Context Browser].[Workflow Context Browser].[Process Instance].Folio

Registering the instance of a workflow

By registering the instance against the workflow type allows the framework to keep track of how many instances of a particular type is running and to make sure the correct details and other key values are being used.

Create a SmartObject event inside the ‘Setup’ activity and name it ‘Record Workflow’

SmartObject Method Parameters Return
K2C.SMO.ProcInst Register Process Instance
Name Type Value Notes
pParentProcessId Number 0 or process instance Id of a parent process Default is 0 unless this workflow is being called via IPC or case Action then the parent process instance Id is required
pProcessInstanceId Number ProcessId [Context Browser].[Workflow Context Browser].[Process Instance]
pStatus Text Active
pViewFlow Memo View Flow [Context Browser].[Workflow Context Browser].[Process Instance]
pStartDate Date ProcessStartDate [Context Browser].[Workflow Context Browser].[Process Instance]
pBusinessKey Text 0 unless primary key of the main business data is known
pProcessTypeId GUID ProcessTypeId DataField
pProcessId Number 0
PFolio Text ProcessFolio [Context Browser].[Workflow Context Browser].[Process Instance]
pOriginator Text ProcessOriginatorFQN [Context Browser].[Workflow Context Browser].[Process Instance]
pDataFieldKey GUID DataFieldId Data Field
ID, assigned to K2CID datafield

Creating a Audit entry

Now that the instance is registered, against the process type, we need to make an entry in the audit log so we can show what record that the instance has started. Create a SmartObject event inside the ‘Setup’ Activity and call it ‘Add to Audit’

SmartObject Method Parameters
K2F.SMO.Audit Add Audit
Name Type Value Notes
pDetails Text Starting [Name of workflow] Free text. what ever is appropriate
pProConfigId Number 0
pProcessInstanceId Number ProcessId [Context Browser].[Workflow Context Browser].[Process Instance]
pCreatedDate Date ProcessStartDate [Context Browser].[Workflow Context Browser].[Process Instance]
pCreatedBy Text ProcessOriginatorFQN [Context Browser].[Workflow Context Browser].[Process Instance]

The ‘Add to Audit’ event can be copied and pasted into other activities in the workflow and you just need to change pDetails, and pCreatedDate

  1.  The ‘Setup’ Activity should now look like this

setup1

The workflow is now setup  to use the framework

 

 Client Events with the framework

With framework the form’s URL is dynamic and is stored externally, allowing the form to be changed at any point. With out the need to  go back into the workflow

To do set this up in the workflow carry out the following steps

  1. Drag a ‘Default Client Event’ on to the canvas or into an Activity
  2. Give your event a name
  3. Check ‘Task Item URL’
  4. Add the following SmartObject Method

 

 

SmartObject Method Parameters Return
K2F.SMO.Client ClientEventURL
Name Type Value Notes
pClientEventName Text ActivityInstanceName [Context Browser].[Workflow Context Browser].[Activity Instance]
pProcessTypeId GUID ProcessTypeId
FormUrl

 

defaultclientevent

 

  1. Make a note of the name the Activity is called where you have put the client event.
  2. Goto  orm url here’

Updating the  framework of  a workflow instance status

The framework likes to keep track of the status of the workflow that is registered. This allows us to do reporting and to offer additional functionality based on it’s status.

To update the framework of a status change follow these steps

  1. Create a SmartObject Event
  2. Select ‘K2F.SMO.ProcInst’ SmartObject
  3. Select the ‘Update Status’ method
  4. For the input mapping ‘pStatus’ enter in free text to what the status is
  5. For ‘pId’ input parameter use the data field ‘K2FID’

updatestatus

This event like the audit can be copied into other activities where the status needs to be updated.

 

Dynamic Escalation and Start Rules

When there needs to be an escalation on an ‘Activity’ or ‘Process’ we can use K2CF, to make the time frames dynamic.

Setting up the escalation

  1. Click onclock  on the Acitivity
  2. Click ‘Next’, give the escalation a name
  3. Select a ‘Rule Template’, in this case i am going to select ‘Escalate After’
  4. Click ‘Next’
  5. Click on eclipse  in ‘Days’ to bring up the context browser.
  6. Add the following SmartObject Method

emptyescalation

 

SmartObject Method Parameters Return
K2C.CMN.SMO.Time Read
Name Type Value
pTimeId GUID
References Value Filter
Configuration Data ProcessConfigValue ProcessConfigName = TaskTime

It doesn’t have to be ‘TaskTime’ it could be what ever you have called reference

 

Days

 

 

finishedescalation

  1. Repeat steps 5 and 6 for Hours, Minutes and Seconds, changing the return value for to the correct return property based on section. For example if you are on hours , then the return would be hours.
  2. Make a note of the name you passed into the ProcessConfigName. for example ‘TaskTime’
  3. You can also do the same for a ‘Start Rule’

 

End Activity

Every workflow must have an ‘End’ Activity.  In this activity we delete all external data fields relating to the instance of this workflow. We update the status of the process instance information and  update the last entry in the audit table for the instance.

Deleting External Data Fields

  1. Create a SmartObject’ event
  2. Select ‘K2F.SMO.DataField’ SmartObject
  3. Select the method ‘Delete Data Fields’
  4. It will ask for the ‘pDataFieldKey’ so assign it ‘DatafieldId’ Data field
  5. Complete the event.  Now when this event is executed it will delete all the datafields that were created for this instance.  Making the workflow efficient in cleaning up data it no longer needs.

externaldatafield4

Audit Trail

The end activity will also need an entry into the audit to say that it has finished

Updating the framework on process status

The framework process instance status will also need to be updated to ‘Complete’

Creating an external Data Field

To create an external data field

  1. Drag  a SmartObject event onto the canvas
  2. Call the event ‘Create DataField’ or something similar
  3. Find  K2F.SMO.DataField SmartObject from the context browser
  4. Select the ‘Create Data Field’ method
  5. Now we need to supply the method with some additional information
    1. pDataFieldDescription, description of the data field we are creating
    2. pDataFieldKey, this is data field Key that we created earlier
    3. pDataFieldTitle, the name we are giving the data field
    4. pDataFieldValue, the value of the data field
    5. pProcessInstanceId, the id of the process instance
  6. Click ‘Next’ and ‘Finish’
  7. We have now created a external data field
  8. Repeat steps 1 to 6 for additional external data fields if needed

externaldatafield

Using an external data field

To use an external data field, in the event or the line rule you are in.

externaldatafield1

  1. Open the context browser
  2. Go to the data tab (3rd tab)
  3. Expand references
  4. Expand ‘External Datafields’
  5. Drag ‘DataFieldValue’ into the section you want to get the data field value
  6. The filter window opens up, click on ‘Add’
  7. In ‘Left’ select ‘DataFieldTitle’ from the drop down list
  8. Operation should be ‘=’
  9. In ‘Right’ enter the name of the data field you want to get the value of.

externaldatafield2

10. Click ‘Next’

11. Make Sure ‘DataFieldValue’ is selected and click ‘Next’

12.  Make sure ‘Return a single item’ is checked and click ‘Finish’

13. We have now called the external data fields to get the value for a particular data field

externaldatafield3

Line Rules

We can also use framework for line rules and other workflow logic

  1. Right click on a line
  2. Give the line appropriate name and prefix the name with ‘ln’. For example ‘lnVotingAge’
  3. In the label name section prefix it with ‘LR’ and then put what the rule is doing.
  4. Click on linerule  and click on ‘Add’
  5. Click on eclipse
  6. Expand ‘References’
  7. Expand ‘Configuration Data’
  8. Drag ‘ProcessConfigValue’ into the  ‘Variable’ section
  9. In Filter click on ‘Add’
  10. Select ‘ProcessConfigName’
  11. Select ‘=’ operation
  12. In Right, enter in the keyword for the value. for example ‘AgeLimit’
  13. Click ‘Next’ and ‘Next’ again
  14. Click ‘Finish’
  15. In the second variable put in your business data as normal.

linerules2

We have now set up a dynamic line rule where both the business data the variable that its being compared against is coming from outside of the workflow

 

 

 

Managing the framework

Adding a form to the form library

All forms created either Smartforms or HTML 5 forms must be registered, to allow the framework to use the forms in the workflows

formlib

  1. Goto form K2F.ADM.SMF.ProcessManager
  2. Click on ‘Add’ on the Form Library
  3. Enter in the name of the form
  4. Complete URL of the form
  5. Tick the checkbox
  6. Click ‘Save’
  7. The form is now registered and can be used.

Adding A Form to A Task

For Default Client Events to get a form in a workflow, we need to link up the form and client event together.

  1. Click on ‘Client Event’
  2. Search for the desired form in the form library
  3. Give the ‘Client event name’ the same as  the activity the client event is in the workflow.
  4. Click on Save
  5. We can use this form now in the workflow as described above

task

 

Swagger Definition Library

 

K2F provides support for managing the swagger definitions of the REST Broker. It will do this in two ways.

  1. There will be a Web API Handler that accepts Posts, the idea being that when the global registry changes it will post to the service with the new host address and application name. The service then goes into the library and finds the correct swagger definition and updates it with the new host address. It then exports the swagger definition file to the  swagger folder on the server and then updates K2 with the new definition. ToDo
  2. In the portal you can view all the swagger definitions, where new ones can be added and old ones can be edited. Once created in the library, this will then create Swagger JSON file on the K2 Server.

swaggerdefintionlib

Add  a new Swagger file

  1. Click on ‘Add Swagger Definition’
  2. Form loads up where you can enter in the definition name, host address and the definition it self
  3. demoswagger
  4. Click on ‘Submit’
  5. This will save the swagger definition in the library and also create its corresponding JSON file on to the server.

5.You will still need to create the instance manually using the service object tester tool

savedswagger

Script Library

Some smartforms may require additional scripts to be used such as the K2C.CMN.VWI.Footer view. Like with the workflows instead of hard coding the script into the view and having to check out and check in the view every time there is change. The script library allows us to make the change from the management tool, without the  risk of breaking the view or form.

scriptlibrary

From the script library the following actions are possible

  1. Add
  2. Edit

script2

when you have finished adding and editing scripts click on ‘Save’

 

 

Registering a process

dash-1

  1. Deploy Process (This can be done at anytime during the workflow setup, but must be done before we carry on with the following steps)
  2. Goto form K2F.ADM.SMF.ProcessManager
  3. Click on ‘Register Process Type’
  4. A pop up window will appear enter in the workflow name, a description and a display name
  5. Click on submit, we have now told K2F about this workflow.
  6. We can now start to add in configutation data about the workflow.

addproctypr

Process Configuration Data

processconfiguration-1

In the process configuration form, we can add,edit configuration data of a workflow.

Navigating to this form can be done either by clicking on the process type or by adding a new process type.

Adding  Time Configuration

To add time configuration that will be used for Escalations and Start Rules as mentioned above.

timeconfiglist

  1. Click on ‘Add Time’
  2. Time configuration window opens
  3. Enter in a name, description
  4. Select a value from the drop down list for each of the time sections
  5. Click on Submit
  6. Time is now added

So that we use the time frame in  a workflow , we now need to link the time frame to the ‘Configuration Data’

Configuration Data

Configuration data is used to hold information that will be needed for every instance of a particular workflow.

configdata

  1. Click on ‘Add’
  2. Enter in a description of config data being added
  3. Enter in the actual value
  4. Enter in a name for the config data
  5. In this example we are going to add the link the to the time frame we created earlier.

sample

 

 

As you can see the framework allows workflows to be flexible and allows for apps to be build apps fast and easily.

Now that the framework has been introduced , next time we will look at what else it can do  and build a workflow using it.

 

 

 

 

 

 

 

 

Best Practice for developing K2 Apps

This guide goes through some  best practices for developing K2 applications.

Forms

Below is a list of additional things that are needed for every K2 Smartform.

Form Name

Form name should be made of the following

[ProjectName].[Area].smf.[Name of form]

So for example a form name would look like DMO.TST.SMF.DemoForm

 

Form Description

The form description needs to be filled in with a basic description of what it does and also its story number and form number. The form number should be in this format < form number >. So for example <FM0001>.  This will make it easier for people to easily identify what the form does what it relates too.

 

 

formdescription

 

 

For this project it’s recommended to use concurrently if you do not need to pass data between the actions or rely on the result of the previous action.

Views

Below is a list of additional items needed for a view.

View Name

The name of the view is made up in the same way as a Smartform. The only difference is that instead of .smf.formname.  The instead of ‘.smf’ its one of the following

[ProjectName].[Area].vwi.[Name of view]

 

View Type

Abbreviation

Item View VWI
List View VWL

 

View Description

The view description needs to be filled in with a basic description of what it does and also its story number.  This will make it easier for people to easily identify what the view does what it relates too.

 

View Properties

View input controls and labels are in the same column.

 

View Controls

The controls for the view also has naming conventions

Display Controls

Type Property Name Size Example
Content ctt cttMyControl
Label lbl 200px lblMyControl
Picture pic picMyControl

Input Controls

AutoComplete auo 200px auoMyControl
Calendar cal 100px calMyControl
Check Box chk 150px chkMyControl
Check Box list chl 150px chlMyControl
Choice che 200px cheMyControl
Data Label dbl 200px dblMyControl
Drop-Down list ddl 200px ddlMyControl
Hyperlink hyp 200px hypMyControl
List Display lst 200px lstMyControl
Lookup lku 200px lkuMyControl
Multi-Select msl 300px mslMyControl
Picker pkr 200px pkrMyControl
Radio Button rdo 150px rdoMyControl
Radio Button Group rdg 150px rdgMyControl
Radio Button list rdl 150px rdlMyControl
Rating rtg 150px rfgMyControl
Rich Text rxt 400px rxtMyControl
Slider sld 150px sldMyControl
Text Area tar 400px txtMyControl
Text Box txt 200px txtMyControl
Tree tre treMyControl

Action Controls

Button btn btnMyControl
Timer tmr tmrMyControl
Toolbar Button tbr tbrMyControl

Attachment Controls

File Attachment fat fatMyControl
Image Attachment iat iatMyControl

Layout Controls

Table tbl tblMyControl

The size is only a guide and common sense must still be used for controls apart from the label control.

 

Control Watermarks

Each control should have its own watermark, with the watermark relating the label the control belongs to and not just the default ‘Type a value’

The watermark must be all in lower case

The image below should help demonstrate this

nameemaildate

 

When to use a drop down list or a picker control

Using drop down lists to select reference data and for most cases is an acceptable approach as everybody knows now to use a drop down list. But it does have a couple of drawbacks.

  1. Can take the form a long time to load if there is a huge amount of data to populate the drop down list
  2. When the user interacts with a dropdown list with huge amount of data, the drop down list becomes a nightmare to interact with.

So we do have an answer to this and that is to use to the picker control instead. This will allow for speedier loading times and also allows the user to search for the specific data in an easier fashion.

The rules for moving to a picker control is if the reference data is longer than 20 rows then use a picker control. You still setup the control in exactly the same way as you would a drop down list. The only differences you can choose which properties can be searched against.

Setting up a picker control

1.Drag  a picker control on to the view or change an existing control into a picker control

pick1

 

pick2

2.Give the control a  name with the correct naming conventions

3.Select ‘SmartObject’ under data and setup as normal if you would a drop down list

4.Select which properties you want to filter against

pick3

5. Click ‘Ok’

 

6. In the picker control properties unchecked ‘Allow Multiple’

prop

Required fields

If a control is required, we can graphically represent this by adding a *. To do this do the following steps.

  1. Select the label of the  field/control you want to modify
  2. In the properties tab, find the ‘Text’ property
  3. At the end of the text, put in the following html ‘<b style=’color:red’>&nbsp;*</b>’
  4. Literal property is enabled
  5. In the ‘Tooltip’ put in the text of the label without the html ‘<b style=’color:red’>&nbsp;*</b>’.

prop1

 

 

Control Data Cache

Please make sure that data caching is not enabled on your data list controls like dropdown lists etc…

prop2

Label to Control spacing

As mentioned in the table at the beginning of ‘View Controls’ the labels must set to 200px with the ’Wrap Text’ property value set to ‘True’

 

Read Only Controls

Data that is read only will be data labels with ‘Read Only’ enabled and with a name that starts with ‘dblReadOnly’

So for example a ‘Lastname’ needs to be read only, it would be called ‘dblReadOnlyLastname’

 

View Rules

The pattern for RCT is that we have smart views and dummy forms. So to get this to work properly we have use unbound rules.

 

How to create a unbound rule

Unbound rule is something that is not bound to an event such as a button is clicked.

To create unbound rules do the following steps

  1. Click on Rules, ‘Add Rule’
  2. In the right hand panel and click on ‘Enter Rule Name’ as seen in the image below

rule1

3. Start the Rule name with the word ‘Unbound’ and the rest of the rule name should be what it does.

4. You can then add in conditions and actions as normal

Reducing the load with editable list views

Due to the amount of rules being called on an editable list, there is new approach that dramatically reduces those calls.

1.Create a list view, but don’t enable the editing options

rule2

2.On the view layout, drag a couple of ‘Toolbar buttons’ on the to the top of the view and name them ‘Add’ and ‘Edit’

rule3

3.Add a parameter for the primary key of the data the list view is bound to

4.Click on ‘Finish’

5.Create an item view based on the same SmartObject that was used for the list view.

rule4

6.On the item view add  a button and lets call ‘Save’

7.Add a parameter with same name and type that you used in the list view.

rule5

8. Add in any other additional parameters you will require such the rest of the best practice parameters.

9.Click on ‘Rules’ and click on ‘Add Rule’

Event When a view executes a method  initialize
Condition If the view parameter contains a value  (Primary key parameter)
Action Execute a view method Load
Input Parameter ID
Parameter Value View primary key parameter

rule6

10.Click on ‘Ok’

11.Create a unbound rule to check the validation of the view

12.Create an unbound rule to either save or update the data depending on if the parameter id is populated like in step 9.

rule7

13. Now create a rule for when the save button is clicked which will check for the view validation rule and then call the ‘Unbound Save Data‘ rule that you just created.

rule8

14. Now that we have built the rules to save and update and load the information we can now add it to the list view. Open the list view in ‘Edit’ mode and go to the rules

15. Create a unbound view called ‘Unbound PopUp View

Rule Name Unbound PopUp View
Action Open Subview (Select your item view you just created)
Configure Execute a view method Load
Source Parameter List View Primary Key parameter
Destination Parameter Item View primary key parameter

rule9

rule10.png

16. Now create rule for the ‘btnAdd’ that calls the unbound rule

Event Control on view raises an event
Action Transfer Data

 

Parameter Primary Key value
Parameter Value Empty (Tick the check box, but leave empty)
Action Execute another rule Unbound PopUp View

rule11.png

17. Do the same for the ‘btnEdit’ except without the ‘transfer data’ action

rule12

18.Create a rule that when you click on the list item it transfers the primary key from the list into the view parameter.

rule13

rule14

19.The last set of rules we need to do is add an action to close the sub view once the ‘Save’ button has been clicked. Click on the ‘btnSave is clicked’ rule and click on ‘Edit’

20. Click on the ‘Actions’ tab and search for ‘Close’ and select ‘Close Subview’ action. The edit rule should look like this.

rule15

21. Click on ‘Ok’ and ‘Finish’

22.You can now test the view to make sure it’s behaving correctly. When you click on ‘Add’ it should load up the item view empty and when you click on ‘Save’ it should insert a new record into the list. If you click on an item in the list and click on ‘edit’ it should load up the data for you to edit. When you click on ‘Save’ it should update any changes.

 

Workflow

This section looks at what is needed in every workflow

Where to create the workflow?

The workflow is to be created in visual studio, so it can be checked into TFS

Workflow Name

The name like the views and Smartforms must represent what part of the project it relates to

[ProjectName].[Area].wf.[Name of workflow]

Workflow Description

Work flow description should be filled in, so people know what it does and what story it relates to

Workflow Data fields

Data fields should be kept to a minimum and be used to only hold reference data like a primary or foreign key. You can then use ‘reference option’ to get the rest of the data that you need.

The following check boxes also need to be unchecked

  • Hidden
  • On Demand
  • Keep Audit

 

Workflow Activities

Workflow activities should contain a name in what they do and the activity description should contain information such as whether there is a start rule or any escalations.

To describe what activity rules are being used you can use the following abbreviations

Activity Rule

Abbreviation

Proceeding Rule PR
Start Rule SR
Escalation Rule ER
Destination Rule DR

 

workflow

Activities are colour coded to make it easier to identify what they are doing

Activity Type

Activity Colour

Start Green
Client Activity Blue
Server Activity Grey
IPC Activity Yellow
End Activity Red

 

Activity Events

Activity Events should say what they are doing and not be left with the default names

If a server event is used with some code then the event name should be prefixed with ‘CC’

Please note it is not ideal to put code directly into the workflow and you must consult with your K2 lead before doing so.

 

Line Rules

Lines Rules must have a label that is prefixed with LR and then what the rule is checking for. As seen in the image above.

 

Line Colours

The outcome lines should also be coloured to represent what they are doing and to also help visually navigate what the workflow is doing. This is especially helpful in larger workflows

Line Type

Colour

Approve (Positive Line) Green
Reject (Negative Line) Red
Rework line Blue
Normal line Default

 

 

 

SmartObjects

This sections looks at what is needed in each SmartObject. If you have a number of stored procedures that interact with same table, then same SmartObject can be used with the additional methods and properties added.

Where to create a SmartObject

SmartObject should be created in visual studio and in the correct folder

SmartObject Name

The name of the SmartObject like the rest of the K2 artefacts should carry the same naming convention.

[ProjectName].[Area].smo.[Name of SmartObject]

SmartObject Description

The description of the SmartObject should describe what it does and what story it belongs to.

SmartObject Parameters

All required input properties should be parameters and be prefixed ‘p’

SmartObject Properties

  Naming Conventions

The property names must be named correctly with no ‘PropertyName(1)’ used

There should be no names with spacing between words or no _ . Camel case should be used

  Property Description

A description of the property used be filled in describing what the property does.

  Property Data types

The correct property type should be used and most of the time K2 will take care of this automatically. The only exception is for file types. You will need to manually change the property data type for the file property from ‘Memo’ to ‘File’

   Methods

When creating a method the following needs to be checked.

Method Name

The name should represent what it does

Method Description

Should describe what the method does and what store procedure it is calling

Method Types

In some cases the correct method type is not selected. After the service object method is collected.

methodtype

So you once the service object method has been selected and the properties created and bound to the service object. You need to edit the method and choose the correct method type.

An example of this sometimes a store procedure that returns a single row of data is sometimes returned as a list method. This is incorrect and would cause problems later on in the view design.

So in this case you would need to change the method type to ‘Read’ and then save.

 

Testing SmartObjects

After creating the SmartObject, it can be tested by using the SmartObject Service Tester which if its not on your task bar it can be found here ‘C:\Program Files (x86)\K2 blackpearl\Bin\SmartObject Service Tester.exe’

direct.png

 

Using this application allows you then to test your SmartObject to see if it is working correctly.

 

How to test your SmartObject

To test your SmartObject do the following

  1. Open the tester tool
  2. Expand ‘SmartObject Explorer’
  3. Navigate to your SmartObject
  4. Right Click on the object and select ‘Execute SmartObject’

ServiceObjectTester

5.From here we can now test the methods of the SmartObject. Select a method from the dropdown list and fill out the parameters as required and then click on ‘Execute’

6.The returned data if there is any will be returned in the underneath the parameters.

Building a K2 Smartform spider part 6

So we have looked at the API and we have made the endpoint assembly and this into a couple of SmartObjects. Now that we have these  we can now  build some View and Smartforms to show the data on the Smartforms.

SmartObject ReCap

So we have two SmartObjects one for the Views and one for the Smartforms.

Spider.smo.Views

Looks at details of the view from it’s basic details to it’s rules

View.smo

Spider.smo.Smartform

Looks at details of the view from it’s basic details to it’s rules

form.smo

 

The views of FormSpider

VIEWSPIDER

Now we can build some views so we can interact with the SmartObjects. Majority of the views will be list views as the methods return back multiple rows of data. The only two that are not going to be list views will be the two forms that allow us to search for a specific Smartform or view.

 

Each of the items views contains the following fields

  1. Artifact name (picker control)
  2. Description (data label)
  3. Version (data label)

view

formheader

Each of the item views has the same rule, which is when the picker control is changed we then transfer the description, version etc.. from the selected row into the view data labels.

pickerchanged

List Views

Parameters

Each of the list views will have the parameter of ‘ViewName’ or ‘FormName’ depending on whether it’s a form view or view view. This allows us to pass in a form name or view name and then load up the parameters for that arifact.

parameters

Properties

We also have view to show all the properties of the artifact

properties

Views in a form and forms that have a certain view in

viewsinform

Controls

The control views shows all the controls on the artifact, whether they are hidden controls such table properties and user controls.

formcontrols

By clicking on a control in the list,  there will be a pop up that show’s all the properties of that control.

CONTROLPROP

Rules

There is also a view to show all the events on the artifact

rules

By clicking on an event,  we are then shown  all the conditions, actions for that event

viewrules

 

SmartObjects

Using the Form Spider assembly, we can also access the details of the SmartObjects, such as its properties, methods and the views the being used by that SmartObject.

SmartObjectsSpider

We could take this even further by getting the details of the service object, but for this version I have just kept it with the basic details

Workflow

Like with the SmartObjects we can also use Form spider to have a look at the workflows on the K2 server as well.

The current version will show the basic details about the workflow such as its description, number of versions etc..

It also shows the process data fields and XML data fields, the activities that make yo the workflow and also  what Smartforms and Views are linked to that particular workflow.

workflowspider

Also by clicking on activity we also get to see the events that make up that activity as well.

workflowevents

Below is a video that goes through the  a demo of Form Spider

Building a K2 Smartform spider part 5

In parts 1 to 4 we looked at some of the different classes and methods that are available in the Smartform API. In part 5 we will be taking these methods and building a complete assembly for us to use.

Smartform spider library

The Smartform spider library is what we will use to create an endpoint assembly so we can create SmartObjects from it and then eventually Smartforms.

Getting Started

1. Open up Visual studio and create a class library project

2.Add the following references to the library

using SourceCode.Forms.Management;
using SourceCode.Forms.Deployment;
using SourceCode.Forms;
using SourceCode.Forms.Authoring;

 

3.Create a class called ‘Common.cs’, this will contain all the common classes to hold the form and view data.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CloudFish.FormSpider
{
public class SmartFormViewSml
{
public string name
{
get;
set;
}
public string displayname
{
get;
set;
}

public string description
{
get;
set;
}

public Guid guid
{
get;
set;
}
public int version
{
get;
set;
}
}

public class SmartFormView
{
public string name
{
get;
set;
}
public string displayname
{
get;
set;
}

public string description
{
get;
set;
}

public Guid guid
{
get;
set;
}
public int version
{
get;
set;
}

public string theme
{
get;
set;
}

}

public class SmartFormViewProperties
{
public string name
{

get;
set;
}

public string value
{

get;
set;
}
}
public class SmartFormViewControls
{
public string name
{
get;
set;
}

public string type
{
get;
set;
}

public Guid guid
{
get;
set;
}

}

public class SmartFormViewParameters
{
public string name
{
get;
set;
}

public string type
{
get;
set;
}

public string defaultvalue
{
get;
set;
}
}

public class SmartFormViewEvents
{
public string name
{
get;
set;
}

public string type
{
get;
set;
}

public Guid GUID
{
get;
set;
}
}
public class SmartFromViewHandlers
{

public string Name
{

get;
set;
}

public Guid GUID
{

get;
set;
}

 

public Guid GUID
{

get;
set;
}

}

public class SmartFormViewActions
{
public Guid GUID
{

get;
set;
}

public Guid viewguid
{
get;
set;
}

public Guid formguid
{
get;
set;
}

public string method
{
get;
set;
}

public string executiontype
{
get;
set;
}

public Guid controlguid
{
get;
set;
}

public string actiontype
{
get;
set;
}
}
public class SmartFormViewActionParameters
{

 

public string targettype
{

get;
set;
}
public string targetpath
{

get;
set;
}
public string targetid
{

get;
set;
}

public string sourcevalue
{

get;
set;
}

public string sourcetype
{

get;
set;
}
public string sourcepath
{

get;
set;
}

public string sourceid
{

get;
set;
}
}
public class SmartFormViewActionValidation
{

public string status
{
get;
set;
}
}

public class SmartFormViewActionValidationMessage
{

public string message { get; set; }
}

}

 

4.Create another class called ‘properties.cs’, this will contain a generic method to get the properties of an item.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CloudFish.FormSpider
{
class Properties
{

public List<SmartFormViewProperties> ArtefactProperties(SourceCode.Forms.Authoring.PropertyCollection properties)
{
List<SmartFormViewProperties> list = new List<SmartFormViewProperties>();
foreach (SourceCode.Forms.Authoring.Property prop in properties)
{
list.Add(new SmartFormViewProperties
{
name = prop.Name,
value = prop.Value,

});

}
return list;
}
}
}

SmartObject Object

5.Now lets create a class called ‘Smartform.cs’ this class will contain all the public static methods for accessing key information from a Smartform and is the object that will be used as a Service Object by K2.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SourceCode.Forms.Management;
using SourceCode.Forms.Deployment;
using SourceCode.Forms;
using SourceCode.Forms.Authoring;

/// <summary>
/// Explores the content of the SmartForm
/// </summary>
namespace CloudFish.FormSpider
{
public class Smartform
{

/// <summary>
/// Gets the details of the form
/// </summary>
/// <param name=”FormName”></param>
public static SmartFormView LoadForm(string formname)
{

FormsManager frm = new FormsManager(“dlx”, 5555);
FormInfo forminfo = frm.GetForm(formname);
SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetViewDefinition(formname));

SmartFormView sf = new SmartFormView();
sf.guid = form.Guid;
sf.description = form.Description;

sf.displayname = form.DisplayName;
sf.name = form.Name;
sf.guid = form.Guid;
sf.theme = form.Theme;

return sf;
}
/// <summary>
/// Get a list of all the forms
/// </summary>
/// <returns></returns>
public static List<SmartFormView> GetAllForms()
{
List<SmartFormView> list = new List<SmartFormView>();
FormsManager frm = new FormsManager(“dlx”, 5555);
FormExplorer formexplorer = frm.GetForms();
foreach (SourceCode.Forms.Management.FormInfo forminfo in formexplorer.Forms)
{

SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(forminfo.Name));

list.Add(new SmartFormView
{
name = forminfo.Name,
displayname = forminfo.DisplayName,
description = forminfo.Description,
guid = forminfo.Guid,
version = forminfo.Version

});

}
return list;
}
/// <summary>
/// Get a list of all the forms that contain a certain view
/// </summary>
/// <param name=”ViewName”></param>
/// <returns></returns>
public static List<SmartFormView> GetAllFormsbyView(string ViewName)
{
List<SmartFormView> list = new List<SmartFormView>();
FormsManager frm = new FormsManager(“dlx”, 5555);
FormExplorer formexplorer = frm.GetFormsForView(ViewName);
foreach (SourceCode.Forms.Management.FormInfo forminfo in formexplorer.Forms)
{
SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetViewDefinition(forminfo.Name));

list.Add(new SmartFormView
{
name = forminfo.Name,
displayname = forminfo.DisplayName,
description = forminfo.Description,
guid = forminfo.Guid,
version = forminfo.Version

});

}
return list;
}
/// <summary>
/// List of form properties
/// </summary>
/// <param name=”FormName”></param>
/// <returns></returns>
public static List<SmartFormViewProperties> GetFormProperties(string FormName)
{
List<SmartFormView> list = new List<SmartFormView>();
FormsManager frm = new FormsManager(“dlx”, 5555);

SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(FormName));
Properties prop = new Properties();
return prop.ArtefactProperties( form.Properties);

}
/// <summary>
/// Gets a list of the form parameters
/// </summary>
/// <param name=”FormName”></param>
/// <returns></returns>
public static List<SmartFormViewParameters> FormParameters(string FormName)
{
List<SmartFormViewParameters> list = new List<SmartFormViewParameters>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(FormName));

foreach (SourceCode.Forms.Authoring.FormParameter parameter in form.Parameters)
{
list.Add(new SmartFormViewParameters
{
name = parameter.Name,
type = parameter.DataType.ToString(),
defaultvalue = parameter.DefaultValue
});
}

return list;

}

/// <summary>
/// Form Controls
/// </summary>
/// <param name=”FormName”></param>
/// <returns></returns>
public static List<SmartFormViewControls> FormControls(string FormName)
{
List<SmartFormViewControls> list = new List<SmartFormViewControls>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(FormName));

foreach (SourceCode.Forms.Authoring.Control control in form.Controls)
{
list.Add(new SmartFormViewControls
{
name = control.Name,
type = control.Type,
guid = control.Guid,

});

}
return list;
}
/// <summary>
/// Form Events
/// </summary>
/// <param name=”FormName”></param>
/// <returns></returns>
public static List<SmartFormViewEvents> FormEventsEvents(string FormName)
{
List<SmartFormViewEvents> list = new List<SmartFormViewEvents>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(FormName));

foreach (SourceCode.Forms.Authoring.Eventing.Event ev in form.Events)
{

if (ev.SourceType == SourceCode.Forms.Authoring.Eventing.EventSourceType.Rule)
{
list.Add(new SmartFormViewEvents
{
name = ev.Name,
type = ev.EventType.ToString(),
GUID = ev.Guid

});

}
}

return list;
}

/// <summary>
/// Event Handlers
/// </summary>
/// <param name=”EventGUID”></param>
/// <returns></returns>
public static List<SmartFromViewHandlers> FormHandlers(String FormName,Guid EventGUID)
{
List<SmartFromViewHandlers> list = new List<SmartFromViewHandlers>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(FormName));
var ev = form.Events[EventGUID];

SourceCode.Forms.Authoring.Eventing.Event e = form.Events[EventGUID];

foreach (SourceCode.Forms.Authoring.Eventing.Handler handle in e.Handlers)
{
list.Add(new SmartFromViewHandlers
{

Name = handle.HandlerType.ToString(),
GUID = handle.Guid
});

}
return list;

}

/// <summary>
/// Conditions
/// </summary>
/// <param name=”FormName”></param>
/// <param name=”EventGUID”></param>
/// <param name=”HandleGUID”></param>
/// <returns></returns>
public static List<SmartFormViewConditions> ArtefactConditions(String FormName,Guid EventGUID,Guid HandleGUID)
{
List<SmartFormViewConditions> list = new List<SmartFormViewConditions>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(FormName));
var ev = form.Events[EventGUID].Handlers[HandleGUID];
SourceCode.Forms.Authoring.Eventing.Handler e = form.Events[EventGUID].Handlers[HandleGUID];

foreach (SourceCode.Forms.Authoring.Eventing.Condition condition in e.Conditions)
{

list.Add(new SmartFormViewConditions
{
GUID = condition.Guid,

});
}

return list;
}
/// <summary>
/// Actions
/// </summary>
/// <param name=”HandleGUID”></param>
/// <returns></returns>
public static List<SmartFormViewActions> ArtefactActionss(String FormName, Guid EventGUID, Guid HandleGUID)
{
List<SmartFormViewActions> list = new List<SmartFormViewActions>();
FormsManager frm = new FormsManager(“dlx”, 5555);

SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(FormName));
var ev = form.Events[EventGUID].Handlers[HandleGUID];
SourceCode.Forms.Authoring.Eventing.Handler e = form.Events[EventGUID].Handlers[HandleGUID];

foreach (SourceCode.Forms.Authoring.Eventing.Action action in e.Actions)
{
list.Add(new SmartFormViewActions
{
GUID = action.Guid,

viewguid = action.ViewGuid,
method = action.Method,
formguid = action.FormGuid,
executiontype = action.ExecutionType.ToString(),
controlguid = action.ControlGuid,
actiontype = action.ActionType.ToString()
});
}
return list;
}
/// <summary>
/// Actions Parameters
/// </summary>
/// <param name=”FormName”></param>
/// <param name=”EventGUID”></param>
/// <param name=”HandleGUID”></param>
/// <param name=”ActionGUID”></param>
/// <returns></returns>
public static List<SmartFormViewActionParameters> SmartFormViewActionParameters(String FormName, Guid EventGUID, Guid HandleGUID,Guid ActionGUID)
{
List<SmartFormViewActionParameters> list = new List<SmartFormViewActionParameters>();
FormsManager frm = new FormsManager(“dlx”, 5555);

SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(FormName));
SourceCode.Forms.Authoring.Eventing.Action e = form.Events[EventGUID].Handlers[HandleGUID].Actions[ActionGUID];

foreach (SourceCode.Forms.Authoring.Eventing.Mapping map in e.Parameters)
{

list.Add(new SmartFormViewActionParameters
{
targettype = map.TargetType.ToString(),
targetpath = map.TargetPath,
targetid = map.TargetID,
sourcevalue = map.SourceValue,
sourcetype = map.SourceType.ToString(),
sourcepath = map.SourcePath,
sourceid = map.SourceID

});
}

return list;
}
/// <summary>
/// Actions Results
/// </summary>
/// <param name=”FormName”></param>
/// <param name=”EventGUID”></param>
/// <param name=”HandleGUID”></param>
/// <param name=”ActionGUID”></param>
/// <returns></returns>
public static List<SmartFormViewActionParameters> SmartFormViewActionResults(String FormName, Guid EventGUID, Guid HandleGUID, Guid ActionGUID)
{
List<SmartFormViewActionParameters> list = new List<SmartFormViewActionParameters>();
FormsManager frm = new FormsManager(“dlx”, 5555);

SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(FormName));
SourceCode.Forms.Authoring.Eventing.Action e = form.Events[EventGUID].Handlers[HandleGUID].Actions[ActionGUID];

foreach (SourceCode.Forms.Authoring.Eventing.Mapping map in e.Results)
{

list.Add(new SmartFormViewActionParameters
{
targettype = map.TargetType.ToString(),
targetpath = map.TargetPath,
targetid = map.TargetID,
sourcevalue = map.SourceValue,
sourcetype = map.SourceType.ToString(),
sourcepath = map.SourcePath,
sourceid = map.SourceID

});
}

return list;
}
/// <summary>
/// Validation Messages
/// </summary>
/// <param name=”FormName”></param>
/// <param name=”EventGUID”></param>
/// <param name=”HandleGUID”></param>
/// <param name=”ActionGUID”></param>
/// <returns></returns>
public static List<SmartFormViewActionValidationMessage> SmartFormViewActionValidation(String FormName, Guid EventGUID, Guid HandleGUID, Guid ActionGUID)
{
List<SmartFormViewActionValidationMessage> list = new List<SmartFormViewActionValidationMessage>();
FormsManager frm = new FormsManager(“dlx”, 5555);

SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(FormName));
SourceCode.Forms.Authoring.Eventing.Action e = form.Events[EventGUID].Handlers[HandleGUID].Actions[ActionGUID];

foreach (SourceCode.Forms.Authoring.ValidationMessage val in e.Validation.Messages)
{

list.Add(new SmartFormViewActionValidationMessage
{
message = val.Message
});
}

return list;
}

}
}

View Object

6.Create a class called ‘View.cs’, like ‘Smartform.cs’ this class will contain all the methods for accessing information about views  on the K2 environment

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SourceCode.Forms.Management;
using SourceCode.Forms.Deployment;
using SourceCode.Forms;
using SourceCode.Forms.Authoring;

namespace CloudFish.FormSpider
{
public class Views
{

/// <summary>
/// Gets all the views on the environment
/// </summary>
/// <returns></returns>
public static List<SmartFormView> GetAllViews()
{
List<SmartFormView> list = new List<SmartFormView>();
FormsManager frm = new FormsManager(“dlx”, 5555);
ViewExplorer formexplorer = frm.GetViews();
foreach (SourceCode.Forms.Management.ViewInfo viewinfo in formexplorer.Views)
{

SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(viewinfo.Name));

list.Add(new SmartFormView
{
name = viewinfo.Name,
displayname = viewinfo.DisplayName,
description = viewinfo.Description,
guid = viewinfo.Guid,
version = viewinfo.Version,
});

}
return list;
}

/// <summary>
/// Gets all the views that are attached to a form
/// </summary>
/// <param name=”formname”></param>
/// <returns></returns>
public static List<SmartFormView> GetAllViews(string formname)
{
List<SmartFormView> list = new List<SmartFormView>();
FormsManager frm = new FormsManager(“dlx”, 5555);
ViewExplorer formexplorer = frm.GetViewsForForm(formname);
foreach (SourceCode.Forms.Management.ViewInfo viewinfo in formexplorer.Views)
{

SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(viewinfo.Name));

list.Add(new SmartFormView
{
name = viewinfo.Name,
displayname = viewinfo.DisplayName,
description = viewinfo.Description,
guid = viewinfo.Guid,
version = viewinfo.Version,

});

}
return list;
}
/// <summary>
/// List of view properties
/// </summary>
/// <param name=”ViewName”></param>
/// <returns></returns>
public static List<SmartFormViewProperties> GetViewProperties(string ViewName)
{
List<SmartFormView> list = new List<SmartFormView>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(ViewName));
Properties prop = new Properties();
return prop.ArtefactProperties(view.Properties);

}
/// <summary>
/// Gets a list of the view parameters
/// </summary>
/// <param name=”ViewName”></param>
/// <returns></returns>
public static List<SmartFormViewParameters> ViewParameters(string ViewName)
{
List<SmartFormViewParameters> list = new List<SmartFormViewParameters>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(ViewName));

foreach (SourceCode.Forms.Authoring.ViewParameter parameter in view.Parameters)
{
list.Add(new SmartFormViewParameters
{
name = parameter.Name,
type = parameter.DataType.ToString(),
defaultvalue = parameter.DefaultValue
});
}

return list;

}

/// <summary>
/// View Controls
/// </summary>
/// <param name=”ViewName”></param>
/// <returns></returns>
public static List<SmartFormViewControls> ViewControls(string ViewName)
{
List<SmartFormViewControls> list = new List<SmartFormViewControls>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(ViewName));

foreach (SourceCode.Forms.Authoring.Control control in view.Controls)
{
list.Add(new SmartFormViewControls
{
name = control.Name,
type = control.Type,
guid = control.Guid,

});

}
return list;
}
/// <summary>
/// View Events
/// </summary>
/// <param name=”ViewName”></param>
/// <returns></returns>
public static List<SmartFormViewEvents> ViewEventsEvents(string ViewName)
{
List<SmartFormViewEvents> list = new List<SmartFormViewEvents>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(ViewName));

foreach (SourceCode.Forms.Authoring.Eventing.Event ev in view.Events)
{
if (ev.SourceType == SourceCode.Forms.Authoring.Eventing.EventSourceType.Rule)
{
list.Add(new SmartFormViewEvents
{
name = ev.Name,
type = ev.EventType.ToString(),
GUID = ev.Guid

});

}
}

return list;
}

/// <summary>
/// Event Handlers
/// </summary>

/// <param name=”ViewName”></param>
/// <param name=”EventGUID”></param>
/// <returns></returns>
public static List<SmartFromViewHandlers> ViewHandlers(String ViewName, Guid EventGUID)
{
List<SmartFromViewHandlers> list = new List<SmartFromViewHandlers>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(ViewName));
var ev = view.Events[EventGUID];

SourceCode.Forms.Authoring.Eventing.Event e = view.Events[EventGUID];

foreach (SourceCode.Forms.Authoring.Eventing.Handler handle in e.Handlers)
{
list.Add(new SmartFromViewHandlers
{

Name = handle.HandlerType.ToString(),
GUID = handle.Guid
});

}
return list;

}

/// <summary>
/// Conditions
/// </summary>
/// <param name=”ViewName”></param>
/// <param name=”EventGUID”></param>
/// <param name=”HandleGUID”></param>
/// <returns></returns>
public static List<SmartFormViewConditions> ArtefactConditions(String ViewName, Guid EventGUID, Guid HandleGUID)
{
List<SmartFormViewConditions> list = new List<SmartFormViewConditions>();
FormsManager frm = new FormsManager(“dlx”, 5555);
SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(ViewName));
var ev = view.Events[EventGUID].Handlers[HandleGUID];
SourceCode.Forms.Authoring.Eventing.Handler e = view.Events[EventGUID].Handlers[HandleGUID];

foreach (SourceCode.Forms.Authoring.Eventing.Condition condition in e.Conditions)
{

list.Add(new SmartFormViewConditions
{
GUID = condition.Guid

});
}

return list;
}
/// <summary>
/// Actions
/// </summary>
/// <param name=”HandleGUID”></param>
/// <returns></returns>
public static List<SmartFormViewActions> ArtefactActionss(String ViewName, Guid EventGUID, Guid HandleGUID)
{
List<SmartFormViewActions> list = new List<SmartFormViewActions>();
FormsManager frm = new FormsManager(“dlx”, 5555);

SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(ViewName));
var ev = view.Events[EventGUID].Handlers[HandleGUID];
SourceCode.Forms.Authoring.Eventing.Handler e = view.Events[EventGUID].Handlers[HandleGUID];

foreach (SourceCode.Forms.Authoring.Eventing.Action action in e.Actions)
{
list.Add(new SmartFormViewActions
{
GUID = action.Guid,

viewguid = action.ViewGuid,
method = action.Method,
formguid = action.FormGuid,
executiontype = action.ExecutionType.ToString(),
controlguid = action.ControlGuid,
actiontype = action.ActionType.ToString()
});
}
return list;
}
/// <summary>
/// Actions Parameters
/// </summary>
/// <param name=”ViewName”></param>
/// <param name=”EventGUID”></param>
/// <param name=”HandleGUID”></param>
/// <param name=”ActionGUID”></param>
/// <returns></returns>
public static List<SmartFormViewActionParameters> ViewActionParameters(String ViewName, Guid EventGUID, Guid HandleGUID, Guid ActionGUID)
{
List<SmartFormViewActionParameters> list = new List<SmartFormViewActionParameters>();
FormsManager frm = new FormsManager(“dlx”, 5555);

SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(ViewName));
SourceCode.Forms.Authoring.Eventing.Action e = view.Events[EventGUID].Handlers[HandleGUID].Actions[ActionGUID];

foreach (SourceCode.Forms.Authoring.Eventing.Mapping map in e.Parameters)
{

list.Add(new SmartFormViewActionParameters
{
targettype = map.TargetType.ToString(),
targetpath = map.TargetPath,
targetid = map.TargetID,
sourcevalue = map.SourceValue,
sourcetype = map.SourceType.ToString(),
sourcepath = map.SourcePath,
sourceid = map.SourceID

});
}

return list;
}
/// <summary>
/// Actions Results
/// </summary>
/// <param name=”ViewName”></param>
/// <param name=”EventGUID”></param>
/// <param name=”HandleGUID”></param>
/// <param name=”ActionGUID”></param>
/// <returns></returns>
public static List<SmartFormViewActionParameters> ViewActionResults(String ViewName, Guid EventGUID, Guid HandleGUID, Guid ActionGUID)
{
List<SmartFormViewActionParameters> list = new List<SmartFormViewActionParameters>();
FormsManager frm = new FormsManager(“dlx”, 5555);

SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(ViewName));
SourceCode.Forms.Authoring.Eventing.Action e = view.Events[EventGUID].Handlers[HandleGUID].Actions[ActionGUID];

foreach (SourceCode.Forms.Authoring.Eventing.Mapping map in e.Results)
{

list.Add(new SmartFormViewActionParameters
{
targettype = map.TargetType.ToString(),
targetpath = map.TargetPath,
targetid = map.TargetID,
sourcevalue = map.SourceValue,
sourcetype = map.SourceType.ToString(),
sourcepath = map.SourcePath,
sourceid = map.SourceID

});
}

return list;
}
/// <summary>
/// Validation Messages
/// </summary>
/// <param name=”ViewName”></param>
/// <param name=”EventGUID”></param>
/// <param name=”HandleGUID”></param>
/// <param name=”ActionGUID”></param>
/// <returns></returns>
public static List<SmartFormViewActionValidationMessage> ViewActionValidation(String ViewName, Guid EventGUID, Guid HandleGUID, Guid ActionGUID)
{
List<SmartFormViewActionValidationMessage> list = new List<SmartFormViewActionValidationMessage>();
FormsManager frm = new FormsManager(“dlx”, 5555);

SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(ViewName));
SourceCode.Forms.Authoring.Eventing.Action e = view.Events[EventGUID].Handlers[HandleGUID].Actions[ActionGUID];

foreach (SourceCode.Forms.Authoring.ValidationMessage val in e.Validation.Messages)
{

list.Add(new SmartFormViewActionValidationMessage
{
message = val.Message

});
}

return list;
}

}
}

7.Now we can build the solution and we are ready to tell K2 about it and make it a service object and then a SmartObject.

You can download the code from this location

 

Creating the Service Object

Creating the service object is really easy, open up the ‘SmartObject Service Tester’

ServiceObjectTester

1.Right Click on the ‘Endpoints Assembly’ and click on ‘Register Service Instance’

AddServiceInstance

2.In the ‘Assembly Full Path’ enter in the full address of where the dll of the assembly is kept.

3.Click on ‘Next’ and enter in ‘Form Spider’ in the ‘Display Name’ and ‘System Name’

4.Click ‘Finish’

K2 will then integrate the Dll and map out all the public static methods.

ServiceObjectTester

Now that we have the service objects we can now create the SmartObjects. For this project i have just created two SmartObjects one for Smartforms and for the Views. Each of these objects will contain all the public static methods we created in the class library earlier.

Below is an example of one

ServiceObjectTester

 

 

 

More information on Endpoint Assemblies can be found here 

 

In the final part of this series we will take the SmartObjects and build the  Smartforms and Views.

 

 

 

 

Building a K2 Smartform spider part 4

Rules

In part 3, we looked at how to get additional information from Smartform such as parameters and controls. In part 4 we will now look at how to be able to get the rules from the forms and views.

This is probably most complex part of the API as it involves a number of different methods to get the  necessary information.

To access the rules  of the artifact we need to use the following code as we did in part 3 this will give us access to the Events object.

Code for a Smartform

SourceCode.Forms.Authoring.Form form = new SourceCode.Forms.Authoring.Form(frm.GetFormDefinition(‘form name’));

Code for a view

SourceCode.Forms.Authoring.View view = new SourceCode.Forms.Authoring.View(frm.GetViewDefinition(‘View Name’));

So lets take a look and see what we can from the events object

Events

Majority of rules in Smartforms and views start with an event.  An event is when something happens such as a ‘click of a button’.

public List<SmartFormViewEvents> ArtefactEvents(SourceCode.Forms.Authoring.Eventing.EventCollection events)
{
List<SmartFormViewEvents> list = new List<SmartFormViewEvents>();
foreach (SourceCode.Forms.Authoring.Eventing.Event ev in events)
{

if (ev.SourceType == SourceCode.Forms.Authoring.Eventing.EventSourceType.Rule)
{
list.Add(new SmartFormViewEvents
{
name = ev.Name,
type = ev.EventType.ToString(),
handlers = ev.Handlers,
properties = ev.Properties

});

}
}

return list;
}

The above method gives a list of all the events on the artifact and access to more additional objects

First thing we do is we loop through the event collection and we check to see if the event type is the ‘Rule’ type.

if (ev.SourceType == SourceCode.Forms.Authoring.Eventing.EventSourceType.Rule)

The objects we want to have access to are the following

  1. Name
  2. Type
  3. Handlers
  4. Properties

The important object from this is the Handlers, as this will give us access to the Conditions and Actions of the rule.

Handlers

The handler object is where we can drill down into the rule and access the conditions of the rule and also the actions of the rule.

public List<SmartFromViewHandlers> ArtefactHandlers(SourceCode.Forms.Authoring.Eventing.HandlerCollection handlers)
{
List<SmartFromViewHandlers> list = new List<SmartFromViewHandlers>();
foreach (SourceCode.Forms.Authoring.Eventing.Handler handle in handlers)
{
list.Add(new SmartFromViewHandlers
{
Actions = handle.Actions,
Conditions = handle.Conditions,
Name = handle.HandlerType.ToString()
});

}

return list;

}

The methods starts by passing in the handler object from the previous method, now the method can loop through the all the handlers for that particular event

foreach (SourceCode.Forms.Authoring.Eventing.Handler handle in handlers)

On each loop, we can then access the following objects

  1. Name
  2. Conditions
  3. Actions

Conditions

Conditions check to see if something is equal to true, for example ‘all the required fields are entered’.

public List<SmartFormViewConditions> ArtefactConditions(SourceCode.Forms.Authoring.Eventing.ConditionCollection conditions)
{
List<SmartFormViewConditions> list = new List<SmartFormViewConditions>();
foreach (SourceCode.Forms.Authoring.Eventing.Condition condition in conditions)
{

list.Add(new SmartFormViewConditions
{
Property = condition.Properties
});
}

return list;
}

 

Actions

So we can access the event, the conditions and now lets look how we can access the actions of a rule.

public List<SmartFormViewActions> ArtefactActionss(SourceCode.Forms.Authoring.Eventing.ActionCollection actions)
{
List<SmartFormViewActions> list = new List<SmartFormViewActions>();

foreach (SourceCode.Forms.Authoring.Eventing.Action action in actions)
{
list.Add(new SmartFormViewActions
{
properties = action.Properties,
parameters = action.Parameters,
results = action.Results,
validation = action.Validation,
viewguid = action.ViewGuid,
method = action.Method,
formguid = action.FormGuid,
executiontype = action.ExecutionType.ToString(),
controlguid = action.ControlGuid,
actiontype = action.ActionType.ToString()
});
}
return list;
}

Using the above method, we can get the complete information of the action.  The action opens up some additional objects that we can explore to get more detailed information about the action.

Below are some examples of the more useful ones

Action Parameters

public List<SmartFormViewActionParameters> SmartFormViewActionParameters(SourceCode.Forms.Authoring.Eventing.MappingCollection parameters)
{
List<SmartFormViewActionParameters> list = new List<SmartFormViewActionParameters>();
foreach (SourceCode.Forms.Authoring.Eventing.Mapping map in parameters)
{

list.Add(new SmartFormViewActionParameters
{

validation = map.Validation,
targettype = map.TargetType.ToString(),
targetpath = map.TargetPath,
targetid = map.TargetID,
sourcevalue = map.SourceValue,
sourcetype = map.SourceType.ToString(),
sourcepath = map.SourcePath,
sourceid = map.SourceID

});
}

return list;
}

Action Results

public List<SmartFormViewActionParameters> SmartFormViewActionResults(SourceCode.Forms.Authoring.Eventing.MappingCollection Results)
{
List<SmartFormViewActionParameters> list = new List<SmartFormViewActionParameters>();
foreach (SourceCode.Forms.Authoring.Eventing.Mapping result in Results)
{

list.Add(new SmartFormViewActionParameters
{

validation = result.Validation,
targettype = result.TargetType.ToString(),
targetpath = result.TargetPath,
targetid = result.TargetID,
sourcevalue = result.SourceValue,
sourcetype = result.SourceType.ToString(),
sourcepath = result.SourcePath,
sourceid = result.SourceID

});
}
return list;
}

Action Validation

public List<SmartFormViewActionValidation> SmartFormViewActionValidations(SourceCode.Forms.Authoring.ValidationResult validations)
{

List<SmartFormViewActionValidation> list = new List<SmartFormViewActionValidation>();

SourceCode.Forms.Authoring.ValidationResult validation = validations;

list.Add(new SmartFormViewActionValidation
{
messages = validation.Messages,
status = validation.Status.ToString()
});
return list;
}

Action Messages

public List<SmartFormViewActionValidationMessage> SmartFormViewActionValidationMessages(ValidationMessageCollection messages)
{
List<SmartFormViewActionValidationMessage> list = new List<SmartFormViewActionValidationMessage>();
foreach (ValidationMessage message in messages)
{
list.Add(new SmartFormViewActionValidationMessage
{
message = message.Message
});
}
return list;
}

 

In the next part, we will take the methods we have explored in parts 1 to 4 and put it altogether to build the form spider.

I will make the source code available at the end of this series.