Developer Guide

1. General




When building applications, the Developer Guide is intended to create a uniform technical basis for developing applications. Please refer to our style guide for more information about designing applications.

2. Namespace for applications

The namespace for the application should be used for naming data groups, request parameters and session parameters. The namespace can be a company name or a combination of the company and application. This is the greatest challenge because with a large amount of partners and applications, it is very likely that names will overlap.
Make a list of at least the used request parameters and the possible values in the application handbook. This is important if you want to safeguard the application with request value validators in the portal.

3. Data groups

Intrexx data groups created by Intrexx are automatically given names, based on their type, to avoid the same name being given twice. So that they can easily be recognized in SQL statements (e.g. in Groovy or Velocity) all table names should be adjusted accordingly. In principle, the following rules should be kept when naming data groups:

3.1. Intrexx data groups

Intrexx data groups automatically receive the name "XDATAGROUP" followed by a random key to avoid a name being given twice.

3.2. File data groups

When creating a file data field, a child data group. The data groups receive the label "XFILEDATAGROUP" followed by a random key. Name these groups with the same logic as the standard data groups.

3.3. System data groups

System data groups should be named in the same way as Intrexx data groups. Their name should always end with "SETTING".

3.4. External data groups

Should external data groups with a connection to Intrexx standard data groups (e.g. DSUSER, DSOBJECT, etc.) or to the application's own data groups be implemented, these do not need to be renamed. External data groups connected to Intrexx standard data groups (e.g. DSUSER, DSOBJECT, etc.) or the application's own data groups do not need to be renamed.
It should however be noted that there are no database-specific prefixes in the data group definition (dbo.<data group name>) but rather just the data group name in block capitals.

4. Data fields

The appropriate naming of data fields is also important if applications are modified or expanded later and if accesses are executed via SQL statements in Velocity or Groovy. The legibility and also the internal use of the application play a role here.

4.1. Field names

The data fields generated by Intrexx receive a type key to begin with followed by the title which was entered while the field, or also the corresponding element, was created (e.g. "STR_TITLE_6A585D30"). Here, umlauts will be transformed and the field name is shortened to 30 characters if necessary. Field names should be defined as follows to avoid using SQL keywords and to achieve an optimal legibility: Here is an overview of the prefixes:

4.2. Primary key

The data type of the primary key can either be an integer value or a GUID. Preferably, this should be used because the GUID has multiple advantages over an integer:

4.3. References

References automatically receive the prefix "REF_" followed by a random key. To change the name of a reference, show the data fields in the application structure by selecting "Show data fields" from the Edit menu while the corresponding data field is selected. Then, go to the Expert tab in properties of the reference data field and adjust it accordingly. Please note that the name can only be adjusted before the application is published for the first time.

4.4. Index

To optimize performance, indices should be recorded for data fields that are searched through regularly or are used for sorting. The index name may not be longer than 18 characters and should be constructed based on the following schema: IX_<DG-Key>_<Counter>, e.g. IX_SHAREFEED_1.

5. Naming elements

To keep names in the Application Designer as uniform as possible, certain rules should be considered here. Principally, the following applies: give as many elements as possible a corresponding name and do not keep the default presets.

5.1. View pages

The page names are currently also the displayed name in the browser (e.g. in the breadcrumb path, tooltip title, etc.). For that reason, the titles for development cannot be separated from the displayed titles. However, certain view pages used for specific cases are an exception to this:

5.2. Groupings

Because groupings are often shown and hidden using JavaScript, these should be given descriptive names based on the following pattern: "_grpGroup name". The group name should be specified in English.

5.3. Naming conventions

The United Planet Quality Assurance performs automatic application tests. Here, the application structure, programming code (JavaScript, Groovy, Velocity) and layout will be checked as to whether they meet certain requirements. The following names are part of this:

Test type Test Comment
Names Email Naming with hyphen, such as e-mail, is not allowed. This refers to labels – not the data fields themselves.
Table length The compliance to the maximum length for the table name
Table column Length The compliance to the maximum length for the column name
Column name LID Data type may only be integer
Column name FKLID Data type may only be integer
Column name STRID Data type may only be string
Column name FKSTRID Data type may only be string

