Table of Contents
Objective: To understand how to customize the web user interface for a new document type and understand the relationship between the larger Nuxeo EP server UI and the UI of a particular document type.
I was taught that the way of progress was neither swift nor easy. Marie Curie, Polish/French Physicist, twice winner of the Nobel Prize (1867-1934)
If you have any comments, questions, or general-purpose harassment you would like give us about this book, then please use the comment form at the bottom of each page! We promise that we will try to incorporate any feedback you give (minus the profanity, of course), will respond to your questions, and credit you appropriately in section 4 below.
As in the last lesson, we have a prepared a skeleton for you to start from for this lesson. This skeleton contains all the code from the last lesson so we can improve the look of our new document type. To retrieve, unpack and build an eclipse project, use a set of commands like this:
[~] $ cd /nuxeo/workspace/ [/nuxeo/workspace] $ svn export http://svn.nuxeo.org/nuxeo/sandbox/iansmith/book/lesson-basic-ui/ [/nuxeo/workspace] $ cd lesson-basic-ui [/nuxeo/workspace/lesson-basic-ui] $ mvn eclipse:eclipse
Note that we are using the same eclipse workspace as in the
previous lesson, /nuxeo/workspace,
and that we have created another project for Eclipse to use, this
time called lesson-basic-ui. Also as
before, you will need to import the project into your eclipse
workspace and then verify in eclipse that your source code path is
free of Maven-generated excess; you should have only src/main/java and src/test/java in your source code path.
We will start updating the user interface by adding the ability
to actually use the fields we so carefully added in the previous
lesson. To do this, we are going to use another extension point,
layout, in the WebLayoutManager component. The following content
should be placed in a file called layout-contrib.xml in the OSGI-INF directory.
<component name="org.nuxeo.book.component.layout">
<extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
point="layouts">
<layout name="upcoming">
<templates>
<template mode="any">/layouts/layout_default_template.xhtml</template>
</templates>
<rows>
<row>
<widget>upcoming_when</widget>
</row>
<row>
<widget>upcoming_who</widget>
</row>
<row>
<widget>upcoming_howmuch</widget>
</row>
</rows>
<widget name="upcoming_when" type="datetime">
<labels>
<label mode="any">When?</label>
</labels>
<translated>false</translated>
<fields>
<field>up:occursOn</field>
</fields>
<properties widgetMode="edit">
<property name="required">true</property>
</properties>
</widget>
<widget name="upcoming_who" type="text">
<labels>
<label mode="any">Who?</label>
</labels>
<translated>false</translated>
<fields>
<field>up:presenter</field>
</fields>
</widget>
<widget name="upcoming_howmuch" type="text">
<labels>
<label mode="any">How much to get in?</label>
</labels>
<translated>false</translated>
<fields>
<field>up:admissionPrice</field>
</fields>
<properties widgetMode="edit">
<property name="required">true</property>
</properties>
</widget>
</layout>
</extension>
</component>
This is a much more substantive contribution than the prior ones
have been. This contribution can be broken into three parts: the
top section, surrounded by the templates tag, the middle section inside
the rows tag, that defines
where the items are laid out
on the screen, and the bottom part, inside each of the three widget
tags, which defines what is
going to be laid out. We are going to defer discussing the first
part of the contribution until a later time in this lesson.
The middle section defines three rows, each one with a widget in
it. You can place all the widgets in one row if you want to see
what happens; we just felt this was the most appropriate for our
data. (If you want to use two rows and make things line up, this
<widget/> can be used as
a spacer.) The values in the middle section's widget nodes, such as
upcoming_who, must match up with
existing widget definitions (defined elsewhere in Nuxeo) or those
in the remainder of the file. In our case, all three widgets we are
using are defined in the lower section. We have prefixed our
widgets with the the schema name of the of the data they display
because we want to be sure that these are unique. It is possible to
share widget definitions between layouts by using the same widget
names, so it is advised that you take steps to make your widget
names unique if you want to be sure.
The latter section of this contribution defines our three new
widget types, one for each field in our upcoming schema. Roughly, a widget definition in this
file tells Nuxeo's web interface what type of objects you want on
screen. So, looking at the upcoming_when widget definition you can see that
the "type" is an object capable of displaying the date and time:
this will result in a calendar plus a spot to enter the time of
day. Similarly, upcoming_who widget
uses an object capable of entering text. Strangely, the
upcoming_howmuch widget does too! This
is because the widget type describes only what visual form the user will see
on the screen; it is unrelated to the type of data in the schema.
You should notice that each widget says what fields of the schema
data in corresponds to, one widget for each field in this example.
The definition of upcoming_howmuch
says that it will use a text object on screen, yet the schema
definition (upcoming.xsd) says that
the field up:admissionPrice is a
floating point number! The nuxeo server will take care of doing
most of these transformations from the display representation to
the stored representation for you without any help. You can also
add your own, as we will see in a future lesson.
Each widget definition above declares a label and some text.
This is the text that will be shown to the user just beside the
value of the field in the schema. Just after the label definition,
we have set <translated>false</translated>
because we want the text used literally. In the next lesson, you
will see how to change a layout definition to work correctly in
multiple languages.
The definitions of the first and last widgets, upcoming_when and upcoming_howmuch have a properties section. There a number of
properties that can be set on a widget, but we will limit ourselves
to just discussing two key issues. Obviously, the first and third
widget (which work out to the fields occursOn and admissionPrice on the schema) are required to be present... but under what
circumstances? The attribute widgetName and its value,
edit, indicate that these fields are
required in when the document's meta-data can be changed, such as
when you create a document or when you modify the meta-data via the
web UI's tab. Similarly the
label tag has the attribute
mode which is also
referring to which state the widget is currently in; all our labels
above use the mode any to indicate we
always want the same text displayed. One could, for example, set
the label to be "Please Enter a Date" for mode edit and "Date" for mode view. The full list of widget modes is
edit, view, any (to
indicate both edit and view), and hidden. The last of these is to allow a widget to
be defined, but not displayed.
After adding this new contribution, do not forget to update your
manifest file. Further, this new extension means that we will now
need another bundle to be required so we can have access to the
WebLayoutManager. Here is the updated
MANIFEST.MF.
Manifest-Version: 1.0 Bundle-ManifestVersion: 1 Bundle-Name: lesson4 project Bundle-SymbolicName: org.nuxeo.book.upcoming;singleton:=true Bundle-Version: 0.0.1 Bundle-Vendor: YOU! Nuxeo-Require: org.nuxeo.ecm.core, org.nuxeo.ecm.core.schema, org.nuxeo.ecm.webapp.core Nuxeo-Component: OSGI-INF/schema-contrib.xml, OSGI-INF/doctype-contrib.xml, OSGI-INF/layout-contrib.xml, OSGI-INF/ui-contrib.xml
Again, the ordering of contributions is tricky in Nuxeo-Component because there is a dependency
between the layout contribution and the UI contribution so our new
layout contribution must come first.
We now need to inform Nuxeo that our document type, Upcoming, wants to use the layout we have constructed
(sadly, also customarily called "upcoming") as part of its display.
This is done by replacing the previous list of layouts with the
upcoming layout; this controls which
layout or layouts is used to display or edit the meta-data of the
document type Upcoming. Below is a
snippet from the ui-contrib.xml file
showing the change:
<type id="Upcoming" coretype="Upcoming">
<label>Upcoming Event</label>
<icon>/icons/upcoming.png</icon>
<default-view>view_documents</default-view>
<layouts mode="any">
<layout>upcoming</layout>
</layouts>
</type>
The result of all this work can be seen in this screen capture of creating a new upcoming document:

