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"> <templates> <template mode="any">/layouts/layout_default_template.xhtml</template> </templates> <layout name="upcoming"> <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.