Centre For Design, eZ Publish Case Study - Development

Development The development phase started at the same time as the HTML prototype. We were able to do the initial setup and configuration while the prototype was being built. Install eZ publish For this project, the first release of eZ publish 3.0 (3.0-1) was used initially, and later upgraded to a bug fix release 3.0-2. Since then, there have been another two major releases that have introduced a much-improved configuration process. Installation of the eZ publish system is a relatively simple process. We incorporated the setup of an eZ publish site into our existing development environment setup. While the eZ publish initial configuration process has improved remarkably in subsequent releases, the installation process remains largely similar to the first version. As a matter of policy, we use a regular site for the web server and a secure site for the admin section. For example: http://projectname.devserver/ Regular site https://admin.projectname.devserver/ Admin site The main steps in the installation of eZ publish are: 1. Set up virtual servers (regular and secure) as normal. 2. Create a MySQL database and database user. 3. Uncompress the eZ publish distribution in the site DOCROOT. 4. Change permissions and ownership on the var and settings directories to allow the web server to write to these. 5. Visit the site URL and process through the eZ publish configuration screens. 6. Manually modify the eZ publish configuration files to match our setup. (This involves changing the default way the regular site is distinguished from the admin site. By default, eZ publish uses the "URL" method, but we used port numbers. This part of the process is now included in the setup process via web pages). 7. Setup and configure design directories for the admin and regular sites. The CMS is now ready for configuration. Define Content Classes and Sections Because of the time spent in getting the information architecture correct, the setup of the content type was a simple process. We used the existing content types Folder and Article, with the Article type requiring the addition of a number of attributes. The other content types were added using the Admin pages. Configure Roles and Permissions Due to the workflow requirements being left for a future production stage, the roles required for this project were limited to three—the default Administrator and Anonymous roles, and the additional Editor role for data entry. The Administrator role has access to all functionality, including the creation, modification, and removal of content, and the ability to modify configuration of the CMS itself. We did not need to modify this role in any way. The Editor role has access to create content where it is appropriate (this is based on the Content model outlined earlier), and to modify and delete this content. Items in the misc folder may only be edited, as these are statically linked in the templates and would break links if removed. Similarly, adding content to this folder would be of no advantage. Access to the configuration aspects of the CMS is not permitted by this role. The Anonymous role, like that of the Administrator, is an existing role. The only modification required to this role is to grant read access to the newly created content objects. It is a common mistake not to give the Anonymous role read access to newly created content types. If you cannot view data in the site, check to see if the Anonymous role has read permission for that particular content type. Each role was assigned to a corresponding user group, and a number of Editor users were created for the client for the content population staff. Apply Display Logic and Templates All page types (home, primary, secondary, tertiary), content types (article, folder, etc.), and sectional summary pages are defined in the Information Architecture document and created in a HTML prototype. This allows for the associated templates to be created directly from the HTML prototype. Also, this process becomes a simple copying of the core HTML and replacement of the sample data with the appropriate eZ publish template code. Templates in the eZ publish system are divided into two main types: page layout and content templates. We will now see how these templates were created, along with the templates for navigation and summaries of content. Specifically, we will look at how the templates were created for: • Page layout • Navigation • Summarized content • Content Create Page Layout Templates The first step in applying the templates is to create the page layout template. This template determines the layout of the page. This site has different page layouts for the home page and all secondary pages and this process is performed for both. All external files (images, stylesheets, and JavaScript) are copied to the design directory on the eZ publish server. The HTML prototype is copied to pagelayout.tpl and all references to external files (images, stylesheets, and JavaScript) are converted to use the ezdesign and ezimage functions. For example, the following HTML to include a JavaScript file:

 
<script language="JavaScript"
src="script/common.js"
type="text/JavaScript">
</script>
becomes:
<script language="JavaScript"
src={"script/common.js" |ezdesign}
type="text/JavaScript">
</script>
The following HTML to include a stylesheet:
<link rel="stylesheet" href="stylesheets/style.css"
type="text/css">
becomes:
<link rel="stylesheet" href={"stylesheets/style.css"|ezdesign}
type="text/css">
The following HTML to include an image:
<img src="images/nv_0202_montage.jpg" width="675" height="96"
alt="Welcome to the Centre for Design">
becomes:
<img src={"nv_0202_montage.jpg"|ezimage}
width="675" height="96"
alt="Welcome to the Centre for Design">

