Obsolete Pages{{Obsolete}}
The official documentation is at: http://docs.alfresco.com
NOTE: This document details the Forms Engine for Alfresco 3.2, for the latest documentation click here.
The Changes section outlines the modifications applied between the 3.2 Community Release and 3.2 Enterprise Release.
The Alfresco web client presents 'forms' (data view & entry dialogs) throughout its user interface (DM & WCM). In v2.X, these 'forms' are implemented in multiple ways, from hand-coding JSF to declaring an XSD.
The v3 web client is being built on a web framework that is designed to enable the development of all kinds of web sites (by the wider community). Forms play an important part of web sites and as such the framework needs to provide a convention for implementing them.
The Forms architecture in v3.2 onwards replaces the monolithic and duplicated approach of the JSF property sheet component and xforms.js in v2.X. The same services will be used for both DM and WCM forms, meaning there will be only one configuration syntax and one set of UI controls.
At the time of writing the only place the new Forms Engine is used is for the View and Edit Metadata pages within the Share client.
A screenshot of the form component in use on the Edit Metadata page is shown below.
As the contents of the form is completely driven from configuration custom types, custom aspects, their properties and their associations can be displayed within Share without a single line of code.
In the future the Forms Engine will be used for all forms in the Share client, the Records Management module and will be heavily used by the Web Studio application.
The Forms Engine consists of 4 major parts, the Form Service, the Form Component, Form Configuration and the JavaScript FormUI component which includes the Forms Runtime.
The diagram below shows a high level architecture diagram of the Forms Engine.
High Level Forms Architecture.png
When a request is made to a page containing the Form Component the following sequence of events occur.
At this point the form is ready for the user to interact with, as they do the Forms Runtime constantly checks the validation rules enabling and disabling the submit button appropriately. When the user submits the form the following sequence of events occur.
Controls are represented by a Freemarker template snippet, each field has a control and an optional set of parameters.
The controls available out-of-the-box are described below.
association.ftl
category.ftl
checkbox.ftl
date.ftl
encoding.ftl
invisible.ftl
mimetype.ftl
period.ftl
selectone.ftl
selectmany.ftl
size.ftl
textarea.ftl
textfield.ftl
Although backwards compatibility is ideal, there are some features required, such as control configuration and grouping of fields, that are not compatible with the existing property sheet configuration objects. Additionally, a few of the attributes used in the JSF client don't apply to Surf-based applications. As a result, a new schema has been defined for forms.
The existing configuration service is used so anyone used to property sheet configuration in v2.x should already be familiar with the new configuration syntax shown below.
<config>
<forms>
<default-controls>
<type name='type' template='path'>
<control-param name='name'>value</control-param>
</type>
</default-controls>
<constraint-handlers>
<constraint type='id' validation-handler='function' [message-id='key'] [message='string'] [event='string'] />
</constraint-handlers>
<dependencies>
<js src='path' />
<css src='path' />
</dependencies>
</forms>
</config>
<config evaluator='node-type' condition='type'>
<forms>
<form [id='string'] [submission-url='url']>
<view-form template='path' />
<edit-form template='path' />
<create-form template='path' />
<field-visibility>
<show id='string' [for-mode='view|edit|create'] [force='boolean'] />
<hide id='string' [for-mode='view|edit|create'] />
</field-visibility>
<appearance>
<set id='string' appearance='fieldset|panel' [parent='string'] [label='string'] [label-id='key'] [template='path'] />
<field id='string' [label-id='key'] [label='string'] [description-id='key'] [description='string']
[read-only='boolean'] [mandatory='boolean'] [set='string']>
<control [template='path']>
<control-param name='name'>value</control-param>
</control>
<constraint-handlers>
<constraint type='id' validation-handler='function' [message-id='string'] [message='string'] [event='string'] />
</constraint-handlers>
</field>
</appearance>
</form>
</forms>
</config>
The default Forms configuration can be found in the file named 'web-framework-config-commons.xml' which can be found in the WEB-INF/classes/alfresco folder when deployed in an appserver. The source file can be found in the root/projects/web-framework-commons/config/alfresco folder. This file contains all the default controls and constraint handlers for the Alfresco content model and the form configuration for the 'cm:content' and 'cm:folder' types. An example of configuring the 'cm:content' type can also be found here.
Form configuration customizations should be supplied in a file named either 'web-framework-config-custom.xml', 'webscript-framework-config-custom.xml' or 'share-config-custom.xml' if the Share application is being used, the file needs to be placed in the 'alfresco.web-extension' package, which in Tomcat will be the tomcat-home/shared/classes/alfresco/web-extension folder.
Each part of the XML configuration syntax is described in detail below.
default-controls
constraint-handlers
dependencies
form
view-form
edit-form
create-form
field-visibility
show
hide
The algorithm for determining whether a particular field will be shown or hidden works as follows
appearance
set
field
control
constraint-handlers
The Form component is a SURF Component, as such it can be placed in a template via a region tag, for example:
As with any SURF Component it is bound to the page and configured using an XML file.
As mentioned above the Edit Metadata page in Share uses the form component, it's component binding is shown below:
<component>
<scope>template</scope>
<region-id>edit-metadata</region-id>
<source-id>edit-metadata</source-id>
<url>/components/form</url>
<properties>
<itemKind>node</itemKind>
<itemId>{nodeRef}</itemId>
<mode>edit</mode>
<submitType>json</submitType>
<showCaption>true</showCaption>
<showCancelButton>true</showCancelButton>
</properties>
</component>
The form component accepts several properties, most of which are shown above, the complete set are described below.
itemKind
itemId
formId
mode
submissionUrl
submitType
destination
showCaption
showCancelButton
showResetButton
There are multiple points in the Forms Engine stack where customizations can be applied, the most common ones are outlined below and some examples are also available.
Probably the most common customization will be added new controls. A control is classed as the label for the field and UI the user interacts with in order to set and/or edit the value for the field.
A control is defined as a Freemarker template snippet i.e. it just includes the markup to define the control. Refer to the Configuration section above for details on specifying any dependencies the control has.
As you'd expect a model is available representing the field and form being generated, represented by a field and form object, respectively.
The structure of the form object is detailed in the Form Template section.
The structure of the field object is shown below (using the cm:name property as an example):
{
kind : 'field',
id : 'prop_cm_name',
configName : 'cm:name',
name : 'prop_cm_name',
dataType : 'd:text',
type : 'property',
label : 'Name',
description : 'Name',
mandatory : true
disabled : false,
repeating : false,
dataKeyName : 'prop_cm_name',
value : 'plain-content.txt',
control:
{
params: {},
template : 'controls/textfield.ftl'
}
}
Although the id property provides a unique identifier for the field it is only scoped to the current form. If there are multiple forms on the page containing the same field this id will not be unique. The remaining model property to mention fieldHtmlId should be used as the id for the control as this is guaranteed to be unique for the page.
The state of the disabled property must always be adhered to when implementing controls as this is driven from the field definition returned from the FormService and from the read-only attribute in the form configuration. If this is set to 'true' the control should never allow the value to be edited.
The control is also responsible for rendering an appropriate UI representation for the mode the form is currently in. The form mode can be retrieved from the form.mode property. A pattern used by most the out-of-the-box controls is shown below.
The final rule for controls is that they MUST supply the fields current value in a DOM element that has a value property and the id property set to the value of fieldHtmlId Freemarker variable. For advanced controls i.e. association, date, period etc. this usually means a hidden form field.
An example custom control can be seen in this example.
A validation handler is a small JavaScript function that gets called by the Forms Runtime when a field value needs to be validated.
The interface for a validation handler is shown below.
/**
* Validation handler for a field.
*
* @param field {object} The element representing the field the validation is for
* @param args {object} Object containing arguments for the handler
* @param event {object} The event that caused this handler to be called, maybe null
* @param form {object} The forms runtime class instance the field is being managed by
* @param silent {boolean} Determines whether the user should be informed upon failure
* @param message {string} Message to display when validation fails, maybe null
* @static
*/
function handler-name(field, args, event, form, silent, message)
The definition of the built in 'mandatory' validation handler is shown below.
Alfresco.forms.validation.mandatory = function mandatory(field, args, event, form, silent, message)
The field parameter is usually the HTML DOM element representing the field's value, this is normally an HTML input DOM element so that the value property can be accessed. The structure of the args parameter is totally dependent on the handler being implemented, by default these will be the parameters of the constraint defined on the field. All the other parameters are self sufficiently described above in the code documentation.
The handler is responsible for taking the value from the field and using the args to calculate whether the current value is valid or not returning true if it is valid and false if it is not.
The out-of-the-box templates that generate the form UI are fairly limited in terms of layout, without sets fields are just rendered from top to bottom in a single column. Sets can be used to group fields and have them rendered within a standard HTML fieldset or within a headed panel. Customized sets will be possible in the future but that still not be flexible enough for some scenarios.
Via configuration it is possible to specify an alternative Freemarker template to use for the form in each mode using the view-form, edit-form and create-form elements. If present the FormUI component will use the custom template instead of the default one.
As you'd expect a model is available representing the form, sets and fields being generated, represented by a form object as shown below (using a cm:content node as an example):
{
mode : 'edit',
submissionUrl : '/share/proxy/alfresco/api/node/workspace/SpacesStore/fe06c598-2a97-4807-aaff-aacbf614bd87/formprocessor',
showCaption : true,
showCancelButton : true,
showResetButton : false,
enctype : 'application/json',
method : 'POST'
arguments :
{
itemId : 'workspace://SpacesStore/fe06c598-2a97-4807-aaff-aacbf614bd87',
itemKind : 'node'
},
fields :
{
'prop_cm_name' :
{
id : 'prop_cm_name',
...rest of field info goes here...
}
...rest of fields go here...
},
structure :
[
{
id : '',
kind : 'set',
label : 'General',
appearance : 'panel'
children :
[
...fields and/or sets go here...
]
}
],
constraints :
[
{
params : '{}',
event : 'keyup',
validationHandler : 'Alfresco.forms.validation.mandatory',
fieldId : 'prop_cm_name'
}
],
data :
{
prop_cm_name : 'example.txt',
...rest of data goes here...
}
}
The fields property is an object representing a map of all available field objects.
The structure property is an array of objects representing the root sets. The children property is also an array, an array of further nested sets or the field objects that belong to the set.
The constraints property represents all the constraints defined for all the fields for the whole form, these are registered with the Forms Runtime to enable validation.
The custom form template is free to use as much or as little of the supplied form model as it wants, the only caveat is that the generated form UI must ensure that any fields that need to be disabled are rendered as such.
The FormService uses a FormProcessor to handle the generation and persistence of a form. The default FormProcessor implementations provide custom hook points via the Front Controller pattern, very similar to the approach used by Servlet Filters.
The Filter interface defined by the Forms Engine is shown below.
package org.alfresco.repo.forms.processor;
...
public interface Filter<ItemType, PersistType>
{
public boolean isActive();
public void beforeGenerate(ItemType item, List<String> fields, List<String> forcedFields, Form form, Map<String, Object> context);
public void afterGenerate(ItemType item, List<String> fields, List<String> forcedFields, Form form, Map<String, Object> context);
public void beforePersist(ItemType item, FormData data);
public void afterPersist(ItemType item, FormData data, PersistType persistedObject);
}
A filter registry is associated with each FormProcessor implementation, each registered Filter is then called (within the same transaction) for each request processed by the FormProcessor. The order the Filters are executed is not guaranteed.
To register a new Filter (for the NodeFormProcessor) the following Spring configuration needs to be defined.
<bean id='yourCustomFilter'
class='your.CustomFilter'
parent='baseFormFilter'>
<property name='filterRegistry' ref='nodeFilterRegistry' />
</bean>
As a majority of the Forms Engine stack is based on Web Scripts all the information pertaining to Web Script Administration is valid here.
Any changes made to custom config files, custom controls and custom templates (as long as they are outside the core WEB-INF classloader) can be reloaded via the Refresh button on the Web Script index page.
Logging can also be enabled, for Web Scripts logging can be enabled with the following log4j statements:
log4j.logger.org.alfresco.web.scripts.ScriptLogger=debug
log4j.logger.org.alfresco.repo.jscript.ScriptLogger=debug
and FormService logging can be enabled with the following log4j statements:
log4j.logger.org.alfresco.repo.forms=debug
log4j.logger.org.alfresco.web.config.forms=debug
A temporary test 'Form Console' is also currently available in the Share client via the url: http://share-host:share-port/share/page/form-test, this will probably be re-factored at some point soon though to use the Share admin console framework.
Several examples of configuration and customization of the Forms Engine can be found here.
There is still a lot of work ahead for the Forms Engine, some of the planned features are listed below:
This section highlights the minor changes made to the Forms Engine since the 3.2 Community Release, all these changes are available in SVN HEAD and will be part of the 3.2 Enterprise Release.
A new FormProcessor implementation has been added which provides support for creating new instances of types via a form, to use this type of form the item kind needs to be set to 'type' and the item id is the name of the type to create i.e. 'cm:folder'. To support this a new config evaluator named 'model-type' has been added allowing the fields for the 'create' form to be defined.
In order to create a new instance a destination location is required, this can either be supplied via a hidden field named 'alf_destination' in the form or the new 'destination' component binding property can be set which will automatically add a hidden field to the generated form.
Several changes have been made to enhance the support for custom templates. Any custom template can now directly access a field's model via a new fields property in the form UI model, to support this however a change was necessary in the default freemarker templates. The items property has been renamed to structure and the object representing a field within a set now only consists of the kind property and the id field which is now essentially a pointer to the new fields property.
The custom template example has also been updated to reflect this change.
A custom template can now also be provided for an individual set thus allowing a group of fields to be laid out independently of the main form template, a new example has been added to show this new feature.
A use case was found where more context than just the item was required during the Form generation phase. In order to pass this context through the stack several interfaces have had to be altered slightly to accommodate the context parameter, namely the Form Filter interface.
In order to minimize the potential changes in the future and in the interest of stabilizing the APIs as early as possible now was deemed to be the best time to make these changes as it's unlikely there are many (if any) custom form processors or filter implementations yet. The changes have currently only been made at the Java API layer, the ability to pass context via the REST and JavaScript APIs will be added at a later date as these changes can be made in a backwards compatible manner.
Generics have been added to the FilteredFormProcessor class and Filter interface to allow stronger typing of the 'item' expected by a FilteredFormProcessor or Filter implementation. Furthermore, the FilterRegistry used by the FilteredFormProcessor is also strongly typed meaning only Filters declared with the appropriate types can be registered. Overall, this greatly reduces the likelihood of runtime errors from casting exceptions.
This list of allowable values for the appearance attribute in the set element has been increased, it is now 'fieldset', 'panel', 'bordered-panel', 'title', 'whitespace' and '', an example of the effect these have is shown in this example.
The CSS class name for the 'panel' appearance has changed from 'form-panel*' to 'set-panel*' to follow the naming convention of 'set-[appearance-value]'.
An optional field can now be marked as mandatory via a new mandatory attribute on the field element, see the Configuration section for details.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Ask for and offer help to other Alfresco Content Services Users and members of the Alfresco team.
Related links:
By using this site, you are agreeing to allow us to collect and use cookies as outlined in Alfresco’s Cookie Statement and Terms of Use (and you have a legitimate interest in Alfresco and our products, authorizing us to contact you in such methods). If you are not ok with these terms, please do not use this website.