Theming Tutorial

  1. Part 1: Theme Basics
    1. What is a theme
    2. What should I know before making a theme?
    3. How does a theme work?
    4. Where are the theme files located?
    5. Theme structure
  2. Part 2: Making a theme
    1. theme_description.php
    2. theme.png
    3. index.php
    4. album.php
    5. image.php
    6. search.php
  3. Part 3: Advanced Theme Functionality
    1. Custom pages with the example of archive.php and slideshow.php
    2. Custom functions
    3. Theme options

Part 1: Theme Basics

What is a theme?

In technical terms, it’s a set of files that use a markup language ((X)HTML and CSS) and embedded PHP code to dynamically create the layout and style of your gallery. In other words, a theme is just a way to give your gallery a different look.

What should I know before making a theme?

During the process of this article, we will be looking at the basic files and function elements that are necessary to form a working Zenphoto theme, We’ll use the default theme as an example. We will not go into every single detail of the code because we assume that you have an good understanding of (X)HTML and CSS. You should also have a basic understanding what PHP is and how PHP code is put into a webpage and communicates with the server and returns information back to the client.

For more detailed information on the ZenPhoto template functions please visit our functions guide and it’s accompaning How To Read The Zenphoto Functions Guide article.

How does a theme work?

Themes are done in a pretty straightforward way. They’re just PHP files that use a set of theme functions (from zp-core/template-functions.php) to display the albums or images in any way. The current theme is selected in the admin themes panel. (Admin uses the file theme_description.php within the theme’s folder as the sorce of its information about the theme.) The root index.php file then looks at the URL arguments (album, image, etc. or lack thereof) and decides whether to load the theme’s index file, album page, or image page. Pretty simple.

Where are the theme files located?

By default, when you first install ZenPhoto, you should have a file structure that looks similar to the image on the left. You have the “root” or main folder of ZenPhoto, then in it you have four other folders named albums, cache, themes, and zp-core. The themes folder is where the default themes exist, where you should install new themes that you’ve downloaded, and where you will put all your new themes. ZenPhoto comes with four themes: default, example, effervescence_plus and stopdesign.

Theme Structure

Here’s a brief description of the basic files that are needed for a theme:

These are the required basic files every theme must have:

  • theme_description.php - This is the description file which gives ZenPhoto information about your theme. This information is displayed in the themes page of the ZenPhoto admin backend. Older themes before ZenPhoto 1.2 may use a theme.txt file for the same purpose.
  • theme.png - This is the theme thumbnail used in the Options page of ZenPhoto.
  • index.php - This is the default php file. It’s read and displayed when someone visits your main gallery page.
  • album.php - This page is displayed when someone clicks on an album link from your gallery page (index.php). It then displays all the pictures in the selected album as thumbnails.
  • image.php - This page is displayed when someone clicks on a specific thumbnail picture on your album page (album.php). It then displays the selected picture (the sized image), all it’s comments, and the “add comment” form.
  • search.php - The page that displays search results. Actually this page is optional if you don’t want to use the search on your site. It’s included in all standard themes.

The following files are optional as they are not required for a basic functioning theme:

  • archive.php - A custom page that is used to display the monthly archive and a tag cloud. It’s included in all standard themes except stopdesign.
  • themeoptions.php - This provides the option handling interface used by the admin backend on the theme options page.
  • slideshow.php - A page that displays the slideshow. The design is generic so that it works with all themes without design adjusting, just a black background with the image on it, but it’s possible to make it look like your other theme pages as well. Related files that are required to run the slideshow are the slideshow.css and the slideshow-controls.png and the slideshow plugin must be installed and activated.

A diagram that shows the relations of the theme files:

Part 2: Making a Theme

We will now look at the necessary theme pages and the basic structure of these file that Zenphoto needs to process a theme. You can then take this knowledge and use it to create your own theme. Again, we won’t discuss any functions (or any (X)HTML or CSS styling) in detail.

More info on specific functions can be found in our functions guide.

