Application Sample

Some sneak peaks deeper into the code

For the fun lets have a look at some other places of the sample application, which maybe should not be treated in depth for this small tutorial, but may give you some helpful hints about what is possible in OpenWGA and where to start looking.

"Real" WebTML Actions

So this is the code of our only WebTML action which executes TMLScript, from "scripts/tmlscript/addresses/create-demo-addresses.tmlscript":

var records = [
  {
    firstname: "George",
    lastname: "Washington",
  },
  {
    firstname: "John",
    lastname: "Adams",
  },
...
];

records.forEach(
  function(record) {
    record.city = "Washington";
    record.zip = "12345";
    record.address = "White House 1";

    HDBModel.createContent("address", null, record);
  }
);

log.info("Demo addresses created.");


As you may see by the code: TMLScript is actually JavaScript. It only does not run in the browser and therefor does not have any browser-specifiy objects in access.

But there are other objects available which represent the OpenWGA runtime and which you can use to perform your tasks, indeed any task that OpenWGA is capable of doing. So the real "message"behind this action may be, that any time you need to run some OpenWGA operation in a more custom way you may do so using TMLScript, as the whole functionality is also available there.

In this code we just define an array of objects which represent our demo code. We iterate over these records, add some example data to them, and finally create new content documents with their data using HDBModel.createContent().

As arguments we are giving:

  • The content class of the document to create
  • null where a "reference document" for the creation may be given. We don't need one.
  • The record containing the data

However how does HDBModel know how to treat this custom record object? Well, this directly leads us to the second "sneak peak":

HDBModel event scripts

In HDBModel it is possible to define custom event scripts for every create/update/delete operation that is done. The demo application also has some scripts defined which you will find under "/scripts/tmlscript/hdbmodel/address.tmlscript":

this.onCreate = function(e) {
  if (e.customParam != null) {
    for (fieldName in e.customParam) {
      content().setItemValue(fieldName, e.customParam[fieldName]);
    }
  }
}

this.onSave = function(e) {
  content().setTitle(lastname + ", " + firstname);
}

HDBModel searches the module with these scripts "by convention" of module name. They must be in a folder "hdbmodel" and their name must equal the content class for which they are effective.

Here we have two scripts, one "onCreate" running when a new document is created, another one "onSave" running on each save operation done on the document. Their functionality is explained quickly:

  • "onCreate" looks for a parameter object "e.customParam", which is where the third parameter of HDBModel.createContent() from "create-demo-addresses.tmlscript" appears. If it is given it will write the properties of this param object to the new content document which is in context of the script (yes, TMLScript also knows document contexts). The context document here is returned by method "content()". It is returned as an object of the WGAPI, OpenWGAs internal content management API, which is used quite frequently in TMLScript to perform writing operations.
  • "onSave" does something else, you might not have noticed until now. It sets the title of the document and calculates it from the specified "lastname" and "firstname" fields. This is why <tml:meta name="title"/> (returns the metadata field "title" from the document) from "mode-view" returns such a nicely formatted display of both.


WebTML forms and options

We haven't had a look into the "::form" module yet, which defines our form fields. It also contains some interesting code. This is the code of "tml/html/addresses/form.tml":

<tml:[modules:field] o_label="Firstname:" o_field="firstname">
  <tml:input name="firstname" focus="true"/>
</tml:[modules:field]>

<tml:include ref="modules:field" o_label="Lastname:" o_field="lastname">
   <tml:input name="lastname" validation="$E_VALUE!=''" message="Lastname may not be empty"/>
</tml:include>

<tml:[modules:field] o_label="Zip/City:" o_field="zip">
   <tml:input name="zip">size="4"</tml:input> <tml:input name="city"/>
</tml:[modules:field]>

<tml:[modules:field] o_label="Address:" o_field="address">
  <tml:input name="address" type="textarea"/>
</tml:[modules:field]>

First, we see here a different way of specifying <tml:include>: <tml[module:name]> is the same as <tml:include ref="module:name">. This sometimes leads to clearer code and may be used instead of the full version at your liking.

The module "modules:field" - included here four times, once for every field - predefines a way in which the input fields should get formatted for output. There are several things to explore here:

  • The definition of a validation with attributes "validation" and "message": The variable $E_VALUE used in the TMLScript validation expression represents the entered value for this field. Default action $store automatically performs validation and fails if one of the expressions evaluates to false.
  • The fact that the HTML input field generated by <tml:input> can be given HTML attributes directly by specifying them inside the tag content. See the size="4" part inside the third field.

But most notably there are those attributes on the includes beginning with "o_". These are WebTML options and their main purpose is to parametrize the included module.  The part behind the "o_" prefix is the option name and the attribute value is the option value. They could as well have been defined by using a tag called <tml:option> inside the <tml:include>, so this "o_" is only a handy short form.

The included module "modules:field" reads those option values to determine what to do. Here's the code from "tml/html/modules/field.tml":

<label style="float:left; width: 200px; text-align:right; font-weight:bold">
  <tml:option name="label"/>
</label>

<div style="margin-left:210px">
  <tml:option name="body"/>
  <tml:case condition="option('field') && tmlform.hasMessage(option('field'))">
    <span style="color:red">
      <== <tml:script expression="tmlform.getMessage(option('field'))"/>
    </span>
  </tml:case>
</div>

You see that the options "label" and "field" are read, one in WebTML using <tml:option> (which just reads options when it has no content), one in TMLScript using method option(). The expressions "tmlform.hasMessage()" and "tmlform.getMessage()" test for and retrieve validation messages which may have been generated.

You also see the use of an option which has not explicitly been defined in the include: body: This is where the tag content of the <tml:include>, which included this module is served. So it contains the code generated by <tml:input> from "form.tml" in this case.


Continue on next page ...