6 January 2007

Building a Symphony Theme: Part 5

Symphony is an application that is built on web standards: XML, XSLT, XHTML, and CSS. Symphony accomplishes this using open source scripting and database software: PHP and MySQL, respectively. Up to this point, we haven’t really touched on CSS or Cascading Style Sheets other than to apply some style to a link with a class of “current”. Primarily we have been concerned with content: using Symphony’s XSLT templating system to transform XML data, stored in the MySQL database, to HTML.

At this point, we should have a better understanding about how XSLT works and how we can use Symphony to build a basic HTML page structure. If we know anything about Cascading Style Sheets (CSS), we understand how easily we can separate the presentation of a web page from the content of the page. We can create XSLT templates that output HTML pages that use inline styles, embedded style sheets and/or external CSS files. Before we try adding any additional functionality to our site, let’s add some style to our pages with CSS.

Creating the Theme

Since we are creating a theme for Symphony based on the qwilm theme created by Lokesh Dhakar, let’s link our templates to an external CSS file that contains the styles for the theme, using relative links for the images used by the CSS stylesheet. All the files that we create in Symphony are stored in the /workspace/ folder so that we can easily move the site to a different location on the server, or to an entirely different server. Essentially, the /symphony/ directory contains the application, through which you administer your site, and the /workspace/ directory contains all the files that are specific to your site and control the page structures, behaviours and styles. Note that Symphony’s File Manager allows us to create files only in the /workspace/ directory, but it cannot create additional directories. While it is not necessary to store external CSS files in the /workspace/ directory, it makes sense to do so, especially when developing a theme. In effect, everything that exists in the workspace directory is the theme. Change the /workspace/ directory and you can have a completely different site.

Connect to the CSS file

Download the ZIP file that contains the CSS and image files for the qwilm theme. The files are all contained within a directory named “css”. Place this directory in the /workspace/ directory. The style.css file within the /css/ directory contains style declarations with relative URLs for images in the /css/images/ directory. The Symphony File Manager allows us to save and view GIF, JPEG, PNG, CSS, Javascript, XSL and PHP files contained in any directory or subdirectory within the /workspace/ directory. Symphony finds all the CSS and Javascript files in the /workspace/ directory and displays a list of links to each of these files in the Blueprints : Components page under the heading “Assets”. To modify the style.css file, you can click on the link to open and edit the file. You can also upload files to any directory or subdirectory in your /workspace/ directory by going to Publish : File Manager and clicking on the upload button. View the files in the /workspace/ directory by selecting the directory you would like to view from the pull-down menu in the upper right corner of the page.

You can view a list of the image files in the /workspace/css/images/ directory and you should see something like this:

Symphony Admin : Publish : File Manager : CSS Images

Add the following code to the <head> element of the default.xsl Master template:

                  <link rel="stylesheet" type="text/css" media="screen" href="{$root}/workspace/css/style.css" />


You could also use the following without the $root parameter, since you would need some additional XSLT code to properly add the value for $root to the URL.

                  <style type="text/css" media="screen">
    @import url(/workspace/css/style.css);


If you are interested in dynamically modifying the URL for the CSS file, it is possible to use the XSL concatenate function to create the text string for the `import directive:

                  <style type="text/css" media="screen">
    <xsl:value-of select="concat('`import url(',$root,'/workspace/css/style.css);')"/>


We can view the front end of our site and see that the pages are now connected to the CSS file.

Duplicate the intended HTML structure

Now, we need to duplicate the intended HTML structure before the pages will display properly. The easiest way to accomplish this would be to copy the HTML structure into the Master template, then start replacing the static HTML elements with XSLT templates. In part two, I provided the basic page structure of the qwilm theme with some sample content that represents most of the HTML elements that we will need to reproduce with our XSLT templates.

First, let’s create a new file called qwilm.html by going to Blueprints : Components : Assets : Create New and give the asset the name qwilm.html and paste the HTML page from Part 2 into the body of this new asset.

Symphony Admin : Blueprints : Components

If you would rather, you can also download the qwilm.html file and upload the file to your /workspace/ directory by going to Publish : File Manager and clicking on the Upload button.

Symphony Admin : Publish : File Manager : Upload

Navigate to this test page at the following address:



Now, we should be able to see the structure and style displaying as intended.

Replace static HTML elements with XSLT code

