Page Flow
1. What is the Page Flow Controller?
The PresentationServer Page Flow Controller dispatches incoming user requests to
individual pages based on a declarative description of the pages, built out of
forms, models and views, following the model-view-controller (MVC) architecture.
A Page Flow is an easy to use yet powerful way to declare an entire site navigation
logic. With a central place where the navigation logic is defined, pages can be
developed completely independently from each other.
2. Separation of Concerns

The Page Flow Controller encourages designing applications with a total separation
between:
-
Site Logic or Page Flow: when, and how to navigate from one
page to the other.
-
Page Logic (the Model in MVC): how data entered by the user is
processed (for example validated, then fed to a backend), and how data is retrieved
from a backend.
-
Page Layout (the View in MVC): how information is displayed and
presented to the user on the browser.
-
Site Presentation: the layout and look and feel common to all pages in
the Web application or the Web site, e.g.: site navigation menus, headers and
footers, table backgrounds, or number formatting.
2.1 Site Logic

The site logic, also called page flow, describes the conditions that trigger the
navigation from one page to the other. It also describes how arguments are passed
from one page to the other. In a simple Web application simulating an ATM, the
navigation logic could be similar to the one described in the diagram on the right.
In this diagram, the square boxes represent pages and diamond-shaped boxes represent
actions performed by the end-user.
With the Page Flow Controller, the site logic is expressed outside of the
pages. Consequently, the pages can be completely independent from each other.
The benefits of a clear separation between site logic and page logic and layout
include:
- Simplicity: the site logic is declared in one place and in a declarative
way. You don't need to write custom logic to perform redirects between pages or
pass arguments from page to page.
- Maintainability: having different developers implementing independent page
becomes much easier. Since the relationship between pages is clearly stated in
the Page Flow, it also becomes much easier to modify a page in an existing
application without affecting other pages.
2.2 Page Logic and Layout
In the MVC architecture, the page logic is
in the model, and the page layout in the view. The MVC
architecture promotes the separation of the model, view and controller:
- The model is responsible for calling or implementing the business
logic. In some cases the model produces data that is going to be displayed by
the view.
- The view receives data from the model and usually generates an HTML
page.
- The controller is responsible for dispatching a request to the
appropriate model/view and connecting the model with the view.
For instance, for a news page, the model would retrieve the list of headlines and
pass this information in an XML format to the view. The view would produce an HTML
page by creating a table with those headlines and potentially adding a logo at the
top of the page, a copyright at the bottom, and so on.

2.3 Site Presentation
In general, you want to have a common look and feel across pages. Instead of
duplicating the code implementing this look and feel in every view, it is
possible to put the presentation logic in a central location called the
epilogue. If specified, the epilogue points to the pipeline which is applied to
the XML data produced by the view of each page, as shown in the following
diagram.

The configuration below shows an epilogue as represented in the previous
example.
<config> <page path-info="/login" model="login/model.xpl" view="login/view.xml"/>
<page path-info="/search" model="search/model.xpl" view="search/view.xml"/>
<epilogue url="epilogue.xpl"/>
</config>
3. The Configuration File
3.1 Overview
A Page Flow file is comprised of three
parts:
- The files elements list the files that must be sent directly
to the client, such as images or CSS files.
- The page elements declare pages or groups of similar pages and
define the XForms model, the MVC model, and the MVC view for each page.
- The epilogue and not-found-handler elements
define properties that apply to all the pages.
3.2 Static Files
Some files are not dynamically generated and need to be sent to the client
as-is. This is typically the case for GIF, JPEG, and CSS files.
You can add one or more files elements in the controller
configuration as illustrated in the example below:
<config> <files path-info="*.gif"/>
<files path-info="*.css"/>
<files path-info="*.png"/>
<page path-info="/login" model="login/model.xpl" view="login/view.xml"/>
</config>
This specifies the files that must be sent directly to the client. For example,
if you set <files path-info="*.gif"/> and a request comes in
for images/logo.gif then the file images/logo.gif
stored in the resource repository will be sent in response to that request.
3.3 Pages
Consider a "view account" page in a hypothetical ATM Web application. The page
displays the current balance and lets the user enter an amount of money to withdraw
from the account. It could look like:

This page is composed of different parts (also illustrated in the figure
below):
- The XForms model declares the data model of the form, and other form
related information as described in the XForms
processor section.
- The model retrieves the current balance.
- The view displays the balance, and the form for the user to enter
the amount to withdraw.
- An action is executed when the user enters an amount in the text
field. This action checks if the amount entered is inferior or equal to the
account balance. If it is, the balance is decreased by the amount entered and
the transaction is considered valid. Otherwise, the transaction is considered
illegal. Depending on the validity of the transaction, a different page is
displayed. If the transaction is valid, the anything-else page is
displayed; otherwise the low-balance page is displayed.