theme_description.php
<?php // Zenphoto theme definition file
$theme_description['name'] = ‘Default’;
$theme_description['author'] = ‘<a href=”http://www.noscope.com”
target=”_blank”>Joen Asmussen</a>
and <a href=”http://www.levibuzolic.com” target=”_blank”>Levi Buzolic</a>’;
$theme_description['version'] = ‘1.5′;
$theme_description['date'] = ‘10/20/2007′;
$theme_description['desc'] = “The default theme in 4 distinct flavors.
After choosing this theme you can pick the theme color by going to the
Admin Options tab
and changing the <em>Theme colors</em> option.”; ?>

First of all, the first line is REQUIRED. This line will tell ZenPhoto that this file is about to describe a theme. ZenPhoto will then look at the rest of the information and process it. The information is set up as key-value relationships using the format key::value. This means that whatever the name of the key, the value is shown after the “=” . So now let’s take a look at the list of keys and what they’re for.

  • name - This is the name of the theme.
  • author - This is the theme author’s name.
  • version - This should be the version number of the theme.
  • date - This should be the last known date of modification of the theme.
  • description - This should be a short description of the theme. It would be a good idea if you include within the description the Zenphoto version for which you actually made this theme.

So just change the values of the keys to match your theme.

theme.png/theme.gif/theme.jpg

This will most likely be the very last file that you create for your theme; it’s an image used to represent your theme. This image can either be a .png, .gif, or .jpg and the dimensions of the file should be 150 x 150px. Most themes use a screenshot of thefinished theme for this image.

index.php

Look at the code in index.php, it’s all just a standard xHTML-based webpage, but we’ll try and explain some of the PHP code that’s in there.

<?php if (!defined('WEBPATH')) die(); ?>

This bit of code makes sure that the configuration for ZenPhoto is setup correctly, if not it will stop everything right here. (There’s no point in processing the rest of the webpage if the configuration is incorrect, right?)

<?php printGalleryTitle(); ?>

As you can read from the function name, this function will place the actual name of the Gallery Title on the displayed page. So for example, if the title of your gallery were John’s Flower Garden then the code would go from looking like this:

<title><?php printGalleryTitle(); ?></title>

to like this:

<title>John's Flower Garden</title>

The next bit of PHP code:

<?php echo $_zp_themeroot ?>

Tells ZenPhoto to show the current path to the current theme’s root directory. This is why it’s used in the following context:

<link rel="stylesheet" href="<?php echo $_zp_themeroot ?>/index.css" type="text/css" />

This will give the correct path to the .css file.

<?php zenJavascript(); ?>

This will include some theme supporting JavaScript that, for instance, allows the logged in administrator to dynamically edit the description and title of your images and albums using AJAX.

<?php if (getOption('Allow_search')) { printSearchForm(''); } ?>

If you set it in the Allow_search options this will print the search form.

<?php printHomeLink('', ' | '); echo getGalleryTitle(); ?>

This is actually a short breadcrumb navigation that will print this if you use Zenphoto within a website and set the link to that site within the options:
Site name | Gallery title

The following code is the main chunk of code for this file. It’s the so called “album loop” that lists all the albums in the gallery:

<?php while (next_album()): ?> <div class="album">
<div class="albumthumb"><a href="<?php echo getAlbumLinkURL();?>"
title="<?php echo getAlbumTitle();?>">
<?php printAlbumThumbImage(getAlbumTitle()); ?></a></div>
<div class="albumtitle">
<a href="<?php echo getAlbumLinkURL();?>"
title="<?php echo getAlbumTitle();?>"></a>
<?php printAlbumTitle(true); ?></div> </div> <?php endwhile; ?>

Let’s take this line by line. First you’re starting a while loop in PHP and tell ZenPhoto that ‘as long as there’s a another album that’s next in the list of albums, do the following code’. The ‘code’ that you want ZenPhoto to process for every album is placed in between the the lines ‘<?php while (next_album()): ?>’ and ‘<?php endwhile; ?>’
That is why when you look at the xHTML source from your browser you see a div with an album class more than once, because it will put every album into a div tag with the album class.
Now inside that while loop you will see more PHP code. The functions getAlbumLinkURL(), getAlbumTitle(), printAlbumThumbImage(), and printAlbumTitle() are all theme functions of which we won’t explain because there are just too many to create a list here and you can also look them up in the theme function reference. However it’s pretty obvious what they do just by looking at the function names. Keep in mind that all these functions provide information about the album and this information will be repeated for each album in the list of albums while going through the while loop.

