Forms Design Ideas

Document created by resplin Employee on Jun 6, 2015
Version 1Show Document
  • View in full screen mode

Obsolete Pages{{Obsolete}}

The official documentation is at: http://docs.alfresco.com




Introduction


This page contains design ideas for potential future features of the Alfresco Forms Engine as listed in JIRA.

Please note this page is not a commitment to those features mentioned but simply ideas of how those features may potentially be implemented.


Considerations


  • Accessibility (progressive enhancement)
  • Repeating fields
  • Association management
  • Modularisation
  • Grouped fields
  • Dependent fields (visibility & values)
  • Form data population
  • Error handling (validation)
  • XSD/XML support
  • Competitive analysis (ExtJS, Forms Lite, wForms, js-forms etc.)

Design Ideas


This section shows current (in some cases, old) thinking of how some of the features slated for future releases of the forms engine could be implemented.


JMX Forms


Investigate the plausibility of implementing a form processor to manage MBeans via the Form Service.

The form will provide the following capabilities:


  • Display all MBean attributes
  • Display a button for each parameter-less operation
  • Allow writable attributes to be updated
  • Visible attributes to be configured via standard form configuration
  • A transient 'operations' field to show executable operations

Notes:


  • All attributes appear to be exposed as 'java.lang.String' so boolean will not automatically use the checkbox control
  • The REST layer is changing ':' to '_' so has to be decoded in the getTypedItem method (if any encoding/decoding is required for URLs/REST this should be done solely at this layer, maybe something to consider when making the form service webscript logic java backed)

Configuration


As there is no translation required i.e. NodeRef to type, Task Id to type the built-in string config evaluator can be used. The form config for the '' MBean would therefore be as follows:



<config evaluator='string-compare' condition='Alfresco:Name=Runtime'>
   <forms>
      <form>
         <field-visibility>
            <show id='FreeMemory' />
            <show id='MaxMemory' />
            <show id='TotalMemory' />
         </field-visibility>
      </form>
   </forms>
</config>

As noted above boolean attributes appear to be represented as strings. This means the text control will be rendered by default, it can of course be forced to use the checkbox control, the checkbox control will have to be updated though to accept a boolean value represented as a string.

Some MBean also have operations that can be executed, to show all operations the 'operations' transient field can be included as shown below.



<config evaluator='string-compare' condition='Alfresco:Type=Configuration,Category=email,id1=outbound'>
   <forms>
      <form>
         <field-visibility>
            <show id='mail.host' />
            <show id='mail.port' />
            <show id='mail.from.default' />
            <show id='operations' />
         </field-visibility>
      </form>
   </forms>
</config>

The presence of the 'operations' field (may need to add some qualifier to the name in case there is an attribute called operations!) will render a button for each operation on the MBean that does not have any parameters.


Form Controls


The only new form control required is for the operations buttons. As with the workflow transition buttons the form definition will return a string representing the buttons to render and their labels.

When a button is clicked it will update a hidden field with the value of the button and submit the form. See the next section for details on how the form processor handles this.

NOTE: As we have 2 similar controls maybe a generic 'buttons' control could be created? A new data type of 'buttons' could be used in form definitions to invoke the control. The hidden field used to submit the value should be configurable too.


Form Processor


As JMX is an Enterprise only feature the JMX Form Processor will also be an Enterprise only feature and therefore be located in the 'enterpriseprojects' folder.

The form processor will extend FilteredFormProcessor. The form processor will represent everything as property fields, association fields will not be used. The JMX 'ObjectName' object will be used as the typed version of the item i.e. the class definition will be as follows:




public class JMXFormProcessor extends FilteredFormProcessor<ObjectName, ObjectName>

If the default form is requested for an MBean all attributes and no parameter operations will be returned.

If certain fields are requested FilteredFormProcessor uses a field processor registry to look for and build the selected fields. This approach may be overkill for this form processor, but on the other hand it does provide another extension point. If it is not too much more work to provide the necessary parts this mechanism will be used, there will be an AttributeFieldProcessor and an OperationsFieldProcessor.

However the 'Field' objects get generated they will use the JMX Attribute's name and description. The isWritable method on the MBeanAttributeInfo class will be used to determine if the attribute can be edited by the user in the form. The data type of the field will always be 'text' unless we find a situation where the getType method on MBeanAttributeInfo does not return 'java.lang.String'!

The operations field will use the same approach as the workflow transitions buttons and pass back a string of the form 'value|label,value|label' to represent the operations buttons.

FilteredFormProcessor does not provide a default implementation for persisting fields. The JMX form processor will just iterate over the form data, if the 'operations' field is found the operation will be executed on the MBean, but only after all attributes have been processed. The form processor will build a JMX AttributeList object containing all the attribute values to set. As each attribute was represented as a property field it will have a 'prop_' prefix so this will need to be removed before attempting to look for the attribute on the MBean.

