Introduction to scripts in ELMA
This article helps you get started with writing scripts in ELMA. It is presumed that you are familiar with ELMA basic functions and meet the script developer requirements. If you are new to ELMA, read ELMA BPM quick start guide.
ELMA API is a tool for developing scripts. Follow this link to learn more.
Getting started. Writing your first script in ELMA.
Scripts are created in ELMA Designer. You can use scripts in many components of ELMA (e.g. business processes, objects, forms). Commonly, scripts are used in business processes.
To create a script, first, you need to create a business process:
- Add a dynamic swimlane (to allow any ELMA user to start the process);
- Add context variables:
- Displayed name = Input; Type = Fraction; Can be empty = unchecked; Default value = 0;
- Displayed name = Calculation result; Type = Fraction; Can be empty = unchecked; Default value = 0;
- Add Start event;
- Add User Task activity and add both context variables to the task form (Input = editable property, Calculation result = read-only property);
- Add Script activity;
- Add End event;
The created model should be similar to the one shown below:
After that, you can create the script. To do so, go to the settings of added Script activity – Script tab – click Add Script – click Create.
The following window will appear:
ELMA provides two means of working with scripts:
- Script Builder – visual editor for scripts. You can learn more about it in ELMA Help;
- Script Editor – standard markup editor.
Click Open Script Editor button to create a new script – DoCalculations. We recommend using Latin letters and CamelCase for script names.
After the script is created, click Go To to open it in the script editor. It should look like in the picture below:
This section contains all the scripts created in this business process. The script editor provides limited functions of IDE – integrated development environment (e.g. code formatting, code highlighting, IntelliSense). What do limited functions mean? By default, projects with business process scripts in ELMA Designer have only a few assemblies referenced, do not allow debugging and so on.
You can open a project in fully-functional IDE (either in SharpDevelop or in Microsoft Visual Studio) to get a full set of tools required for large and complicated projects:
For each business process, ELMA creates a class that inherits the base class EleWise.ELMA.Workflow.Scripts.ProcessScriptBase<Context>. Context is the process data class. On the picture above, you can see the definition of the class:
Context = EleWise.ELMA.Model.Entities.ProcessContext.P_Calculation;
Each method in this class represents a script in a business process. In this case, a new DoCalculations method is created and the process context is one of this method’s parameters:
public partial class P_Calculation_Scripts : EleWise.ELMA.Workflow.Scripts.ProcessScriptBase<Context>
{
/// <summary>
/// DoCalculations
/// </summary>
/// <param name="context">Process context</param>
public virtual void DoCalculations (Context context)
{
}
}
Let’s create a method that will add the Input value entered by a user to the previous calculation results:
public virtual void DoCalculations (Context context)
{
context.CalculationResult = context.Input + context.CalculationResult;
}
The script is quite simple. When a user starts the business process and enters the Input value in the Input data task, the script will add this value to the previous calculation results. After that, the user will see the calculation results on the task form.
However, the real projects' scripts will be much more complex. This example was only intended to introduce you to the basics of creating scripts in ELMA.
Second example. Using objects in scripts
Create/edit/receive information about ELMA objects is something that you need all the time when it comes to creating scripts. Let’s create a new object Pet in ELMA Designer, CRM group (it will store information about customers’ pets).
Add the following attributes to this object:
- Displayed name = Name; Type = String; Required field = yes; is a name = yes;
- Displayed name = Type; Type = Drop-Down List;
- Displayed name = Age; Type = Integer; Can be empty = yes;
- Displayed name = Owner; Type = Contact (All objects – CRM – Contact); Required field = yes; is a name = yes;
Save and publish the object and restart the server.
Now create a business process that will:
- Check if a customer has a pet;
- If they have no pets yet, a new one will be created.
Thus, the process includes two tasks:
- Search for objects according to selected criteria;
- Create a new object instance.
Creating an object instance
You have to create another business process for this (call it New Pet). Add the following context variables to this process:
- Displayed name = Contact; Type = Contact (All objects – CRM – Contact); Input variable = yes;
- Displayed name = Pet name; Type = String;
- Displayed name = Pet type; Type = Drop-Down-List (cat, dog, fish, snake);
- Displayed name = Pet age; Type = Integer; Can be empty = yes;
- Displayed name = New pet; Type = Pet (All objects – CRM – Pet);
- Displayed name = Error message; Type = String;
You need the Error message variable to show an error message if a field is filled in incorrectly.
Now add two user tasks and one script to the process model. The process model should look like in the figure:
In the Enter information about a pet task, add Contact, Pet name, Pet type and Pet age context variables to the task form. Mark Contact, Pet type, and Pet age as required. Allow Pet name variable to be editable but do not make it required – a script will check if a user fills it in.
In the Check the created pet task, add Contact, Pet name, Pet type, Pet age, New pet and Error message variables and make them read-only.
Now create a script.
Go to Add a pet task settings and create a new CreateNewPet script with the following code:
//Check if pet’s name is specified
if (String.IsNullOrWhiteSpace(context.PetName))
{
context.ErrorMessage = "Pet name is not specified";
// Exit the script
return;
}
//Create new Pet object instance
var newPet = EntityManager<Pet>.Create();
// Assign values to object properties
newPet.Name = context.PetName;
newPet.Type = context.PetType;
newPet.Age = context.PetAge;
newPet.Owner = context.Contact;
//Save the newly created property in the database
newPet.Save();
//Pass the new instance to the process context
context.NewPet = newPet;
As you can see, comments clearly describe the code. However, we need to clarify the usage of the EntityManager<Pet> class.
Entity Manager
ELMA has different types of entity managers. To learn more about them, please read this article.
EntityManager<T> class is typed and to access a specific manager you have to specify the object type (in our case the class of our object is Pet). After that, you can use the static field Instance to receive the entity manager’s object or simply call the static method Create(), that will create a new object of a specified type (in our case it is Pet):
var newPet = EntityManager<Pet>.Create();
Then you assign values to object properties from the process context and save the object to the database using the Save() method.
When you create an object instance, you always have to call the Save() method to save your object to the database. Note that the object will be saved only after the entire script has been executed. This means, that if you try to make a selection of the object instances in the same script, the newly created instance will be ignored.
Always make sure that all the required fields are filled in before saving the object!
If you now try to publish the business process, you will see the following error:
Script editor in ELMA Designer does not provide any assembly suggestions, so you can open this script in Visual Studio 2013 to solve this issue. As you can see in the picture below, IntelliSense found an assembly that you need to reference in order to define the Pet class:
You can add references to both ELMA assemblies and any .NET assemblies registered in GAC. In ELMA Community Edition, you can work with ELMA assemblies only.
After the assembly reference is added, you can publish the business process and start it in ELMA web application to create new pets for your customers.
Moreover, you can start the process right from the Contact page in ELMA web application, since the Contact variable of the business process is marked as Input:
Besides, if you do not enter the pet’s name, you will see the error message:
Search objects by selected criteria
You can search ELMA objects by selected criteria and process the results. You can use this to check if a customer already has a pet.
Let us add some lines (bold ones) to the script from the New pet business process that we created earlier:
public virtual void CreateNewPet (Context context)
{
//Check if the pet name is specified
if (String.IsNullOrWhiteSpace(context.PetName))
{
context.ErrorMessage = "Pet name is not specified";
// Exit the script
return;
}
//Searching pets by two criteria:
// 1) Specify the customer (the pet's owner)
// 2) Specify the pet's name (exact match)
var pets = EntityManager<Pet>.Instance.Find(p => p.Owner == context.Contact && p.Name == context.PetName);
//Check if there any search results
if (pets.Any())
{
context.ErrorMessage = "A pet with the specified name already exists";
//Exit the script
return;
}
//Create new Pet object instance
var newPet = EntityManager<Pet>.Create();
// Assign values to object properties
newPet.Name = context.PetName;
newPet.Type = context.PetType;
newPet.Age = context.PetAge;
newPet.Owner = context.Contact;
//Save the newly created property in the database
newPet.Save();
//Pass the new instance to the process context
context.NewPet = newPet;
}
As you can see, you use the entity manager again, but now you call the Find() method to search by selected criteria.
You can learn more about searching objects in ELMA in this article.
In this case, you search all objects of the Pet type where:
- Owner property value meets the value of the Contact context variable of the business process;
- Name property value meets the value of the Pet name context variable of the business process.
var pets = EntityManager<Pet>.Instance.Find(p => p.Owner == context.Contact && p.Name == context.PetName);
After that, check if there are any results found. If there are some, an error message is displayed:
Third example. Drop-down lists and editing.
In this example, use the same object and business process. Now you will create a script to do the following:
- When you select a pet’s owner, ELMA will show all the pets this person has;
- You will allow the user to edit the pet’s information right from the task form.
Add the following variable to the process context:
- Displayed name = Pet; Type = Drop-Down List; Select from list only = unchecked; Input variable = yes;
This list will contain the pets of the selected owner.
If you start the process from the contact page, the Contact object will be passed to the process instance automatically. You need to add a script that will fill in the information about the customer’s pets. In the process model, this script will be placed right after the Start event:
Code of the script:
public virtual void AtProcessStart (Context context)
{
AddPetToList(context);
}
private void AddPetToList(Context context)
{
//Get settings of the Pet variable (this variable is of the Drop-Down List type)
var settings = (DropDownListSettings)context.GetSettingsFor(c => c.Pet);
//Clear the elements this list may contain
settings.Items.Clear();
if (context.Contact == null)
{
//Check if the process was started from the Contact page. If it was started from the main page - exit the script
return;
}
//Find all the pets for the selected contact
var pets = EntityManager<Pet>.Instance.Find(p => p.Owner == context.Contact);
foreach(var pet in pets)
{
//Add a pet to the drop down list
settings.Items.Add(new DropDownItem(pet.Id.ToString(), pet.Name));
}
//Save the drop down list settings
settings.Save();
//Reset the value of the Pet context variable
context.Pet = null;
}
As you can see, here you define the additional method AddPetToList(), where you search the pets of the selected owner and then add them to the drop-down list. Note, that drop-down list settings are tied to the process context – this means, that they will be different for each process instance.
The next step is to configure the form of the first user task to allow creating/editing pet information. To do so, add the following context variables to the task form: Contact, Pet, Pet type and Pet age. Allow these variables to be editable.
After that, add a new script (call it OnAddPetFormLoad) that will be executed once the task form is loaded:
Code of the script:
public virtual void OnAddPetFormLoad (Context context, EleWise.ELMA.Model.Views.FormViewBuilder<Context> form)
{
var pet = GetPets(context);
if (pet != null)
{
//Make Pet type variable read-only on the form of this task
form.For(c => c.PetType).ReadOnly(true);
}
}
private Pet GetPets (Context context)
{
if (context.Contact == null) {
return null;
}
var petName = context.Pet.Value;
if (string.IsNullOrWhiteSpace(petName))
{
return null;
}
var pet = EntityManager<Pet>.Instance.Find (p => p.Owner == context.Contact && p.Name == petName).FirstOrDefault ();
return pet;
}
As you can see, here you make the Pet type variable read-only, if an existing pet is selected. We need this to forbid a user to change the pet type if this task was assigned following the Re-enter the data transition:
You can learn more about how to work with context variables on the forms in this article.
Now you have to configure how the task form changes when a user enters data. To do so, go to the settings of the Enter information about a pet task and add scripts to the task context variables. The scripts will be executed when the variable value is changed (these are so-called OnChange scripts).
Add OnContactSelect script to the Contact variable. To do so, go to the Contact variable settings – Additional tab – Add Script:
public virtual void OnContactSelect (Context context, EleWise.ELMA.Model.Views.FormViewBuilder<Context> form)
{
AddPetToList(context);
form.For(c => c.PetName).ReadOnly(false);
context.PetType = null;
context.PetAge = null;
}
Add OnPetSelect script to the Pet variable:
public virtual void OnPetSelect (Context context, EleWise.ELMA.Model.Views.FormViewBuilder<Context> form)
{
if (context.Contact == null)
{
return;
}
//Select a pet
var pet = GetPets(context);
if (pet != null)
{
//if a pet already exists for this contact, add the information about it to the process context
context.PetName = pet.Name;
context.PetAge = pet.Age;
context.PetType = pet.Type;
context.NewPet = (Pet)pet;
//Forbid to edit the Pet age variable
form.For(c => c.PetType).ReadOnly(true);
}
else
{
// if no pet exists for this contact, fill in the name of the selected pet and reset the values of other context variables
context.PetName = context.Pet.Value;
context.PetAge = 0;
context.PetType = null;
context.NewPet = null;
//Allow the user to edit the Pet type variable
form.For(c => c.PetType).ReadOnly(false);
}
Now when a user selects another Contact on the task form, the pets from the related Pet object will be added to the list and the values of other context variables will be reset. When a user selects a pet, values of Pet age and Pet type will be updated, and Pet type property will be set to read-only mode:
If you now enter the name of Pet, that does not exist for Contact yet, the Pet type field will be editable again:
Thus, using the drop-down lists and scripts on task forms, you can create dynamic (cascading) lists.
Further reading
Data model structure and object managers