To apply this structure to our templates, let’s copy all the elements inside the <body> element and paste these into the <body> element of the default Master template. For now, replace everything that we have created so far in the <body> element of the default.xsl template. You can copy these elements from the code samples below to place the XSL code into the appropriate areas of the intended page structure.

The website name should appear in two different places in the qwilm theme. The <h1> element has been hidden by a CSS rule, but the element is still there. Find the <div id="header"> element and replace it with the following:

                  <div id="header">
        <a href="{$root}/" title="Home">
            <xsl:value-of select="$website-name"/>


There is another <div> element at the bottom of the page that contains the home page link. While the <h1> at the top of the page is hidden by a CSS rule, this <h3> element at the bottom becomes the home page link that displays on the page. Replace this with the following:

                  <div class="box sidebar" id="sidebar-3">
        <a href="{$root}/" title="Home">
            <xsl:value-of select="$website-name"/>


The page heading and the content body can go in the “sidebar-2” <div> element. We’ll keep the list of Meta links for now, even though the feed links don’t yet point to anything that exists.

                  <div class="box sidebar" id="sidebar-2">
            <h2>Main Menu</h2>
            <xsl:call-template name="main-menu"/>
            <h2><xsl:value-of select="$page-title"/></h2>
            <xsl:copy-of select="data/content/entry[fields/page/`handle=$current-page]/fields/body/*"/>
                <li><a href="/feed/" title="Syndicate this site using RSS 2.0">Entries <abbr title="Really Simple Syndication">RSS</abbr></a></li>
                <li><a href="/comments/feed/" title="The latest comments to all posts in RSS">Comments <abbr title="Really Simple Syndication">RSS</abbr></a></li>
                <li><a href="" 
                title="The Symphony Web Publishing System from Twentyone Degrees">Symphony</a></li>


If all goes well, you should see a page that looks like this (depending on the size of your browser window):

qwilm theme : Home Page : Click to view a larger image

To view the full-size image of the theme, click on the image. Use the back button of your browser to return to this page.

Symphony’s Image Function

Just as an aside, Symphony has a great built-in image function that uses PHP to dynamically resize images on the fly before sending the results to the browser. For the above image, I uploaded only the full-size image. Symphony has created the smaller image for me. The HTML code to accomplish this bit of magic is as follows:

                  <a href="/workspace/assets/images/sym_qwilm_home_04.jpg">
    <img src="/image/540/0/0/fff/assets/images/sym_qwilm_home_04.jpg" 
        title="qwilm theme : Home Page : Click to view a larger image" 
        alt="qwilm theme : Home Page : Click to view a larger image" />


For details on how to utilize this in your templates, refer to the Overture Forum thread regarding Symphony’s built-in image functions(Overture Forum: built in image functions) or view the Image Function(Overture Wiki: Image Function) entry in the Overture Wiki.

For the sake of demonstration, following are some links for different sizes of this same image:

This image function opens up some interesting possibilities, such as using Symphony to manage an image library and dynamically serve XML data and images to Flash for SlideShowPro, or using Lokesh Dhakar’s very own Lightbox to develop a CSS image gallery. But, I’ll get to that some time later.

Adding the Blogging Features

Lokesh Dhakar’s qwilm design, which he describes as “just as simple blog,” was intended to be used as a WordPress blog theme, so it contains the usual features: Archives, Recent Posts, Categories, Search, Comments and XML/RSS feeds. All of these items are content related to weblog entries and the different ways you can find entries (archives, recent, categories, search) or interact with them (reading and posting comments, receiving notification of the latest additions through a RSS news reader). These features go beyond the basic Data Source that we created for the Content Section. We could use the Content Section, but we don’t want the static content being mixed with weblog entries. We could use XSLT to filter the results so as not to include static content. But why not create another Section to create a clear separation in the XML data between static content and weblog entries? So, let’s create another Section for weblog entries called “Entries”.

Determine what Custom Fields are required

It is possible to create several Custom Fields to associate with a Section, then create a Data Source and later add some additional Custom Fields. However, these new Custom Fields are not automatically added to the Data Source we created before. To include the new Custom Fields, we need to go back to Blueprints : Controllers : Data Sources to modify the Data Source to include the new Custom Fields. The same principle holds true if you rename a Custom Field. The Data Source you created previous to the name change will be looking for the old name for the Custom Field and will not find it. The Data Source needs to be updated to include the Custom Field with its new name.

