« Back to zenphoto.org

Zenphoto Hacks

This is a page that details the variety of ZenPhoto "modifications" or "hacks" that can be done to Zenphoto's core code and functions that extend the capabilities of ZenPhoto. Please bear in mind that there may be little or no support for these hacks, so use them at your own risk. If you have questions about any hacks, search the ZenphotoForums for help, or start a new topic there. ZenPhoto has a very helpful community, so don't be shy!

These hacks are not officially supported by the ZenPhoto development team. There is a plugin API currently under development which will allow for more robust plugin capability in the near future. Please check the Roadmap for more details.


  1. zenphoto as a 'plug-in'
  2. safe_glob() hack
  3. lighttpd rewrite rules
  4. Password Protection
  5. RSS Hacks
  6. Comment Hacks
    1. Akismet Hack
    2. Captcha Hack
    3. Recent Comments Custom Function
  7. Image and Album Hacks
    1. Slimbox, Lightbox Hack
    2. Watermarking Hack
    3. Image Rating Hack
    4. Hit Count Hack
    5. Highest Rated Custom Function
    6. Most Popular Custom Function
    7. Latest Images Custom Function
    8. Latest Albums Custom Function
    9. Random Image Custom Functions
    10. Image and Album Counting Functions
      1. SQL Counting Functions
      2. Image counter for image.php
    11. Album Menu Functions
      1. Album menu functions for the official 1.0.8.2 zenphoto release
      2. Album menu function
  8. Album Zip
  9. Utilities
    1. Regenerate thumbnails in cache
  10. Client side optimization
  11. Server side optimization


zenphoto as a 'plug-in'

USE ZENPHOTO TEMPLATE FUNCTIONS FROM OUTSIDE OF THE ZENPHOTO FOLDERS

You can use zenphoto features in your main web pages by including template-functions.php in your php pages. To do this successfully when the page is not in the zenphoto folders include the following PHP code in you page (this assumes zenphoto is installed in a folder named zenphoto):

define('ZENFOLDER', 'zp-core');
define('WEBPATH', 'zenphoto'); 
require_once(WEBPATH . "/" . ZENFOLDER . "/template-functions.php");

You will now be able to use zenphoto functions on your page to, for instance, place an random image from your album on your index page. Code for this is:

$randomImage = getRandomImages();
$randomImageURL = getURL($randomImage);
echo "<a href='".$randomImageURL."' title='Random Picture...'><img src='".
$randomImage->getSizedImage(getOption('image_size')) .
      "' alt=\"random image\"\n" . $randomImage->getTitle() .
      '" /></a>';

NOTE: Many of the zenphoto template-functions operate on $_zp_current_album, $_zp_current_image, and $_zp_gallery. The latter are refered to as the current album and current image in the function documentation (see below.) These variables normally are setup by the process of loading the root index.php file and proceding to your theme php file. They will NOT be setup automatically for you when you use zenphoto as a plugin. You can use the functions zp_load_gallery(), zp_load_album($folder), and zp_load_image($folder, $filename) to set these variables up. $folder is the path from the album folder to the album. $filename is the name of the image within $folder.

For further details on the template functions visit: http://www.zenphoto.org/trac/wiki/TemplateFunctionsGuide


safe_glob() hack

Use this if your ISP has disabled the glob() function in PHP.

Note: This hack will be incorporated in the 1.1.4 release of zenphoto. With that release, to enable the safe_glob function change the SAFE_GLOG define at the front of functions.php from false to true

If the dropdown controls in Admin for theme colors, watermarks, and spam filters are empty it may be because the PHP glob() function has been disabled on your server. glob() is used in admin-functions.php in the generateListFromFiles() function and in class-albums.php in the deleteAlbums() function. (The latter is post the zenphoto 1.1.3 release.) You can add the following code to functions.php and replace the calls on glob() with calls on safe_glob() to correct this problem.

/**
* an alternative to glob if the ISP has disabled it
**/
function safe_glob($pattern, $flags=0) {
    $split=explode('/',$pattern);
    $match=array_pop($split);
    $path=implode('/',$split);
    if (empty($path)) { $path = '.'; };

    if (($dir=opendir($path))!==false) {
        $glob=array();
        while(($file=readdir($dir))!==false) {
            if (fnmatch($match,$file)) {
                if ((is_dir("$path/$file"))||(!($flags&GLOB_ONLYDIR))) {
                    if ($flags&GLOB_MARK) $file.='/';
                    $glob[]=$file;
                }
            }
        }
        closedir($dir);
        if (!($flags&GLOB_NOSORT)) sort($glob);
        return $glob;
    } else {
        return false;
    }    
}
if (!function_exists('fnmatch')) {
    function fnmatch($pattern, $string) {
        return @preg_match('/^' . strtr(addcslashes($pattern, '\\.+^$(){}=!<>|'), array('*' => '.*', '?' => '.?')) . '$/i', $string);
    }
}

lighttpd rewrite rules

Rewrite rules for lighttpd

Provided by Hinrik

Add this to a new file called /etc/lighttpd/zp-rewrite.conf:

url.rewrite-once = (
    "^"+zendir+"admin/?$" => "$0",
    "^("+zendir+"(albums|cache|themes|zp-core).*)$" => "$1",
    "^(/\?.*)" => "$1",
    "^("+zendir+"[a-z]+\.php(\?.*)?)$" => "$1",

    "^"+zendir+"page/([0-9]+)/?$" => zendir+"index.php?page=$1",
    "^"+zendir+"page/([A-Z]a-z0-9\-_]+)/?$" => zendir+"index.php?p=$1",
    "^"+zendir+"(.*)/page/([0-9]+/)?$" => zendir+"index.php?album=$1&page=$2",

    # Rewrite rule addition for search
    "^"+zendir+"page/([A-Za-z0-9\-_]+)/([A-Za-z0-9\-_]+)/?$" => zendir+"index.php?p=$1&words=$2",
    "^"+zendir+"page/([A-Za-z0-9\-_]+)/([A-Za-z0-9\-_]+)/([0-9]+)/?$" => zendir+"index.php?p=$1&words=$2&page=$3",

    # Rewrite rule addition for archive-/date-search (needed to be separated because of the same structure)
    "^"+zendir+"page/([A-Za-z0-9\-_]+)/archive/([A-Za-z0-9\-_]+)/?$" => zendir+"index.php?p=$1&date=$2",
    "^"+zendir+"page/([A-Za-z0-9\-_]+)/archive/([A-Za-z0-9\-_]+)/([0-9]+)/?$" => zendir+"index.php?p=$1&date=$2&page=$3",

    "^"+zendir+"(.*)/image/(thumb|[0-9]{1,4})/([^/\\]+)$" => zendir+"zp-core/i.php?a=$1&i=$3&s=$2",
    "^"+zendir+"(.*)/image/([^/\\]+)$" => zendir+"zp-core/i.php?a=$1&i=$2",

    # Catch-all - everything else gets handled in PHP for compatibility.
    "^"+zendir+"(.+)/?$" => zendir+"index.php?album=$1"
)

url.redirect = (
    "^"+zendir+"admin" => ""+url+"/"+zendir+"zp-core/admin.php"
)

Then edit your /etc/lighttpd/lighttpd.conf and add the following:

server.modules += ( "mod_rewrite", "mod_redirect" )

# change the hostname to match yours
$HTTP["host"] =~ "my\.photosite\.com" {
    # path to your zenphoto installation in the webroot
    # don't forget the trailing slash!
    var.zendir = "/zenphoto/"
    var.url = "http://domain.tld"
    include "zp-rewrite.conf"
}

Add more $HTTP["host"] sections (or augment the regex) if you have more zenphoto installations running on your server.


Password Protection

PASSWORD PROTECTION IS NOW PART OF ZENPHOTO

Password protection is available in the nightly build and will be part of the next release after version 1.1.2. Options have been added to password protect the gallery, individual albums/subalbums, and search pages.


RSS Hacks

THE FOLLOWING IS INCLUDED IN ZENPHOTO 1.1

Developed by acrylian, Alenonimo, thinkdreams, and WillHamilton?.

Using the following code snippets you can setup RSS feeds for your entire gallery, individual albums and all comments.

Save the code samples as "rss.php" and "rss-comments.php" within your theme folder then place the links into your themes:

1) On any theme file the common rss feed for the whole gallery:

<a href="index.php?p=rss">RSS</a>