Note: If you password protect an album via the admin backend and have the image display use lock image option set, Zenphoto uses a default image err-passwordprotected.gif as a replacement for the actual album thumb. This image is located within /zenphoto/zp-core/image/ folder. But a theme can have its own image, all that needs to be done is to place a custom image of the same name like this: /zenphoto/<themefolder>/images/err-passwordprotected.gif.

<?php printPageListWithNav('<', '>'); ?>

This code will create an unordered list of links that link to the number of pages of albums in the gallery. So, for example, if you have three pages of albums, it will create the follwing code:

<ul class="pagelist"> <li class="prev"><span class="disabledlink"><</span></li>
<li class="current"><a href="http://www.zenphoto.org:8080/zp"
title="Page 1 (Current Page)">1 </a>
</li> <li><a href="http://www.zenphoto.org:8080/zp/page/2/" title="Page 2">2</a></li>
<li><a href="http://www.zenphoto.org:8080/zp/page/3/" title="Page 3">3</a></li>
<li class="next"><a href="http://www.zenphoto.org:8080/zp/page/2/"
title="Next Page">></a></li>
</ul>

Most themes then contain a footer that prints the gallery rss link and the link to the (optional) archive page:

<div id="credit"><?php printRSSLink('Gallery','','RSS', ' | '); ?>
<a href="?p=archive"><?php echo "Archive View"; ?></a> | <?php echo "Powered by"; ?>
<a href="http://www.zenphoto.org" title="<?php echo 'A simpler web photo album'; ?>">
zenphoto</a></div>

This is followed by the admin tool box that lets you directly jump to the admin backend if you are logged in, althought not necessary for a theme, this can be very useful:

<?php printAdminToolbox(); ?>
album.php

This page is almost exactly identical to index.php, so we won’t explain everything again. But there are some differences though:

<?php if (!defined('WEBPATH')) die(); $firstPageImages = normalizeColumns('2', '6'); ?>

As you might notice the second part starting with $firstPageImags is new. Basically (very simplified view) it figures out how many images and albums will fit on a page. In particular, it figures this out for the “transition” page where there are both album and image thumbnails on the same pagem which is not unusual if you have subalbums.
Then again we have the expanded breadcrumb navigation, that now prints additionally the album title, too.

<?php printHomeLink('', ' | '); ?>
<a href="<?php echo getGalleryIndexURL();?>" title="<?php echo 'Albums Index'; ?>">
<?php echo getGalleryTitle();?></a> | <?php printParentBreadcrumb(); ?></span>
<?php printAlbumTitle(true);?></h2>

The <?php printParentBreadcrumb(); ?> part is for the breadcrumb display if you have one or more subalbum levels.
The next necessary element is then again the “album loop” you know already from index.php, that now displays the subalbums if available.
But now comes a really new part, the “image loop”:

<?php while (next_image(false, $firstPageImages)): ?><div class="image">
<div class="imagethumb">
<a href="<?php echo htmlspecialchars(getImageLinkURL());?>"
title="<?php echo getImageTitle();?>">
<?php printImageThumb(getImageTitle()); ?></a> </div>
</div><?php endwhile; ?>

This works just like the “album loop” but displays the thumbnails of the images that are in the current selected album.

The remaining code is then again the page navigation and the footer that is the same as on index.php, except that you might want to use the album rss link instead of the gallery rss link.

image.php

Since you should be used to the theme functions by now, we won’t go into what everyone does, but we’ll try and explain the more difficult bits of code.
Right at the top of this file (right before you define where the AJAX code goes) you’ll notice a new bit of JavaScript.

function toggleComments() { var commentDiv = document.getElementById("comments");
if (commentDiv.style.display == "block") { commentDiv.style.display = "none";
} else { commentDiv.style.display = "block"; } } </script>

This code will basically allow you to toggle whether or not you want the comments to show or not. Just use it in a link and use the OnClick method of a link to run the toggleComments() function.

<div class="navigation"> <?php if (hasPrevImage()) { ?>
<a href="<?php echo getPrevImageURL();?>" title="Previous Image"><</a><?php } ?>
<?php if (hasNextImage()) { ?><a href="<?php echo getNextImageURL();?>" title="Next Image">>
</a><?php } ?> </div>

