13. Tying Up the Loose Ends – Apache OFBiz Development

Chapter 13. Tying Up the Loose Ends

We have seen how the use of screen widgets and Form Widgets provides us with an incredibly quick way of producing XHTML forms, and how we can use them to input and display data.

For back-end office components this is often more than adequate. However, customer facing components may require a more complex and detailed design than Form Widgets can offer. In this chapter we will be taking a look at how we can use FreeMarker to give us a finer degree of control over the layout of the output or even change the output to give us XSL-FO and create PDFs.

In this chapter we will study some final techniques that will enable us to set up a customized instance of OFBiz. We'll learn how to:

  • Skin OFBiz and change the look and feel

  • Use FreeMarker

  • Use OFBiz transform tags to aid code reuse

  • Use some of the OFBiz utilities

  • Call utility methods from FreeMarker

  • Output Different Formats

The OFBiz Look and Feel

Over the years the OFBiz look and feel has remained similar. Although the underlying XHTML has changed from a table-based structure to a div-based structure, making it easier for us to control the page layout using stylesheets, the design has remained the same. We are already familiar with the two different looks and feels in OFBiz. Firstly the customer facing ecommerce component:

Secondly the backend office components:

Out of the box, the look and feel for both are similar. But, they purposefully use different stylesheets and decorators so the styles and structure of each can be changed, independent of one another.

The primary Cascading Style Sheet (CSS) for the ecommerce component can be found in the directory ${ofbizInstallFolder}\framework\images\webapp\images and is called ecommain.css.

This image component is in the framework directory since it supplies images, stylesheets and JavaScripts throughout the entire application. If we upload a product image through the product screens, it is stored here. For large shops with a wide product range, this directory could end up being very large and, as we will learn later in the chapter, OFBiz has made it easy for us to move this directory to a different location, even onto a different dedicated image server if we so wish.

Start OFBiz and open the ecommerce component by navigating your browser to http://localhost:8080/ecommerce . View the source of the home page.

We can see that design of the XHTML source is <div> based. This structure is ultimately controlled by the screen widget main-decorator located in the file ${component:ecommerce}\widget\CommonScreens.xml.

The ecommerce component has been specifically designed to be as configurable as possible and made as easy for us to change to our needs as possible. As such, every effort has been made to stop us from needing to delve into the code and change this structure. The location of the logo and the location of the CSS are stored within the ProductStore entity in the database.

Changing the Customer Facing Site

OFBiz is capable of serving different websites with different product stores, each having different categories and products. By default, the ecommerce component is "bound" to the WebSite record with the websiteId of value WebStore by a context parameter in the ecommerce webapp's web.xml file. Because of this context parameter, the ecommerce application knows which ProductStore data to load.

Take a look at how this WebSite can be configured by opening the Content Manager Application at https://localhost:8443/content/control/main and clicking on the WebStore link.

From here, the http and https hosts and ports that the customer facing website listens on can be changed, giving us the ability to access more than one site. In the next chapter we will learn how to configure Apache to handle this more elegantly, so the customer can access each different site using different url's, rather than using the same url and changing the port.

Notice that the productStoreId field is set to the OFBiz E-Commerce Store. Select the Catalog tab from the tab menu at the top of the page, select Stores and then OFBiz E-Commerce Store [9000].

The ProductStore entity fields allow us to control and quickly change many aspects of our store. Most are self explanatory. For the purpose of design configuration we are going to change the Store Name, Company Name, Title, and Sub-Title fields to the following:

and half-way down the form change the Style Sheet and Header Logo to:

Select Update. Our changes will not yet be visible. Since this record in the ProductStore entity is so commonly accessed (don't forget, every person who visits the site will perform a lookup on this record), it is cached. Clear the cache by selecting WebTools from the top tab menu, then select Cache Maintenance and finally Clear All Caches.

Return to the ecommerce site and press Ctrl+F5 to ensure that our browser pulls a fresh copy of the site from the server and not from its local cache. Unsurprisingly, all we see is text with no formatting. We have not yet created the stylesheet.

Create a simple logo with a height of 69 px using a graphics package and save as learning_logo.jpg in ${component:images}\webapp\images. for example:

Finally, in the same directory, create a new file called learning.css, and copy into it the entire contents of the file ecommain.css.

Now we can have some fun! Keeping the ecommerce application open in the browser in the background, perform a Find and Replace All (Ctrl+F in Eclipse) in the learning.css file and change all instances of #000099 to #009933. This should change the OFBiz color scheme from blue to green. Return to the browser and press F5 to refresh and see our modified store.

Obviously, this is the most basic change in design that is possible. How far you want to change the design is up to you. To gain a feel for how customizable the customer facing application is, take a look at the Who Is Using Apache OFBiz section of the OFBiz site (http://www.ofbiz.org).

Changing the Back Office Screens

The back office screens can also be customized to give OFBiz your corporate identity. Obviously, however, having a web designer completely change the design of the back office screens could be a waste of money. Customers should never see these screens; therefore the design of these screens is not as critical as those in the ecommerce component.

We have already seen how the main decorator GlobalDecorator is shared throughout the entire back office and is found in ${ofbizInstallFolder}\framework\common\widget\CommonScreens.

By reading through this decorator, it becomes very easy to see how to customize these screens.

Each section of the screen is broken up into four main sections. The header, the appbar, the center area, and the footer.

The main style for these screens is in the maincss.css file in framework\images\webapp\images.

The Header

The Header displays the logo, login and locale information to the user.

Location: ${component:common}\webcommon\includes\header.ftl

The header.ftl file can be found in the common component. If you were to change the logo location within this file the change will be apparent across all of the back office screens.

The Applications Bar (appbar)

The appbar's job is to display tabs at the top of the back-office screens allowing us to quickly enter other components.

Location: ${component:common}\webcommon\includes\appbar.ftl

By default, the tabs will appear. By adding the attribute app-bar-display="false" to the webapp element, they will be suppressed. For example, to stop the ecommerce component from appearing:

<webapp name="ecommerce"
title="eCommerce"
server="default-server"
location="webapp/ecommerce"
mount-point="/ecommerce"
app-bar-display="false"/>

The base permission from the webapp is also compared with the logged in user's permissions. If the logged in user's permissions meets or exceeds the base permission, the tab will be displayed:

<webapp name="party"
title="Party"
server="default-server"
location="webapp/partymgr"
base-permission="OFBTOOLS,PARTYMGR"
mount-point="/partymgr"/>

The Central Area

The central area of the screen is component-specific and changes to display the information and data that you wish to see. This area can be again broken down into smaller areas, including the main body area, where our forms and data are displayed.

The Application Header

Location: ${somponent:xxxxx}\webapp\xxxxx\includes\appheader.ftl

This contains the main title for the component and top level links that are at the moment important destinations for this component. They are visible from most (if not all) pages within the component.

The Footer

Finally, The Footer displays copyright information, or any other information we wish to display on every page.

Location: ${component:common}\webcommon\includes\footer.ftl

Once again, a common shared file where any changes are propagated throughout the entire back office.

Using FreeMarker

FreeMarker is another Open Source project. It is a template engine which has some programming capabilities. For those who are familiar with Velocity (another templating engine), the migration to FreeMarker is easy, as they share many of the same concepts. OFBiz makes great use of FreeMarker's flexibility to display data that has been returned from event and services or lookups performed in the screen widgets actions in different formats, usually XHTML. The separation of the presentation into these FreeMarker templates allows web designers to concentrate solely on the design and layout and keeps their need to know any other programming languages to a minimum. Freemarker Template Language is commonly referred to as FTL, and FreeMarker templates have the file extension .ftl.

Throughout the course of this book, we have used some FreeMarker templates already. The messages.ftl file that has been used in so many of the examples is a very good example of such a file. Let's remind ourselves how it is included in the screen widgets:

<platform-specific>
<html>
<html-template location="component://common/webcommon/ includes/messages.ftl"/>
</html>
</platform-specific>

Using FreeMarker, we are able to iterate through lists passed to us from actions that have prepared data and perform some simple programming tasks to help us manipulate and display data.

To demonstrate this let's take a look at the screen that prepared and displayed for us the list of planet reviews in our LearningScreens.xml file:

<screen name="ListPlanetReviews">
<section>
<actions><entity-condition entity-name="PlanetReview" list-name="planetReviews"/></actions>
<widgets>
<decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
<decorator-section name="body">
<include-form name="PlanetReviews" location="component://learning/widget/ learning/LearningForms.xml"/>
</decorator-section>
</decorator-screen>
</widgets>
</section>
</screen>

Here we allowed the form widget to perform the iterative work for us, cycling through each element in the planetReviews List that was obtained from the <entity-condition> lookup.

Fire an http request ListPlanetReviews to the webapp learning. If you do not have any reviews listed here first send a request to PlanetReview and, logged in as allowed, create some test reviews before any changes are made. This way there will definitely be some reviews for our FreeMarker template to display.

In the ListPlanetReviews screen, replace the line:

<include-form name="PlanetReviews" location="component://learning/widget/learning/ LearningForms.xml"/>

with our FTL file. Include:

<platform-specific>
<html>
<html-template location="component://learning/webapp/ learning/planets/reviews.ftl"/>
</html>
</platform-specific>

And finally create a new directory ${component:learning}\webapp\learning\planets and in it add a new file reviews.ftl with the contents:

<#assign index = 0 />
<table cellspacing="0" class="basic-table">
<tr class="header-row">
<td>Review Id</td>
<td>Created By</td>
<td>Planet Id</td>
<td>Review</td>
<td>Update</td>
<td>Delete</td>
</tr>
<#list planetReviews as review>
<form name="reviewForm_o_${index}" action="<@ofbizUrl>PlanetReview</@ofbizUrl>" method="post">
<input type="hidden" name="reviewId" value="${review.reviewId}" />
<tr>
<td>${review.reviewId}</td>
<td>${review.userLoginId}</td>
<td>${review.planetId}</td>
<td>${review.review}</td>
<td><input type="submit" name="submit" value="Update" /></td>
<td><a href="<@ofbizUrl>RemovePlanetReview? reviewId=${review.reviewId}</@ofbizUrl>">Remove</a></td>
</tr>
</form>
<#assign index = index + 1 />
</#list>
</table>

We have now replicated the same functionality given to us in the form widget. The FTL, though more complicated to code, produces slightly simpler XHTML since the JavaScript used to submit the forms in the form widget has been ignored and replaced with a standard Submit button.

Notice the forms action and also the RemovePlanetReview links are surrounded by <@ofbizUrl></@ofbizUrl> tags. These transform tags resolve the correct URLs for us.

OFBiz Transform Tags

There are a number of incredibly handy and widely used bespoke FreeMarker tags which as the template is rendered, transforms the contents of the tags for us.

<@ofbizUrl>

This tag will resolve the action in the above forms from

<@ofbizUrl>PlanetReview</@ofbizUrl>

to:

/learning/control/PlanetReview

By allowing the framework to resolve these tags for us, we are able to completely ignore within the templates whether the links should point to https:// or http://. This can now be controlled and changed in just one place, in the controller.xml request-map's <security> elements.

Port and host name settings are checked first from the WebSite entity and then from the url.properties (${ofbizInstallFolder}\framework\webapp\config) file. These settings are checked on the resolution of every link to ensure the link is pointing to the correct place.

The transformation adds the correct webapp name, the control path and the desired request. This allows us to reuse screens widgets or FreeMarker templates from their existing locations without having to import a copy into our component and then having to change all the links. All we have to do is reference them from within our screen widgets and ensure that our controller.xml file has corresponding request-maps. Take a look through some of the screen widget files in the ecommerce component and see how many .ftl files from the order component are referenced.

<@ofbizContentUrl>

The principle behind this transform tag is similar to <@ofbizUrl>. It will resolve all links to point to the correct image location. By default, the images folder containing all JavaScripts, stylesheets, and all images belongs in ${ofbizInstallFolder}\framework\images. However, with the product images being included in this directory, along with all the design content, this directory could grow very large. We may be running multiple instances of OFBiz on different servers and want to ensure that the images are all uploaded to and served from the same location. In this case we would need to move this images directory to its own dedicated image server. So long as every time we reference an image from the FreeMarker templates using the <@ofbizContentUrl> tags we can tell the framework in just one place that the location of the images folder has changed. This is the content.url.prefix.standard property found in the url.properties file.

To include an image in the FreeMarker template, the src would become:

<img src="<@ofbizContentUrl>/images/example.gif</@ofbizContentUrl>"/>

Using these transform tags is a good practice and is a good habit to keep from the start. It can save hours of trawling through code changing every reference once you decide to change the system architecture.

<@ofbizCurrency>

This transform tag prepares and displays currency data for us, stopping us from having to hard code currency symbols. This tag formats the number passed in and rounds it to the correct number of decimal places. To show the amount 16.45 in US Dollars we would use the code:

<@ofbizCurrency amount=16.45 isoCode="USD"/>

If you wish to completely understand what these transform tags do and how they work, take a look at the OfbizUrlTransform, OfbizContentTransform, and OfbizCurrencyTransform classes in the package org.ofbiz.webapp.ftl.

Calling Java from FreeMarker

The FreeMarkerViewHandler prepares an environment that enables us to use the OFBiz objects that were made available for us in the services and events:

  • delegator

  • dispatcher

  • security

  • userLogin

  • session

  • request

To make life even easier, we can access any request parameters or attributes using:

  • requestParameters

  • requestAttributes

  • sessionAttributes

No further actions must be performed to use these objects from the FreeMarker templates. For example, to display a parameter parameterOne, that has been passed into the request we would write:

${requestParameters.parameterOne}

The framework also prepares the environment such that it allows us to call static Java methods directly from the FTL.

For example, to prepare a Map and assign it to a FreeMarker variable we could put:

<#assign exampleMap = ${Static["org.ofbiz.base.util.UtilMisc"].toMap ("keyOne", "valueOne", "keyTwo", "valueTwo")} />

If we then wished to see the contents of this exampleMap, then we just put ${exampleMap} in the template. By doing that, the entire content is very conveniently written out to the screen.

This can be a very handy way to debug FreeMarker and quickly examine the contents of the variables.

This becomes particularly useful when examining the contents of a looked up entity record. To see this in action in reviews.ftl immediately under the line:

<#list planetReviews as review>

add:

<#assign userLogin = review.getRelatedOne("UserLogin") />
<tr><td colspan="6">${userLogin}</td></tr>

and refresh the page. We should now see each UserLogin entity record written out in full above the record.

Had the UserLogin entity not been a foreign key relation of PlanetReview, we would have had to perform a delegator.findByPrimaryKey lookup to find the record. For example:

<#assign userLogin = delegator.findByPrimaryKey("UserLogin", Static["org.ofbiz.base.util.UtilMisc"].toMap("userLoginId", review.userLoginId))/>

OFBiz Utilities

OFBiz contains a large number of very useful Utility classes that can be called from anywhere throughout the application, including FreeMarker templates.

Some of the most useful ones are contained in the package org.ofbiz.base.util. Some of their uses include String manipulation, validation, encryption, debugging, and date manipulation.

It is worth spending some time becoming familiar with the methods found in these classes. Very often the simple challenges we face have already been overcome by somebodyelse.

The following section is a guide to a few of the most useful and most commonly used methods from some of these Java classes.

UtilMisc

These miscellaneous utility methods are generally to aid List or Map retrieval, iteration or creation.

Map returnMap = UtilMisc.toMap("keyOne", "valueOne");

A map of up to six key value pairs can be created in this way.

List returnList = UtilMisc.toList("valueOne", "valueTwo");

Again a List of up to six values can be created in this way.

UtilValidate

Validation methods can save time and code by performing validation tests on Strings, Maps, Lists, or other Objects

To check if a variable is empty.

boolean isEmpty = UtilValidate.isEmpty(returnMap);

or

boolean isNotEmpty = UtilValidate.isNotEmpty(returnMap);

In the case of Lists and Maps, a null value is checked for first, followed by a check for an empty instantiated object with no elements.

UtilDateTime

This class contains handy java.sql.Timestamp and java.util.Date manipulation methods.

To return a java.sql.Timestamp object to the nearest millisecond:

Timestamp now = UtilDateTime.nowTimestamp();

Debug

These widely used logging utility methods allow us to use log4j to quickly write to the logs. The level of logging to be displayed is determined in ${ofbizInstallFolder}\framework\base\config\debug.properties:

Debug.logError(e.getMessage(), module);

Outputting Different Formats

So far we have only seen one type of View Handler: the ScreenWidgetViewHandler, which renders screen widgets as XML, usually XHTML.

Sometimes we may need to change the output to something else. We may just wish to produce a formatted report of all sales this month that can be e-mailed or saved, or we may wish to have the system produce shipping label files that can be automatically sent to a printer and a copy saved to the hard disk.

Outputting a PDF

Using similar screen widget concepts to those we have already met, it is possible to produce PDFs using the screenfop View Handler, ScreenFopViewHandler.

Formatting Objects Processor (FOP) is another Apache top level project and is a print formatter that can produce PDFs from XSL-FO (Extensible Stylesheet Language—Formatting Objects). In essence, we use screen widgets to produce XSL-FO which is actually just a different type of XML, which is then formatted into a PDF by FOP.

This is not as complicated as it sounds and much of this work is performed in the background by the framework. The FOP libraries, like the FreeMarker libraries, are already included in the OFBiz project and the ScreenFopViewHandler is already written and in use throughout the code. All we have to do is create request-maps, view-maps, and a new ftl file which will render the XSL-FO.

Open the learning component's controller.xml file, and underneath the screen <handler> element add:

<handler name="screenfop" type="view" class="org.ofbiz.widget.screen.ScreenFopViewHandler"/>

We have now told the control servlet how to handle this new type of view.

Add the request-map and the view-map to the same file in the appropriate places (underneath the ListPlanetReviews mappings).

<request-map uri="ListPlanetReviewsPDF">
<security auth="false" https="false"/>
<response name="success" type="view" value="ListPlanetReviewsPDF"/>
</request-map>
<view-map name="ListPlanetReviewsPDF" type="screenfop" page="component://learning/widget/learning/ LearningScreens.xml#ListPlanetReviewsPDF"
content-type="application/pdf" encoding="none"/>

The view-map's content-type and encoding attributes are used to inform the browser what type of file has been received. If the browser is configured properly and application/pdf types are associated with Adobe Acrobat Reader or a similar installed PDF reader, streams of this content-type will be automatically opened using this application.

To LearningScreens.xml add the new ListPlanetReviewsPDF screen widget:

<screen name="ListPlanetReviewsPDF">
<section>
<actions><entity-condition entity-name="PlanetReview" list-name="planetReviews"/></actions>
<widgets>
<platform-specific>
<html><html-template location="component://learning/ webapp/learning/planets/reviews.fo.ftl"/></html>
</platform-specific>
</widgets>
</section>
</screen>

Even though this actually generates XSL-FO, the HTML oriented render will output the XSL-FO correctly, so our new FreeMarker template is called as if it were an HTML template.

This screen widget does not use a decorator. All of the information needed to produce the PDF will be contained in the file reviews.fo.ftl. It is possible to wrap the screen widgets in a decorator if many of them are using the same chunk of XSL-FO over and over again.

Next, in the directory learning\webapp\learning\reviews add the FTL reviews.fo.ftl and to it add:

<?xml version="1.0" encoding="iso-8859-1"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="review-page">
<fo:region-body margin="1in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="review-page">
<fo:flow flow-name="xsl-region-body">
<fo:block>
<fo:table text-align="center" table-layout="fixed">
<fo:table-column column-width="2.00cm"/>
<fo:table-column column-width="4.00cm"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell>
<fo:block line-height="6pt" space-before.optimum="1.5pt" space-after.optimum="1.5pt" keep-together="always">
<fo:inline font-size="6pt">Review Id</fo:inline>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block line-height="6pt" space-before.optimum="1.5pt" space-after.optimum="1.5pt" keep-together="always">
<fo:inline font-size="6pt"> Review</fo:inline>
</fo:block>
</fo:table-cell>
</fo:table-row>
<#list planetReviews as review>
<fo:table-row>
<fo:table-cell>
<fo:block line-height="6pt" space-before.optimum="1.5pt" space-after.optimum="1.5pt" keep-together="always">
<fo:inline font-size="6pt"> ${review.reviewId}</fo:inline>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block line-height="6pt" space-before.optimum="1.5pt" space-after.optimum="1.5pt" keep-together="always">
<fo:inline font-size="6pt"> ${review.review}</fo:inline>
</fo:block>
</fo:table-cell>
</fo:table-row>
</#list>
</fo:table-body>
</fo:table>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>

Finally fire an http request ListPlanetReviewsPDF to webapp learning to see the PDF produced.

XSL-FO can be tricky to learn and can also be an unforgiving language. Whereas unclosed table row tags (</tr>) may not necessarily effect the way the browser displays the page, or if there is an error in the HTML, more often than not something will at least be displayed giving some clue as to what needs fixing. With FOP an unclosed tag will often result in an error producing nothing. It can be difficult to debug and time consuming.

There are other types of View Handlers already written and available to use including Jasper Reports, JSP, and Velocity (see the package org.ofbiz.webapp.view). With some study of the existing View Handlers it becomes quite simple to write your own should you need to output other formats.

Summary

In this chapter, we have taken a look at some of the final missing pieces of our knowledge about OFBiz and gone a few steps further by changing the output from XHTML to something else entirely.

We now have the knowledge to change the look and feel of both the customer facing pages and the back office pages

We have seen how powerful and versatile FreeMarker can be and how we can use the OFBiz transform tags to help us reuse FreeMarker templates from one component in another component without duplication.

In this chapter we have:

  • Skinned OFBiz and changed the look and feel

  • Used Freemarker with OFBiz transform tags

  • Used some of the OFBiz utilities

  • Called utility methods from Freemarker

  • Outputted a simple PDF report

Now that we are drawing to the end of our learning journey, it may be time to consider looking at the steps that are necessary or recommended by other OFBiz users to prepare our instance for production. The Apache OFBiz Technical Production Setup guide is a must read. In this document you will find instructions on how to change the ports to the standard port 80 and 443, change the debug and cache settings (changing these will have a massive impact on performance), set up security and install an SSL certificate. This document can be found at http://docs.ofbiz.org/display/OFBTECH/Apache+OFBiz+Technical+Production+ Setup+Guide.

In the next chapter we will be taking a look at some advanced techniques with OFBiz, including how to manage a project and keep up to date with new bug fixes and features from the repository. We'll also see some architectural design ideas as well as seeing how to run OFBiz behind an Apache HTTP server.