2) On album.php and image.php the album rss feed for the currently selected album:

<a href="index.php?p=rss&albumnr=<?php echo getIDforAlbum(); ?>&albumname=<?php printAlbumTitle(); ?>">RSS for this album</a>

Or you can place the rss.php within the zenphoto root in this thread:folder and called it via your theme like this:

<a href="rss.php">RSS</a>

or

<a href="rss.php?&albumnr=<?php echo getIDforAlbum(); ?>&albumname=<?php printAlbumTitle(); ?>">RSS for this album</a>

3) On any theme file the rss feed for all of your gallery's comments:

<a href="index.php?p=rss-comments">RSS</a>

4) To allow for feed auto-discovery within browsers, put the following code within your HEAD section:

<link rel="alternate" type="application/rss+xml" title="RSS" href="rss.php" />

rss.php

<?php
header('Content-Type: application/xml');
require_once("zen/template-functions.php");
$themepath = 'themes';
require_once("zen/zp-config.php");

$albumnr = $_GET[albumnr];
$albumname = $_GET[albumname];

if ($albumname != "")
	{ $albumname = " - for album: ".$_GET[albumname]; }

?>
<rss version="2.0">
<channel>
<title><?php echo $conf['gallery_title']; ?><?php echo $albumname; ?></title>
<link><?php echo "http://".$_SERVER["HTTP_HOST"].WEBPATH; ?></link>
<description><?php echo $conf['gallery_title']; ?></description>
<language>en-us</language>
<pubDate><?php echo date("r", time()); ?></pubDate>
<lastBuildDate><?php echo date("r", time()); ?></lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<generator>Acrylian's ZenPhoto Album RSS Generator based on AlenĂ´nimo's ZenPhoto RSS Generator which is based on ThinkDreams' Generator</generator>
<managingEditor><?php echo $conf['admin_email']; ?></managingEditor>
<webMaster><?php echo $conf['admin_email']; ?></webMaster>
<?php 
$iw = $cw = 300; // Image Width
$ih = $ch = 300; // Image Height
$items = 10; // # of Items displayed on the feed

mysql_connect($conf['mysql_host'],$conf['mysql_user'],$conf['mysql_pass']);
mysql_select_db($conf['mysql_database']) or die( "Unable to select database");

if ($albumnr != "")
	{ $sql = "SELECT * FROM ". prefix("images") ." WHERE albumid = $albumnr ORDER BY id DESC LIMIT ".$items;}
else
 	{ $sql = "SELECT * FROM ". prefix("images") ." ORDER BY id DESC LIMIT ".$items; }
 	
$result = mysql_query($sql);

while($r = mysql_fetch_array($result)) {
$id=$r['albumid'];

if ($albumnr != "") 
	{ $sql="SELECT * FROM ". prefix("albums") ." WHERE id = $albumnr"; }
else
	{ $sql="SELECT * FROM ". prefix("albums") ." WHERE id = $id"; }

$album = mysql_query($sql);
$a = mysql_fetch_array($album);
?>
<item>
<title><?php echo $r['title'] ?></title>
<link><?php echo '<![CDATA[http://'.$_SERVER["HTTP_HOST"].WEBPATH.'/index.php?album='.$a['folder'].'&image='.$r['filename'] . ']]>';?></link>
<description><?php echo '<![CDATA[<a title="'.$r['title'].' in '.$a['title'].'" href="http://'.$_SERVER["HTTP_HOST"].WEBPATH.'/index.php?album='.$a['folder'].'&image='.$r['filename'].'"><img border="0" src="http://'.$_SERVER["HTTP_HOST"].WEBPATH.'/zen/i.php?a='.$a['folder'].'&i='.$r['filename'].'&w='.$iw.'&h='.$ih.'&cw='.$cw.'&ch='.$ch.'" alt="'. $r['title'] .'"></a>
' . $r['desc'] . '
]]>';?> <?php if($exif['datetime']) { echo '<![CDATA[Date: ' . $exif['datetime'] . '
]]>'; } ?></description>
<guid><?php echo '<![CDATA[http://'.$_SERVER["HTTP_HOST"].WEBPATH.'/index.php?album='.$a['folder'].'&image='.$r['filename'] . ']]>';?></guid>
</item>
<?php } ?>
</channel>
</rss>

rss-comments.php

<?php
header('Content-Type: application/xml');
require_once("zen/template-functions.php");
$themepath = 'themes';
require_once("zen/zp-config.php");
?>
<rss version="2.0">
<channel>
<title><?php echo $conf['gallery_title']." - latest comments"; ?></title>
<link><?php echo "http://".$_SERVER["HTTP_HOST"].WEBPATH; ?></link>
<description><?php echo $conf['gallery_title']; ?></description>
<language>en-us</language>
<pubDate><?php echo date("r", time()); ?></pubDate>
<lastBuildDate><?php echo date("r", time()); ?></lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<generator>Acrylian's ZenPhoto Comment RSS Generator based on Tris's Latest Comments function from zenphoto admin.php</generator>
<managingEditor><?php echo $conf['admin_email']; ?></managingEditor>
<webMaster><?php echo $conf['admin_email']; ?></webMaster>
<?php
mysql_connect($conf['mysql_host'],$conf['mysql_user'],$conf['mysql_pass']);
mysql_select_db($conf['mysql_database']) or die( "Unable to select database");
$comments = query_full_array("SELECT c.id, i.title, i.filename, a.folder, a.title AS albumtitle, c.name, c.website," . " c.date, c.comment FROM ".prefix('comments')." AS c, ".prefix('images')." AS i, ".prefix('albums')." AS a " . " WHERE c.imageid = i.id AND i.albumid = a.id ORDER BY c.id DESC LIMIT 10");
foreach ($comments as $comment)
{
$author = $comment['name'];
$album = $comment['folder'];
$image = $comment['filename'];
$albumtitle = $comment['albumtitle'];
if ($comment['title'] == "") $title = $image; else $title = $comment['title'];
$website = $comment['website'];
$comment = truncate_string($comment['comment'], 123);
?>
<item>
<title><?php echo $albumtitle.": ".$title." by ".$author; ?></title>
<link><?php echo '<![CDATA[http://'.$_SERVER['HTTP_HOST'].WEBPATH.'/index.php?album='.$album.'&image='.$image.']]>';?></link>
<dc:creator><?php echo $author; ?></dc:creator>
<description><?php echo $comment; ?></description>
<guid><?php echo '<![CDATA[http://'.$_SERVER['HTTP_HOST'].WEBPATH.'/index.php?album='.$album.'&image='.$image.']]>';?></guid>
</item>
<?php } ?>
</channel>
</rss>

Comment Hacks

These are hacks specifically related to the ZenPhoto comment system, primarily for comment spam-bot prevention.

Akismet Hack

AN AKISMET SP@M FILTER IS INCLUDED IN ZENPHOTO 1.1

Developed by GameDudeX, this is a hack that extends the comment functionality for ZenPhoto to include spam filtering. This is a feature which will eventually be included in the core code (see Roadmap) but for now will hold those that need it over until that version of Zenphoto is released.

From the author: "This hack is an answer to lots of calls for a solution to comment spam for the ZenPhoto gallery."

Download it here: attachment:"zp_akismet_hack.zip"

Captcha Hack

This is a hack that implements Captcha SPAM protection to the ZenPhoto commenting system. A demo is available here. You can obtain the zip file below attached to this page or at this website here. Designed by Gregb34. Support can be found on the ZenphotoSupport forums.

Recent Comments Custom Function

This hack allows for a custom function to be used to display recent comments from anywhere the function is called in your theme. This function was originally created by Anton and is no longer maintained at the link provided.

To use, place this file in a custom function file of your choice, make sure you declare the use of that custom functions file, and then replace the text in the code labeled YOURTHEME with the directory name of your theme. For more information on how to create a custom functions file, see the FAQ.

/* Show Latest Comments */
/* http://anton.gektoras.lt/2007/05/15/zenphoto-addons/ */

function my_truncate_string($string, $length) {
   if (strlen($string)> $length) {
              $short = substr($string, 0, $length);
              return $short. '...';
          } else {
              return $string;
          }
      }       

