Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

Thanks Jacopo Cappellato for the original document creation on OFBiz CMS, contents in this document are referred from it.

Table of Contents

Basic Concepts: DataResource and Content records

Let's suppose that we want to define content (that we will include in a screen) for the text "This is the text that will appear on screen.".

Data Resource

First of all we have to define a DataResource representing this text. The most common options we have are the following: SHORT_TEXT, ELECTRONIC_TEXT, URL_RESOURCE.

Data Resource of type SHORT_TEXT

Code Block
<DataResource dataResourceId="DS_01" dataResourceTypeId="SHORT_TEXT" objectInfo="This is the text that will appear on screen."/>

This is the simplest version and is useful when you have to store short texts.

Data Resource of type ELECTRONIC_TEXT

Code Block
<DataResource dataResourceId="DS_01" dataResourceTypeId="ELECTRONIC_TEXT"/>
<ElectronicText dataResourceId="DS_01">
    <textData><![CDATA[
        This is the text that will appear on screen.
    ]]></textData>
</ElectronicText>

This is similar to the SHORT_TEXT version, but gives you greater flexibility because you can store long texts in the textData field.

Embedding FTL markup in the text

The tree above DataResource records works well when the content is a simple text or a markup language (html, xml etc...) that just needs to be rendered as is. If you want to embed FTL markup (directives etc...) that needs to be processed before the rendering you can achieve this using the dataTemplateTypeId attribute:

Code Block
<DataResource dataResourceId="DS_01" dataResourceTypeId="ELECTRONIC_TEXT" dataTemplateTypeId="FTL"/>

in this way all the Freemarker instructions in the text will be executed before rendering the screen.

Content

Now we have to create a Content record that is associated to the DataResource. 
No matter what type of data resource you have choosen, the Content record is the same:

Code Block
<Content contentId="CN_01" contentTypeId="DOCUMENT" contentName="Content 01" dataResourceId="DS_01"/>

Note that the contentTypeId is "DOCUMENT": all pages and sections of pages should be this type.

The Sub-Content Pattern

This is a pattern used find and render sub-content of a Content record by specifying two things:
contentId - the parent contentId to find sub-content of
mapKey - the value of the mapKey field in the ContentAssoc between the parent- and sub-content to look up by
The ContentAssoc.contentTypeId should be SUB_CONTENT.
There can be multiple ContentAssoc records between the parent- and sub-content because of the fromDate field that is part of the primary key. To find the one we want we follow the standard pattern of sorting by -fromDate (newest first) and make sure the current date is greater than the fromDate and that either the thruDate is null or the current date is less than or equal to the thruDate.
We will see the Sub-Content Pattern in action below in two scenarios:
to decorate the various sections of a screen DECORATOR template (see section "Content and decorator templates")
to provide a clean way to reference content in URL for web content sites (see section "Defining the publish point")

Content and decorator templates

If you want to embed the content of a DataResource in an existing template (to decorate/fill a region of it) you can use the decoratorContentId attribute:

Code Block
<Content contentId="CN_01" contentTypeId="DOCUMENT" contentName="Content 01" dataResourceId="DS_01" decoratorContentId="CN_DEC"/>
where "CN_DEC" is the contentId of a Content record that looks like:
<DataResource dataResourceId="DS_DEC" dataResourceTypeId="ELECTRONIC_TEXT" dataTemplateTypeId="FTL"/>
<ElectronicText dataResourceId="DS_DEC">
    <textData><![CDATA[
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml">
          <head>
            Main Decorator
          </head>
          <body>
            ${decoratedContent}
          </body>
        </html>
    ]]></textData>
</ElectronicText>
<Content contentId="CN_DEC" contentTypeId="DECORATOR" contentName="Decorator" dataResourceId="DS_DEC"/>
When using this decorator, the DataResource text defined by DS_01 will be inserted in place of the ${decoratedContent} variable.
You can also define more placeholders/regions in the decorator template:
<DataResource dataResourceId="DS_DEC" dataResourceTypeId="ELECTRONIC_TEXT" dataTemplateTypeId="FTL"/>
<ElectronicText dataResourceId="DS_DEC">
    <textData><![CDATA[
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml">
          <head>
            Main Decorator
          </head>
          <body>
            ${(thisContent.subcontent.header)?if_exists}
            <hr/>
            ${decoratedContent}
          </body>
        </html>
    ]]></textData>
</ElectronicText>
<Content contentId="CN_DEC" contentTypeId="DECORATOR" contentName="Decorator" dataResourceId="DS_DEC"/>

<DataResource dataResourceId="DS_DEC_HEADER" dataResourceTypeId="SHORT_TEXT" objectInfo="This is the text for the decorator header."/>
<Content contentId="CN_DEC_HEADER" contentTypeId="DOCUMENT" contentName="Decorator Header" dataResourceId="DS_DEC_HEADER"/>
<ContentAssoc contentId="CN_DEC" contentIdTo="CN_DEC_HEADER" contentAssocTypeId="SUB_CONTENT" mapKey="header" fromDate="2001-01-01 00:00:00"/>

In this decorator we have placed a new placeholder for the header section, using a variable named "header" that is referenced using the syntax

Code Block
${(thisContent.subcontent.header)?if_exists}

Then we have defined a new DataResource/Content pair to set the content for the header ("This is the text for the decorator header."). The association is done using the ContentAssoc entity (see the last line in the code block): the mapKey attribute is where you set the name of the variable used in the decorator template.
 Here are a few hints to consider when you are preparing the content for a decorator template:
you can always get a reference to the Content for the Decorator (and associated resources) using following variable

Code Block
${thisContent}

