-
Notifications
You must be signed in to change notification settings - Fork 29
Open Lowcode Developper Guide
This version supports Open Lowcode Release v1.0.0. You may also want to consult the Frequently Asked Questions.
Table of Content
- Architecture
- Development Environment
- Declaration of Data Objects
- Development on Open Lowcode
- Framework Versioning
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.
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
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
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).
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.
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.
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.
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.
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 |
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 |
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 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 can be added on modules.
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 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));
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.
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.
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 are small pieces of code written by the developer to precise a behaviour.
A multi-field Constraint manages that only allowed combinations of fields are authorized.
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.
- recalculate the restrained values taking into account for each field:
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)
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));
…
}
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();
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 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).
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).
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);
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
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):
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);
- 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);
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);
}
}
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.
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);
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 |
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";
}
}
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.