function printLatestComments($number) {
	echo '<div id="showlatestcomments">';
	echo '<ul>';
	$comments = query_full_array("SELECT c.id, i.title, i.filename, a.folder, a.title AS albumtitle, c.name, c.website,"
	  . " c.date, c.comment FROM ".prefix('comments')." AS c, ".prefix('images')." AS i, ".prefix('albums')." AS a "
	  . " WHERE c.imageid = i.id AND i.albumid = a.id ORDER BY c.id DESC LIMIT $number");
	foreach ($comments as $comment) {
	  $author = $comment['name'];
	  $album = $comment['folder'];
	  $image = $comment['filename'];
	  $albumtitle = $comment['albumtitle'];
		  if ($comment['title'] == "")  {
			$title = $image;
		  } else {
			$title = $comment['title'];
		  }
	  $website = $comment['website'];
	  $comment = my_truncate_string($comment['comment'], 40);
	  $link = $author.' commented on '.$albumtitle.' / '.$title ;
	  $short_link = my_truncate_string($link, 40);
	  echo '<li><div class="commentmeta"><a href="';
		  if (zp_conf('mod_rewrite') == false) {
			echo WEBPATH.'/index.php?album='.urlencode($album).'&image='.urlencode($image).'/"';
		  } else {
			echo WEBPATH.'/'.$album.'/'.$image.'" ';
		  }
	  echo 'title="'.$link.'">';
	  echo $short_link.'</a>:</div><div class="commentbody">'.$comment.'</div></li>';
	}
	echo '</ul>';
	echo '</div>';
}

Image and Album Hacks

These are hacks to the image processing system of ZenPhoto, or for display of images and albums using custom functions.

Slimbox, Lightbox Hack

A lot of people ask me to send them my theme, which uses slimbox java script window for previewing images, or they ask me how to adopt any theme to use lightbox/slimbox java script. It's very easy, the easiest way is to download already made package from Chillfrei. But if you want to adopt your theme to use Slimbox you have to download two files and make slight changes in your album.php

Watermarking Hack

WATERMARKING OF IMAGES AND VIDEO THUMBS IS INCLUDED IN ZENPHOTO 1.1

The Watermarking Hack allows you to display a watermark image on your images that are run through ZenPhoto and its image processor. This is done on-the-fly during image thumbnail and display. It requires modification to the zp-config.php file, a small modification to the i.php file, and also a 24-bit alpha transparency capable PNG file to be created that is used as the watermark. There is more about this hack in the forum article here.

Instructions

1. Create a 24 bit alpha transparent PNG image of your choice. Name it something intuitive (ex. watermark.png) and make sure the code you insert below into zp-config.php reflects this name. Text or simple graphics are a good choice, since you don't want anything too distracting.
2. Upload the image to the /zen/images directory in your zenphoto installation folder on your webserver.
3. Modify the core zenphoto code in these two locations.

Code to add to zp-config.php (located in /zen directory):

// Watermarking Options

// turn the watermarking on and off
$conf['perform_watermark'] = true;
// the image (png-24) in the zen/images/ directory
$conf['watermark_image'] = "images/watermark.png";

Code to add at the top of i.php (located in /zen directory). Look for the comment field:

// Set the config variables for convenience.

And then add:

$perform_watermark = zp_conf('perform_watermark');
$watermark_image = zp_conf('watermark_image');

Code to add to i.php (located in /zen directory) below the unsharp mask routine:

//-- watermarking
			if ($perform_watermark == true && $thumb == false) {
			
			$watermark = imagecreatefrompng($watermark_image);
			imagealphablending($watermark, false);
			imagesavealpha($watermark, true);
			$watermark_width = imagesx($watermark);
			$watermark_height = imagesy($watermark);
			
			// Position Overlay in Bottom Right
			$dest_x = imagesx($newim) - $watermark_width;
			$dest_y = imagesy($newim) - $watermark_height;
			
			imagecopy($newim, $watermark, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height);
			imagedestroy($watermark);
			}
			//-- watermarking

4. Empty your cache folder (i.e. delete all the files in the folder) and you're done. Deleting all the files forces ZenPhoto to reprocess all the images and thumbnails, applying the watermark to correctly sized images.

Image Rating Hack

This hack allows you to have other people "rate" or vote on your images, giving each image a "star rating" system similar to this or this. This hack requires database modifications and other additions to ZenPhoto and will make upgrading more difficult later on. This hack was derived from this code and modified for use with ZenPhoto by an unknown party and is no longer maintained at its original location. You can grab the files from the wiki here. There is a ZenphotoForums post here which details some troubleshooting information.

This hack has been tested on versions 1.0.3, 1.0.5, and 1.0.5 + security fix.

Basic Instructions (pulled from forum article)

1. Obtain zip file copied from original funbox.ro archive (which is no longer there, files supplied above)
a. Read through the readme file included with the original archive. This is the original readme from the funbox.ro site.
2. Expand zip file to a temporary directory.
3. Edit the _config_rating.php file with your DB parameters to connect to your zenphoto DB
4. About line 38 in the _drawrating.php file, you'll want to edit the path to be the absolute path to your zenphoto installation.
5. Add these lines to your image.php file:

HEAD Section

<?php require('_drawrating.php'); ?>

<!-- Rating System -->
<script type="text/javascript" language="javascript" src="<?= $_zp_themeroot ?>/js/rating.js"></script>
<link rel="stylesheet" type="text/css" href="<?= $_zp_themeroot ?>/css/rating.css" />

BODY Section

<?php $id=$_zp_current_image->id; rating_bar($id,5); ?>

6. Copy the contents of the temporary directory (NOTE: with the exception of the index.php file) you created to your theme folder, and make sure the directories (images/css/js) stay intact. If you already have these folders, that's ok. 7. You will have to add some custom stuff to your DB:

CREATE TABLE 'ratings' (
'id' varchar(11) NOT NULL,
'total_votes' int(11) NOT NULL default '0',
'total_value' int(11) NOT NULL default '0',
'used_ips' longtext,
PRIMARY KEY ('id')
) TYPE=MyISAM AUTO_INCREMENT=3 ;

Note that you should be able to successfully put this information in your Images table as well with no issues. Just make sure in the config file (_config_rating.php) that the table is pointing to the correct one.

After this, it should work properly. Should you need help, ask a question at the ZenphotoForums or reference the forum article.

Hit Count Hack

IS INCLUDED IN ZENPHOTO 1.1

This hack allows the user to display how many times an image has been "hit" or displayed by viewers. This hack requires database modifications to the ZenPhoto database and should be used with caution. This hack could possibly make it more difficult to upgrade later if the database structure changes. You may use this hack in conjunction with the Most Popular Image Custom Function to display the most popular images in your theme's index page.

(More information is coming soon.)

Highest Rated Custom Function

This functions displays the highest rated images at the point where the function is called. The number is configurable, so you can display as many images as you prefer. Please note that this function requires the Image Rating Hack to be installed and working before being able to display images. This hack was originally designed by Anton and is no longer maintained at this link.

/* Show Highest Rated Images */
/* Modified from show_latest_images from http://anton.gektoras.lt/zenphoto/zenphoto-latest-images */


      function show_highest_rated($number) {
          $images = query_full_array("SELECT images.albumid, images.filename AS filename, images.title AS title, images.total_value AS totalvalue, images.total_votes as votes, albums.folder AS folder FROM ".prefix('images')." AS images, ".prefix('albums')." AS albums WHERE images.albumid = albums.id AND images.show = 1 ORDER BY (images.total_value/images.total_votes) DESC LIMIT $number");
          $size = '_'.zp_conf('thumb_size').'_cw'.zp_conf('thumb_crop_width').'_ch'.zp_conf('thumb_crop_height');

          foreach ($images as $image) {
              $filename = $image['filename'];
              $album = $image['folder'];
              $desc = $image['title'];
              $totalvalue = @number_format($image['totalvalue']/$image['votes'],2); 
             	echo '<li>';
                  if (zp_conf('mod_rewrite') == false) {
                      echo '<a href="'.WEBPATH.'/index.php?album='.$album.'&image='.$filename.'" title="'.$desc.' with a rating of '.$totalvalue.'">';
                  } else {
                      echo '<a href="'.WEBPATH.'/'.$album.'/'.$filename.'" title="'.$desc.' with a rating of '.$totalvalue.'">';
                  }

              echo '<img src="'.WEBPATH.'/zen/i.php?a='.$album.'&i='.$filename.'&s=thumb"></a>';
              echo '</li>';
          }
     }  

Most Popular Custom Function

INCLUDED IN ZENPHOTO 1.1.3