This tells the template system where to find the external files. The main content area is then replaced with {module-result.content}. This is substituted by the template system with the content for a specific page. Navigation The next stage is for the navigation to be programmed. This process requires the CMS to be populated with sample content. It is always good to use real content if possible for this process as this gives a real sense of how the system will eventually work. Primary Navigation on Home Page Primary Navigation on Secondary & Content Pages The CFD site has the following top-level content areas • Research and Consulting • Training and Professional Development • Publications • Sustainable Products and Product Systems • Sustainable Buildings • Life Cycle Assessment • Links • News These sections are created via the admin interface under the root node using the Section Overview content type. With most websites, there is usually content associated with the site that describes the site or the content. A folder is created and called misc (short for Miscellaneous) and the following articles are created in this folder: • More about the Centre • Copyright • Disclaimer • Sitemap • Site credits • Register for newsletter (The "Register for newsletter" item is a different content type used to collect e-mail addresses for the mailing list) This provides us with a skeletal site into which the navigation can be programmed. Footer on Home Page Footer on All Other Pages Setting Up As the navigation differs for various sections of the site, the first step is to detect which section the current node belongs to and to set some variables. This is done by including an initialization (design/cfs/templates/common/initialization.tpl) file in the page layout template. The initialization file detects which section of the site the user is currently in and stores the following items in variables: • section_top_node_id: The node ID of the top node of this section • section_img: The section banner image • section_alt: The alt attribute for the banner image • sec_nav: The secondary navigation for this section The first step of the initialization is the setup of arrays that hold the information for each section. The first array holds the banner image and all text for items in the misc folder. These objects have individual banner images that we directly relate to their node ID; hence the key to the array is the node ID of each object in this folder. This code creates and initializes variables that are used in this file:

{let section_hash=false()
misc_node_hash=false()
section_top_node=false()
}
The misc items information array is as follows:
{set misc_node_hash=hash(
31,hash('image','images/hdr_moreabout.gif',
'image_alt','more about the centre',
),
32,hash('image','images/hdr_copyright.gif',
'image_alt','copyright',
),
33,hash('image','images/hdr_disclaimer.gif',
'image_alt','disclaimer',
),
59,hash('image','images/hdr_sitecredits.gif',
'image_alt','site credits',
),
65,hash('image','images/hdr_sitemap.gif',
'image_alt','sitemap',
),
81,hash('image','images/hdr_register.gif',
'image_alt','register for newsletter',
),
)
}

The following array stores the section information. In addition to the banner information, the name of the file used for generating the secondary navigation for the section is added. The key for this array is the section top node ID.

