Forms in Citeck ECOS
What are forms
A form is a graphical representation of a repository object as a set of fields, corresponding to the attributes of the object. It is possible to create new objects, view and edit existing ones, using the forms. An example of the edit form with two fields is shown in the figure below:
Structure of forms
Forms consist of three different kinds of blocks:
- field is a field that matches an attribute;
- view is a set of fields, the entire form is also view;
- region is a named part of the field, for example, a text box (label), an input field (input) or an option button (select).
In the figure, view (corresponds to the entire form) is marked red, field (2 fields for attributes Registration Number and Registration Date) is blue, region (in the first field regions are mandatory, label, input, select, in the second field they are mandatory, label, input) is green.
Description of forms is done in XML, in particular, description can be as follows for the presented form:
<view class="letters:income" id="register"> <field prop="idocs:registrationNumber"> <region name="select" template="number-generate"> <param name="template">idocs-income-number-template</param> </region> <invariant on="mandatory" language="explicit">true</invariant> </field> <field prop="idocs:registrationDate"> <invariant on="mandatory" language="explicit">true</invariant> </field> </view>
From this description it can be seen that view, field, and region are nested inside each other in XML. The structure of the form is specified in XML and defines how it will look in the user interface.
Nesting rules are as follows:
- view, field, and region elements can be nested within other view elements;
- region elements can be nested within field elements;
- region elements cannot be nested.
In addition, it is obvious that not all regions are represented in the description. The remaining regions are filled by default. Forms description language allows for a very flexible management of these defaults.
Finally, one can see from the given example of the XML definition of the form that additional invariants can be specified for fields. These invariants will be valid only for the form involved. More information on invariants can be found in the document “Modeling content in Alfresco ECM and Citeck ECOS”.
Templates
Each element (view, field, region) shall have a specific template (template). The template determines the appearance of the element in the interface. The template can be set explicitly in a form layout, or it can be assigned through rules by default (see Rules).
To set the template explicitly, it is specified in the attribute template:
<region name="select" template="number-generate" />
There is its own template set for each type of form elements (view, field, region):
- view:
table is a table layout – similar regions in different fields are below each other: the text box under the text box, the input field under the input field, the tooltip under the tooltip, etc. All nested view have a template rowset by default and all nested field have a template row.
rowset is a set of rows in the table layout. All nested view have a template rowset, field – row.
- field:
row is a row in the table layout. It prints regions in the same order: mandatory, label, input, select, help, message.
checkbox-row is a row with a check mark in the table layout. Unlike “row”, it displays the check mark first (checkbox in the region input), and then the text box (label).
half-width is a half the row for the table layout. It displays the regions in several lines: first, mandatory, label, and help then input, then select, then message.
none does not display a field. - region:
templates intended for regions of input:
- view displays the current field data
- text is a single-line text field
- textarea is a multi-line text field
- number is a number input field
- checkbox displays a check mark
- date is a date input
- datetime is a date and time input
- select is a drop-down list
- autocomplete is a drop-down list with autosuggestion
templates intended for regions of select:
- select-orgstruct is to select from the organizational structure
- number-generate is a number generation by an auto-numbering template (the auto-numbering template is specified in the parameter template)
templates intended for other regions
- mandatory displays an asterisk (*), if the field is required. In view mode, it displays an exclamation mark (!), if the field is required and not completed.
- label displays a label for the field
- help displays a question mark (?) with the hint text for the field. In view mode, it is not displayed
- message displays a message about the cause of the error. In view mode, it is not displayed.
- none does not display a region.
Parameters can be passed to templates, for example, in the template number-generate the parameter template specifies whether auto-numbering template to use:
<region name="select" template="number-generate"> <param name="template">idocs-income-number-template</param> </region>
Identifying elements
Most of the form elements have IDs.
A field (field) always matches any attribute (property or association). In order to set an attribute, to which the field corresponds, the following notation is used:
<field prop="idocs:registrationNumber" /> <field property="idocs:registrationDate" /> <field assoc="letters:reporterOrganization" /> <field association="letters:subdivision" />
A set of fields (view) can be either a whole form or part of the form. If the view defines the entire form, it shall conform to some type, for which this form is designed.
<view class="letters:income"> ... </view>
If the view is simply a set of fields within the form, it usually has no ID.
<view> ... </view>
A region (region) matches any attribute characteristic, but it is identified by the name (name). A set of regions for fields is set by default, so, it makes sense to put the regions in the field only if there is some additional configuration for them, for example, the template (template) or template parameters (param).
<region name="select" template="select-orgstruct" />
If the form contains several elements with the same identification information, then they are considered to be the same element, the configuration of them is combined, and only one element is displayed in the form. If it is required to display the same element several times due to some reasons, the attribute id is used to differentiate between them. For example, one can display several different buttons to select values in a variety of ways:
<region name="select" id="select" template="select-journal" /> <region name="select" id="create" template="create-variants" />
Similarly, it is possible to add the same field several times or make several different forms for the same type. For example, one can define a general form for income letters and make separate forms to specify additional attributes, for example, the income registration form, as in the example above.
<view class="letters:income"> ... </view> <view class="letters:income" id="register"> ... </view>
Form files
Forms are defined in XML files of the following structure:
<?xml version="1.0" encoding="UTF-8"?> <views xmlns="http://www.citeck.ru/ecos/views/1.0"> <imports>...</imports> <!—form definition --> </views> Instead of a comment <!-- form definition -->, form definitions are situated in files as described above: <view class="letters:income> ... </view>
All names of types and attributes that are specified in the class, prop, and assoc shall be from the namespaces listed in the section imports. In this section, the namespaces are listed in the same way as in the models:
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" /> <import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" /> <import uri="http://www.citeck.ru/model/content/idocs/1.0" prefix="idocs" /> <import uri="http://www.citeck.ru/model/letters/1.0" prefix="letters" />
Loading forms
The form can be loaded during the server starting, using “bootstrap” procedure (similar to how models and invariants are loaded). To do this one shall specify the following strings in the file *-context.xml:
<bean parent="nodeViewsDeployer"> <property name="location" value="alfresco/extension/views/letters-views.xml" /> </bean>
The property Location contains the path to the XML file with forms relative to the classpath root (folder tomcat/shared/classes).
Rules
In addition to the forms in the form files, the rules can be specified.
The rules are different in that they can specify the marking not for the specific elements (view, field, region), but for the sets of elements. These sets are identified by special elements views, fields, regions.
In the elements views, fields, regions, it is not necessary to specify identifying information (i.e. the view class, field prop, field assoc, region name), though it is not forbidden. If we specify in the form definition, for example:
<fields prop="idocs:registrationDate" template="half-width" />
then it will not add the field to the property idocs:registrationDate, however, all fields for this property will receive the template half-width.
Specifying the attribute prop for the rule fields, we limit the scope of this rule to all fields with this prop value. The scope can be also constrained by other keys:
- views: class + id + mode, class + id, id, mode, kind, template
- fields: prop + id, assoc + id, id, kind, nodetype, datatype, template
- regions: name + id, id, kind, template
We consider these keys.
For view (views), we can specify the form mode (mode), it may take one of the following values: create, edit, view. In this connection, the keys class + id + mode and mode are supported, i.e. it is possible to set rules, for example, for a particular form in view mode and for all forms in view mode.
For the fields, we can specify the nodetype and datatype. The key datatype is a type of a property specified in the model in the characteristic type, for example, d:text. For associations, datatype is d:noderef. In such a manner, one can specify the default templates for various types of fields, for example:
<fields datatype="d:text"> <regions name="input" template="text" /> </fields>
Key nodetype is the type of the object referenced by the association; it is specified in the model in the characteristic target class. For properties with datatype = d:category, the key nodetype equals cm:category, it is not specified for other properties. Using the nodetype, the rules can be specified for associations with certain data types.
For all kinds of elements, the key kind can be specified. The key kind is an arbitrary “kind” of an element, to which a set of settings can be associated through the rules. For example, the kind autocomplete-groups that will set a template autocomplete for the field with settings to search for groups can be determined:
<regions kind="autocomplete-groups" template="autocomplete"> <param name="attribute">cm:authorityDisplayName</param> <param name="predicate">string-contains</param> </regions> <fields kind="autocomplete-groups"> <regions name="input" kind="autocomplete-groups" /> </fields> Next, more fields with this kind can be simply added: <field assoc="group1" kind="autocomplete-groups" /> <field assoc="group2" kind="autocomplete-groups" /> <field assoc="group3" kind="autocomplete-groups" />
Next, the rule for the “autocomplete-groups” kind can be changed and all regions with this kind will be changed accordingly.
Rules by a template can also be created. For example, one can specify default settings for the template:
<regions template="autocomplete"> <param name="attribute">all</param> <param name="predicate">string-starts-with</param> </regions>
When processing rules listed above, the keys are searched through one at a time, and the first matching attribute (or combination of attributes) that is specified in the rule is the key. For example, in the rule
<views mode="view" kind="custom" template="table" />
the key is the attribute mode, and kind and template are not the keys. In other words, the Correct interpretation of this rule is only one:
- for all view with the mode view, there should be the kind custom and the template table
Wrong interpretation:
- for all view with the kind custom, there should be the mode view and the template table
- for all view with the template table, there should be the mode view и the kind custom
- for all view with the mode view and the kind custom, there should be the template table
- for all view with the mode view and the template table, there should be the kind custom
- …
Moreover, these wrong interpretations cannot be recorded in the forms language.
The order of the element attributes does not affect the meaning in the XML, so the following rules are equivalent and indicate the same thing: for all view with the mode view, there should be the kind custom and the template table:
<views mode="view" kind="custom" template="table" /> <views kind="custom" mode="view" template="table" /> <views template="table" mode="view" kind="custom" />
Therefore, to avoid confusion, we recommend to record the attributes in the order of the keys succession (see above), and then the first recorded attribute (in this case, mode) will be the key.
If it is required to use the rule for all elements in general (for example, for all “view” elements), it is necessary to write the attribute any="true" in the rule. For example, one can specify that all fields in the form shall have the template half-width:
<view class="…"> <fields any="true" template="half-width" /> ... </view>
Form filling procedure
Form definitions and rules described above are used to obtain the structure of the form by its identifier. Let us assume that it is necessary to draw the view form for the “Income” type. Originally, only the type of a document (letters:income) and the mode (view) are known for the form:
<view class="letters:income" mode="view" />
Next, the scanning of the matching rules begins. In the beginning, definitions and rules are searched by the key class + id + mode, i.e. the definitions in form of:
<view class="letters:income" mode="view" …> … </view> <views class="letters:income" mode="view" …> … </views>
Then definitions and rules are searched by the key class + id, i.e. the definitions in form of:
<view class="letters:income" …> … </view> <views class="letters:income" …> … </views>
Then only rules are searched: by the keys mode, template, etc.
As a result, the form is "overgrown" by content. For example, if the following rule was found
<views mode="view" template="table" />
then the form will be the following:
<view class="letters:income" mode="view" template="table" />
After that, the form will already be suitable for, specifically, that rule:
<views template="table"> <views any="true" template="rowset" /> <fields any="true" template="row" /> </views>
Once all the appropriate definitions and rules are applied, a similar procedure for child objects begins. For example, if the following field was set in the form definition
<field prop="idocs:registrationNumber" />
then on account of the previous template (<fields any="true" template="row" />) the template row is added to the field:
<field prop="idocs:registrationNumber" template="row" />
The form filling procedure continues until the form is fully completed.
Using the forms
The form procedure allows defining different forms for the same type, depending on the mode (mode) or just a few different forms with different ID (id). For example, if it is required that the form has an extra set of fields in view mode (fields for viewing only), one can write it as follows:
<view class="letters:income"> <!—form definition for all regions --> </view> <view class="letters:income" mode="view"> <!-- additional fields to the view mode --> </view>
In this case, only the first form definition would be suited for creating and editing modes and both definitions are suited for view mode, where the second definition would be found and used, and then the first one.
If it is required that the view form does not contain any fields, one can specify the template none for the field:
<field prop="…" template="none" />
If it is required that the edit and view forms are completely different, then it can be written as follows:
view class="letters:income" mode="create"> <!-- form definition for the creation mode --> </view> <view class="letters:income" mode="edit"> <!-- form definition to edit mode --> </view> <view class="letters:income" mode="view"> <!-- determination of the form for viewing mode --> </view>
If it is required that the edit and view forms are the same, one can select the general part, giving it a separate ID (id):
view class="letters:income" id="create-edit"> <!-- form definition for the creation and editing --> </view> <view class="letters:income" mode="create"> <view class="letters:income" id="create-edit" /> </view> <view class="letters:income" mode="edit"> <view class="letters:income" id="create-edit" /> </view>
Another method of separation of the fields by modes is to use different kinds of fields. For example, one can specify the kinds of fields, such as “mode-create”, “mode-not-create”, “edit mode”, “mode-not-edit”, etc. with the following rules:
<views mode="create"> <fields kind="mode-edit" template="none" /> <fields kind="mode-view" template="none" /> <fields kind="mode-not-create" template="none" /> </views> ...
Next, only one form can be defined and the visibility of fields in different modes can be restricted, using various kinds:
<view class="letters:income"> <field prop="…" kind="mode-view" /> <field prop="…" kind="mode-not-create" /> ... </view>
The disadvantage of this approach is that the “template” attribute is used to hide fields, so if “kind” and “template” were specified, the field would not be hidden:
<!-- this field would not be hidden in view mode --> <field prop="…" kind="mode-not-view" template="text" />
A few different forms for objects with different IDs can be specified, for example, a normal form and registration form:
<view class="letters:income"> <!-- definition of normal shape --> </view> <view class="letters:income" id="register"> <!-- determine the form of registration --> </view>
Next, the form can be referred by ID on a page with the form; for this purpose, it is enough to generate a link correctly (see below).
The form for the same type can be shared across multiple files. For example, there is a standard form in the system for an income document. Let us assume that one field shall be added to the incoming document upon implementation. In a model, this field was added in appearance, and for a form, this can be done as follows:
<view class="letters:income"> <field prop="custom:incomeField" /> </view>
Perhaps, one has gone the other way and made the subtype for the incoming upon implementation. The form for a new type based on an existing form can be defined, simply including it:
<view class="custom:income"> <view class="letters:income" /> </view>
If it is required to remove any fields then the template none can be used, and if it is required to add any fields, they can be added before or after the usage of the standard form:
<view class="custom:income"> <field prop="custom:newFieldBefore" /> <view class="letters:income"> <field prop="letters:fieldToRemove" template="none" /> </view> <field prop="custom:newFieldAfter" /> </view>
Invariants support
Invariants can be set for the fields of the form:
<field name="…"> <!-- required field on the form --> <invariant on="mandatory" language="explicit">true</invariant> </field>
These invariants would be in effect only for this form (the form, for which they will not work, can be defined).
With the help of invariants, the following scenarios for the form can be maintained, for example:
Necessity of the field for a form:
<invariant on="mandatory" language="explicit">true</invariant>
Conditional necessity of the field (the field is required, if the document status is not “New”):
<invariant on="mandatory" language="javascript"> node.properties["idocs:documentStatus"] != "new" </invariant>
Default value:
<invariant on="default" language="explicit">1</invariant>
Field protected from editing:
<invariant on="protected" language="explicit">true</invariant>
Any other invariants can be specified for any supported characteristics (mandatory, default, protected, valid, ...) and they would work for the form. For more information on modeling, using the invariants, see the document “Simulation in Citeck ECOS”.
Since invariants can be set not only for the form, but also for the types/aspects and properties/associations, one shall answer the following question when creating the invariant: Is this invariant valid only for this form or for the whole type in general? The answer to this question specifies which scope shall be set to the invariant.
Pages with forms
Two standard pages with forms are available in ECOS: to create new objects and to edit existing ones.
The page node-create is designed to create new objects. It takes the following arguments:
- type is the type of the object to create
- viewId is the ID of the form
- destination is the folder where the object will be created
- param_* defines the transfer of arbitrary parameters in the form
For example, the page to create incoming documents is as follows:
node-create?type=letters:income&destination=workspace://SpacesStore/...
The parameter destination may not be specified, then the object will be created in the default folder.
The page “Node-edit” is designed to edit existing objects. It takes the following arguments:
- nodeRef is an object ID for editing
- viewId is the ID of the form
- param_* defines the transfer of arbitrary parameters in the form
For example, the page to edit incoming documents is as follows:
node-edit?nodeRef=workspace://SpacesStore/...
and the page to register incoming documents is as follows:
node-edit?nodeRef=workspace://SpacesStore/...&viewId=register
Using the parameters, arbitrary objects can be transferred to the form. Further, they can be used in the invariants of the form fields. For example, if the parameter param_outcome is transferred, it can be referred to the invariants as follows: view.params.outcome:
<field assoc="letters:outcome"> <invariant on="value" language="javascript">view.params.outcome</invariant> </field>
In this case, if this parameter is specified, the field for the association letters:outcome (outgoing document) would be automatically filled and cannot be edited (to make the field editable, the characteristic default should be used instead of value). For example, the page to create incoming documents based on the outgoing is as follows:
node-create?type=letters:income¶m_outcome=workspace://SpacesStore/...
Then the action "Create incoming" can be made in the outgoing document card that would take to this page.