6. Permissions

The permissions required for the application may only be defined using groups. The groups have to be in English and according to the following pattern:

Application.<application name>.<role>

The following foundational permission objects appear in many applications:
User group Description
Application.<application name>.User Normal user of the application
Application.<application name>.Administrator Administrator of the application who manages settings and master data
Application.<application name>.Manager Users who, for example, are responsible for editing tasks in an application
Application.<application name>.Approver Validators and/or approvers if the application has a corresponding workflow
Application.<application name>.Reviewer Auditors and/or people who provide feedback regarding a procedure
Application.<application name>.Responsible Responsible person, such as department leaders, who are given access to the data from their department

The "Users" user group should not be used to keep the permission control flexible. If required, the role should be mapped in the portal to the user role in the application. Special permission objects can be defined depending on the application. The implemented permission objects and their function must be documented in the application's handbook as depicted in the table above. To limit the selection of users in a context-related manner, a special filter is available; this should be documented with the defined permission groups. The selection of users via the UserID is limited when using "Is contained in" with the system value "Set and containing sets". What is important with this filter variation is that inheritance is also taken into account.

7. Script

The programming code delivered with the application should be done in a style that's as uniform as possible. This means that the methods, function names, variables and comments should be created

7.1. JavaScript coding conventions

If possible, the use of JavaScript should be avoided as much as possible. Conditional display of groupings and buttons with server-side Velocity code is more secure and furthermore, supports barrier-free requirements.

7.1.1. Error handling

In some error handling cases, the Intrexx notifier function is needed to provide the user with feedback – this helps maintain a uniform standard.
Notifier.status.notify("A date was not entered.", "Notice");
Notifier.status.error("The specified cost center could not be found.", "Error");

7.2. Velocity coding conventions

7.2.1. Security

Among other things, entire HTML constructions, can be created with Velocity. If errors occur, this can hinder an entire page being loaded or even the portal itself. Additionally, information can be provided using Velocity templates for which the user does not have permission, according to the Intrexx framework. For that reason, Velocity templates must be checked well and errors intercepted.

If you use your own Velocity files in applications which, for example, perform your own SQL requests and therefore circumvent the usual permissions structure of the Intrexx business logic, the authorized use of the Velocity code should be checked. If this check is not performed, the file can simply be called up using the browser and the content can be displayed without a permissions check. This problem is similar to that of an SQL injection. User entries are applied without verification and forwarded to API functions; this should be prevented. Your code should be enclosed by the following construct to guarantee the server-side verification:
#if($AccessController.hasPagePermission("APP_GUID", "PAGE_GUID", "access"))
 ## Your code ##
#end
The GUID of the target application is entered as the first parameter, the GUID of the target page as the second. As the third parameter, the key "access" is entered. The parameters should be specifically entered into the call. The transfer in the form of request values makes the verification more dynamic but makes you more susceptible to attack. If access to a data group should be verified using Velocity (before the database is accessed), the following construct can be placed around the function:
#if($AccessController.hasDatagroupPermission($ProcessingContext,"APP_GUID", "DATAGROUP_GUID", "read"))
 ## Your code ##
#end
The processing context is transferred using the first parameter. The GUID of the target application is specified as the second parameter, the GUID of the data group as the third. As the fourth parameter, one of the following keys should be entered:

Key Function
create Create
delete Delete
delete-own Delete own data
read Read
read-own Read own data
write Edit
write-own Edit own data

The parameters should be specifically entered into the call. The transfer in the form of request values makes the verification more dynamic but makes you more susceptible to attack.

7.2.2. Velocity includes

Velocity files should always be included in the application package to ensure that installation is straightforward. If you are using overarching Velocity templates, these are to be stored in in the directory portal directory internal/system/vm/html/include in their own subdirectory. All other directories are reserved for Intrexx.

7.3. Groovy coding conventions

7.4. Documenting codes