This function is designed for use with the Hit Count Hack, and displays the "most popular" images from your database based on how many times each image has been hit. This hack was originally designed by Anton and is no longer maintained at this link.

/* Show Most Popular Images */
/* Modified from show_latest_images from http://anton.gektoras.lt/zenphoto/zenphoto-latest-images */

      function show_most_popular($number) {
          $images = query_full_array("SELECT images.albumid, images.filename AS filename, images.title AS title, images.hit AS hit, albums.folder AS folder FROM ".prefix('images')." AS images, ".prefix('albums')." AS albums WHERE images.albumid = albums.id AND images.show = 1 ORDER BY images.hit DESC LIMIT $number");
          $size = '_'.zp_conf('thumb_size').'_cw'.zp_conf('thumb_crop_width').'_ch'.zp_conf('thumb_crop_height');

          foreach ($images as $image) {
              $filename = $image['filename'];
              $album = $image['folder'];
              $desc = $image['title'];
              $hit = $image['hit'];
             	echo '<li>';
                  if (zp_conf('mod_rewrite') == false) {
                      echo '<a href="'.WEBPATH.'/index.php?album='.$album.'&image='.$filename.'" title="'.$desc.' with '.$hit.' hit(s)">';
                  } else {
                      echo '<a href="'.WEBPATH.'/'.$album.'/'.$filename.'" title="'.$desc.' with '.$hit.' hit(s)">';
                  }

              echo '<img src="'.WEBPATH.'/zen/i.php?a='.$album.'&i='.$filename.'&s=thumb"></a>'; 
              echo '</li>';
          }
     }

Latest Images Custom Function

SHOW LATEST IMAGES IS INCLUDED IN ZENPHOTO 1.1 This function allows you to display the latest images you've added to your ZenPhoto database based on the ID number of the image. The number displayed is configurable in the code. This hack was originally designed by Anton and is no longer maintained at this link.

/* Show Latest Images */
/* http://anton.gektoras.lt/zenphoto/zenphoto-latest-images */


      function printLatestImages($number) {
          $images = query_full_array("SELECT images.albumid, images.filename AS filename, images.title AS title, albums.folder AS folder FROM ".prefix('images')." AS images, ".prefix('albums')." AS albums WHERE images.albumid = albums.id AND images.show = 1 ORDER BY images.id DESC LIMIT $number");
          $size = '_'.zp_conf('thumb_size').'_cw'.zp_conf('thumb_crop_width').'_ch'.zp_conf('thumb_crop_height');

          foreach ($images as $image) {
              $filename = $image['filename'];
              $album = $image['folder'];
              $desc = $image['title'];
             	echo '<li>';
                  if (zp_conf('mod_rewrite') == false) {
                      echo '<a href="'.WEBPATH.'/index.php?album='.$album.'&image='.$filename.'" title="'.$desc.'">';
                  } else {
                      echo '<a href="'.WEBPATH.'/'.$album.'/'.$filename.'" title="'.$desc.'">';
                  }
							/* echo '<img src="'.WEBPATH.'/cache/'.$album.'/'.$filename.$size.'.jpg"></a>'; */
              echo '<img src="'.WEBPATH.'/zen/i.php?a='.$album.'&i='.$filename.'&s=thumb"></a>';
              echo '</li>';
          }
     }

Latest Albums Custom Function

This hack allows you to display the latest albums added to your database based on the ID number. The number displayed is configurable in the code or when the function is called. This hack was originally designed by Anton and is no longer maintained at this link. You will need ZenPhoto version 1.1+ to use this.

/* Show Latest Albums */
/* http://anton.gektoras.lt/zenphoto/zenphoto-latest-albums */


      function printLatestAlbums($number) {
  $sql = "SELECT * FROM ". prefix("albums") ." ORDER BY id DESC LIMIT $number";
  $result = mysql_query($sql);
  while($r = mysql_fetch_array($result)) {
    if ($r['thumb']==NULL) {
      $image = getAlbumImage($r['id']);
    } else {
      $image = $r['thumb'];
    }
    echo '<li>';
    if (getOption('mod_rewrite') == false) {
      echo '<a href="'.WEBPATH.'/index.php?album='.$r['folder'].'" title="View album: '.$r['title'].'">';
      echo '<img src="'.WEBPATH.'/zen/i.php?a='.$r['folder'].'&i='.$image.'&s=thumb" alt="Test" /></a>';
      // echo '<h3><a href="'.WEBPATH.'/index.php?album='.$r['folder'].'" title="View album: '.$r['title'].'">'.$r['title'].'</a></h3>';
    } else {
      echo '<a href="'.WEBPATH.'/'.$r['folder'].'" title="View album: '.$r['title'].'">';
      echo '<img src="'.WEBPATH.'/'.$r['folder'].'/image/thumb/'.$image.'" alt="Test" /></a>';
      // echo '<h3><a href="'.WEBPATH.'/'.$r['folder'].'" title="View album: '.$r['title'].'">'.$r['title'].'</a></h3>';
    }
    echo '</li>'; 
   }
}

function getAlbumImage($id) {
  $sql = "SELECT * FROM ". prefix("images") ." WHERE albumid = $id";
  $result = mysql_query($sql);
  while($r = mysql_fetch_array($result)) {
    return $r['filename'];
  }
}

Random Image Custom Functions

INCLUDED IN ZENPHOTO 1.1

The3se functions allow you to display a random image from your gallery or subalbum anywhere the function is called. It is unknown who originally created the code, but it is used in a number of ZenphotoThemes, including Effervescence.

getRandomImage will pick an image from anywhere in your gallery.

getRandomAlbumImage will pick an image located somewhere in the chain of album folders of the current album.

printHeadingImage prints a custom sized image from the parameter passed. Pass one of the random image functions to print a random image. This routine safely handles missing images.

// Get Random Images 
 
function getRandomImages() {
    $result = query_single_row('SELECT '.prefix("images").'.filename,'.prefix("images").'.title, '.prefix("albums").'.folder FROM '.prefix("images").' INNER JOIN '.prefix("albums").' ON '.prefix("images").'.albumid = '.prefix("albums").'.id ORDER BY RAND() LIMIT 1');  
    $image = new Image(new Album(new Gallery(), $result['folder']), $result['filename']);
    return $image;
  }
  
function getURL($image) {
	if (zp_conf('mod_rewrite')) 
	{
		return WEBPATH . "/" . urlencode($image->getAlbumName()) . "/" . urlencode($image->name);
		} 
		else 
	{
		return WEBPATH . "/index.php?album=" . urlencode($image->getAlbumName()) . "&image=" . urlencode($image->name);
		}
} 

function getAlbumId() {
global $_zp_current_album;
if (!isset($_zp_current_album)) { return null; }
return $_zp_current_album->getAlbumId();
}

function getRandomImagesAlbum() {
$result = query_single_row("(SELECT ".prefix('images').".filename, ".prefix('images').".title, ".prefix('albums').".folder FROM ".prefix('images').
" INNER JOIN ".prefix('albums')." ON ".prefix('images').".albumid = ".prefix('albums').".id WHERE ".prefix('albums').".id = ".getAlbumId().
")UNION(SELECT ".prefix('images').".filename, ".prefix('images').".title, ".prefix('albums').".folder FROM ".prefix('images').
" INNER JOIN ".prefix('albums')." ON ".prefix('images').".albumid = ".prefix('albums').".id WHERE ".prefix('albums').
".parentid = ".getAlbumId().") ORDER BY RAND( ) LIMIT 1");
$image = new Image(new Album(new Gallery(), $result['folder']), $result['filename']);
return $image;
}

function printHeadingImage($randomImage) {
	echo '<div id="randomhead">';
	$id = getAlbumId();
	if ($randomImage->getFilename() == '') { 
	  echo '<a href="/zen/images/zen-logo.gif" title="Random Picture..."><img src ="/zen/images/zen-logo.gif" width=620 height=180 alt="No images" /></a>';
    } else {
	  $randomAlbum = $randomImage->getAlbum();
	  $randomAlt1 = $randomAlbum->getTitle();
      if ($randomAlbum->getAlbumId() <> $id) {
	    $randomAlbum = $randomAlbum->getParent();
		while (!is_null($randomAlbum) && ($randomAlbum->getAlbumId() <> $id)) {
		  $randomAlt1 = $randomAlbum->getTitle().":\n".$randomAlt1;
		  $randomAlbum = $randomAlbum->getParent();
		  }
		}
	  $randomAlt1 = str_replace('"', "''", $randomAlt1);
	  $randomImageURL = getURL($randomImage);
	  print "<a href='".$randomImageURL."' title='Random Picture...'><img src='".
	  $randomImage->getCustomImage(1000, 620, 180, 620, 180, 300, 300).
	  "' width=620 height=180 alt=".'"'.
	  $randomAlt1.":\n".$randomImage->getTitle().
	  '" /></a>';
    }
	echo '</div>';
}