The last when and how much fields have a red asterisk because they are required. Clicking on the grey button to the right of "When" opens a calendar widget.
If you have forgotten, you should build your now updated version
of lesson-simple-ui with mvn clean
install in the lesson-basic-ui directory. When it completes
successfully, you will find the file lesson-basic-ui.0.0.1.jar in the target subdirectory. You should make sure your
Nuxeo server is not running, then copy this jar file into the
plugins subdirectory inside the
nuxeo.ear subdirectory of your
server, this would be /nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear
if you have been following our instructions . Restart your server
and try logging in and creating a new Upcoming document!
In general, it is bad to have your Nuxeo system have multiple different definitions for a single document type or schema while you are doing development. When this happens, you are likely to experience strange results from using the system. This also happens when you end up with multiple layout or ui contributions to the same point, but it is easier to see what is happening and realize you are running the older version of the code!
If you have not configured any database system into your nuxeo installation-and we haven't covered that yet!-you will probably want to use commands like this to destroy the databases maintained by Nuxeo like this:
[~] $ cd /nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/data [/nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/data] $ rm -rf h2/nuxeo.* hypersonic/* derby/*
Do not do this if you have content in your database you want to keep! This will also revert your Administrator password back its default setting of "Administrator". Where is administrator password stored? Does it really reset?
However, during normal software development you will find that a common sequence is to kill the running server, make some changes to your source code in Eclipse, rebuild your jar file, copy the jar file into the plugins directory, destroy the old databases, destroy the old logs, and restart the server. To make this easier, one of the authors uses multiple tabs in a shell window like this:

This makes it a bit easier to complete any of the steps listed since hitting the up arrow once or twice in any tab gets the desired effect.
Above we suggested that it is a good idea to clear the old logs
(/nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/logs)
before any run. We suggest this because we have found it useful to
turn the logging up to the maximum (DEBUG level, as shown in a previous lesson) and
then use grep to find the lines of interest in the logs. Here is
any example from the log tab above:
[~] $ cd /nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/log [/nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/log] $ cat *.log | grep -i upcoming 2009-01-25 21:17:12,807 INFO [org.nuxeo.runtime.jboss.deployment.preprocessor.DeploymentPreprocessor] Running custom installation for fragment: org.nuxeo.book.upcoming 2009-01-25 21:17:47,785 INFO [org.nuxeo.osgi.BundleRegistry] Registering resolved bundle: org.nuxeo.book.upcoming
This is easy way to find errors that may have occurred as the system was booting or as your bundle was being loaded.
Giving the Upcoming document its own icon is easy-and visually pleasing so the Upcoming object lines up when showing the list of document types that can be created. You can use any image you want so long as it is in any image format that a web browser understands such as jpeg, gif, or png. If you want to your icon to line up with others, use a 16 pixel by 16 pixel icon.
Start by creating a new subdirectory of src/main/resources in your Eclipse project called
nuxeo.war. This will be a peer of the
OSGI-INF directory both in Eclipse
and in the final bundle file. Inside nuxeo.war create a subdirectory icons. Place your graphics file in this directory
and then change the start of your ui-contrib.xml file to point to it with an
icon tag like your existing
label tag. Here is a snippet
from the improved ui-contrib.xml:
<type id="Upcoming" coretype="Upcoming">
<label>Upcoming Event</label>
<icon>/icons/upcoming.png</icon>
<default-view>view_documents</default-view>
The icon path should start with a '/' character.
In the prior lesson we noted that the deployment fragment can be used to help "deploy" resources that are part of your program into the Nuxeo EP server. Now you have a need for this functionality, since you are going to end up with your icon file inside your bundle file. Here is the updated deployment-fragment.xml that copies all the contents of the nuxeo.war part of your project into Nuxeo server.
<fragment>
<extension target="application#MODULE">
<module>
<java>${bundle.fileName}</java>
</module>
</extension>
<install>
<!-- Unzip the contents of our nuxeo.war into the server -->
<unzip from="${bundle.fileName}" to="/">
<include>nuxeo.war/**</include>
</unzip>
</install>
</fragment>
The notation nuxeo.war/** means to
include all the files in the directory nuxeo.war in your bundle and recursively copy the
contents of all subdirectories. Nuxeo deployment fragments use the
same notation for included and excluded files as the Ant and Maven
build tools, if you are familiar with those.
After running through the steps of section 2.1 again, when you create a new document via the web interface to Nuxeo you should see a set of document types like this, but with your chosen image:
If you studied the deployment fragment above carefully, you
would have noticed that target of the unzip operation is the
directory /, yet if you look in your
filesystem root you will not find your icons directory! The / above is relative to the nuxeo.war directory inside the nuxeo.ear portion of your server. Here is a
snippet from that directory:
[~] $ cd /nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear/nuxeo.war/ [/nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear/nuxeo.war] $ ls create_document.xhtml members_management.xhtml create_domain.xhtml META-INF create_file.xhtml nuxeo_error.jsp [... many more lines elided...]
The nuxeo.war directory contains
all the web resources for the Nuxeo server. One of the many
subdirectories of nuxeo.war is
icons, where the system icons are
held. Thus, our deployment fragment puts our upcoming icon in among
all the other icons and this is normal practice for plugin
developers:
[/nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear/nuxeo.war] $ cd icons/ [/nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear/nuxeo.war/icons] $ ls u* unchecked.gif unknown.png upcoming.png user.gif user_go.png
Returning to our discussion of the layout contribution above in
section 2 (layout-contrib.xml) it may
now be more clear why we waited to explain the top section of the
file. The layout contribution tells Nuxeo to use a template (again,
starting with /) /layouts/layout_default_template.xhtml and it
should not be a surprise where this file is actually located:
[/nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear/nuxeo.war] $ cd layouts [/nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear/nuxeo.war/layouts] $ ls layout_default_template.xhtml
A crucial idea of the Nuxeo system, we reiterate, is that
Nuxeo's ECM functionality is provided by bundles that are "just
like yours" and there are no special, hidden mechanisms exploited
by the Nuxeo "system" as you see it through the web interface. As
an example, the Note document type you can create via the web UI,
uses exactly the same
extension points and xml configuration that we have walked through
for building the Upcoming document type (although Note exploits a
bit more of the functionality!). As you can see from the
nuxeo.war directory, you can even
re-purpose the web resources that are used to implement the Nuxeo
ECM functionality.
If you would like to practice more with the concepts in this lesson, here are some suggested exercises.
Using chapter 8 of the Nuxeo Developer's Documentation
(http://doc.nuxeo.org/5.2/books/nuxeo-book/html/)
change the ui for Upcoming documents:
Use a non-editable label to display the date and time for the
field occursOn when the user cannot
edit the data. The user interface should continue to use the
calendar widget in situations where the user can change the value
of the field. It is ok if the display in non-editable situations
only shows the date of the event, not the date and time.
Solving this without duplication of code (a big no-no!) requires
the use of an additional extension point in your layout-contrib.xml.
If you create a document of type Upcoming now, you will notice that it is displayed with your icon but with a rather ugly title:

This is because the title is not being set when you create a document (dublincore:title), so Nuxeo defaults to chosing a title which is a unique identifier for the document. Correct this by adding back in the support for the layout type heading.