Skip to content

Open Lowcode Developper Guide

Open Lowcode edited this page Feb 7, 2021 · 25 revisions

This version supports Open Lowcode Release v1.0.0. You may also want to consult the Frequently Asked Questions.

Table of Content

Architecture

Open Lowcode framework allows to build quickly an application. An application is made of different modules. A module is composed of coupled data objects. A data object is made of “bricks”: fields and properties.

Application Architecture

When a data object is defined, the Open Lowcode framework will build automatically all the action and pages necessary for the application to work. Just define your data objects, and you get a working application. Open Lowcode uses a three-tier architecture with:

  • the database storing all database
  • An application server managing business logic and security
  • A client that displays pages from server and launches actions to the server

Execution Architecture

Development Environment

To develop an Open Lowcode Application, you need a java project with the following content

Library Jar Name IDE Client Server
Derby database Derby.jar Yes (for local tests)
Server JAR OLcServer.jar Yes Yes
Client JAR OLcClient.jar Yes Yes Yes (for download during client upgrade procedure)
Javamail library Javax.mail.jar Yes Yes
Apache PDF Library commons-logging.jar
fontbox-2.0.7.jar
pdfbox-2.0.7.jar
Yes Yes Yes
Apache POI (to generate and read spreadsheets) commons-collections4-4.2.jar
commons-compress-1.18.jar
poi-4.0.1.jar
poi-ooxml-4.0.1.jar
poi-ooxml-schemas-4.0.1.jar
xmlbeans-3.0.2.jar
Yes Yes
Javafx runtime Jfxrt.jar Yes Yes

In your IDE, for every application module, the following folders will be used. In the example below, the module path is defined as bugkiller.app. An application is made of:

  • A model file that is written by the user. If you only assembly bricks on objects, you just have to write this file1
  • Automatically Generated classes: they are built by running the generation script on the model, and contain all files necessary for the application to work.
  • You may need to develop your own custom actions, custom pages and utilities if you declare some in the model file

Automatic generation is launched for a package by running the java class Module ( org.openlowcode.design.module.Module ) with as a unique argument the class of your module model (e.g. bugkiller.model.BugKiller ).

To run the Open Lowcode server, you need to deploy the compiled classes of your module on the server. There is no need to deploy the classes on the client. The diagram below showns an example of the structure of an Open Lowcode project in an IDE

IDE Structure Example

There is a risk your module model is overwritten at code generation if it is present in the core folder of your module, and has the same class name as the module (e.g. your root folder is bugkiller.app, and your module name is Bugkiller: you should not put your model in the path bugkiller.app.Bugkiller).

Declaration of Data Objects

Data objects

A data object is declared by a command as per the example below. The name of data object has to be unique across the different packages. It is possible to specify that the data object will appear in the menu of the module by specifying “false” to the “force hide” parameter in the last argument of the definition.

appuser = new DataObjectDefinition("appuser","Application User",this,false);

When set as visible (forcehide false), the search page for the object (called 'Start With...') appears in the module menu.

Module Menu

Fields

A field can be added to a data object by a command as per the example below

appuser.addField(new StringField("EMAIL",
   "E-Mail",
   "e-mail at which the user can be reached. The system may send e-mails at this address",
   80,
   StringField.INDEXTYPE_EASYSEARCH));
Name Feature Artefact Declaration
StringField A text field of specified length, can be made searchable
DecimalField A field to store a decimal number
TimestampField A precise date and time
EncryptedStringField Text field that will be transported and stored encrypted Note: can be encrypted two ways (encrypted text can be retrieved) or one-way (only possible to compare a text with the encrypted reference to check if they are equal)
ChoiceField A choice field holding one value in a predefined list Choice Fields uses a ChoiceCategory that needs to be declared and registered in the package.
MultipleChoiceField A choice field holding several values in a predefined list Choice Fields uses a ChoiceCategory that needs to be declared and registered in the package.
TimePeriodField A field holding a time period (year, quarter, month...)

