All Nuxeo Platform documentation versions

Nuxeo Platform 5.6
Nuxeo Platform 5.5
Nuxeo Enterprise Platform (EP) 5.4
Nuxeo Enterprise Platform (EP) 5.3

Nuxeo Platform Dev version (unreleased)

This documentation refers to the unreleased development version of the Nuxeo Platform. For the current production version, refer to the 5.6 documentation.

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Added TOC and related pages
Section
Column
width70%

Some templating

...

features have been made available to make it easier to control the layouts and widgets rendering.

Column
Panel
bgColor#FFFFFF
titleIn this section
Table of Contents

Custom layout template

A layout can define an xhtml XHTML template to be used in a given mode. Let's take a look at the default template structure.

Code Block
xhtml
<f:subview
  xmlns:c="http://java.sun.com/jstl/core"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:nxl="http://nuxeo.org/nxforms/layout"
  xmlns:nxu="http://nuxeo.org/nxweb/util"
  id="#{layout.id}"
  styleClass="#{layoutProperty_styleClass}">

  <c:set var="isEditMode" value="#{nxl:isBoundToEditMode(layout.mode)}" />

  <table class="dataInput">
    <tbody>

      <nxl:layoutRow>
        <tr class="#{layoutRow.properties['styleClass']}">
          <nxl:layoutRowWidget>
            <nxu:set var="fieldColspan"
              value="#{nxu:test(layoutRow.size==1, 3*layout.columns-2, 1) + nxu:test(widget.handlingLabels, 1, 0)}">
              <c:if test="#{not widget.handlingLabels}">
                <td class="labelColumn">
                  <ui:include src="/widgets/incl/widget_label_template.xhtml" />
                </td>
              </c:if>
              <td class="fieldColumn" colspan="#{fieldColspan}">
                <nxl:widget widget="#{widget}" value="#{value}" />
              </td>
            </nxu:set>
          </nxl:layoutRowWidget>
        </tr>
      </nxl:layoutRow>

    </tbody>
  </table>

<script>
  jQuery(document).ready(function() {
    jQuery(".widgetHelpLabel").tooltip({relative: true, position: 'bottom center'});
  });
</script>

</f:subview>

...

This layout intends to render columns within a table: each line will be filled thanks to a layout configuration. It is only used in view mode. Let's take a look at the default listing template structure.

Code Block
xhtml
<f:subview
  xmlns:c="http://java.sun.com/jstl/core"
  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"
  xmlns:a4j="https://ajax4jsf.dev.java.net/ajax"
  id="#{layout.id}">

<c:if test="false">
  Layout template applying to an item instance of PageSelections&lt;DocumentModel&gt; named "documents"

  Other needed parameters are:
  - provider: instance of a PageProvider&lt;DocumentModel&gt; to handle sort
  - layoutListingStatus: iteration status, used to print table header
    matching widget label.
</c:if>

<nxu:set var="hasSeveralSorts"
  value="#{provider.getSortInfos().size() > 1}"
  cache="true">