Image and Album Counting Functions

SQL Counting Functions

/* SQL Counting Functions */
function show_subalbum_count() {
$sql = "SELECT COUNT(id) FROM ". prefix("albums") ." WHERE parentid <> \"NULL\"";
$result = query($sql);
$count = mysql_result($result, 0);
echo $count;
}
function show_sub_count_index() {
echo getNumSubalbums();
}

Image counter for image.php

A BETTER COUNTER IS INCLUDED IN ZENPHOTO 1.1 Developed by acrylian Prints a image counter like "Image 1 of 20 Images". Use the parameters for the text to be shown before, between and after the numbers. Works best if you set a sort order to your images even if you did not sort anything.

function printImageCounter($text1,$separator,$text2) {
$id = getIDforAlbum();
		
// check if album images have sort_order. if not the id is take as sorting method. 
$sql = "SELECT sort_order FROM ". prefix('images') ." WHERE albumid = $id LIMIT 1,1";
$result = mysql_query($sql);
$check = mysql_result($result, 0);

if ($check === null)
	{ $order = "id";  }
else 
	{ $order = "sort_order"; }
	
//and now the image counter
$sql = "SELECT title FROM ". prefix('images') ." WHERE albumid = $id ORDER BY $order";
$result = mysql_query($sql);
$number = 0; 	
while($row = mysql_fetch_array($result))
	{
	$number++;
		  	 	
	if (getImageTitle() === $row['title']) // check which is the current image				 
		{ echo $text1 . $number . $separator . getNumImages() . $text2; }
	
		
	}
  }

Album Menu Functions

Album menu functions for the official 1.0.8.2 zenphoto release

Developed by acrylian

<?php	

/* printAlbumMenu Custom Function 1.1 for official zenphoto 1.0.8.2 installs WITHOUT subalbum sorting
What's new:
- Option for album list or a drop down jump menu if you want to save space 
- Displays the number of images in the album (like e.g. wordpress does with articles)
- Option for disabling the counter
- Parameters for CSS-Ids for styling, separate ones for main album and subalbums
- Renamed the function name from show_album_menu() to more zp style printAlbumMenu()

IMPORTANT: 
This version lists main albums by sort order. So I recommend that you always set a sort order to your albums, even if you have not sorted anything. 
Subalbums are listed by title because the missing subalbum sorting in the 1.0.8.2 release.

Usage:
Copy the function either to your custom_functions.php or include it directly in the head of your theme files:
<?php require_once("print_album_menu-official.php"); ?>

Place where ever you want to show the menu:
<?php printAlbumMenu($option1,$option2,$id1,$active1,$id2,$active2); ?>

$option1 = 
"list" for the menu as a list like in wordpress 
"jump" for a space saving jump menu
This option must be set.

$option2 = 
"count" shows the number of images in the album in brackets behind the album name
"" = for no image numbers

Example: If you want a list without counter write this:
<?php printAlbumMenu("list",""); ?>

The CSS parameters are optional if you want specific CSS-ID to style. If not leave blank.
$id1 = Insert here the ID for the main album menu like this: "mainmenu".
$active1 = Insert the ID for the currently active album link
$id2 = Insert here the ID for the sub album sub menu if you want to style it differently as the main menu
$active2 = Insert the ID for the currently active subalbum link. 
Of course these parameters are useless with the jump menu option.

*/

function printAlbumMenu($option1,$option2,$id1,$active1,$id2,$active2) {

// check if parameters are used
if ($id1 != "") { $id1 = " id='".$id1."'"; }
if ($active1 != "") { $active1 = " id='".$active1."'"; }
if ($id2 != "") { $id2 = " id='".$id2."'"; }
if ($active2 != "") { $active2 = " id='".$active2."'"; }

// main album query
$sql = "SELECT id, parentid, folder, title FROM ". prefix('albums') ." WHERE parentid is NULL ORDER BY sort_order";
$result = mysql_query($sql);

while($row = mysql_fetch_array($result))
    {
    $number++;
    $idnr[$number] = $row['id']; 
	$parentid[$number] = $row['parentid']; 
	$folder[$number] = $row['folder'];
	$title[$number] = $row['title'];

	switch($option2) {
	case "count": 
	// count images in the main albums
	case "count": 
	// count images in the albums
	$sql = "SELECT COUNT(id) FROM ". prefix('images') ." WHERE albumid = $idnr[$number]";
	$result2 = query($sql);
	$count = mysql_result($result2, 0);
	$imagecount[$number] = " (".$count.")";
	}
	}
	
// sub album query
$sql = "SELECT id, parentid, folder, title FROM ". prefix('albums') ." WHERE parentid ORDER BY title";
$result = mysql_query($sql);

while($row = mysql_fetch_array($result))
    {
    $subnumber++;
    $subidnr[$subnumber] = $row['id']; 
	$subparentid[$subnumber] = $row['parentid']; 
	$subfolder[$subnumber] = $row['folder'];
	$subtitle[$subnumber] = $row['title'];

	switch($option2) {
	case "count": 
	// count images in the albums
	$sql = "SELECT COUNT(id) FROM ". prefix('images') ." WHERE albumid = $subidnr[$subnumber]";
	$result2 = query($sql);
	$count2 = mysql_result($result2, 0);
	$subimagecount[$subnumber] = " (".$count2.")";
	}
	}

switch($option1) {

/*** LIST MENU SECTION ***/
case "list": 
// open main albums list
echo "<ul".$id1.">"; 

if (getAlbumTitle() === false) // for a full menu here the Gallery index link
	{ echo "<li><strong><a href='".getGalleryIndexURL()."' title='Gallery Index'>Gallery Index</a></strong></li>"; }
else
	{ echo "<li><a href='".getGalleryIndexURL()."' title='Gallery Index'>Gallery Index</a></li>"; }

// main album loop start	
for ($nr = 1;$nr <= $number; $nr++) 
	{	 	
		if ($title[$nr] === getAlbumTitle())// if check if the album is the current album
			{ echo "<li".$active1."><strong><a href='index.php?album=".$folder[$nr]."'>".$title[$nr]."</a></strong>".$imagecount[$nr]; }
		else 
			{ echo "<li><a href='index.php?album=".$folder[$nr]."'>".$title[$nr]."</a>".$imagecount[$nr]; }
		
			for ($nr2 = 1;$nr2 <= $subnumber; $nr2++) // Open subalbum sublist if there is at least one subalbum that has a parent album. 
				{ if ($idnr[$nr] === $subparentid[$nr2])  {	echo "<ul".$id2.">"; break; } }
				
				for ($nr2 = 1;$nr2 <= $subnumber; $nr2++) // Subalbum loop start 
					{ 
				
						if ($idnr[$nr] === $subparentid[$nr2]) // print avaiable subalbums to current album
							{ 
							if ($subtitle[$nr2] === getAlbumTitle()) // check if active album
								{ echo "<li".$active2."><strong><a href='index.php?album=".$subfolder[$nr2]."'>".$subtitle[$nr2]."</a></strong>".$subimagecount[$nr2]."</li>"; }
							else
								{ echo "<li><a href='index.php?album=".$subfolder[$nr2]."'>".$subtitle[$nr2]."</a>".$subimagecount[$nr2]."</li>"; }
							}

					} 
				for ($nr2 = 1;$nr2 <= $subnumber; $nr2++) // Close subalbum sublist
				{ if ($idnr[$nr] === $subparentid[$nr2])  {	echo "</ul>"; break; } } 
			
			echo "</li>"; // album item close
		
	// main album loop end
	}
	
	// if you want to add an "About"-link or whatever you should add this here

// close main album list
echo "</ul>";


/*** JUMP MENU SECTION ***/
case "jump": 
?>

<form name ="AutoListBox">
<p><select name="ListBoxURL" size="1" language="javascript" onchange="gotoLink(this.form);"
<option value="<?php getGalleryIndexURL(); ?>">Gallery Index </option>

<?php
// main album loop start	
for ($nr = 1;$nr <= $number; $nr++) 
	{	 	
		
		echo "<option value='index.php?album=".$folder[$nr]."'>".$title[$nr].$imagecount[$nr]."</option>"; 
					
				for ($nr2 = 1;$nr2 <= $subnumber; $nr2++) // Subalbum loop start 
					{ 
				
						if ($idnr[$nr] === $subparentid[$nr2]) // print avaiable subalbums to current album
							{ 
							echo "<option value='index.php?album=".$subfolder[$nr2]."'>".$title[$nr].": ".$subtitle[$nr2].$subimagecount[$nr2]."</option>"; 
							}
					} 

	// main album loop end
	}
; ?>
<option selected>Select an album</option>
</select></p>

<script language="JavaScript">
<!--
function gotoLink(form) {
   var OptionIndex=form.ListBoxURL.selectedIndex;
  parent.location = form.ListBoxURL.options[OptionIndex].value;}
//-->
</script>

</form>

<?php }
} // switch end
; ?>