Note: a LargeBinary field is also present but should not be used directly on a data object. You should use instead the FileContent or ImageContent properties.

Properties

A property provides an object with value added features. It can be added to a data object by a command as shown below

appuser.addProperty(new StoredObject());

Properties can have business rules added to precise the behaviour of the property.

Object Simple Properties

The following properties are available

Name Feature Dependent Properties Business Rules
StoredObject Allows persistence in database (do not use for transient objects such as reports)
UniqueIdentified Identifies an object, allows to update it StoredObject
CreationLog Stores the date of creation and user who created the object UniqueIdentified CreationLog Change Display
UpdateLog Stores the date of update and user who updated the object UniqueIdentified Updatelog Change Display
Numbered A unique number for the object UniqueIdentified The number can be autogenerated by specifying an autonumbering rule
Named A “title” for the object UniqueIdentified The name can be autogenerated by specifying an autonaming rule
Iterated Records all updates of the object UniqueIdentified
ImageContent Displays an image on the object UniqueIdentified
FileContent Allows to store file as an attachment UniqueIdentified
ComputedDecimal A calculated field that can use decimal fields or other computed fields on the object or linked objects
Timeslot Manages the object as a period of time with start date and end date
Schedule Manage a schedule as a list of objects with timeslot, with an auto-link to manage dependencies Timeslot

Note: the property genericlink should not be used directly.

Lifecycle and Workflows

The following properties are available in this area

Name Feature Dependent Properties Business Rules
Lifecycle Provides a status to the object UniqueIdentified Roll-up lifecycle state to parent
TargetDate Allows to record a target date for closing the object Lifecycle
SimpleTaskWorkflow A one task workflow that will allow to dispatch objects to team Lifecycle
ComplexWorkflow A multi-task workflow Lifecycle
PrintOut A generated pdf document Lifecycle
FileContent

Links

Name Feature Dependent Properties Business Rules
AutoLink Allows to link two objects of the same type. This property should be put on the object link UniqueIdentified for link object and object linked Symetric link allows to display links without directions
Constraint on Same Parent (only allowed to link object which have the same parents)
LinkedToParent Allows to specify a foreign key. This property should be used on the “child object” UniqueIdentified for link object and parent Subobject : a subobject does not appear in main menus, and any update of the subojbect is considered update of the parent
LinkObject Allows to link two objects of a different type. This property should be put on the object link. UniqueIdentified for link object and object linked Constraint on Same Parent (only allowed to link object which have the same parents)
UniqueFromLeft : if a second link is added, the first one is deleted, to ensure there is always one object linked from left

Fields and Properties priorities

Display elements of properties and fields are shown given a priority. Priority is a figure from -1000 to 1000, with the highest number being the highest priority. Data is displayed:

  • by order of priority in an object display, with the highest priority fields first
  • for display of tables all fields with priority higher than the watermark (the limit of priority shown in a table).

Display elements for most common properties are documented per order of priority in the table below:

Priority Property Display Element Comment
900 Numbered Number Label may change if specified
850 Versioned Version
710 Lifecycle State
700 Named Name Label may change if specified
700 Targetdate Target Date
600 Timeslot Start and End Date
500 - Fields in link tables All fields with priority greater than 500 will show in array of links
100 Computeddecimal Decimal field Decimal field will have the name specified by user
-30 Creationlog Create Time Can be shown in title or bottom note
-50 Iterated Iteration Update Note
-90 Creationlog User summary Number, first and last name
-90 Updatelog User Summary Number, first and last name
-100 Updatelog Update time
-110 Lifecycle Final State time

Specific object Layout

Specific layout allows to specify the order and tab on which different property widgets are placed. In order to do so, thefollowing can be specified:

  • Tabs can be declared on an object;
  • It is possible to define a widget priority to a property of an object when defining the property, as in the sample below. You can assign a widget display priority either in the default tab or in a specified alternative tab.
   // step 1 – define tabs
