Table of Contents
Let our artists go wild on imaginative page layouts.
-- Grant Morrison
Layouts are used to generate pages rendering from an xml configuration.
In a document oriented perspective, layouts are mostly used to display a document metadata in different use cases: present a form to set its schemas fields when creating or editing the document, and present these fields values when simply displaying the document. A single layout definition can be used to address these use cases as it will be rendered for a given document and in a given mode.
In this chapter we will see how to define a layout, link it to a document type, and use it in XHTML pages.
A layout is a group of widgets that specifies how widgets are assembled and displayed. It manages widget rows and has global control on the rendering of each of its widgets.
It's all the same machine, right? The Pentagon, multinational corporations, the police! You do one little job, you build a widget in Saskatoon and the next thing you know it's two miles under the desert, the essential component of a death machine!
-- Holloway, Cube
A widget defines how one or several fields from a schema will be presented on a page. It can be displayed in several modes and holds additional information like for instance the field label. When it takes user entries, it can perform conversion and validation like usual JSF components.
A widget definition includes the mention of its type. Widget types make the association between a widget definition and the jsf component tree that will be used to render it in a given mode.
The layout modes can be anything although some default modes are included in the application: create, edit, view and search.
The widget modes are more restricted and widget types will usually only handle two modes: edit and view. The widget mode is computed from the layout mode following this rule: if the layout is in mode create, edit or search, the widget will be in edit mode. Otherwise the widget will be in view mode.
It is possible to override this behaviour in the widget definition, and state that, for instance, whatever the layout mode, the widget will be in view mode so that it only displays read-only values. The pseudo-mode "hidden" can also be used in a widget definition to exclude this widget from the layout in a given mode.
The pseudo mode "any" is only used in layouts and widgets definitions to set up default values.
Custom layouts can be contributed to the web layout service, using its extension point. The layout definition is then available through the service to control how it will be displayed in a given mode.
Some jsf tags have been added to the Nuxeo ECM layout tag library to make then easily available from an xhtml page.
Layouts are registered using a regular extension point on the Nuxeo ECM layout service. Here is a sample contribution.
<?xml version="1.0"?> <component name="org.nuxeo.ecm.platform.forms.layouts.webapp"> <extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager" point="layouts"> <layout name="heading"> <templates> <template mode="any">/layouts/layout_default_template.xhtml</template> </templates> <rows> <row> <widget>title</widget> </row> <row> <widget>description</widget> </row> </rows> <widget name="title" type="text"> <labels> <label mode="any">label.dublincore.title</label> </labels> <translated>true</translated> <fields> <field>dc:title</field> </fields> <properties widgetMode="edit"> <property name="required">true</property> </properties> </widget> <widget name="description" type="textarea"> <labels> <label mode="any">label.dublincore.description</label> </labels> <translated>true</translated> <fields> <field>dc:description</field> </fields> </widget> </layout> </extension> </component>
Example 8.1. Sample layout contribution to the layout service.
The above layout definition is used to display the title and the description of a document. Here are its properties:
name
: string used as an identifier. In the
example, the layout name is "heading".
templates
: list of templates to use for this
layout global rendering. In the example, the layout template in any
mode is the xhtml file at "/layouts/layout_default_template.xhtml".
Please refer to section about custom layout templates for
more information.
rows
: definition about what widgets will have
to be displayed on this row. Each row can hold several widgets, and an
empty widget tag can be used to control the alignment. The widget has
to match a widget name given in this layout definition. In the
example, two rows have been defined, the first one will hold the
"title" widget, and the second one will hold the "description"
widget.
widget
: a layout definition can hold any
number of widget definitions. If the widget is not referenced in the
rows definition, it will be ignored. Since 5.1.7 and 5.2.0, it will be
searched in the global widget registry before being ignored. This new
feature is a convenient way to share widget definitions between
layouts. Please refer the widget
definition section.
Two widget definitions are presented on the above example. Let's look into the "title" widget and present its properties:
name
: string used as an identifier in the
layout context. In the example, the widget name is "title".
type
: the widget type that will manage the
rendering of this widget. In this example, the widget type is "text".
This widget type is a standard widget types, more information about
widget types is available here.
labels
: list of labels to use for this widget
in a given mode. If no label is defined in a specific mode, the label
defined in the "any" mode will be taken as default. In the example, a
single label is defined for any mode to the "label.dublicore.title"
message. If no label is defined at all, a default label will be used
following the convention:
"label.widget.[layoutName].[widgetName]".
translated
: string representing a boolean
value ("true" or "false") and defaulting to "false". When set as
translated, the widget labels will be treated as messages and
displayed translated. In the example, the "label.dublincore.title"
message will be translated at rendering time. Default is true.
fields
: list of fields that will be managed
by this widget. In the example, we handle the field "dc:title" where
"dc" is the prefix for the "dublincore" schema. If the schema you
would like to use does not have a prefix, use the schema name instead.
Note that most of standard widget types only handle one field. Side
note: when dealing with an attribute from the document that is not a
metadata, you can use the property name as it will be resolved like a
value expression of the form #{document.attribute}.
properties
: list of properties that will
apply to the widget in a given mode. Properties listed in the "any"
mode will be merged with properties for the specific mode. Depending
on the widget type, these properties can be used to control what jsf
component will be used and/or what attributes will be set on these
components. In standard widget types, only one component is used given
the mode, and properties will be set as attributes on the component.
For instance, when using the "text" widget type, every property
accepted by the "<h:inputText />" tag can be set as properties
on "edit" and "create" modes, and every property accepted by the
"<h:outputText />" tag can be set as properties. Properties can
also be added in a given widget mode.
Additional properties can be set on a widget:
helpLabels
: list that follows the same
pattern as labels, but used to set help labels.
widgetModes
: list of local modes used to
override the local mode (from the layout).
subWidgets
: list of widget definitions, as
the widget list, used to describe sub widgets use to help the
configuration of some complex widget types.
Here is a more complex layout contribution that shows the syntax to use for these additional properties:
<?xml version="1.0"?> <component name="org.nuxeo.ecm.platform.forms.layouts.webapp"> <!-- WARNING: this extension point is only available in 5.1.7 and 5.2.0 --> <extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager" point="widgets"> <!-- global definition of a widget so that it can be used in several layouts --> <widget name="description" type="textarea"> <labels> <label mode="any">description</label> </labels> <translated>true</translated> <fields> <field>dc:description</field> </fields> <properties widgetMode="edit"> <property name="styleClass">dataInputText</property> </properties> </widget> </extension> <extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager" point="layouts"> <layout name="complex"> <templates> <template mode="any">/layouts/layout_default_template.xhtml</template> </templates> <rows> <row> <widget>identifier</widget> </row> <row> <!-- reference a global widget --> <widget>description</widget> </row> </rows> <widget name="identifier" type="text"> <labels> <label mode="any">label.dublincore.title</label> </labels> <translated>true</translated> <fields> <field>uid:uid</field> </fields> <widgetModes> <!-- not shown in create mode --> <mode value="create">hidden</mode> </widgetModes> <properties widgetMode="edit"> <!-- required in widget mode edit --> <property name="required">true</property> </properties> <properties mode="view"> <!-- property applying in view mode --> <property name="styleClass">cssClass</property> </properties> </widget> </layout> </extension> </component>
Example 8.2. Sample complex layout contribution to the layout service.
Layouts can be linked to a document type definition by specifying the layout names:
<layouts mode="any"> <layout>heading</layout> <layout>note</layout> </layouts>
Layouts are defined in a given mode; layouts in the "any" mode will be used as default when no layouts are given in specific modes.
Since 5.2.GA, it is possible to merge layouts when redefining the document type, adding a property append="true":
<layouts mode="any" append="true"> <layout>newLayout</layout> </layouts>
Layouts can be displayed thanks to a series a JSF tags that will query the web layout service to get the layout definition and build it for a given mode.
For instance, we can use the documentLayout
tag to
display the layouts of a document:
<div xmlns="http://www.w3.org/1999/xhtml" xmlns:nxl="http://nuxeo.org/nxforms/layout"> <nxl:documentLayout mode="view" value="#{currentDocument}" /> </div>
We can also display a specific layout for a document, even if it is not specified in the document type definition:
<div xmlns="http://www.w3.org/1999/xhtml" xmlns:nxl="http://nuxeo.org/nxforms/layout"> <nxl:layout name="heading" mode="view" value="#{currentDocument}" /> </div>
Please refer to the tag library documentation available at http://doc.nuxeo.org/current/tlddoc/.
A series of widget types has been defined for the most generic uses cases.
Please refer to the tag library documentation available at http://doc.nuxeo.org/5.1/tlddoc/ for nuxeo jsf tags.
The text widget displays an input text in create or edit mode, with
additional message tag for errors, and a regular text output in any other
mode. Widgets using this type can provide properties accepted on a
<h:inputText />
tag in create or edit mode, and
properties accepted on a <h:outputText />
tag in other
modes.
The int widget displays an input text in create or edit mode, with
additional message tag for errors, and a regular text output in any other
mode. It uses a number converter. Widgets using this type can provide
properties accepted on a <h:inputText />
tag in create
or edit mode, and properties accepted on a <h:outputText
/>
tag in other modes.
The secret widget displays an input secret text in create or edit
mode, with additional message tag for errors, and nothing in any other
mode. Widgets using this type can provide properties accepted on a
<h:inputSecret />
tag in create or edit mode.
The textarea widget displays a textarea in create or edit mode, with
additional message tag for errors, and a regular text output in any other
mode. Widgets using this type can provide properties accepted on a
<h:inputTextarea />
tag in create or edit mode, and
properties accepted on a <h:outputText />
tag in other
modes.
The datetime widget displays a javascript calendar in create or edit
mode, with additional message tag for errors, and a regular text output in
any other mode. It uses a date time converter. Widgets using this type can
provide properties accepted on a <nxu:inputDatetime />
tag in create or edit mode, and properties accepted on a
<h:outputText />
tag in other modes. The converter will
also be given these properties.
The template widget displays a template content whatever the mode. Widgets using this type must provide the path to this template; this template can check the mode to adapt the rendering.
Information about how to write a template is given in the custom widget template section.
The file widget displays a file uploader/editor in create or edit
mode, with additional message tag for errors, and a link to the file in
other modes. Widgets using this type can provide properties accepted on a
<nxu:inputFile />
tag in create or edit mode, and
properties accepted on a <nxu:outputFile />
tag in
other modes.
The htmltext widget displays an html text editor in create or edit
mode, with additional message tag for errors, and a regular text output in
other modes (without escaping the text). Widgets using this type can
provide properties accepted on a <nxu:editor />
tag in
create or edit mode, and properties accepted on a <nxu:outputText
/>
tag in other modes.
The selectOneDirectory widget displays a selection of directory
entries in create or edit mode, with additional message tag for errors,
and the directory entry label in other modes. Widgets using this type can
provide properties accepted on a <nxd:selectOneListbox
/>
tag in create or edit mode, and properties accepted on a
<nxd:directoryEntryOutput />
tag in other modes.
The selectManyDirectory widget displays a multi selection of
directory entries in create or edit mode, with additional message tag for
errors, and the directory entries labels in other modes. Widgets using
this type can provide properties accepted on a
<nxd:selectManyListbox />
tag in create or edit mode,
and properties accepted on a <nxd:directoryEntryOutput
/>
tag in other modes.
The list widget displays an editable list of items in create or edit
mode, with additional message tag for errors, and the same list of items
in other modes. Items are defined using sub wigdets configuration. This
actually a template widget type whose template uses a
<nxu:inputList />
tag in edit or create mode, and a
table iterating over items in other modes.
Some templating feature have been made available to make it easier to control the layouts and widgets rendering.
A layout can define an xhtml template to be used in a given mode. Let's take a look at the default template structure.
<f:subview xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:nxl="http://nuxeo.org/nxforms/layout" xmlns:nxu="http://nuxeo.org/nxweb/util" xmlns:nxd="http://nuxeo.org/nxweb/document"> <f:subview rendered="#{layout.mode != 'edit' and layout.mode != 'create'}"> <table class="dataInput"> <tbody> <nxl:layoutRow> <tr> <nxl:layoutRowWidget> <td class="labelColumn"> <h:outputText value="#{widget.label}" rendered="#{!widget.translated}" /> <h:outputText value="#{messages[widget.label]}" rendered="#{widget.translated}" /> </td> <td class="fieldColumn"> <nxl:widget widget="#{widget}" mode="#{widget.mode}" value="#{value}" /> </td> </nxl:layoutRowWidget> </tr> </nxl:layoutRow> </tbody> </table> </f:subview> <f:subview rendered="#{layout.mode == 'edit' or layout.mode == 'create'}"> <table class="dataInput"> <tbody> <nxl:layoutRow> <tr> <nxl:layoutRowWidget> <td class="labelColumn"> <h:outputText value="#{widget.label}" rendered="#{!widget.translated}" styleClass="#{nxu:test(widget.required, 'required', '')}" /> <h:outputText value="#{messages[widget.label]}" rendered="#{widget.translated}" styleClass="#{nxu:test(widget.required, 'required', '')}" /> </td> <td class="fieldColumn"> <nxl:widget widget="#{widget}" mode="#{widget.mode}" value="#{value}" /> </td> </nxl:layoutRowWidget> </tr> </nxl:layoutRow> </tbody> </table> </f:subview> </f:subview>
Example 8.3. Default layout template
This template is intended to be unused in any mode, so the layout mode is checked to provide a different rendering in "edit" or "create" modes and other modes.
When this template is included in the page, several variables are made available:
layout
: the computed layout value ; its mode
and number of columns can be checked on it.
value
or document
: the
document model (or whatever item used as value).
The layout system integration using facelets features requires that
iterations are performed on the layout rows and widgets. The
<nxl:layoutRow> and <nxl:layoutRowWidget /> trigger these
iterations. Inside the layoutRow tag, two more variables are made
available: layoutRow
and
layoutRowIndex
. Inside the layoutRowWidget, two more
variables are made available: widget
and
widgetIndex
.
These variables can be used to control the layout rendering. For instance, the default template is the one applying the "required" style on widget labels, and translating these labels if the widget must be translated. It also makes sure widgets on the same rows are presented in the same table row.
The template widget type makes it possible to set a template to use as an include.
Let's have a look at a sample template used to present contributors to a document.
<f:subview xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:a4j="https://ajax4jsf.dev.java.net/ajax" xmlns:t="http://myfaces.apache.org/tomahawk" xmlns:nxdir="http://nuxeo.org/nxdirectory"> <t:dataList id="#{widget.id}" var="listItem" value="#{field_0}" layout="simple" styleClass="standardList"> <h:graphicImage value="/icons/html.png" /> <h:commandLink value="#{listItem}" immediate="true" action="#{userManagerActions.viewUser}"> <f:param name="usernameParam" value="#{listItem}" /> </h:commandLink> <br /> </t:dataList> </f:subview>
Example 8.4. Sample template for a widget
This widget presents the contributors of a document with specific links on each on these user identifier information.
Having a widget type just to perform this kind of rendering would be overkill, so using a widget with type "template" can be useful here.
When this template is included in the page, the
widget
variable is made available:
Some rules must be followed when writing xhtml to be included in templates:
Use the widget id as identifier: the widget id is computed to be unique within the page, so it should be used instead of fixed id attributes so that another widget using the same template will not introduce duplicated ids in the jsf component tree.
Use the variable with name following the
field_n
pattern to reference field values. For
instance, binding a jsf component value attribute to
#{field_0}
means binding it to the first field
definition.
The standard widget type "list" is actually a widget of type "template" using a static template path: /widgets/list_widget_template.xhtml. If this default behaviour does not suit your needs, you can simply copy this template, make your changes, and use a widget of type "template" with the new template path.
This template assumes that each element of the list will be displayed using subwidgets definitions.
For instance, to handle a list of String elements, you can use the definition:
<widget name="contributors" type="list"> <fields> <field>dc:contributors</field> </fields> <subWidgets> <widget name="contributor" type="text"> <fields> <field></field> </fields> </widget> </subWidgets> </widget>
The empty field definition in the subwidget is used to specify that each element of the list is itself the element to display.
To handle a list of complex properties (each entry of the list is a map with keys 'name' and 'email' for instance), you can use the definition:
<widget name="employees" type="list"> <fields> <field>company:employees</field> </fields> <subWidgets> <widget name="employee" type="template"> <labels> <label mode="any"></label> </labels> <fields> <field></field> </fields> <properties mode="any"> <property name="template"> /widgets/complex_widget_template.xhtml </property> </properties> <!-- subwidgets for complex --> <subWidgets> <widget name="name" type="text"> <fields> <field>name</field> </fields> </widget> <widget name="email" type="text"> <fields> <field>email</field> </fields> </widget> </subWidgets> </widget> </subWidgets> </widget>
A builtin template has been added to handle complex properties. It is available at /widgets/complex_widget_template.xhtml. It assumes that each element of the complex property will be displated using subwidgets definitions.
To handle a complex property (the value is a map with keys 'name' and 'email' for instance, you can use the definition:
<widget name="manager" type="template"> <fields> <field>company:manager</field> </fields> <properties mode="any"> <property name="template"> /widgets/complex_widget_template.xhtml </property> </properties> <subWidgets> <widget name="name" type="text"> <fields> <field>name</field> </fields> </widget> <widget name="email" type="text"> <fields> <field>email</field> </fields> </widget> </subWidgets> </widget>
Custom widget types can be added to the standard list thanks to another extension point on the web layout service.
Here is a sample widget type registration:
<?xml version="1.0"?> <component name="org.nuxeo.ecm.platform.forms.layout.MyContribution"> <extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager" point="widgettypes"> <widgetType name="customtype"> <handler-class> org.myproject.MyCustomWidgetTypeHandler </handler-class> <property name="foo">bar</property> </widgetType> </extension> </component>
Example 8.5. Sample widget type contribution to the layout service.
The custom widget type class must follow the org.nuxeo.ecm.platform.forms.layout.facelets.WidgetTypeHandler interface.
Additional properties can be added to the type registration so that the same class can be reused with a different behaviour given the property value.
The widet type handler is used to generate facelet tag handlers dynamically taking into account the mode, and any other properties that can be found on a widget.
The best thing to do before writing a custom widget type handler is to go see how standard widget type handlers are implemented, as some helper methods can be reused to ease implementation of specific behaviours.
Layouts can be used with other kind of objects than documents.
The field definition has to match a document property for which setters and getters will be available, or the "value" property must be passed explicitely for the binding to happen. Depending on the widget, other kinds of bindings can be done.