Album menu function

ALBUM MENU IS INCLUDED IN ZENPHOTO 1.1 Developed by acrylian

<?php	

/* printAlbumMenu Custom Function 1.2 for the zenphoto_updated community build WITH subalbum sorting

1.2 What's new: Now works with sbillard's album publishing function.

1.1. What's new:
- Option for album list or a drop down jump menu if you want to save space 
- Displays the number of images in the album (like e.g. wordpress does with articles)
- Option for disabling the counter
- Parameters for CSS-Ids for styling, separate ones for main album and subalbums
- Renamed the function name from show_album_menu() to more zp style printAlbumMenu()

IMPORTANT: 
This version lists all albums by sort order. Thanks to sbillard's new subalbum functions and aitf311's zenphoto_updated build we now can
sort subalbums.
Therefore I suggest you use the zenphoto_updated package even if it is not the official version and if you want the list displayed 
correctly set a sort order to your albums and subalbums even if you have not sorted anything. 

Usage:
Copy the function either to your custom_functions.php or include it directly in the head of your theme files:
<?php require_once("print_album_menu-updated.php"); ?>

Place where ever you want to show the menu:
<?php printAlbumMenu($option1,$option2,$id1,$active1,$id2,$active2); ?>

$option1 = 
"list" for the menu as a list like in wordpress 
"jump" for a space saving jump menu
This option must be set.

$option2 = 
"count" shows the number of images in the album in brackets behind the album name
"" = for no image numbers

Example: If you want a list without counter write this:
<?php printAlbumMenu("list",""); ?>

The CSS parameters are optional if you want specific CSS-ID to style. If not leave blank.
$id1 = Insert here the ID for the main album menu like this: "mainmenu".
$active1 = Insert the ID for the currently active album link
$id2 = Insert here the ID for the sub album sub menu if you want to style it differently as the main menu
$active2 = Insert the ID for the currently active subalbum link. 
Of course these parameters are useless with the jump menu option.

*/
function printAlbumMenu($option1,$option2,$id1,$active1,$id2,$active2) {

// check if parameters are used
if ($id1 != "") { $id1 = " id='".$id1."'"; }
if ($active1 != "") { $active1 = " id='".$active1."'"; }
if ($id2 != "") { $id2 = " id='".$id2."'"; }
if ($active2 != "") { $active2 = " id='".$active2."'"; }

// main album query
$sql = "SELECT id, parentid, folder, title FROM ". prefix('albums') ." WHERE `show` = 1 ORDER BY sort_order";
$result = mysql_query($sql);

while($row = mysql_fetch_array($result))
    {
    $number++;
    $idnr[$number] = $row['id']; 
	$parentid[$number] = $row['parentid']; 
	$folder[$number] = $row['folder'];
	$title[$number] = $row['title'];

	switch($option2) {
	case "count": 
	// count images in the albums
	$sql = "SELECT COUNT(id) FROM ". prefix('images') ." WHERE albumid = $idnr[$number]";
	$result2 = query($sql);
	$count = mysql_result($result2, 0);
	$imagecount[$number] = " (".$count.")";
	}
	}
	
switch($option1) {

/*** LIST MENU SECTION ***/
case "list": 
// open main albums list
echo "<ul>";

// here the Gallery index link
if (getAlbumTitle() === false) 
	{ echo "<li><strong><a href='".getGalleryIndexURL()."' title='Gallery Index'>Gallery Index</a></strong></li>"; }
else
	{ echo "<li><a href='".getGalleryIndexURL()."' title='Gallery Index'> - Gallery Index</a></li>"; }

/* main album loop start */	
for ($nr = 1;$nr <= $number; $nr++)
	{
 	if ($parentid[$nr] === NULL ) // check if album is main album
		{
		// if check if the album is the currently selected album
		if ($title[$nr] === getAlbumTitle()) 
			{ echo "<li><strong><a href='index.php?album=".$folder[$nr]."'>".$title[$nr]."</a>".$imagecount[$nr]."</strong>"; }
		else 
			{ echo "<li><a href='index.php?album=".$folder[$nr]."'>".$title[$nr]."</a>".$imagecount[$nr]; }
		
		/*  open sublist for subalbums */
		// check if any subalbums are available. If then open sublist for subalums
		for ($nr2 = 1;$nr2 <= $number; $nr2++)
			{	     	 
			if ($idnr[$nr] === $parentid[$nr2]) 
			   { echo "<ul>"; break; }
			}  
		// subalbum subloop start			
		for ($nr2 = 1;$nr2 <= $number; $nr2++)
			{	 
			// check if any subalbums are available		     	 
			if ($idnr[$nr] === $parentid[$nr2]) 
			   { 			  
				// if check if the subalbum is the currently selected album
			    if ($title[$nr2] === getAlbumTitle()) 
					{ echo "<li><strong><a href='index.php?album=".$folder[$nr2]."'>".$title[$nr2]."</a>".$imagecount[$nr2]."</strong></li>"; }
				else 
					{ echo "<li><a href='index.php?album=".$folder[$nr2]."'>".$title[$nr2]."</a>".$imagecount[$nr2]."</li>"; }   					   			
				}				
			} // subalbum loop end 
		// close subalbums sublist (only printed if there are subalbums) 
		for ($nr2 = 1;$nr2 <= $number; $nr2++)
			{	     	 
			if ($idnr[$nr] === $parentid[$nr2]) 
			   { echo "</ul>"; break; }
			}  
		echo "</li>"; // close main album list item
		} // close main album check
	} // main album loop end
echo "</ul>"; // close main album list
break; 

/*** JUMP MENU SECTION ***/
case "jump": 
?>

<form name ="AutoListBox">
<p><select name="ListBoxURL" size="1" language="javascript" onchange="gotoLink(this.form);"
<option value="<?php echo getGalleryIndexURL(); ?>">Gallery Index</option>

<?php
/* main album loop start */	
for ($nr = 1;$nr <= $number; $nr++)
	{

	
 	if ($parentid[$nr] === NULL ) // check if album is main album 
		{
		// if check if the album is the currently selected album
			echo "<option value='index.php?album=".$folder[$nr]."'>".$title[$nr].$imagecount[$nr]."</option>"; 
		// open sublist for subalbums
		// subalbum subloop start			
		for ($nr2 = 1;$nr2 <= $number; $nr2++)
			{	 
				     	 
			if ($idnr[$nr] === $parentid[$nr2]) // check if any subalbums are available and print them	 
			   { 
				echo "<option value='index.php?album=".$folder[$nr2]."'>".$title[$nr].": ".$title[$nr2].$imagecount[$nr2]."</option>";		  					   			
				}
							
			} // subalbum loop end 		
		
		} // close main album check	
	} // main album loop end

; ?>
<option selected>Choose an album</option>
</select></p>

<script language="JavaScript">
<!--
function gotoLink(form) {
   var OptionIndex=form.ListBoxURL.selectedIndex;
  parent.location = form.ListBoxURL.options[OptionIndex].value;}
//-->
</script>

</form>

<?php } // switch end
} 

; ?>

Album Zip

ALBUM ZIPPING IS INCLUDED IN ZENPHOTO 1.1 A patch that allows you to download a zip file of a particular album.

Save the patch into zp108_zip.diff. Then move the file into your zenphoto directory. You should be able to stick it in your zenphoto directory and run patch -p1 < zp108_zip.diff