ticketothertab = new ObjectTab("OTHER","Miscellanous", ticket);

   // step 2 – define properties
LinkedToParent linkedtoparent = new LinkedToParent("TICKET",ticket,new WidgetDisplayPriority(100, this.ticketcommenttab));

The table below shows the properties that allow to set a WidgetDisplay

Property Widget Comment
LinkedToParent - Defines where the table from parent to all children will be drawn on the parent
AutoLinkObject Priorityfromleft Defines where the table from left objects to right will show (note: this is a tree in case of non-symetric autolink)
AutoLinkObject Priorityfromright Defines where the table from right objects to left will show
FileContent Attachmentwidget The widget showing the attachment list
ObjectWithWorkflow ( SimpleTaskWorkflow & ComplexTaskWorkflow) Workflowtablepriority Table showing all workflow tasks.
LinkObject Displayfromleft Defines where to put the link table on the left object
LinkObject Displayfromright Defines where to put the link table on the right object.

Privileges

Privileges can be added on modules.

Privilege on Action Group

Privileges provide access to an action group for an authority under conditions. They are declared as the example below

this.addPrivilege(new ObjectStatePrivilege(ticket.getReadActionGroup(), 
   external,
   new ChoiceValue[]{CLOSED}));

Privilege directly on Action

Privilege can be added directly on a user defined action. The syntax is generally the same as for an action group

this.addPrivilege(new ObjectDomainPrivilege(
   launchmultiyearreport,
   guestdomain));

Privilege can be added directly on some standard generated actions. This is done by using a get...AutomaticAction static method on the corresponding property (in blue below)

this.addPrivilege(new ObjectDomainPrivilege(
   Lifecycle.getChangeStateAutomaticAction(workitem), 
   businessadmindomain));

List of Action Groups

The following action groups are defined on each object. They provide access to a group of standard actions generated by the Open Lowcode framework

Action Group Summary Special Conditions
Read action Group Allows access to visualization of all data of this object, including attachment and links
Modify Action Group Allows access to modification of all data of this object, including attachment and links (from the left)
Steer Action Group Change state and set target date
Full Action Group All automatic actions for the object
Lookup Action Group Search an object (but is not able to read detailed data) Note: the same authority should not be assigned both the read and lookup action group
Create New Action Group Create a new object or new version of an object Create new version may be excluded by condition on Versioned property
Data Admin Action Group Imports objects by file
Business Admin Action Group Reassign Tasks in workflows
force change state outside of constraints (to be implemented)
Executive Review Action Group

The next page shows the list of actions in each group, and the list of actions where user & access right can be set one by one.

Actions Per Action Group

Development on Open Lowcode

While most Open Lowcode actual application classes are auto-generated from the model files, the developer has, in some cases, to develop some logic that will be executed on the server.

Development Structure

While the model file is transformed to actual code, in the development mentioned below, the code written by the developer will be directly executed on the server.

Utilities

Utilities are small pieces of code written by the developer to precise a behaviour.

Multi-Field Constraints

A multi-field Constraint manages that only allowed combinations of fields are authorized.

Behaviour