If there is an image (in the list of images) previous than the one being viewed, tgis creates a link to that previous image with the text “<”. Then, if there is an image (in the list of images) following the one being viewed, it creates a link to the following image with the text “>”.
Again we will use the breadcrumb naviagtion that is now extended again with the image title:

<?php printHomeLink('', ' | '); ?>
<a href="<?php echo getGalleryIndexURL();?>" title="<?php 'Albums Index'; ?>">
<?php echo getGalleryTitle();?></a> |
<?php printParentBreadcrumb("", " | ", " | ");
printAlbumBreadcrumb("", " | "); ?> </span>
<?php printImageTitle(true); ?>

The next code display the actual image - we call it the “sized image” to separate it from the full size image, that is linked to the full image and if available the image description :

<div id="image"><a href="<?php echo getFullImageURL();?>" title="<?php echo getImageTitle();?>">
<strong><?php printDefaultSizedImage(getImageTitle()); ?></strong> </a> </div>
<div id="narrow"><?php printImageDesc(true); ?>

This is followed by the display of the EXIF information:

<?php if(getImageEXIFData()) {echo "<div id=\"exif_link\">
<a href=\"#TB_inline?height=345&width=300&inlineId=imagemetadata\"
title=\"Image Info\" class=\"thickbox\">Image Info
</a></div>"; printImageMetadata('', false); } ?>

Now we enter the comments section that is actually the most complicated looking stuff on all theme files:

<?php if (getOption('Allow_comments')) { ?> <div id="comments">
<?php $num = getCommentCount(); echo ($num == 0) ? "" :
("<h3>Comments ($num)</h3><hr />"); ?>
<?php while (next_comment()){ ?> <div class="comment">
<div class="commentmeta"> <span class="commentauthor">
<?php printCommentAuthorLink(); ?></span> says: </div> <div class="commentbody">
<?php echo getCommentBody();?> </div> <div class="commentdate">
<?php echo getCommentDate();?>,
<?php echo getCommentTime();?> <?php printEditCommentLink('Edit', ' | ', ''); ?></div>
</div><?php }; ?>

This code will get the number of comments about the current image and display it.
This while loop is similar to those in index.php and album.php, however the main thing that you’re looping through are the comments. You have the functions getCommentDate(), getCommentTime(), printCommentAuthorLink(), and getComment Body(). Respectively, they get the date that the comment was made, the time the comment was posted, the authors name (which will be a link to his site if one was listed), and the actual comment itself.
Before the loop starts there is a check to see if you allowed showing the comments (with the Allow_comments option):

<?php if (getOption('Allow_comments')) { ?> the comment loop <?php }; ?>

The second part of the comments consists of the actual form where the visitors of your gallery can enter their comments:

<?php if (OpenedForComments()) { ?>
<div class="imgcommentform">
<!-- If comments are on for this image AND album... --><h3>
<?php echo "Add a comment:"; ?></h3
<form id="commentform"action="#" method="post">
<div>
<input type="hidden" name="comment" value="1" />
<input type="hidden" name="remember" value="1" />
<?php printCommentErrors(); ?><table border="0">
<tr><td>
<label for="name"><?php echo "Name:"; ?></label>
</td>
<td>
<input type="text" id="name" name="name" size="20"
value="<?php echo $stored[0];?>” class=”inputbox” />
</td></tr>
<tr><td>
<label for=”email”><?php echo “E-Mail:”; ?></label>
</td>
<td>
<input type=”text” id=”email” name=”email” size=”20″
value=”<?php echo $stored[1];?>” class=”inputbox” />
</td></tr>
<tr><td>
<label for=”website”><?php echo “Site:”; ?></label>
</td>
<td>
<input type=”text” id=”website” name=”website” size=”40″
value=”<?php echo $stored[2];?>” class=”inputbox” />
</td></tr>
<?php if (getOption(’Use_Captcha’)) { $captchaCode=generateCaptcha($img); ?> <tr>
<td>
<label for=”code”><?php echo “Enter Captcha:”; ?>
<img src=<?php echo “\”$img\”";?>alt=”Code” align=”absbottom”/>
</label>
</td>
<td>
<input type=”text” id=”code” name=”code” size=”20″ class=”inputbox” />
<input type=”hidden” name=”code_h” value=”<?php echo $captchaCode;?>”/>
</td></tr>
<?php } ?>
</table>
<textarea name=”comment” rows=”6″ cols=”40″>
<?php echo $stored[3]; ?>
</textarea><br />
<input type=”submit” value=”<?php echo ‘Add Comment’; ?>” class=”pushbutton” />
</div> </form> </div> </div>
<?php } else { echo ‘Comments are closed.’; } ?> <?php } ?>

If you want to protect the display of your sized image and/or the comments by album or gallery password, you should place this “if clause” around the whole part of the above code from sized image to comments like this:

<?php if (!checkForPassword()) { ?> stuff for sized image, comments, comments form <?php }; ?>

The remaining is again the same as on album.php.

search.php

This is the last of the actual required theme page. It’s actually nearly the same as the album.php since it does displaying albums and image. But this time the search results and not fixed albums.

<div id="padbox"> <?php if (($total = getNumImages() + getNumAlbums()) > 0) {
if ($_REQUEST['date']){ $searchwords = getSearchDate();
} else { $searchwords = getSearchWords(); }
echo “<p>Total matches for <em>”.$searchwords.”</em>: $total</p>”; } $c = 0; ?>

This is the actual new part that fetches the search queries someone entered and prints the how many matches there are. This is then followed by the already known “album loop” and “image loop” from album.php. After that the following code is added, that as you see is used if there are no matches.

<?php if ($c == 0) { echo "<p>Sorry, no image matches. Try refining your search.</p>"; }

The remaining is once again the same as on all other theme pages.

Part 3: Advanced Theme Functionality

This part of our tutorial is about the more advanced functionalitly you can add to your theme.

Custom pages with the example of archive.php and slideshow.php

“Custom pages” are additional pages that do not display actual gallery stuff like albums or images. You can have multiple custom pages that are called with the url

zenphoto/index.php?p=pagetitle (non mod_rewrite)
or
zenphoto/page/pagetitle (mod_rewrite)

In general a custom page is just like index.php but with the album loop removed. These pages can be used as an “about” or “references” page for example, but the content needs to be hardcoded and can not be adminstrated by zenphoto. (Of course, you can created theme options to allow customization of these pages from the admin theme options tab.)
You can use some of the more general theme functions as archive.php does.
Archive.php is used to display links that organize your images by month & year, and a tag cloud of the most used tags:

<?php if (!checkForPassword()) {?>
<div id="archive"><?php printAllDates(); ?></div>
<div id="tag_cloud"> <p>Popular Tags</p>
<?php printAllTagsAs('cloud', 'tags'); ?> </div> <?php } ?>

slideshow.php is actual a slim custom page that is outside the actual theme since it is meant to be used as a generic slideshow page. It consists of a basic html structure and the function

<?php printSlideShow(); ?>
custom functions

A custom function can be used to add additional functionality to your theme. Actually it is just a extra template function that is placed as a extra PHP file within your theme’s folder and called on top of your theme files with

<?php require_once('customfunctions.php'); ?>

We recommend naming this file customfunctions.php, especially if you need several extra functions, but actually you can call it what you like.

Theme options

Themes may create options that change the behavior or appearance of the theme. (All the distributed themes have options for these reasons.) Options are defined by PHP code in a file called themeoptions.php which is located in your theme folder. The file is optional, if you have no options you need not have themeoptions.php. Theme options are one class of Zenphoto plugins. Plugins are described here. You define the options in this code and make use of them in your theme by calling the function getOption() passing the option name. (It is a good idea to be sure any unique options you create have unique names so that they don’t conflict with options some other theme builder might have made.)

Some of the ’standard’ theme options (as defined by the distributed themes) are: ‘Allow_comments’, ‘Allow_search’, and ‘Theme_colors’. These are used in the distributed themes to enable comment forms, enable serach forms, and select a CSS for the theme. See the default theme for examples.

Themeoptions are a class of Zenphoto Plugin so reading about the plugin architecture might also be of some value.