Then add <?php echo printAlbumZip(); ?> to your favorite theme/album.php file. Example at http://mu.thenullpointer.net/

--- zenphoto_orig/index.php	2006-11-09 01:28:08.000000000 -0800
+++ zenphoto/index.php	2007-04-23 18:39:46.000000000 -0700
@@ -11,7 +11,9 @@ header ('Content-Type: text/html; charse
 if (in_context(ZP_IMAGE)) {
   include("$themepath/$theme/image.php");
 } else if (in_context(ZP_ALBUM)) {
-  include("$themepath/$theme/album.php");
+  if(isset($_GET['zipfile']) && is_dir(realpath('albums/' . $_GET['album']))){
+    createAlbumZip($_GET['album']);
+  } else { include("$themepath/$theme/album.php"); }
 } else if (in_context(ZP_INDEX)) {
   if (isset($_GET['p'])) {
     $page = str_replace(array('/','\\','.'), '', $_GET['p']);
--- zenphoto_orig/zen/functions.php	2007-03-01 15:31:22.000000000 -0800
+++ zenphoto/zen/functions.php	2007-04-23 18:39:43.000000000 -0700
@@ -498,4 +498,25 @@ function zp_mail($subject, $message, $he
   }
 }
 
+function createAlbumZip($album){
+  $rp = realpath('albums/' . $album) . '/';
+  $p = $album . '/';
+  if(is_dir($rp)){
+    include_once('zip/zipfile.php');
+    $z = new zipfile();
+    $z->add_dir($p);
+    if ($dh = opendir($rp)) {
+      while (($file = readdir($dh)) !== false) {
+        if($file != '.' && $file != '..'){
+          $z->add_file(file_get_contents($rp . $file), $p . $file);
+        }
+      }
+      closedir($dh);
+    }
+    header('Content-Type: application/zip');
+    header('Content-Disposition: attachment; filename="' . $album . '.zip"'); 
+    echo $z->file();
+  }
+}
+
 ?>
--- zenphoto_orig/zen/template-functions.php	2007-02-26 10:12:55.000000000 -0800
+++ zenphoto/zen/template-functions.php	2007-04-23 18:39:44.000000000 -0700
@@ -423,6 +423,14 @@ function next_image() { 
   }
 }
 
+function printAlbumZip(){
+  global $_zp_current_album;
+  echo'<a href="' . rewrite_path("/" . pathurlencode($_zp_current_album->name),
+      "/index.php?album=" . urlencode($_zp_current_album->name)) . 
+    '?zipfile" title="Download Zip of the Album">Download a zip file ' .
+    'of this album</a>';
+}
+
 
 /*** Image Context ************************/
 /******************************************/
--- zenphoto_orig/zen/zip/zipfile.php	1969-12-31 16:00:00.000000000 -0800
+++ zenphoto/zen/zip/zipfile.php	2007-04-23 18:39:45.000000000 -0700
@@ -0,0 +1,188 @@
+<?php  
+/* 
+Zip file creation class 
+makes zip files on the fly... 
+
+use the functions add_dir() and add_file() to build the zip file; 
+see example code below 
+
+by Eric Mueller 
+http://www.themepark.com 
+
+v1.1 9-20-01 
+  - added comments to example 
+
+v1.0 2-5-01 
+
+initial version with: 
+  - class appearance 
+  - add_file() and file() methods 
+  - gzcompress() output hacking 
+by Denis O.Philippov, webmaster@atlant.ru, http://www.atlant.ru 
+
+*/  
+
+// official ZIP file format: http://www.pkware.com/appnote.txt 
+
+class zipfile   
+{   
+
+    var $datasec = array(); // array to store compressed data 
+    var $ctrl_dir = array(); // central directory    
+    var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00"; //end of Central directory record 
+    var $old_offset = 0;  
+
+    function add_dir($name)    
+
+    // adds "directory" to archive - do this before putting any files in directory! 
+    // $name - name of directory... like this: "path/" 
+    // ...then you can add files using add_file with names like "path/file.txt" 
+    {   
+        $name = str_replace("\\", "/", $name);   
+
+        $fr = "\x50\x4b\x03\x04";  
+        $fr .= "\x0a\x00";    // ver needed to extract 
+        $fr .= "\x00\x00";    // gen purpose bit flag 
+        $fr .= "\x00\x00";    // compression method 
+        $fr .= "\x00\x00\x00\x00"; // last mod time and date 
+
+        $fr .= pack("V",0); // crc32 
+        $fr .= pack("V",0); //compressed filesize 
+        $fr .= pack("V",0); //uncompressed filesize 
+        $fr .= pack("v", strlen($name) ); //length of pathname 
+        $fr .= pack("v", 0 ); //extra field length 
+        $fr .= $name;   
+        // end of "local file header" segment 
+
+        // no "file data" segment for path 
+
+        // "data descriptor" segment (optional but necessary if archive is not served as file) 
+        $fr .= pack("V",$crc); //crc32 
+        $fr .= pack("V",$c_len); //compressed filesize 
+        $fr .= pack("V",$unc_len); //uncompressed filesize 
+
+        // add this entry to array 
+        $this -> datasec[] = $fr;  
+
+        $new_offset = strlen(implode("", $this->datasec));  
+
+        // ext. file attributes mirrors MS-DOS directory attr byte, detailed 
+        // at http://support.microsoft.com/support/kb/articles/Q125/0/19.asp 
+
+        // now add to central record 
+        $cdrec = "\x50\x4b\x01\x02";  
+        $cdrec .="\x00\x00";    // version made by 
+        $cdrec .="\x0a\x00";    // version needed to extract 
+        $cdrec .="\x00\x00";    // gen purpose bit flag 
+        $cdrec .="\x00\x00";    // compression method 
+        $cdrec .="\x00\x00\x00\x00"; // last mod time & date 
+        $cdrec .= pack("V",0); // crc32 
+        $cdrec .= pack("V",0); //compressed filesize 
+        $cdrec .= pack("V",0); //uncompressed filesize 
+        $cdrec .= pack("v", strlen($name) ); //length of filename 
+        $cdrec .= pack("v", 0 ); //extra field length    
+        $cdrec .= pack("v", 0 ); //file comment length 
+        $cdrec .= pack("v", 0 ); //disk number start 
+        $cdrec .= pack("v", 0 ); //internal file attributes 
+        $ext = "\x00\x00\x10\x00";  
+        $ext = "\xff\xff\xff\xff";   
+        $cdrec .= pack("V", 16 ); //external file attributes  - 'directory' bit set 
+
+        $cdrec .= pack("V", $this -> old_offset ); //relative offset of local header 
+        $this -> old_offset = $new_offset;  
+
+        $cdrec .= $name;   
+        // optional extra field, file comment goes here 
+        // save to array 
+        $this -> ctrl_dir[] = $cdrec;   
+
+          
+    }  
+
+
+    function add_file($data, $name)    
+
+    // adds "file" to archive    
+    // $data - file contents 
+    // $name - name of file in archive. Add path if your want 
+
+    {   
+        $name = str_replace("\\", "/", $name);   
+        //$name = str_replace("\\", "\\\\", $name); 
+
+        $fr = "\x50\x4b\x03\x04";  
+        $fr .= "\x14\x00";    // ver needed to extract 
+        $fr .= "\x00\x00";    // gen purpose bit flag 
+        $fr .= "\x08\x00";    // compression method 
+        $fr .= "\x00\x00\x00\x00"; // last mod time and date 
+
+        $unc_len = strlen($data);   
+        $crc = crc32($data);   
+        $zdata = gzcompress($data);   
+        $zdata = substr( substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug 
+        $c_len = strlen($zdata);   
+        $fr .= pack("V",$crc); // crc32 
+        $fr .= pack("V",$c_len); //compressed filesize 
+        $fr .= pack("V",$unc_len); //uncompressed filesize 
+        $fr .= pack("v", strlen($name) ); //length of filename 
+        $fr .= pack("v", 0 ); //extra field length 
+        $fr .= $name;   
+        // end of "local file header" segment 
+          
+        // "file data" segment 
+        $fr .= $zdata;   
+
+        // "data descriptor" segment (optional but necessary if archive is not served as file) 
+        $fr .= pack("V",$crc); //crc32 
+        $fr .= pack("V",$c_len); //compressed filesize 
+        $fr .= pack("V",$unc_len); //uncompressed filesize 
+
+        // add this entry to array 
+        $this -> datasec[] = $fr;  
+
+        $new_offset = strlen(implode("", $this->datasec));  
+
+        // now add to central directory record 
+        $cdrec = "\x50\x4b\x01\x02";  
+        $cdrec .="\x00\x00";    // version made by 
+        $cdrec .="\x14\x00";    // version needed to extract 
+        $cdrec .="\x00\x00";    // gen purpose bit flag 
+        $cdrec .="\x08\x00";    // compression method 
+        $cdrec .="\x00\x00\x00\x00"; // last mod time & date 
+        $cdrec .= pack("V",$crc); // crc32 
+        $cdrec .= pack("V",$c_len); //compressed filesize 
+        $cdrec .= pack("V",$unc_len); //uncompressed filesize 
+        $cdrec .= pack("v", strlen($name) ); //length of filename 
+        $cdrec .= pack("v", 0 ); //extra field length    
+        $cdrec .= pack("v", 0 ); //file comment length 
+        $cdrec .= pack("v", 0 ); //disk number start 
+        $cdrec .= pack("v", 0 ); //internal file attributes 
+        $cdrec .= pack("V", 32 ); //external file attributes - 'archive' bit set 
+
+        $cdrec .= pack("V", $this -> old_offset ); //relative offset of local header 
+//        echo "old offset is ".$this->old_offset.", new offset is $new_offset<br>"; 
+        $this -> old_offset = $new_offset;  
+
+        $cdrec .= $name;   
+        // optional extra field, file comment goes here 
+        // save to central directory 
+        $this -> ctrl_dir[] = $cdrec;   
+    }  
+
+    function file() { // dump out file    
+        $data = implode("", $this -> datasec);   
+        $ctrldir = implode("", $this -> ctrl_dir);   
+
+        return    
+            $data.   
+            $ctrldir.   
+            $this -> eof_ctrl_dir.   
+            pack("v", sizeof($this -> ctrl_dir)).     // total # of entries "on this disk" 
+            pack("v", sizeof($this -> ctrl_dir)).     // total # of entries overall 
+            pack("V", strlen($ctrldir)).             // size of central dir 
+            pack("V", strlen($data)).                 // offset to start of central dir 
+            "\x00\x00";                             // .zip file comment length 
+    }  
+}   
+
+?>

Utilities

Regenerate thumbnails in cache

PRE-CACHING IS INCLUDED IN ZENPHOTO 1.1

<?php
/* This template is used to generate cache images. Running it will process the entire gallery,
	supplying an album name (ex: loadAlbums.php?album=newalbum) will only process the album named. */
define('OFFSET_PATH', true);
require_once("template-functions.php");
require_once("admin-functions.php");

function loadAlbum($album) {
  global $_zp_current_album;
  $subalbums = $album->getSubAlbums();
  foreach ($subalbums as $folder) {
    $subalbum = new Album($album, $folder);
    $count = $count + loadAlbum($subalbum);
  }
  $_zp_current_album = $album;
  if (getNumImages() > 0) {
    echo "<br/>" . $album->name . "{";
    while (next_image(true)) {
      echo '<img src="' . getImageThumb() . '" height="8" width="8" /> | <img src="' . getDefaultSizedImage() . '" height="20" width="20" />' . "\n";
      $count++;
    }
    echo "}<br/>\n";
  }
  return $count;
}

if (!zp_loggedin()) {
  printLoginForm();
  exit(); 
} else {
  printAdminHeader();
  printLogoAndLinks();
  printTabs();
  echo '<div id="content">';


  global $_zp_gallery;
  $count = 0;
  
  if (isset($_GET['album'])) {
    $folder = strip($_GET['album']);
    echo '<h2>Refreshing cache for $folder</h2>';
    $album = new Album($album, $folder);
    $count = loadAlbum($album);
  } else {
    echo '<h2>Refreshing cache for Gallery</h2>';
    $albums = $_zp_gallery->getAlbums();
      foreach ($albums as $folder) {
      $album = new Album($album, $folder);
      $count = $count + loadAlbum($album);
    }
  }
  echo "<br/>Finished: Total of $count images.";
  echo '</div>';

  printAdminFooter();
}
?>
</body>
</html>

Client side optimization

AS per yahoo! optimization guidelines http://developer.yahoo.com/yslow/ for making Zenphoto faster at client-side, here are some hacks.

FIRST: Edit .htaccsss file and add the following lines.

<ifmodule mod_expires.c>
<filesmatch "\.(jpg|gif|png|css|js)$">
ExpiresActive on
ExpiresDefault "access plus 1 year"
</filesmatch>
</ifmodule>

FileETag None

That will make your image, javascript,css files cache lifetime 1 year+ and Removes Etags, If Apache WebServer? has attached any. So that webpage header size can be reduced.

SECOND: If your web server is supporting gzip compression, edit index.php and add following line at Top of the page.

header("Vary: Accept-Encoding");
if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) ob_start("ob_gzhandler");
else ob_start();

So, Web server will deliver entire webpage as gzip compression format to the browser. So, network bandwidth can be reduced.

THIRD: Convert your all JavaScript?, CSS files into PHP code (Make simple echo statement) and follow SECOND step for them. Finally, load them in templates. That will make your JavaScript?, css gzip-compressed & faster deliverable.

SampleJavaScript?.js.php

if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) ob_start("ob_gzhandler");
else ob_start();
header("content-type: application/x-javascript; charset: UTF-8");
$gd = "Expires: Monday, January 18, 2038 12:44:06 AM";
header($gd);