If an operation is called no return information will be returned to the client, the normal form response will be sent back. The UI will therefore be responsible for refreshing the representation of the MBean after the MBean has been persisted and/or had an operation invoked.


Progressive Enhancement


The accessibility requirements of the first version of WCAG stated that web applications must remain accessible when JavaScript has been disabled.

The standards and screen reader technologies have now been updated to reflect today's dynamic, one page, web applications. WCAG2 therefore no longer demands that web applications have to work with JavaScript disabled, there are however, still rules that should be followed when using JavaScript. This article provides a nice overview of the new requirements.

This means new features we add to the Forms Engine can rely on JavaScript being present and enabled, this will make the design and implementation of some features a lot simpler, for example, repeating fields!


Modularisation


The Forms Engine (particularly the back-end) has been designed to be Alfresco agnostic. However, as it is currently only used in an Alfresco repository some aspects have become reliant on the Alfresco environment. To solve this the Forms Engine needs to become a little more modular (FORM-83).

To support AWE there is already a build target named 'package-forms-client-jar' to create a JAR file for the client assets. A new target named 'package-forms-service-jar' will be added to create a JAR file for the server assets.


Form Service


The main reliance on the Alfresco environment currently lies with the form-services-context.xml file. The basic FormService and it's associated objects are all generic but the Spring context used to integrate it relies on other Alfresco beans. The generic config will be re-factored into a generic 'forms-service-context.xml' file and an 'alfresco-forms-service-context.xml' file.

The other dependence is the fact that the form service REST API uses the JavaScript interface. The JavaScript object is dependent on the whole Alfresco scripting framework and therefore makes it very difficult (if not impossible) to use the form service REST API in a non-Alfresco web application.

For this reason the form service REST API will become a Java backed web script. This also has the advantage of allowing common processing functionality to be shared across the form definition (generation) and form processor (persistence) web scripts. One area this will be useful for is the handling of invalid/reserved characters in URLs, the encoding/decoding can be done at the REST API layer meaning the form processor does not have to be aware of URL friendly versions of it's itemId.


Form UI Component


In it's current form the Form UI component presumes it is deployed in a Spring Surf application and on a separate tier than the form service back-end. As such the request to get the form definition is hardcoded to use the 'alfresco' endpoint (Spring Surf Connector).

Not only is this inefficient if the form UI component and form service is actually on the same tier it also makes it impossible to retrieve the form definition from anywhere other than the 'alfresco' endpoint.

The retrieval of the form definition is therefore going to be re-factored and made pluggable. A FormDefinitionDataSource interface will be defined for this purpose.

Several FormDefinitionDataSource implementations will be implemented:


  • FormDefinitionDataSourceConnector (the current implementation)
  • DirectFormDefinitionDataSource (calls the FormService Java API directly (the name may be improved!))
  • StringFormDefintionDataSource (allows a hand crafted JSON form definition to be passed)
  • FormDefinitionDataSourceURL (allows an arbitary URL to be called) (optional)
  • FileFormDefinitionDataSource (allows the contents of a file to be used as the form definition) (optional)

Forms Controls


The Forms Runtime and most of the client side controls either directly or indirectly rely on alfresco.js which contains various helper functions and objects. Some of the forms controls also make use of TinyMCE which is also managed via alfresco.js and other Alfresco source files.

It would be fairly easy to separate these pieces, the problem would be avoiding duplication and file clashes when integrating the generic forms library with Share. Careful consideration will be needed here as it will affect Share source code. The client side separation will therefore be tackled at a later date.


Repeating Fields


Currently multi-valued properties are handled using comma separated strings, this needs be enhanced so separate controls are used for each value (FORM-16).

Given we can now rely on JavaScript being enabled (see Progress Enhancement section) we can use JSON to represent the repeating values.

An initial idea was to return the values of a repeating field using indexed suffixes i.e. value_0, value_1, value_2 but this introduces complexity in other areas of the system, particularly when it comes to persisting the field. The current thinking is to use a JSON array to represent the values i.e. {['value', 'value', 'value']}.

All ContentModelFormProcessor derived implementations will therefore return JSON, the client side controls will parse the JSON and dynamically build the relevant user controls (within the guidelines of WCAG2 obviously). The client controls will then also be responsible for building the JSON representation of the client's current value to POST back to the form processor.

It is still to be decided what repeating data types will be supported.


Association Ordering


Form processors and the UI currently manage the 'added' and the 'removed' associations, there is no concept of ordering.

Some use cases require the ability to explicitly order the associations, so support for this needs to be added.