{set section_hash=hash(
16,hash('section_img','images/hdr_susproducts.gif',
'section_alt','Sustainable Products &amp; Product Systems',
'sec_nav','program_navigation.tpl',
),
17,hash('section_img','images/hdr_susbuildings.gif',
'section_alt','Sustainable Buildings',
'sec_nav','program_navigation.tpl',
),
18,hash('section_img','images/hdr_lifecycle.gif',
'section_alt','Life Cycle Assessment',
'sec_nav','program_navigation.tpl',
),
21,hash('section_img','images/hdr_publications.gif',
'section_alt','Publications',
'sec_nav','publication_navigation.tpl',
),
20,hash('section_img','images/hdr_research.gif',
'section_alt','Research &amp; Consulting',
'sec_nav','research_navigation.tpl',
),
19,hash('section_img','images/hdr_training.gif',
'section_alt','Training &amp; Professional Development',
'sec_nav','training_navigation.tpl',
),
22,hash('section_img','images/hdr_links.gif',
'section_alt','Links',
'sec_nav','link_navigation.tpl',
),
23,hash('section_img','images/hdr_news.gif',
'section_alt','News',
'sec_nav',false(),
),
'Search',hash('section_img','images/hdr_search_results.gif',
'section_alt','Search results',
'sec_nav',false(),
),
24,hash('section_img',$misc_node_hash[$module_result.node_id]['image'],
'section_alt',$misc_node_hash[$module_result.node_id]['image_alt'],
'sec_nav',false(),
),
)

Of note in this array are the elements with the keys Search and 24. The Search entry is for search results pages. The element with key 24 is for items in the misc folder. The banner information is set using the misc_node_hash array and the current node ID. The code to determine the top node ID of the current section follows:

{section show=eq($module_result.path[0].text,'Search')}
{set section_top_node='Search'}
{section-else}
{section show=$DesignKeys:used.depth|gt(2)}
{set section_top_node=$module_result.path[1].node_id}
{section-else}
{set section_top_node=$module_result.node_id}
{/section}
{/section}

We check if the current page is a search results page. If this is not the case, the depth of the current node is tested to see if we are not currently on one of the section top nodes (depth less than or equal to 2). If this is the case, we retrieve the section top node ID from the node path; otherwise we use the current node ID. The last step in this process is to assign values to the variables so they can be used in the pagelayout.tpl file.

{set-block variable=section_top_node_id}
{$section_top_node}
{/set-block}
{set-block variable=section_img}
{$section_hash[$section_top_node]['section_img']}
{/set-block}
{set-block variable=section_alt}
{$section_hash[$section_top_node]['section_alt']}
{/set-block}

These lines set the banner information and the section top node ID (used in primary navigation—see below):

{set-block variable=sec_nav}
{section show=$section_hash[$section_top_node]['sec_nav']}
{section show=array('publication','extended')|
contains($DesignKeys:used.viewmode)}
{set-block variable=section_top_node_id}21{/set-block}
{include uri="design:common/publication_navigation.tpl"}
{section-else}
{section show=ne($module_result.node_id,21)}
{include uri=concat(
"design:common/",$section_hash[$section_top_node]['sec_nav'])
section_top_node_id=$section_top_node}
{/section}
{/section}
{/section}
{/set-block}
{/let}

This code block sets the sec_nav variable. For some sections there is no secondary navigation and this variable is empty. Apart from the publication section, this process is simply a matter of including the correct file to generate the navigation. The following code from the pagepayout.tpl file displays the section banner:

<img src={$section_img|ezdesign} alt="{$section_alt}"
width="370" height="26" border="0"><br>

The secondary navigation is simply added in the correct place in the template. The use of the section_top_node_id variable is detailed in the next section. Primary Navigation: Navigation Highlighted by Border All of the primary navigation items are hard-coded in to the page layout template using their unique node IDs. We initially used the URL alias feature but found that when a content editor changed the title of an object the URL alias also changed, breaking the hard-coded link. The new URL Translator feature allows you to bypass this issue and will be implemented in Stage 2. The node IDs of the items in the misc folder are recorded and the links in the pagelayout.tpl are configured using the ezurl template function. Each primary navigation link has 'on', 'off', and 'active' states. The on and off states are controlled by JavaScript using the onmouseover and onmouseout link attributes. The active and non-active states are controlled by the template logic. The following code fragment shows how this is implemented for the More About the Centre link (node ID 31):

<a href={"content/view/full/31"|ezurl}
onMouseOver="imgOn('nv_moreabout')">
{section show=eq($module_result.node_id,31)}
onMouseOut="imgActive('nv_moreabout')">
<img src={"nv_moreabout_active.gif"|ezimage}
{section-else}
onMouseOut="imgOff('nv_moreabout')">
<img src={"nv_moreabout_off.gif"|ezimage}
{/section}
width="175" height="20" border="0"
alt="more about the centre"
name="nv_moreabout">
</a>

This code fragment is repeated for all the miscellaneous primary navigation items. The logic for displaying the navigation for the main primary navigation items is slightly different as these parts of the site have depth and it is not enough to check to see if the current node ID matches. In these cases we need to check if the current node is either the given node or is an ancestor of the given node. The following code fragment shows how this is implemented for the Sustainable Products and Product Systems link (node ID 16):

<a href={"content/view/full/16"|ezurl}
onMouseOver="imgOn('nv_susproducts')">
{section show=eq($section_top_node_id,16)}
"onMouseOut="imgActive('nv_susproducts')">
<img src={"nv_susproducts_active.gif"|ezimage}
{section-else}
"onMouseOut="imgOff('nv_susproducts')">
<img src={"nv_susproducts_off.gif"|ezimage}
{/section}
width="175" height="33" border="0"
alt="sustainable products &amp; product systems"
name="nv_susproducts"></a>

The variable $section_top_node_id is set in initialization.tpl. Secondary Navigation for Life Cycle Assessment (Highlighted by Border) The secondary navigation varies for the different sections of the site. First we'll examine the secondary navigation for the Programs (Sustainable Products & Product Systems, Sustainable Buildings, and Life Cycle Assessment) and Training & Professional Development. Each program is created in the system as a folder and may contain objects of type Project, Publication, Training, Client, Articles, and Links. The secondary navigation displays all items grouped by their type. The following code is used to produce the secondary navigation for each program's sections:

{let program_subsections=hash('Projects', 7,
'Publications',9,
'Training',8,
'Clients',11,
'Articles',2,
'Links',10,
)
}
<!-- 2nd navigation -->
<td valign="top">
<div class="sec_nav_column">
{section loop=$program_subsections}
{$:key}
{let item_list=fetch(content,list,
hash(parent_node_id,$section_top_node_id,
class_filter_type,include,
class_filter_array,array($:item)))
}
<ul>
{section show=$item_list}
{section loop=$item_list}
<li><a href={$:item.object.main_node.url_alias|ezurl}>
{$:item.name}</a></li>
{/section}
{section-else}
<li>No Items</li>
{/section}
</ul>
{/let}
{/section}
</div></td>
<!-- end 2nd navigation -->
{/let}

First we define an array of headings and their corresponding class IDs. This array is then looped and for each class type: • The heading is outputted. • All objects of that type that belong to the current program area ($section_top_node_id) are retrieved. • Links to the objects are outputted. If no objects of a particular type exist, the text No Items is outputted. The content model allows for training objects to be optionally associated with one of the Programs. Additional training objects as well as any articles about training are placed in the Training folder. The secondary navigation for the training section displays the training objects grouped by their program plus any additional training objects and training articles. Secondary Navigation for Training (Highlighted with a Border) The following code is used to produce the secondary navigation for the Training section:

{
let programs=hash(
'Sustainable Products & Product Systems', 16,
'Sustainable Buildings',17,
'Life Cycle Assessment',18,
'Other',19
)
}
<!-- 2nd navigation -->
<td valign="top">
<div class="sec_nav_column">
{* list training *}
{section loop=$programs}
{$:key}
{
let item_list=fetch(content,list,
hash(parent_node_id,$:item,
class_filter_type,include,
class_filter_array,array(8)))
}
<ul>
{section loop=$item_list show=$item_list}
<li><a href={$:item.object.main_node.url_alias|ezurl}>
{$:item.name}</a>
</li>
{section-else} {* no matching content *}
<li>No Items</li>
{/section}
</ul>
{/let}
{/section}
{* list articles *}
{
let articles=fetch(content,list,
hash(parent_node_id,$section_top_node_id,
class_filter_type,include,
class_filter_array,array(2)))
}
Articles
<ul>
{section show=$articles}
{section loop=$articles}
<li><a href={$:item.object.main_node.url_alias|ezurl}>{$:item.name}</a></li>
{/section}
{section-else}
<li>No Items</li>
{/section}
</ul>
{/let}
</div></td>
<!-- end 2nd navigation -->
{/let}

Similar to the Programs navigation, the first thing we do is define an array of items we want to display. In this case, the array contains program names and the associated node_id, as well as the Training node_id. This array is looped, and for each element of the array all Training objects (class_id = 8) are retrieved. Links to these are then displayed. If no training objects exist for a program then No Items is displayed. The second part of the code retrieves and displays any articles that exist under the training folder. Again, if no objects exists then No items is displayed. Summary Pages Secondary pages usually provide a summary of information from a particular section or an alternative view of data from other parts of the site. These pages are programmed based on the Information Architecture document, which defines the source, type, order, and number of items displayed on the page. The Publications page provides a summary of the publications associated with each program. Summaries of the three most recent publications from each program are displayed. Links to the full listing of all publications for each program and an alphabetical listing of all publications are also displayed. As Publication content types are located under each of the program folders, this page template must retrieve the three most recent publications from each of the program folders. A publications folder is created at the top level to provide a point at which the user can access this information. The node ID of the publications folder is 21. The template override system is used to call the publications template (publications.tpl). The following entry in /settings/siteaccess/cfd/override.ini indicates that the publications.tpl template should be used instead of the default full.tpl template for node ID 21:

[publications]
Source=node/view/full.tpl
MatchFile=publications.tpl
Subdir=templates
Match[node]=21
The following code (/design/cfd/override/templates/publications.tpl) provides
the required functionality:
{* Publication Main Template *}
{let programs=hash('Sustainable Products', 16,
'Sustainable Buildings',17,
'Life Cycle Assessment',18,
)
publication_limit=3
}

We begin by creating an array of the program types and their node IDs along with a publication_limit variable. The publication_limit variable allows us to easily change the number of publications displayed. Next, the programs array is looped through. For each program, we retrieve the three most recent (sort_by,array(published,false())) publication objects (class_filter_type, include, class_filter_array,array(9)) that are children of the current program (parent_node_id,$:item).

<div style="width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" align="left" width="100%">
<tr>
{section loop=$programs}
<td valign="top" width="33%">
<div class="heading_2">{$:key}</div>
{* Display 3 most recent items *}
{let publications=fetch(content,list,
hash(parent_node_id,$:item,
class_filter_type,include,
class_filter_array,array(9),
sort_by,array(published,false()),
limit,$publication_limit
)
)
}

A summary of each of the retrieved publications is displayed using the node_view_gui function with the line view mode. (The use of alternative view modes will be examined in detail in the next section.)

{section name=Publication loop=$publications}
{node_view_gui view=line content_node=$Publication:item}
{/section}
{/let}
{delimiter}
</td>
<td><img src={"images/spacer.gif"|ezdesign} alt="" width="30"
height="1" border="0"></td>
{/delimiter}
{/section} {* programs *}
</tr>
<tr>
<td colspan="5">&nbsp;</td>
</tr>
<tr>

This process is repeated for all programs defined in the programs array. The {delimiter} option of the section loop is used to close table data tags and place a spacer cell between the program columns. The programs array is then looped again to produce the links to the full listing of publications for each program:

{section loop=$programs}
<td colspan="2">
<a href={concat('content/view/publication/',$:item)|ezurl}>
<b>Full Listing</b></a></td>
{/section}
</tr>
<tr>
<td colspan="5">&nbsp;</td>
</tr>
<tr>
<td colspan="5"><a href={concat("content/view/allpublications
/",$node.node_id)|ezurl}><b>See all publications listed
alphabetically</b></a></td>
</tr>
</table>
{/let}
</div>

Alternative View Modes This page uses three additional view modes, one for the summary (line) view of the publications, one for showing all publications for a specific program (publications), and one for the full alphabetical view of all publications (allpublications). As with the Publications page, the template used for each of these cases needs to be overridden. In the case of the publication summary (line) view, a combination of the view mode and the content class ID (9 for publications) is used to determine the template for displaying the information:

[publication_line]
Source=node/view/line.tpl
MatchFile=publication_line.tpl
Subdir=templates
Match[class]=9

This results in the file design/cfd/override/templates/publication_line.tpl being used when publications are displayed with the line view mode. The link that displays a full listing of publications for a specific program will look like /content/view/publication/16. The system interprets this to display node ID 16 using the publication view mode. There is no need to create an override.ini setting for this view mode. It is simply a matter of creating the design/cfd/templates/node/view/publications.tpl file. The same applies to the template to display all publications (design/cfd/templates/node/view/allpublications.tpl). Content Templates Finally, the content templates are programmed. These templates determine how each content type is displayed. They are based on the prototype and are usually a simple cut-and-paste of the HTML for the mock-up, where the content is replaced with the appropriate content type attribute variable. Let's look at the process of creating the template for the Publication content type. The publication object attributes are defined in the Information Architecture document and required attributes are marked with an asterisk (*). Refer to the table discussing the Publication content type, earlier in this chapter. Admin View of the Publication Content Class The override system is used to specify the particular template to use when displaying publication content objects. The following entry tells the system to use the template file design/cfd/override/templates/publication.tpl when displaying publication content objects (class_id = 9):

[publication]
Source=node/view/full.tpl
MatchFile=publication.tpl
Subdir=templates
Match[class]=9

The first step is to take the HTML from the prototype and replace the static content with the appropriate template variables. The HTML from the prototype follows:

<div class="heading_2">Design + Environment: A global guide to designing
greener goods</div>
<div class="heading_3">by Helen Lewis & John Gertsakis with Tim Grant, Nicola
Morelli & Andrew Sweatman</div>
<div class="heading_3">Greenleaf Publishing Limited, Sheffield UK</div>
<div class="heading_3">May 2003</div>
<div class="heading_3">$A50.00</div>
<div class="heading_3"><a href="#">Download order form as PDF file</a></div>
<p><img src="images/sample_publication.gif" alt="" width="175" height="215"
border="0" align="left" hspace="5"></p>
<p>There is a scarcity of good, practical resources for those interested in
minimising the environmental impacts of products. A new book called, Design +
Environment from Greenleaf Publishing, has been specifically written to
address this paucity. The authors - Helen Lewis and John Gertsakis with Tim
Grant, Nicola Morelli and Andrew Sweatman - have all been involved in
EcoReDesign(TM), the innovative program developed by the Centre for Design at
RMIT. The aim of EcoReDesign(TM) is to collaborate with Australian companies
to improve the environmental performance of their products by following design
for environment (DfE) principles. </p>
<p>This clear and informative work will prove to be invaluable to practising
designers, to course directors and their students in need of a core teaching
and reference text and to all those interested in learning about the tools and
trends influencing green product design. The book first provides background
information to assists the reader understand how and why DfE has become so
critical to design. Then, a step-by-step guide is presented on how to design a
product that meets requirements for quality, cost, manufacturability and
consumer appeal, while at the same time minimising environmental impacts.
Environmental assessment tools and strategies are also discussed in detail as
are some of the links between the major environmental problems and the
everyday products we consume. </p>
<p>Four further chapters provide detailed strategies and case studies for
packaging, textiles, furniture, and electrical and electronic products.
Finally, Design + Environment takes a look at some of the emerging trends in
DfE that offer opportunities to significantly reduce environmental impacts.
</p>
<p>Design + Environment is available from the Centre for Design at RMIT.</p>
After replacing the sample content with the appropriate template variables, we end up
with the following code in /design/cfd/override/templates/publication.tpl:
{default content_object=$node.object
content_version=$node.contentobject_version_object}
<div class="heading_2">
{attribute_view_gui attribute=$content_version.data_map.title}
</div>
<div class="heading_3">by
{attribute_view_gui attribute=$content_version.data_map.author}
</div>
<div class="heading_3">
{attribute_view_gui attribute=$content_version.data_map.publisher}
</div>
<div class="heading_3">
{attribute_view_gui attribute=$content_version.data_map.cost}
</div>
<div
class="heading_3">{$content_version.data_map.date.data_int|datetime(custom,"F
%Y")}</div>
<div class="heading_3">
<a href={concat("content/download/",
$content_version.data_map.order_form.contentobject_id,
"/",
$content_version.data_map.order_form.id,
"/file/",
$content_version.data_map.order_form.content.original_filename)|ezurl}>
Download order form as PDF file</a></div>
<p>{attribute_view_gui attribute=$content_object.data_map.image alignment=left
hspace=5 vspace=0 image_class=medium}</p>
{attribute_view_gui attribute=$content_version.data_map.description}
{/default}

The next step is to add some code so that any non-mandatory attributes (and associated static information such as a label) are not displayed. Mandatory attributes are indicated with an * in the content type table (refer back to the Content Types section). In this case only the title, author, and description of content attributes are mandatory. The final code follows:

{default content_object=$node.object
content_version=$node.contentobject_version_object}
<div class="heading_2">{attribute_view_gui
attribute=$content_version.data_map.title}</div>
<div class="heading_3">by {attribute_view_gui
attribute=$content_version.data_map.author}</div>
{section show=$content_version.data_map.publisher.content}
<div class="heading_3">{attribute_view_gui
attribute=$content_version.data_map.publisher}</div>{/section}
{section show=$content_version.data_map.cost.content}
<div class="heading_3">{attribute_view_gui
attribute=$content_version.data_map.cost}</div>{/section}
<div
class="heading_3">{$content_version.data_map.date.data_int|datetime(custom,"F
%Y")}</div>
{section show=$content_version.data_map.order_form.content}
<div class="heading_3"><a
href={concat("content/download/",$content_version.data_map.order_form.contento
bject_id,"/",$content_version.data_map.order_form.id,"/file/",$content_version
.data_map.order_form.content.original_filename)|ezurl}>
{section
show=$content_version.data_map.order_form_description.content}{attribute_view_
gui attribute=$content_version.data_map.order_form_description}
{section-else}
Download order form as PDF file
{/section}
</a></div>
{/section}
{section show=$content_object.data_map.image.content}<p>{attribute_view_gui
attribute=$content_object.data_map.image alignment=left hspace=5 vspace=0
image_class=medium}</p>{/section}
{attribute_view_gui attribute=$content_version.data_map.description}
{/default}

The order form is a binary file datatype. This code checks whether an order form has been uploaded as part of the object by checking if there is data in $content_version.data_map.order_form_description.content. If yes, the appropriate link is generated. If a description of the binary file is present then the link is displayed, otherwise the text Download order form as PDF file is displayed:

{section
show=$content_version.data_map.order_form_description.content}{attribute_view_
gui attribute=$content_version.data_map.order_form_description}
{section-else}
Download order form as PDF file
{/section}

Testing whether an attribute is empty is relatively straightforward (and is much easier now with the introduction of the is_empty function in the current version of eZ publish). For most attributes, if there is no associated content, the value content (such as $content_object.data_map.publication.content) is set to false.