zen.css.php

<?php
if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) ob_start("ob_gzhandler");
else ob_start();
header("Content-type: text/css; charset: UTF-8");
$gd = "Expires: Sun, 1 Jan 2017 05:00:00 GMT";
header($gd);
?>

image.php

<link rel="stylesheet" href="/themes/sterile/zen.css.php"></link>

try to put javascript before </body> image.php

<script src="/themes/sterile/SampleJavaScript.js.php"></script>

So, The loading of web-page won't blocked untill javascript is loaded.

You can check the performance of the site http://actress.bollysite.com and test It with Firefox firebug-Yslow plug-in.


Server side optimization

Database result cache

As we know, the database will use many resource and time to query the information of Image. If we want to save time, we could do optimization with cache the result. What I use is two functions, you could use other PHP cache library too. The code I use is version 1.13. !! Please note: because I don't open the comment function, so it may have problem with comment.

1, Edit the functions-db.php under zp-core directory.

2, Search function query_single_row, change it to:

function query_single_row($sql) 
{
  if ($cache = get_cache($sql))
	  return $cache;

  $result = query($sql);
  if ($result) {
    $singlerow =  mysql_fetch_assoc($result);

	store_cache($sql, $singlerow);
	return $singlerow;
  } else {
    return false;
  }
}

3, Search function query_full_array, change it to:

function query_full_array($sql) 
{
  if ($cache = get_cache($sql))
	  return $cache;

  $result = query($sql);
  if ($result) {
    $allrows = array();
    while ($row = mysql_fetch_assoc($result))
      $allrows[] = $row;

	store_cache($sql, $allrows);
    return $allrows;
  } else {
    return false;
  }
}

4, Last thing, add two functions in functions-db.php. You could change "cachedirectory" to your cache directory, and change $cache_time_out to the expire time you want. 30* 24 * 3600 is 30 days.

function store_cache($query, $result_cache)
{
	if (preg_match("/^(insert|delete|update|replace)\s+/i",$query))
		return;
	if (stristr($query, "ORDER BY RAND"))
		return;

	$cache_file = 'cachedirectory/'. md5($query);
	error_log (serialize($result_cache), 3, $cache_file);
}

function get_cache($query)
{
        $cache_time_out = 30* 24 * 3600;

	if (preg_match("/^(insert|delete|update|replace)\s+/i",$query))
		return;
	if (stristr($query, "ORDER BY RAND"))
		return;

	$cache_file = 'cachedirectory/'. md5($query);
	if ( file_exists($cache_file) )
	{
		if ( (time() - filemtime($cache_file)) > $cache_time_out )
		{
			unlink($cache_file);
		}
		else
		{
			$result_cache = unserialize(file_get_contents($cache_file));
			return $result_cache;
		}
	}
}


Copy this (or download the attachment) to your "zen" folder. Then point your browser at the file. This will regenerate all the thumbnails for the gallery. To regenerate just a subalbum, add a query parameter to the URL for the album name: ?album=<album name>.

Attachments