This situation is described in the Page Flow with:
<page id="view-account" path-info="/view-account" xforms="view-account-form.xml" model="view-account-get-balance.xpl" view="view-account-view.xsl"> <action when="/amount != ''" action="view-account-action.xpl"> <result id="success" when="/success = 'true'" page="anything-else"/>
<result id="failure" when="/success = 'false'" page="low-balance"/>
</action> </page>
- On the <page> element:
- The path-info attribute tells the Page Flow Controller
what relative URI corresponds to this page. The URI is relative to the
application context path.
- The xforms attribute points to the XForms model.
- The model attribute points to the page model.
- The view attribute points to the page view.
- The xforms, model, and view
attributes point to a static XML file, an XSL stylesheet or an XPL pipeline.
More on this in a subsequent section.
- The <page> element can contain zero or more
<action> elements. They are named action because they
are executed as a result of an action performed by the end-user. Typically by
clicking on a button or a link.
- The when attributes are evaluated. The first one with a
true value is executed. The when is an XPath
expression executed against the XForms instance. The when
attribute is optional. A missing when attribute is equivalent
to when="true()".
- When the action is executed, if the optional action
attribute is present, the pipeline it points to is executed.
- The <action> element can contain zero or more
<result> elements.
- If an action attribute is specified on the
<action> element, the <result> element
can have a when attribute. The when is an XPath
expression executed against the data output of the action pipeline. A
missing when attribute is equivalent to
when="true()". The first <result> with a
true when attribute is executed.
- A <result> element optionally has a page
attribute. The page attribute points to a page id, declared in
the same Page Flow file. When the result is executed and the
page attribute is present, the user is forwarded to the
corresponding page.
- A <result> element can optionally contain XUpdate instructions. If present the
XUpdate is run against the XForms instance. The XForms instance is either
the current instance if the page attribute is missing or the
XForms instance of the corresponding page otherwise. The updated instance is
used for the rest of the processing if the page attribute is
missing, or passed to the other page otherwise.
See the following examples to understand how <page>,
<action>, and <result> can be used in the Web
application controller configuration:
For further information please also see the Orbeon
PresentationServer Tutorial.
3.4 Path info and matchers
The value of the path-info attribute can be either a simple or a
custom pattern.
Value |
Description |
Simple |
Simple pattern can optionally start or end with a star character (
*). For instance: /about/company.html matches
exactly this URI, about/* matches any URI that starts with
about/, *.gif matches any URI that ends with
.gif.
|
Custom |
In this case, an additional matcher argument must be
specified. The matcher argument must point to a matcher
processor URI. Two matcher processors are provided with PresentationServer: the Perl5
matcher (URI oxf/processor/perl5-matcher) and the Glob
matcher (URI oxf/processor/glob-matcher). The Perl5 matcher
accepts Perl regular
expressions and the Glob matcher accepts Unix
shell glob expressions.
|
This is an example of files element using the Perl5 matcher:
<files path-info="/doc/[^.]*\.html" matcher="oxf:perl5-matcher"/>
As with the files element, a matcher can also be specified. When
using a matcher where groups can be created, the part of the URI matched by
those groups can be passed to the model and the view through the XForms
instance. Note that the only matcher bundled with PresentationServer that
accepts groups is the Perl5 matcher. For example, if you have the following
configuration:
<page path-info="/([^/]*)/(.*)" matcher="oxf:perl5-matcher" xforms="xforms.xml" view="view.xsl"> <param ref="/form/directory"/>
<param ref="/form/file"/>
</page>
And the content of the xforms.xml file is:
<xforms:model xmlns:xforms="
http://www.w3.org/2002/xforms"
> <xforms:instance> </xforms:instance> </xforms:model>
If the requested URI is /doc/webapp-controller, the instance
received by the model and the view is:
<form> <directory>doc</directory>
<file>webapp-controller</file>
</form>
3.5 Site Presentation
The epilogue element specifies the pipeline applying the
application's site presentation logic.
This is an example of epilogue element:
<epilogue url="oxf:/config/epilogue.xpl"/>
3.6 "Not Found" Handler
The not-found-handler element is used to specify the id of a page
that is called when no page element in the Page Flow file matches
the current request URL. There can be only one not-found-handler
per Page Flow file.
This is an example of not-found-handler element and the associated
page element:
<!-- "Not Found" page displayed when no page matches the request URL -->
<page id="not-found" path-info="/not-found" view="/config/not-found.xml"/>
<not-found-handler page="not-found"/>
Note
The not-found-handler element is not used for resources served
through the files element. In that case, the PFC returns instead
a "not found" code to the user agent (code 404 in the case of HTTP).
4. Valid Combinations of the xforms, model, and view Attributes
The path-info is the only attribute required. None of the
xforms, model, and view attributes are required,
but only certain combinations of those attributes make sense. The table below shows some
of the possible combinations:
4.1 View Only
Simple pages with no back-end code can be implemented in a single pipeline: the
view pipeline. The view pipeline must have a data output. The XML
generated by the view then goes to the epilogue and is then sent back to the
browser.

4.2 Model Only
If a page is not sent back to the user (a browser in a Web app), there is no
need for a view. This is typically the case when a redirect needs to be issued,
or a binary file is produced (e.g. using the XSL-FO Processor to generate a PDF
file).

4.3 View Only with XForms
This is a variant of the view only scenario, where an XForms is used. In
this case, the view receives the XForms instance as an input.

4.4 Model Only with XForms
A variant of the model only scenario, where XForms is used.

4.5 View and Model
This is the classic case. A pipeline implements the MVC model and a second
pipeline implements the MVC view with data produced by the model and consumed
by the view.

4.6 View and Model with XForms, Case 1
This is the equivalent of the previous model where XForms is used. In this case
an instance document connects to the input of the model and the view.

4.7 View and Model with XForms, Case 2
This is a variant of the previous case where the model declares an
instance output. This is typically useful when the view displays
some values from the instance but these values are not exactly the same as those
entered by the user. For example, a page with a text field where the user types
an airport code. If the user enters a known city such as San Francisco, the
application may automatically replace it with the corresponding airport code
(SFO in this case).

5. W3C Schema
<xs:schema targetNamespace="http://www.orbeon.com/oxf/controller" elementFormDefault="qualified" attributeFormDefault="unqualified" xsi:schemaLocation="http://www.w3.org/2001/XMLSchema http://www.w3.org/2001/XMLSchema.xsd" xmlns:xs="
http://www.w3.org/2001/XMLSchema"
> <xs:import namespace="http://www.xmldb.org/xupdate" schemaLocation="xupdate.xsd"/>
<xs:element name="config"> <xs:complexType> <xs:sequence> <xs:element name="files" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:attributeGroup ref="c:path"/>
<xs:attribute name="mime-type" type="string" use="optional"/>
</xs:complexType> </xs:element> <xs:element name="page" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="param" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:attribute name="ref" type="string" use="required"/>
</xs:complexType> </xs:element> <xs:element name="action" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="result" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:complexContent> <xs:extension base="xu:InstructionsContainer"> <xs:attribute name="page" type="string" use="optional"/>
<xs:attribute name="id" type="string" use="optional"/>
<xs:attribute name="when" type="string" use="optional" default="true()"/>
<xs:attribute name="instance-passing" type="c:instance-passing-type" use="optional"/>
</xs:extension> </xs:complexContent> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="when" type="string" use="optional" default="true()"/>
<xs:attribute name="action" type="string" use="optional"/>
<xs:attribute name="id" type="string" use="optional"/>
</xs:complexType> <xs:unique name="result-when-unique"> <xs:selector xpath="c:result"/>
<xs:field xpath="@when"/>
</xs:unique> </xs:element> </xs:sequence> <xs:attributeGroup ref="c:path"/>
<xs:attribute name="xforms" type="string" use="optional"/>
<xs:attribute name="model" type="string" use="optional"/>
<xs:attribute name="view" type="string" use="optional"/>
<xs:attribute name="id" type="string" use="optional"/>
</xs:complexType> <xs:unique name="action-when-unique"> <xs:selector xpath="c:action"/>
<xs:field xpath="@when"/>
</xs:unique> </xs:element> <xs:element name="epilogue" minOccurs="0"> <xs:complexType> <xs:attribute name="url" type="anyURI"/>
</xs:complexType> </xs:element> <xs:element name="not-found-handler" minOccurs="0"> <xs:complexType> <xs:attribute name="page" type="string"/>
</xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="instance-passing" type="c:instance-passing-type" use="optional"/>
</xs:complexType> <xs:unique name="path-info-unique"> <xs:selector xpath="./*"/>
<xs:field xpath="@path-info"/>
</xs:unique> <xs:unique name="page-id-unique"> <xs:selector xpath="c:page"/>
<xs:field xpath="@id"/>
</xs:unique> <xs:keyref name="result-page-id-ref" refer="c:page-id-unique"> <xs:selector xpath="c:page/c:action/c:result"/>
<xs:field xpath="@page"/>
</xs:keyref> <xs:keyref name="not-found-handler-page-id-ref" refer="c:page-id-unique"> <xs:selector xpath="c:not-found-handler"/>
<xs:field xpath="@page"/>
</xs:keyref> </xs:element> <xs:attributeGroup name="path"> <xs:attribute name="path-info" type="string" use="required"/>
<xs:attribute name="matcher" type="string" use="optional"/>
</xs:attributeGroup> <xs:simpleType name="instance-passing-type"> <xs:restriction base="NMTOKEN"> <xs:enumeration value="redirect"/>
<xs:enumeration value="forward"/>
<xs:enumeration value="redirect-exit-portal"/>
</xs:restriction> </xs:simpleType> </xs:schema>