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>
  <form>
  <directory/>
  <file/>
  </form>
  </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>