<c:if test="#{showListingHeader and layout.properties.showListingHeader}">
  <thead>
    <tr>
      <nxl:layoutColumn>
        <th>
          <c:choose>
            <c:when test="#{layoutColumn.properties.isListingSelectionBox}">
              <h:selectBooleanCheckbox id="#{layoutColumn.widgets[0].name}_header"
                title="#{messages['tooltip.content.select.all']}"
                value="#{documents.selected}">
                <a4j:support event="onclick"
                  action="#{documentListingActions.processSelectPage(contentView.name, contentView.selectionListName, documents.selected)}"
                  onclick="javascript:handleAllCheckBoxes('#{contentView.name}', this.checked)"
                  reRender="ajax_selection_buttons" />
              </h:selectBooleanCheckbox>
            </c:when>
            <c:when test="#{layoutColumn.properties.isListingSelectionBoxWithCurrentDocument}">
              <h:selectBooleanCheckbox id="#{layoutColumn.widgets[0].name}_header"
                title="#{messages['tooltip.content.select.all']}"
                value="#{documents.selected}">
                <a4j:support event="onclick"
                  onclick="javascript:handleAllCheckBoxes('#{contentView.name}', this.checked)"
                  action="#{documentListingActions.checkCurrentDocAndProcessSelectPage(contentView.name, contentView.selectionListName, documents.selected, currentDocument.ref)}"
                  reRender="ajax_selection_buttons" />
              </h:selectBooleanCheckbox>
            </c:when>
            <c:when test="#{layoutColumn.properties.useFirstWidgetLabelAsColumnHeader}">
              <c:choose>
                <c:when test="#{provider.sortable and !empty layoutColumn.properties.sortPropertyName}">
                  <nxu:set var="ascIndex"
                    value="#{provider.getSortInfoIndex(layoutColumn.properties.sortPropertyName, true)}"
                    cache="true">
                  <nxu:set var="descIndex"
                    value="#{provider.getSortInfoIndex(layoutColumn.properties.sortPropertyName, false)}"
                    cache="true">
                    <h:commandLink immediate="true"
                      action="#{provider.setSortInfo(layoutColumn.properties.sortPropertyName, nxu:test(ascIndex != -1, false, true), true)}"
                      id="#{layoutColumn.widgets[0].name}_header_sort">
                      <h:outputText value="#{layoutColumn.widgets[0].label}"
                        rendered="#{!layoutColumn.widgets[0].translated}" />
                      <h:outputText value="#{messages[layoutColumn.widgets[0].label]}"
                        rendered="#{layoutColumn.widgets[0].translated}" />
                    </h:commandLink>
                    <f:verbatim>&amp;nbsp;</f:verbatim>
                    <c:if test="#{ascIndex != -1}">
                      <h:commandLink immediate="true"
                        action="#{provider.setSortInfo(layoutColumn.properties.sortPropertyName, false, false)}"
                        id="#{layoutColumn.widgets[0].name}_header_sort_desc">
                        <h:graphicImage value="/icons/sort_selected_down.png" />
                        <c:if test="#{hasSeveralSorts}">
                          #{ascIndex + 1}
                        </c:if>
                      </h:commandLink>
                    </c:if>
                    <c:if test="#{descIndex != -1}">
                      <h:commandLink immediate="true"
                        action="#{provider.setSortInfo(layoutColumn.properties.sortPropertyName, true, false)}"
                        id="#{layoutColumn.widgets[0].name}_header_sort_asc">
                        <h:graphicImage value="/icons/sort_selected_up.png" />
                        <c:if test="#{hasSeveralSorts}">
                          #{descIndex + 1}
                        </c:if>
                      </h:commandLink>
                    </c:if>
                    <c:if test="#{ascIndex == -1 and descIndex == -1}">
                      <h:commandLink immediate="true"
                        action="#{provider.addSortInfo(layoutColumn.properties.sortPropertyName, true)}"
                        id="#{layoutColumn.widgets[0].name}_header_sort_add">
                        <h:graphicImage value="/icons/sort_down.png" />
                      </h:commandLink>
                    </c:if>
                  </nxu:set>
                  </nxu:set>
                </c:when>
                <c:otherwise>
                  <h:outputText value="#{layoutColumn.widgets[0].label}"
                    rendered="#{!layoutColumn.widgets[0].translated}" />
                  <h:outputText value="#{messages[layoutColumn.widgets[0].label]}"
                    rendered="#{layoutColumn.widgets[0].translated}" />
                </c:otherwise>
              </c:choose>
            </c:when>
          </c:choose>
        </th>
      </nxl:layoutColumn>
      <c:if test="#{provider.sortable}">
        <th>
          <h:graphicImage value="/icons/lightbulb.png"
            onmouseover="tooltip.show('#{messages['contentview.sort.help']}', 200, 'topleft');"
            onmouseout="tooltip.hide();" />
        </th>
      </c:if>
    </tr>
  </thead>
</c:if>

</nxu:set>

<c:set var="trStyleClass" value="#{nxu:test(layoutListingStatus.index%2 ==0, 'dataRowEven', 'dataRowOdd')}" />
<tr class="#{nxu:test(layout.properties.showRowEvenOddClass, trStyleClass, '')}">
  <nxl:layoutColumn>
    <td class="#{layoutColumn.properties.columnStyleClass}">
      <nxl:layoutColumnWidget>
        <nxl:widget widget="#{widget}" value="#{value}" />
        <c:if test="#{layoutColumn.size > 1 and layoutColumn.size > widgetIndex + 1 and widgetIndex > 0}">
          <br />
        </c:if>
      </nxl:layoutColumnWidget>
    </td>
  </nxl:layoutColumn>
  <c:if test="#{provider.sortable}">
    <td class="iconColumn">
    </td>
  </c:if>
</tr>

</f:subview>

...

Since version 5.6, it uses a "grid" rendering allowing fine-grained control over the place used by widgets. It combines the following layout template with the use of the standard "container" widget type. The container widgets pile up any number of widgets displaying information about the document metadata, its state, relations, publications, etc...

Code Block
xhtml
<f:subview
  xmlns:c="http://java.sun.com/jstl/core"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:nxl="http://nuxeo.org/nxforms/layout"
  id="#{layout.id}">

  <c:if test="false">
    Handles grid layouts, using style classes defined by row properties.
  </c:if>

  <div class="gridContainer">
    <nxl:layoutRow>
      <div class="gridRow">
        <nxl:layoutRowWidget>
          <c:set var="gridStyleClassProp" value="nxl_gridStyleClass_#{widgetIndex}" />
          <div class="gridBox #{layoutRow.properties[gridStyleClassProp]}">
            <nxl:widget widget="#{widget}" value="#{value}" />
          </div>
        </nxl:layoutRowWidget>
      </div>
    </nxl:layoutRow>
  </div>

</f:subview>

When using this layout template, the layout definition will use properties defined on rows to allow more or less place to the widgets. Here is the default summary definition:

Code Block
xhtml
<layout name="grid_summary_layout">
  <templates>
    <template mode="any">
      /layouts/layout_grid_template.xhtml
    </template>
  </templates>
  <rows>
    <row>
      <properties mode="any">
        <property name="nxl_gridStyleClass_0">gridStyle7</property>
        <property name="nxl_gridStyleClass_1">gridStyle5</property>
      </properties>
      <widget>summary_left_panel</widget>
      <widget>summary_right_panel</widget>
    </row>
  </rows>
