Let's talk about designing a Web UI framework, to use big words :)


Sometimes it's difficult to assess the configurability needs for a
framework, and sometimes these needs change and have to evolve. Ideally, the
new design will allow to answer to most common needs with little work, but
will keep it possible to achieve more complex tasks. It will also take care
of restrictions that the underlying frameworks impose.


This is why we decided to introduce a new concept in the Nuxeo UI Framework:
content views.


Content_view


A content view is a notion to define all the elements needed to get a list
of items and perform their rendering. The most obvious use case is the
listing of a folderish document content, where we would like to be able to:

  • define the NXQL query that will be used to retrieve the documents,
    filtering some of them (documents in the trash for instance)
  • pass on contextual parameters to the query (the current container
    identifier)
  • define a filtering form to refine the query
  • define what columns will be used for the rendering of the list, and how to
    display their content
  • handle selection of documents, and actions available when selecting them
    (copy, paste, delete...)
  • handle sorting and pagination
  • handle caching, and refresh of this cache when a document is created,
    deleted, modified...



The content view framework aims at putting together several configurations
that were already available in Nuxeo and have become complicated to maintain
and to configure.

  • query models: XML configuration that makes it possible to define a query,
    its needed parameters, and that can use a document model to retrieve query
    parameters (typically from a search form)
  • result providers: Java classes that handle pagination of a list of
    documents retrieved from a NXQL query to a core session
  • result provider farms: Java classes (typically Seam components) that
    handle resolution of contextual query parameters to pass on to the query
    models
  • result provider cache: Java class handling caching and invalidation of
    result providers
  • select datamodel: Java class encapsulating a list of items to handle
    selection in a JSF environment
  • Seam remoting and Javascript methods to handle re-rendering of available
    actions on selection
  • XHTML pages to handle rendering of needed columns, pagination, sorting...


So it had become overly complicated to define a custom listing of items, and
most developers eventually got lost when introducing result provider
farms. Moreover, Nuxeo Studio could not provide that kind of configurability because it only handles XML configuration or predefined XHTML pages.


Content views make it possible to answer to all these needs, and more, with
the following stategy:

  • handle rendering of listings using layouts: layouts are configured only
    using XML configuration and are reusable in different contexts
  • improve providers, making them able to handle any kind of item type (not
    only documents), handle item selection, pagination, multiple sorting...
  • make it possible to define the provider, its query and its parameters
    through XML configuration, using EL expressions to resolve contextual
    parameters
  • configure caching using cache keys that can be resolved using EL
    expressions, and invalidation using event names
  • make it possible to define one or more content views on a document type,
    but keep them independant from it so that it can be used also for advanced
    forms



Here is a typical content view definition, used when rendering a folderish
document content:


<contentView name="document_content">
<coreQueryPageProvider>
<property name="coreSession">#{documentManager}</property>
<pattern>
SELECT * FROM Document WHERE ecm:parentId = ?
AND ecm:isCheckedInVersion = 0
AND ecm:mixinType != 'HiddenInNavigation'
AND ecm:currentLifeCycleState != 'deleted'
</pattern>
<parameter>#{currentDocument.id}</parameter>
<sort column="dc:title" ascending="true" />
<pageSize>20</pageSize>
</coreQueryPageProvider>
<cacheKey>#{currentDocument.id}</cacheKey>
<cacheSize>10</cacheSize>
<refresh>
<event>documentChanged</event>
<event>documentChildrenChanged</event>
</refresh>
<resultLayouts>
<layout name="document_listing_ajax"
title="document_listing"
translateTitle="true"
iconPath="/icons/document_listing_icon.png" />
<layout name="document_listing_ajax_compact_2_columns"
title="document_listing_compact_2_columns"
translateTitle="true"
iconPath="/icons/document_listing_compact_2_columns_icon.png" />
<layout name="document_listing_ajax_icon_2_columns"
title="document_listing_icon_2_columns"
translateTitle="true"
iconPath="/icons/document_listing_icon_2_columns_icon.png" />
</resultLayouts>
<selectionList>CURRENT_SELECTION</selectionList>
<actions category="CURRENT_SELECTION_LIST" />
</contentView>


Here the core query page provider defines its contextual properties to
perform the query on the core session, and to fill the parameter of its
query (the current document identifier). It also states a sort column, and
the default page size (20 items).


The cache key is also linked to the current document identifier: this is useful
to keep the current page, sorting information, current result layout,
etc... in place, until we need to refresh it because some documents may have
appeared in the application. The cache size is 10: the cache will be
effective until 10 different documents have generated this content
view. When this content view is computed for the 11th document, the cache
for the first document is lost and will need to be recomputed.


The last part of the configuration makes it possible to define the different
listing layouts available for this content view, in case you'd like to
switch between different presentations. The selection list and actions
handle document selection, as well as buttons available on selection. The
re-rendering of available actions is done through ajax calls configured in
the layout templates and widgets, because the seam remoting mechanism
imposed too many restrictions on the HTML structure of action buttons.


The caching mechanism and rendering are handled by generic Seam components
and XHTML templates that will use the defined content view. It is also
possible to attach content views to documents, so that their default page
displays the given content views provided a category. For instance, here is
the configuration of the Folder document type:


<type id="Folder">
<label>Folder</label>
...
<contentViews category="content">
<contentView>document_content</contentView>
</contentViews>
<contentViews category="trash_content">
<contentView>document_trash_content</contentView>
</contentViews>
</type>


Next step is to make Nuxeo Studio benefit from these changes: folderish
documents content, search form and results, virtual navigation results will
be migrated to content views, so that all its features can be configured
using a nice interface.

The complete documentation for content views is available here: https://doc.nuxeo.com/display/NXDOC/Content+views