The codes in JavaScript, Velocity or Groovy should be documented in detail. Primarily, this means that meaningful comments should be implemented into the code to make its function apparent. Use the option to implement JSDOC comments as well.

7.5. Prepared query

The correct use of PreparedQuery for performing SQL statements in Groovy and Velocity prevents an attack via SQL injection. An important prerequisite for this is knowledge regarding the affects and influences of parameters that are entered into the SQL statement. Intrexx provides an abundance of functions for using PreparedQuery. That being said, using it incorrectly can create holes, which should be avoided. In Groovy, a correct PreparedQuery is shown below. It's important that the values in the WHERE clause are defined using the placeholder "?" and then set using set methods. The respective set methods guarantee that the expected format is inserted. This means that if a character string is smuggled in instead of an integer, then this will not be processed and causes an error. The detailed error message must be suppressed in the portal so that the full effect unfolds.
def conn = g_dbConnections.systemConnection

def l_UserId = g_record["GUID"].value

def stmt = g_dbQuery.prepare(conn, 
"SELECT DTBIRTH FROM DSUSER WHERE LID = ?")
stmt.setInt(1, l_UserId)
def rs = stmt.executeQuery()

while (rs.next())
{
  rs.getDateValue(1)
}

rs.close()
stmt.close()
The following construct is defined incorrectly and involves a high risk.
def conn = g_dbConnections.systemConnection

def l_UserId = g_record["GUID"].value

def stmt = g_dbQuery.prepare(conn, "SELECT DTBIRTH FROM DSUSER WHERE LID = '${l_UserId}'")
def rs = stmt.executeQuery()

while (rs.next())
{
  rs.getDateValue(1)
}

rs.close()
stmt.close()
The value from the data field can be manipulated by a hacker and instead of the expected LID, a character string with the expansion of the "WHERE" request can be smuggled in.

Expected:
WHERE LID = 10
Manipulated:
WHERE LID = 10 OR LID > 0
With this change, the hacker receives all database entries in the user database. If the request values are additionally checked using validators, character strings via request values which should return integer values, for example, cannot get so far that they are inserted into the statement. Another very critical construction is shown below. The SQL statement is formed by merging a character string and a variable. In this case as well, the variable, which is additionally transferred via a request value, can be abused to expand the "WHERE" clause.
def l_request = g_request.get("rq_myparameter")

def l_sql = "SELECT DTBIRTH FROM DSUSER WHERE LID = "l_sql += l_request

def stmt = g_dbQuery.prepare(conn, l_sql)
def rs = stmt.executeQuery()

...
The same conditions apply in the Velocity environment. The PreparedQuery must also be used correctly here:
#set($UserId = $DC.getValueHolder("GUID").getValue())

#set($stmt = $PreparedQuery.prepare($DbConnection, 
"SELECT DTBIRTH FROM DSUSER WHERE LID = ?"))
$stmt.setInt(1, $UserId)
#set($rs = $stmt.executeQuery())

#foreach($element in $rs)
  $element.getDateValue(1)
#end

$rs.close()
$statement.close()
An equally critical construction is the definition of a query which is used to enter the data field name dynamically. Especially if the field names are determined by data from the request or other external sources. This applies to Groovy and Velocity.
def ergebnis = g_dbQuery.executeAndGetScalarIntValue(conn, 
"SELECT count(*) FROM DATAGROUP WHERE LID = ? AND $FIELDNAME1 IS NOT NULL AND $FIELDNAME <> ''",0)
A lot of security levels need to envisaged for such a solution. The ideal solution is to avoid such constructions altogether. The following security policies would be advised:

8. Multilingual applications

All information about this subject is available here.

9. Clonable applications

So that an application can be cloned, the Groovy and Velocity scripts in particular must be defined correctly. This particularly applies to the data groups and SQL statements. When cloning/duplicating an application, Intrexx reassigns all of the GUIDs and with that, adjusts the GUIDs in the scripts (Groovy, Velocity, JavaScript) accordingly, so that everything continues to work together. For this reason, the data group definitions must always be made using the data group's GUID:
def l_strIsoLanguage = it
def l_intLanguageDetect = g_dbQuery.executeAndGetScalarValue(conn, 
"SELECT COUNT(*) FROM DATAGROUP('98C0EC3CC539925C8B7644F4AB726BE2F38038F1')
WHERE LANG = ?", 0) {
  setString(1, l_strIsoLanguage)
}