</layout>

...

Let's have a look at a sample template used to present contributors to a document.

Code Block
xhtml
<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:nxu="http://nuxeo.org/nxweb/util"
  xmlns:nxdir="http://nuxeo.org/nxdirectory"
  xmlns:c="http://java.sun.com/jstl/core"
  xmlns:nxp="http://nuxeo.org/nxweb/pdf"
  id="#{widget.id}">
  <div>
    <c:forEach var="username" items="#{field}" varStatus="status">
      <c:if test="#{!status.first}">#{status.last ? andLabel : ', '}</c:if>
      <h:outputText value="#{nxu:userFullName(username)}" 
        title="#{username}" onmouseover="tooltip.show(username, 500);"
        onmouseout="tooltip.hide();"/>
    </c:forEach>
  </div>
</f:subview>

...

Some helper methods make it easier to check the widget mode, here is the complete current definition of the contributors widget type in Nuxeo.

Code Block
xhtml
<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:nxu="http://nuxeo.org/nxweb/util"
  xmlns:nxdir="http://nuxeo.org/nxdirectory"
  xmlns:c="http://java.sun.com/jstl/core"
  xmlns:nxp="http://nuxeo.org/nxweb/pdf"
  id="#{widget.id}">
<c:set var="andLabel" value=" #{messages['label.and']} " scope="page" />
<c:if test="#{nxl:isLikePlainMode(widget.mode)}"><nxu:inputList
  value="#{field}" model="contributorsModel"><h:outputText
  value="#{nxu:userFullName(contributorsModel.rowData)}" /><h:outputText
  rendered="#{contributorsModel.rowIndex != contributorsModel.rowCount -1}"
  value="#{nxu:test(contributorsModel.rowIndex == contributorsModel.rowCount -2, andLabel, ', ')}" /></nxu:inputList></c:if>
<c:if test="#{widget.mode == 'pdf'}">
  <nxp:html>
    <c:forEach var="username" items="#{field}" varStatus="status">
      <c:if test="#{!status.first}">#{status.last ? andLabel : ', '}</c:if>
      <h:outputText value="#{nxu:userFullName(username)}" />
    </c:forEach>
  </nxp:html>
</c:if>
<c:if test="#{nxl:isLikeViewMode(widget.mode)}">
  <div>
    <c:forEach var="username" items="#{field}" varStatus="status">
      <c:if test="#{!status.first}">#{status.last ? andLabel : ', '}</c:if>
      <h:outputText value="#{nxu:userFullName(username)}" 
        title="#{username}" onmouseover="tooltip.show(username, 500);"
        onmouseout="tooltip.hide();"/>
    </c:forEach>
  </div>
</c:if>
</f:subview>

...

Some rules must be followed when writing xhtml XHTML to be included in templates:

...

For instance, to handle a list of String elements, you can use the definition:

Code Block
xhtml
<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.

With nuxeo Nuxeo version <= 5.3.0, 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:

Code Block
xhtml
<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>

With nuxeo Nuxeo version > 5.3.0, 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:

Code Block
xhtml
<widget name="employees" type="list">
  <fields>
    <field>company:employees</field>
  </fields>
  <subWidgets>
    <widget name="employee" type="template">
      <labels>
        <label mode="any"></label>
      </labels>
      <properties mode="any">
        <property name="template">
          /widgets/complex_list_item_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>

...

To handle a complex property (the value is a map with keys 'name' and 'email' for instance, you can use the definition:

Code Block
xhtml
<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>

...

A builtin template has been added to handle sublists: the original "list" widget is equivalent to a widget of type "template" using the file /widgets/list_widget_template.xhtml. To handle the sublist, this template needs to be changed. The file list_subwidget_template.xhtml is available for it since nuxeo Nuxeo version 5.2 GA.

To handle a sublist property, you can use take example on this definition:

Code Block
xhtml
<widget name="employees" type="list">
  <fields>
    <field>company:employees</field>
  </fields>
  <subWidgets>
    <widget name="employee" type="template">
      <labels>
        <label mode="any"></label>
      </labels>
      <properties mode="any">
        <property name="template">
          /widgets/complex_list_item_widget_template.xhtml
        </property>
      </properties>
      <!-- subwidgets for complex -->
      <subWidgets>
        <widget name="phoneNumbers" type="template">
          <fields>
            <field>phoneNumbers</field>
          </fields>
          <properties mode="any">
            <property name="template">
              /widgets/list_subwidget_template.xhtml
            </property>
          </properties>
          <subWidgets>
            <widget name="phoneNumber" type="text">
              <label mode="any"></label>
              <fields>
                <field></field>
              </fields>
            </widget>
          </subWidgets>
        </widget>
      </subWidgets>
    </widget>
  </subWidgets>
</widget>
Content by Label
spaces@self
titleRelated topics
showLabelsfalse
labelssummary-layout
showSpacefalse
typepage