you can always get a reference to the Content for the decorated (current) screen (and associated resources) using following variable

Code Block
${decoratedContent}
Code Block
 both ${thisContent} and ${decoratedContent} are references to objects of type ContentMapFacade: this is a special class that implements the Map interface and can be used to quickly get Content related resources

;
for example:

Code Block
${decoratedContent.subcontent.sub1}:

above code renders the content record associated to the main one with the mapKey "sub1"
when you define a hook for a region, it is usually a good idea to use a syntax like this:

Code Block
${(decoratedContent.subcontent.footer)?default((thisContent.subcontent.footer)?if_exists)}

In this way, when the decorated screen is rendered, if a "footer" record is associated to the decorated screen, then it is used that footer before defaulting to the default one (if available) associated to the decorator Content

Using the <content/> element in a screen definition.

You can easily include the content directly inside the <widgets> section of the screen definition with the following directive:

Code Block
<content content-id="CNT_EXAMPLE"/>

How to setup a content driven website: WebSites, publish points and default pages

Here are some tips to quickly setup a website whose content can be managed using the OFBiz built-in features of the Content management framework.

Initial setup

web.xml file

Make sure that, in the web.xml file you have an entry to set in the session the webSiteId of your site:

Code Block
<context-param>
    <param-name>webSiteId</param-name>
    <param-value>CmsExampleSite</param-value>
    <description>A unique ID used to look up the WebSite entity to get information about content, catalogs, etc.</description>
</context-param>

controller.xml file

Add to the controller.xml file the following (or similar) entries:

Code Block
<default-request request-uri="cms"/>
<request-map uri="main">
    <security https="false" auth="false"/>
    <response name="success" type="request" value="cms"/>
</request-map>
<request-map uri="cms">
    <security https="false" auth="false"/>
    <event type="java" path="org.ofbiz.content.cms.CmsEvents" invoke="cms"/>
    <response name="success" type="none"/>
    <response name="error" type="view" value="error"/>
</request-map>

With the above setup, by default all the incoming requests will be dispatched to the CmsEvents event; this event will use the data in the Content data model to generate the content of the page and will return it back to the browser. In this way it will be possible to add new pages just editing the data in the Content data model and without editing the controller.xml file.

Defining the web site publish points

Next step is to define at least one publish point for the web site.
 A Web Site Publish Point is a special Content record that is not associated to a DataResource and its contentTypeId is WEB_SITE_PUB_PT: this is a Placeholder ContentType that is not meant to be rendered, but rather used to organize other content associated with it.
 The Web Site Publish Point Content entry is associated to the WebSite thru the WebSiteContent entity, using a webSiteContentTypeId equal to "PUBLISH_POINT".
Here is an example of this data setup:

Code Block
<Content contentId="CMSS_PPOINT" contentTypeId="WEB_SITE_PUB_PT" contentName="CMS Site Publish Point" description=""/>
<WebSiteContent webSiteId="CmsExampleSite" contentId="CMSS_PPOINT" webSiteContentTypeId="PUBLISH_POINT" fromDate="2010-01-01 00:00:00"/>

Adding a new page

Defining a new content driven page is as simple as defining a new Content/DataResource pair (as described in the above sections) of type DOCUMENT and then associating it to one of the web site publish points; the association, done thru the ContentAssoc entity (of type SUB_CONTENT) can be direct or indirect (i.e. the content record is a child of one of descendants of the publish point). For some examples of this setup see the section "URI formats to access Content information" below.

Defining the default page

This is an optional step.
 You can define a default content driven page for the web site, that is used when no page (content) is specified in the URI. The Content/DataResource pair for the page is the same of a standard page (the content page needs also to be associated, directly or indirectly, to a publish point) and the association to the WebSite as the default page is done with an entry like the following one:

Code Block
<WebSiteContent webSiteId="CmsExampleSite" contentId="EXAMPLE_CONTENT" webSiteContentTypeId="DEFAULT_PAGE" fromDate="2001-01-01 00:00:00"/>

The page defined by the EXAMPLE_CONTENT content will be used as the default page for the web site.

URI format to access Content information

According to the way the CMS site has been setup, you will be able to use the following URI format to visit the Content managed screens. 
In the following examples we will adopt these values:
<BASE URL>: This is the base url of the application; we will assume "https://localhost:8443/cmssite"
<CMS>: This is the entry point for all the CMS managed content (as defined in the controller.xml file); we will assume "cms"
<CONTENT ID OF THE SCREEN>: This is the contentId of a content record of type DOCUMENT that is associated to a DataResource containing the information to be rendered to the browser; we will assume the value EXAMPLE_CONTENT

<BASE URL>/<CMS>/<CONTENT ID OF THE SCREEN>

Example:https://localhost:8443/cmssite/cms/EXAMPLE_CONTENT
This will only work if the Content record with id EXAMPLE_CONTENT is a node of a tree having a web site publish point as its root.
 The EXAMPLE_CONTENT content can be directly associated to the publish point:

Code Block
<ContentAssoc contentId="CMSS_PPOINT" contentIdTo="EXAMPLE_CONTENT" contentAssocTypeId="SUB_CONTENT" fromDate="2010-01-01 00:00:00"/>
or indirectly associated to it:
<ContentAssoc contentId="CMSS_PPOINT" contentIdTo="ANOTHER_CONTENT" contentAssocTypeId="SUB_CONTENT" fromDate="2010-01-01 00:00:00"/>
<ContentAssoc contentId="ANOTHER_CONTENT" contentIdTo="EXAMPLE_CONTENT" contentAssocTypeId="SUB_CONTENT" fromDate="2010-01-01 00:00:00"/>