For this reason, it is good to create all the custom fields that will be associated with a Section before creating the Data Source, or we might find ourselves returning again and again to the Data Sources Editor to update the Data Source. To keep the page load time down, we may want to limit the amount of data we pass to the XSLT processor. To do this, we may create many different Data Sources which use the same source, but have been optimized to filter entries based on a number of different conditions. If at any time we add another Custom Field to the Section that we want to include in all these Data Sources, we’ll need to update each Data Source individually.

First, then, let’s create a list of the items that we need to display:

  • Title
  • Date and Time
  • Excerpt
  • Body
  • Photo
  • Photo Caption
  • Categories
  • Tags (I just threw this one in)

Create the Entries Section

Go to Structure : Sections and create a new Section called “Entries.” This time, keep all the defaults selected, including “Enable comments for this section.”

Symphony Admin : Structure : Sections : New Section

Save the new Section and we will find ourselves back at the Sections list, which shows that a Primary field has been created with the name “Title”. Click on this link and we can change the name of the Primary field to “Entry Title”.

Create Custom Fields associated with the Entries Section

It is possible to have several Custom Fields with the same name but used for different sections. For instance, a Custom Field called “Title” will be common for most sections. It may be necessary to differentiate between these fields in the XSL code. The simplest way to ensure that the XSL code can differentiate between these fields is to give them a different name. There may be instances where you might want to apply the same template to Data Sources from multiple Sections, in which case, it makes sense to create Custom Fields with the same name. For the sake of this tutorial, I will allow Custom Fields with identical names, although some have recommended otherwise on the Overture Forum (some problems and fixes have been noted due to identical names).

Go to Structure : Custom Fields and create Custom Fields with the following names, choosing the settings shown in parentheses with this syntax: (type, attribute; location). We will configure each of these Custom Fields to be associated with the “Entries” Section:

Note that you will need to create a new directory in the /workspace/ directory for the Entry Photos – unfortunately, Symphony cannot do this (yet). When you create this directory, ensure that it has been assigned the privileges necessary to allow Symphony to read and write files to it.

  • Title (Primary Custom Field, created automatically)
  • Publish Date and Time (created automatically)
  • Excerpt (text area, 10 rows; main content)
  • Body (text area, 25 rows; main content)
  • Photo (file attachment, directory: workspace/photos/; drawer)
  • Photo Caption (text input; drawer)
  • Categories (select box, multiple items: Yes, options: General, Technology, Personal, Work; sidebar)
  • Tags (text input, description: “Comma separated tags”, Split by commas: Yes; main content)
  • Publish (checkbox with description: Publish this entry, Checked by default: Yes; sidebar)

Now, go to Structure : Sections and click on the Reorder button in the upper right. You can control the order of the menu items in the Publish menu by reordering the Sections. Click on the row for Entries and drag the row to the top position in the list, the click on the Done button.

Symphony Admin : Structure : Sections : Reorder

Click on the name of the Entries Section to edit it and click on the checkbox next to Publish so we can add the Publish field to the columns displayed in the Entries list.

Go to the Publish menu to see the new Entries Section added to the top of the menu. Select Entries and click on the Create New button. (At this point, if your /workspace/photos/ directory is not writable, you will see an error until you fix the privileges for the directory. Set permissions to 777 or 755 depending on server requirements.) If all goes well, we should see our new Entries Section, including a couple of additional Custom Fields that can be accessed by clicking on the More Options button. We may not have a photo to associate with every entry, so we can keep the associated Custom Fields from cluttering the page, but they are there when we need them.

Create the Data Source

We can go ahead and start creating entries, but we won’t be able to do anything with the data in our templates until we have created a Data Source to associate with our Master and/or Page templates. Let’s go ahead and do this now. Go to Blueprints : Controllers : Data Sources : Create New, give the Data Source the name “Entries” and use the Entries Section as the source. By default, several elements are selected under Format Options : Included Elements. These elements will work fine for what we want to accomplish. As blogs tend to order entries by starting with the most recent entries, choose “Descending Date (latest first)” from the pull-down menu under Format Options : Sort Results by. Save the Data Source and we’ll be ready to start building the rest of the theme.

We have connected to the CSS stylesheet and we have the Section and Custom Fields created, ready to receive data for the Entries Section. Stay tuned for Part 6, when we will develop the templates that will get the rest of the site working.