#set($stmtFloors = $PreparedQuery.prepare($DbConnection, 
"SELECT T0.STRID, T1.STRNAME, T1.STRSHORTNAME FROM
DATAGROUP('B1156946E0CF3215576D4595A757A3FD0BB31C22') T0 LEFT OUTER JOIN
DATAGROUP('1037FA883B005B35FE5D4B8A10645B9FB2B04933') T1 ON ((T0.REF_PROPERTY
= T1.PROPID) AND (T1.LANG = ?)) WHERE T0.STRPARENTID = ? AND T0.REF_CLASS = ?
ORDER BY T1.LSORT"))
$stmtFloors.setString(1, $lang)
$stmtFloors.setString(2, $Builiding)
$stmtFloors.setString(3, "LEVEL")
#set($rsFloors = $stmtFloors.executeQuery())
GUIDs should also be used when accessing data fields on application pages in the Velocity context. For this, the key "page.requiredDataFields.mode" with the value "all" must be set in the Settings of each page (the quickest method to provide all of the data group's data fields via GUIDs). This is also the basis for the access in the Velocity templates from the previous section.

In the Velocity context, the saved data can be accessed with the following method:
$DC.getValueHolder("GUID_DATAFIELD").getValue()

## If the page is used in a free layout table
$drRecord.getValueHolder("GUID_DATAFIELD").getValue()
The advantage of this method is that the information does not need to be physically positioned on the page as a view field (or where applicable, hidden).

10. Portable applications (databases)

Because various databases can be implemented for use with Intrexx, the SQL statements should be defined as neutrally as possible. This means that database-specific functions should not be used. The ANSI SQL standard is to be upheld here. If there are not any neutral alternatives for an SQL statement, a database switching point must be defined for the SQL statement; this uses the appropriate statement for each database.

Switching points for Groovy:
def conn = g_dbConnections.systemConnection

switch (conn.descriptor.databaseType)
{
	case "Db2":
		// DB2
		break

	case "Derby":
		// Derby/Java DB
		break

	case "Firebird":
		// Firebird
		break

	case "HSQLDB":
		// HSQLDB
		break

	case "Ingres":
		// Ingres
		break

	case "MySQL":
		// MySQL
		break

	case "Oracle8":
		// Oracle 8
		break

	case "Oracle9":
		// Oracle 9
		break

	case "Oracle10":
		// Oracle 10
		break

	case "PostgreSQL":
		// PostgreSQL
		break

	case "MaxDB":
		// MaxDB
		break

	case "MsSqlServer":
		// Microsoft SQL Server
		break

	case "Standard":
		// unspecified
		break

	default:
		assert false : "Unexpected database type."
		break
}
Switching points for Velocity:
#set($DbName =
$DbUtil.getConnection("IxSysDb").getDescriptor().getDatabaseType())

#if($DbName == "MsSqlServer")
  #set($sql = " SELECT TOP 10 * FROM MyTable ORDER BY LID DESC")
#elseif($DbName == "PostgreSQL")
  #set($sql = "SELECT * FROM MyTable ORDER BY LID DESC LIMIT 10")
#elseif($DbName == " Oracle9" || $DbName == " Oracle8")
  #set($sql = " SELECT * FROM (SELECT * FROM MyTable ORDER BY LID DESC) 
  WHERE rownum <= 10")
#else
  $Debug.info("Unexpected database type")
#end

12. Processes

The following should be regarded for processes: Timer actions for data groups should, if possible, restrict the number of results via a filter to keep the number of database operations as low as possible, e.g. filter for incomplete tasks, non-archived articles etc. As standard, the setting "Log only warnings and errors should be activated". If "g_log.info()" is inserted for the purpose of debugging, this can be kept in the code and is ignored during regular use, and thus the log files are kept small.