The algorithm works as defined below:

  • at object initialization, the values possible to enter in each field (filled or unfilled) are calculated. They are calculated for each field as the combination of values allowed by the previous fields (e.g. for field N, values available are the combinations authorized for this field considering fields 0 to N-1. Note: if the combinations allowed for a field do not include the current value, the field is emptied.
  • When editing a field, it is only possible to enter an authorized value
  • After a field has been changed, the following computation is performed for all the other fields of the object in the order of the constraint:
    • recalculate the restrained values taking into account for each field:
      • the values set for the previous fields of the constraints
      • the value just entered
    • If the value currently entered is not valid of the field:
      • if there is only one valid value in the restriction possible, the field is set to the only restriction possible
      • if several values are possible, then the field is emptied.

The multi-constraint is supported for the following field types:

  • Choice Field: those fields suport fully the multi-field constraints
  • BigDecimal: those fields can be constrained, but cannot constrain other fields (they are typically at the end of multi-constraint fields)

Technical Implementation

This utility allows to precise the valid combinations of multiple fields of the object. It is an implementation of an abstract class. The constraint is precised by implementing the addData method and adding several lines of constraints as per the sample below.

@Override
public void addData()  {
this.addOneConstraint(ActivitycategoryChoiceDefinition.get().DEV,
ActivitytypeChoiceDefinition.get().BUSLOG,
ComplexityChoiceDefinition.get().SIMPLE,
new BigDecimal(2975.53));
…
}	

Print-Out Generators

A print-out generator defines the layout of a PDF print-out for an object. It can query the necessary data (in red below), and will then generate the print-out using the Open Lowcode PDFPage library (in blue below).

public class FuslevelPrintoutPrintoutGenerator extends PrintOutGenerator<Fuslevel> {

@Override
public void generateContent(Fuslevel object, PDDocument document) throws IOException {
	Levtv[] measures = object.getallchildrenforparentpvforlevtv(null);
	PDFPage page = new PDFPage(document, false,25,30);
	page.drawBox(false,page.getPageLeft(),
		page.getPagetop(),page.getPageRight(),3);
		...
		page.closepage();
	}
}

A specific feature is the PDFPageBand that allows printing across several pages, with page turning managed automatically by the framework. The following primitives are available :

  • Bullet Text
  • Paragraph Header
  • Section Header
  • Section Page
  • PDF Multi Page Table

The table below shows an example of page band code.

PDFPageBand band = new PDFPageBand(document, headers, true,15,15);
band.printNewSection(new SectionHeader(20, "Section 1 - Small header"));
band.printNewSection(new SectionText("This is a text. "));
PDFMultiPageTable multipagetable = new PDFMultiPageTable(5, 15, new float[]{1,2,0.7f});
multipagetable.setHeaders(new String[]{"Col 1","Col 2","Col 3e aaze eaz"});
multipagetable.addOneLineContent(new String[]{"1 - ...","AZ...\n...AA","AZ ...AA"});
band.printNewSection(multipagetable);
band.closePageBand();

Trigger

A trigger is a method that is executed after an event happens on an object. A trigger is made of a Trigger launch condition, and trigger content

Triggers can be added to an object with the following conditions:

  • Execute after object insertion (TriggerLaunchConditionInsert)
  • Execute after object insertion or update (TriggerLaunchConditionUpdate)
  • Execute after object state change with target states specified (TriggerLaunchConditionSetState)

The content of the trigger is defined by the developer by creating a concrete Trigger utility class implementing the abstract Trigger class that is generated by the frameworks

A trigger will be executed with the following conditions:

  • contrary to an action, a trigger does not have a security control for accessing the data, as it is assumed that any security controls have been made in the action triggering the object update that triggers the trigger.
  • Triggers are executed after all action updates are performed.

The code sample below shows how to implement a trigger. There are two methods to implement:

  • the main trigger method consisting of the code to be executed
  • A method generateTriggerString generating an identification string: if this method is implemented, the trigger code will be launched only if the string has changed since last execution. This is to filter updates with no new content relevant for the trigger. Note: it is recommended to store queried data in the generate trigger string method to be used on the trigger execution method as trigger class attribute. This avoids querying similar data two times (in the example below the array of TwoDataObjects called attendees is performing that).
public class SendemailTrigger extends AbsSendemailTrigger {
 private TwoDataObjects<Ringattendee, Contact>[] attendees;
 
 @Override
 public String generateTriggerString(Ringsession object) {
  mailcontent.append(object.getStarttime().getTime());
  mailcontent.append("@#@");
  mailcontent.append(object.getEndtime().getTime());
  mailcontent.append("@#@");
  attendees = Ringattendee.getlinksandrightobject(object.getId(),null);
  for (int i=0;i<attendees.length;i++) {
   Contact contact = attendees[i].getObjectTwo();
   mailcontent.append(contact.getNr());
   mailcontent.append("|/|");
   }
  return mailcontent.toString();
  }

  @Override
  public void execute(Ringsession object)  {
   ...attendees...	
  }

}

Actions

Actions allow the execution of logic on the server. Actions are either automatically generated or specifically developed for the application (this is called ‘specific action’ in this section). An action :

  • is triggered from a page event
  • executes some business logic, reading, creating and updating data objects.
  • sends back data to the client :
    • either directly to the page displayed on the client (this is called inline action)
    • or using the data to display a new page (this is a normal action).

Define specific actions

Generic actions can be defined in the model file by specifying the type of action (static: with no input attribute) or dynamic (action is given attributes dependent on context).

Define action input and output

An action interface needs to be declared in the module model. The automatic code generation will provide an abstract action class that is then implemented by the developer. An action is declared as shown in the example below:

  • first a declaration of the action as a dynamic or static action in green
  • Then adding input (for dynamic actions) and output attributes in black
  • Then adding the action to the module in blue.
DynamicActionDefinition createrelatedticket 
   = new DynamicActionDefinition("CREATERELATEDTICKET","go to page to create a related ticket");
createrelatedticket.addInputArgument(new ObjectIdArgument("BASETICKETID",ticket));
createrelatedticket.addInputArgument(new ObjectArgument("NEWTICKET",ticket));
createrelatedticket.addInputArgument(new StringArgument("NEWTICKETNAME",64));
createrelatedticket.addOutputArgument(new ObjectIdArgument("NEWTICKETID",ticket));	
this.addAction(createrelatedticket);

Usage of output arguments

Action output arguments can be used:

  • to send data back to a page when called inline. This is used currently for array of objects than can be fed back to a table of objects
  • Directly to call a page
  • To call a page through another preparation action. This is used when, after the action, an object should be shown. Then, the show object action is called to gather all data necessary to show the object page

Action Attributes

Launch specific actions from the interface

Specific actions need to be inserted in the application, which by default consists of automatically generated pages There are several mechanisms to trigger a specific action.

A specific action (static) can be inserted in the module application menu. This is done through the following command: addasMenuAction

StaticActionDefinition getactiverelease 
   = new StaticActionDefinition("GETACTIVERELEASE","Get active release");
getactiverelease.setButtonlabel("Get active release");
getactiverelease.addOutputArgument(new ArrayArgument(new ObjectArgument("RELEASES",release)));
this.addasMenuAction(getactiverelease);

Note that in the example above, to get a nice label on an action, the method “setButtonLabel” can be used.

A specific action can be added in an object page (showpage action) provided it has as only input attribute the id of the object. This is done through the following command:

DynamicActionDefinition preparecreaterelatedticket 
   = new DynamicActionDefinition("PREPARECREATERELATEDTICKET","go to page to create a related ticket");
preparecreaterelatedticket.setButtonlabel("Create related ticket");
this.addAction(preparecreaterelatedticket);
UniqueIdentified uniqueidentifiedforticket 
   = (UniqueIdentified)      ticket.getPropertyByName("UNIQUEIDENTIFIED");
uniqueidentifiedforticket.addActionOnObjectId(preparecreaterelatedticket);

A specific action can be triggered from a specific page. In that case, there is no restriction on the input attributes of the action. The specific page needs to be accessible from a standard page indirectly (through another specific action that is itself reachable from the standard page, either directly or indirectly). The general architecture of specific actions and pages is shown in the diagram below (only selected triggers are shown):

Generated And Specific Actions

Launch static actions from menu page

A static action can be added to the module menu as shown in the code sample below. This is done in 3 steps:

  • declaring the action as a static action (no input parameter)
  • Adding a “button label” to the action
  • Adding the action as a menu action
StaticActionDefinition getallopentickets 
 = new StaticActionDefinition("GETALLOPENTICKETS","sends back all opentickets");
getallopentickets.setButtonlabel("Open tickets");
this.addasMenuAction(getallopentickets);

Adding an action to the show object button bar

  • declaration the action as a dynamic action with as signe unique parameter the object id (in blue)
  • setting a label to show on the button
  • adding the action to the “uniqueidentified” property of the object
DynamicActionDefinition preparecreaterelatedticket 
    = new DynamicActionDefinition(
      "PREPARECREATERELATEDTICKET","
      go to page to create a related ticket");
preparecreaterelatedticket.addInputArgument(
   new ObjectIdArgument("BASETICKETID",ticket))
preparecreaterelatedticket.setButtonlabel("Create related ticket");
uniqueidentifiedforticket.addActionOnObjectId(preparecreaterelatedticket);

Action class

Specific action classes are implementing the abstract class generated by the module automatic generation script. Their structure is as followed :

  • action declared as implementation of abstract action
  • A constructor without any specific logic
  • The logic:this is where the action is querying or updating objects if needed
  • The call to the page to be shown after this action. In the example below, another action is called to show the object.
public class CreaterelatedticketAction extends AbsCreaterelatedticketAction {

	public CreaterelatedticketAction(SModule parent) {
		super(parent);
	}

	@Override
	public DataObjectId<Ticket> executeActionLogic(
			DataObjectId<Ticket> baseticketid, 
                        Ticket newticket,
                        String newticketname,
                        Function<TableAlias,QueryCondition> datafilter) {
		
		<Your logic here>
		
		return newticketid;
	}

	@Override
	public SPage choosePage(DataObjectId<Ticket> newticketid) {
		return AtgShowticketAction.get().executeAndShowPage(newticketid);
	}

}

Pages

In addition to automatically generated pages, specific pages can be added to the application. Specific pages are always reached by being called by a specific action.

Pages Declaration

A page is declared as shown in the code sample below :

  • declaring the page
  • adding input parameters by specifying all the input arguments (data). It is also possible to declare a page as using the output attributes of an action. This second method is very convenient.
  • adding the page to the module
DynamicPageDefinition createrelatedticketpage 
   = new DynamicPageDefinition("CREATERELATEDTICKET");

    // method 1: declare all parameters
createrelatedticketpage.addInputParameter(new StringArgument("CONTEXT", 4000));
createrelatedticketpage.addInputParameter(new ObjectIdArgument("BASETICKETID",ticket));
createrelatedticketpage.addInputParameter(new ObjectArgument("BLANKTICKET",ticket));
createrelatedticketpage.addInputParameter(new StringArgument("NEWTICKETNAME",64));

    // method 2: link page input to action output
createrelatedticketpage.linkPageToAction(createrelatedticket);

this.AddPage(createrelatedticketpage);

Widgets

Widget Name Aim Input Data Actions
SactionButton A button N/A Triggers the action when pushed
SAreaChart Show a diagram with evolution of different values in time An array of objects with the records per category per time Allows to select an array on double click (to be implemented)
SChoiceTextField Field allowing to display and change a choice (set of values) A choice data N/A
SdateField Field allowing to display and change a date A date data N/A
SdecimalField Field allowing to display and change a decimal A decimal data N/A
SfieldSearcher Widget allowing to search an object from a single search criteria. Typically used with SFieldArray N/A Search action for the object is launched
SfileChooser Choose a file from the local machine and send it to the server N/A N/A
SFileDownloader Download a file in the local machine from the server A binary file N/A
SFixSplit Split the page in two columns N/A N/A
SimageChooser Choose an image from local machine (file or clipboard) N/A Can launch an action after image is chosen
SimageDisplay Display an image, with a small thumbnail in the page, and display the full image when click on thumbnail Binary file N/A
Smenu One « section title » of the menu N/A N/A
SmenuBar Top-level element of a menu N/A N/A
SmenuItem A clickable element of a section title of a menu N/A Launches the action linked to menu item
SobjectArray Display several objects as a table An array of object One action on double click,One action at the end of update
SobjectArrayField Shows several objects (typically for links with no content) as a field of the object An array of object An action for lookup of new object, An action to execute when object has been looked-up
SObjectBand A presentation object allowing to put several components in either column or line N/A N/A
SobjectDisplay Display all fields of an object An object N/A
SObjectIdStorage Stores the id of an object in the page to allow its usage by the page action An object ID N/A
SobjectSCurve A s-curve showing opening and closing trend for objects according to lifecycle. An array of objects Show action when clicking on a specific element
SobjectSearcher An integrated widget allowing the search of an object Embedded search action
SobjectTreeArray A tree display of a structure of objects A tree of objects Action (typically show object) triggered on double click on a line
SpageText A label N/A N/A
SpopupButton Display a subpage when pushed N/A N/A
SSTringStorage Stores a string on the page for use in actions on the page A string N/A
STabPane Defines tab panes in the page N/A N/A
StextField A field allowing text entry A string Possible to configure an action when pressing enter

Sample Page

The code sample below shows how to define a page with 3 sections:

  • a trivial constructor
  • the getContent method, where the page is declared:
    • the method returns the main widget of the page , here a vertical ScomponentBand
    • The declaration of an action with the gathering of action data from page widget (in lighter blue)
  • a method that allows to generate the page title
public class CreaterelatedticketPage extends AbsCreaterelatedticketPage {

 public CreaterelatedticketPage(String context, DataObjectId<Ticket> baseticketid,  
 Ticket blankticket,String newticketname)  { 
  super(context, baseticketid, blankticket,newticketname);
  }

 @Override
 protected SPageNode getContent()  {
  SComponentBand mainpageband = new
   SComponentBand(SComponentBand.DIRECTION_DOWN,this);
  SObjectDisplay<Ticket> blanknewticket = new    
   SobjectDisplay<Ticket>("BLANKNEWTICKET",this.getBlankticket(),
  TicketDefinition.getTicketDefinition(), this, false);
  blanknewticket.setHideReadOnly();
  mainpageband.addElement(blanknewticket);
  SObjectIdStorage<Ticket> relatedticketid = new  
   SObjectIdStorage<Ticket>("RELATEDTICKETID",this, this.getBaseticketid());
  mainpageband.addElement(relatedticketid);
  SComponentBand buttonband = new SComponentBand(SComponentBand.DIRECTION_RIGHT,this);

  SActionRef createrelatedticket = CreaterelatedticketAction.get().getActionReference();
  createrelatedticket.addActionBusinessData(relatedticketid.getObjectId(
   CreaterelatedticketAction.get().getBaseticketidRef()));
  createrelatedticket.addActionBusinessData(blanknewticket.getObject(
   CreaterelatedticketAction.get().getNewticketRef()));
  createrelatedticket.addActionBusinessData(objectnameentryfield.getActionData(
   CreaterelatedticketAction.get().getNewticketnameRef()));
  SActionButton create = new SActionButton("Create","",createrelatedticket,this);
  mainpageband.addElement(create);
  return mainpageband;
  }

 @Override
 public String generateTitle(String context, DataObjectId<Ticket> baseticketid, 
  Ticket blankticket, String newticketname)  {
  return "Create related ticket";
  }

}

Versioning

Open Lowcode follows strict semanting versioning. The package may evolve weekly as new features are added and bugs are fixed for current customers. This may result in several minor versions or patches per month. Minor versions follow strictly backward compatibility so you should be able to upgrade to new minor versions smoothly.

Automatic pages and actions may evolve in patches or minor versions to fix issues and improve ergonomics.