The field name is suffixed with _added or _removed to indicate what associations need to be added and removed, respectively. The raw field name could potentially be used to hold the order of the remaining associations, if this field has no value (missing, null or empty string) it indicates that no re-ordering took place.


Extensible Start Location


The ObjectPicker UI component now takes a startLocation parameter, currently this is a restricted list of well known values.

More flexibility is required here, particularly the ability to provide custom start location resolvers. The picker needs to be re-factored to use a dedicated 'location resolver' data web script. This web script will have the ability to call custom algorithms.

An example custom start location is find the first ancestor node of a certain type.


Falling Back To Parent Type


The current behaviour is to request the default form for the item when no config can be found.

For type based forms there are some use cases where it makes more sense to fall back to the form defined for the item's parent type i.e. asking for an article in WQS. This would also help reduce the amount of configuration required.

There are a few issues:


  • How can this be done in a generic way i.e. this behaviour is specific to a item kind and certain use cases?
  • How many levels are ascended until giving up?
  • What are the performance implications in having to look up parent hierarchy?

An initial idea is to provide a callback mechanism that can be invoked via a URL parameter (allows for case by case usage). The callbacks can be registered via Spring (keeps the FormUIGet class generic) and scoped to an item kind.


XSD/XML Support


The following example JSON is what a form definition for an XSD could look like:





{
   'item' : 'uri to xmlformdef or xml instance',
   'submissionUrl' : '/form-model/xml/ROOT/articles/article1.xml'
   'type' : '/xmlformdef/article',
   'definition' :
   {
      'groups' :
      [
         {
            'id' : 'article:author',
            'label' : 'Author'
         },
         {
            'id' : 'article:name',
            'parent' : 'article:author'
            'label' : 'Name'
         },
         {
            'id' : 'article:address',
            'parentId' : 'article:author',
            'label' : 'Address',
            'repeats' : 'true',
            'mandatory' : 'true'
         },
         ...
      ],
      'fields' :
      {
         'article:title' :
         {
            'name' : 'article:title',
            'type' : 'property',
            'dataType' : 'd:text',
            'defaultValue' : '',
            'binding' : '/article/title',
            'label' : 'title',
            'description' : 'Holds the description',
            'mandatory' : 'true',
            'enforced' : 'true',
            'protected' : 'false',
            'repeats' : 'false'
         },
         'article:prefix' :
         {
            'name' : 'article:prefix',
            'type' : 'property'
            'dataType' : 'd:text',
            'defaultValue' : '',
            'binding' : '/article/author/name/prefix',
            'title/label' : 'prefix',
            'description' : 'Holds the description',
            'mandatory' : 'true',
            'enforced' : 'true',
            'protected' : 'false',
            'repeats' : 'false',
            'groupId' : 'article:author.article:name',
            'constraints' :
            [{
               'name' : 'ListOfValues',
               'params' : { 'values' : 'Mr, Mrs, Ms, Miss, Dr' }
            }]
         },
         'article:first' :
         {
            'name' : 'article:first',
            'type' : 'property',
            'dataType' : 'd:text',
            'defaultValue' : '',
            'binding' : '/article/author/name/first',
            'label' : 'first',
            'description' : 'Holds the description',
            'mandatory' : 'true',
            'enforced' : 'true',
            'protected' : 'false',
            'repeats' : 'false',
            'groupId' : 'article:author.article:name'
         },
         'article:last' :
         {
            'name' : 'article:last',
            'type' : 'property',
            'dataType' : 'd:text',
            'defaultValue' : '',
            'binding' : '/article/author/name/last',
            'label' : 'last',
            'description' : 'Holds the description',
            'mandatory' : 'true',
            'enforced' : 'true',
            'protected' : 'false',
            'repeats' : 'false',
            'groupId' : 'article:author.article:name'
         },
         'article:keywords' :
         {
            'name' : 'article:keywords',
            'type' : 'property',
            'dataType' : 'd:text',
            'defaultValue' : '',
            'binding' : '/article/keywords',
            'label' : 'keywords',
            'description' : 'Holds the description',
            'mandatory' : 'false',
            'enforced' : 'false',
            'protected' : 'false',
            'repeats' : 'true'
         },
         ...
      }
   }
   'data' :
   {
      'article:title' : 'The article title',
      'article:author.article:name.article:prefix' : 'Mr',
      'article:author.article:name.article:first' : 'Gavin',
      'article:author.article:name.article:last' : 'Cornwell',
      'article:keywords_0' : 'gavin',
      'article:keywords_1' : 'cornwell',
      'article:keywords_2' : 'alfresco',
      ...
   }
}

Engineering Notes

Attachments

    Outcomes