Zenphoto – Hide ‘Dot’ Files

Pretty annoying there’s no easy built in way to not show images based on hidden file attribute. Best I could determine, one must implement dos “attrib” command or equivalent from PHP “exec” on every file in order to respect hidden file attribute (that sounds like way too much overhead to add on to my already pokey image gallery). I’m floored file attribs aren’t part of a more robust PHP file object but I guess this kind of stuff is hard to support in an OS neutral way. I decided to run with renaming to .file.jpg and then hide those. Using ‘dot’ to represent hidden is very standard and pretty straightforward to accomplish the hiding via Zenphoto’s image_filter plugin API. Zenphoto team provides generic image_filter sample to start with here: http://www.zenphoto.org/news/filter-file_searches My dot file specific implementation (zero rocket science here): https://docs.google.com/open?id=0B5htuLP66oWlOFRnSElPVktlVTA Drop php file in your zenphotoplugins folder and enable via admin > plugins tab.

Add Video overlay Icon to Zenphoto Thumbnails

  • Edit zp-coretemplate-functions.php, look for printCustomSizedImage() function and edit it to switch to using style=”background:url(‘’)” as shown below.
  • Configure overlay image:
    • Start with a completely transparent png the same height and width as your thumbnails (168 pixels in my case)
    • Find a preferred icon for the image (perhaps via Google Image Search) and paste that into your transparent background…
    • downsizing and hiding it in a corner is a nice effect
    • as well as adding a little bit of transparency

For example:

function printCustomSizedImage($alt, $size, $width=NULL, $height=NULL, $cropw=NULL, $croph=NULL, $cropx=NULL, $cropy=NULL, $class=NULL, $id=NULL, $thumbStandin=false, $effects=NULL) {
    global $_zp_current_image;
    if (is_null($_zp_current_image)) return;
    if (!$_zp_current_image->getShow()) {
        $class .= " not_visible";
    $album = $_zp_current_image->getAlbum();
    $pwd = $album->getPassword();
    if (!empty($pwd)) {
        $class .= " password_protected";
    if ($size) {
        $dims = getSizeCustomImage($size);
        $sizing = ' width="'.$dims[0].'" height="'.$dims[1].'"';
    } else {
        $sizing = '';
        if ($width) $sizing .= ' width="'.$width.'"';
        if ($height) $sizing .= ' height="'.$height.'"';
    if ($id) $id = ' id="'.$id.'"';
    if ($class) $id .= ' class="'.$class.'"';
    if (isImagePhoto() || $thumbStandin) {
                      /* $html = '<img src="' . pathurlencode(getCustomImageURL($size, $width, $height, $cropw, $croph, $cropx, $cropy, $thumbStandin, $effects)) . '"' . */
        $html = '<img src="' . VideoOverlayIcon(pathurlencode(getCustomImageURL($size, $width, $height, $cropw, $croph, $cropx, $cropy, $thumbStandin, $effects))) . '"' .
            ' alt="' . html_encode($alt) . '"' .
            $id .
            $sizing .
            ' />';
        $html = zp_apply_filter('custom_image_html', $html, $thumbStandin);
        echo $html;
    } else { // better be a plugin
        echo $_zp_current_image->getBody($width, $height);
function VideoOverlayIcon($url)
    if (isImageVideo()) return '/Photos/albums/Video_Overlay.png" style="background:url(' . $url . ')';
    else return $url;

Self-hosting Zenphoto on Windows 7 (IIS7, PHP & MySQL)

I really like ZenPhoto – it’s a solid photo gallery with an easy point and click web admin GUI.
The main thing I dig is that i can point it at my main photos folder on my hard drive (via a quick symbolic link) and it goes to town dynamically publishing whatever I drop there without any other fiddling… that’s photo sharing nirvana if you ask me. [Update: 25 Oct 2010] After running the gallery for a couple days I’d have to say Apache did a better job at popping the pages back than what I’ve got setup under IIS so far… maybe Apache just deals with these more CGI oriented modules better than IIS can for some fundamental reason… I do have PHP “FastCGI” enabled for IIS… any performance tips would be greatly appreciated 🙂 I’ll have to go find some trace tools to see where it’s spending most of its time.  I guess it could still be caching images since ZenPhoto pulls a new random image for the album cover each time you refresh the page and I did wipe the cache when I migrated over to IIS. [Update: 29 Oct 2010] Setting Admin Options > Image subtab > Full image protection = Unprotected – yields a noticeable speed boost presumably because it skips a bunch of file I/O… now it feels back on par with what I was seeing in Apache… unfortunately, I just don’t remember how I had that setting under Apache. Installs:

  • I installed them all to c:Program Files because that’s my speedy SSD and I want this site to be as performant as possible
    • then i simply SymLink my main photos folder (on a RAID1 volume elsewhere) over the top of c:Program Fileszenphotoalbums
    • here’s an awesome SymLink utility for Windows Explorer!!
  • IIS – I’m on Win7 so it’s IIS7 – Apache’s cool and all but i saw a note somewhere that gave me the impression that on Windows, IIS + PHP via FastCGI module is  going to be more performant than Apache… otherwise, I did previously run it all on Apache just fine via the nifty "XAMPP" stack that installs everything for you in minutes and it "just works" which was honestly much less trouble than getting it all to hang together under IIS7 myself.
  • PHP – there’s a specific Windows/IIS “Fast CGI” version (current version: 5.3.3) (see this for Thread Safe vs Non Thread Safe binaries, non thread safe + IIS FastCGI is most performant)
  • MySQL – and their WorkBench tool is handy (current version: 5.1.51)
    • there’s a lot of environmental tuning questions during the install wizard but i mostly selected default settings
    • I chose to go with a MySQL_Data subfolder for the datafiles
    • configure for TCP/IP access (i don’t yet know how to configure PHP to connect to MySQL over named pipes)
  • ZenPhoto – just an unzip (current version:
  • zpGallerific theme (current version: 1.0)

FIREWALL!!! turn it completely off to begin with so you know whether it’s your main problem or not

    • I had to add these two rules to BitDefender
    • image
    • THE ORDER OF THE RULES MATTERS… MOVE THESE TO THE VERY TOP of the list with the arrow buttons!!
    • helpful: http://forum.bitdefender.com/index.php?showtopic=12764
    • nutshell: to see what’s blocked "Increase Verbosity" and "Show Log" on Activity tab 

Folder Permissions:

  • grant IUSR full permissions to root zenphoto folder (IIS_IUSRS group did NOT work)
    • it was also necessary on the true target of the symlinked albums folder
  • something happened on my win7 box where my albums folder was no longer accessible to zenPhoto/PHP… maybe a Windows Update closed a security loophole or something…

IIS Tweaks:

  • Enable 32bit PHP under IIS on 64bit Windows
    • install IIS6.0 script compatibily
    • cscript %SYSTEMDRIVE%inetpubadminscriptsadsutil.vbs SET W3SVC/AppPools/Enable32bitAppOnW
    • once I got everything fired up I realized that it’d be nice to support my old URLs that I’ve mailed out to everybody already
    • interesting thing was, Apache was doing something cool I didn’t realize… it was mod_rewrite’ing my php urls for me so they looked like pretty folders
    • actually zenphoto was kicking out the pretty urls and mod_rewrite was translating them back into /index.php?album=blah format behind the covers
    • IIS doesn’t do that right out of the box but they have a nice free URL Rewrite module you can drop in to do this very same thing (v2.0 currently)
    • you have to restart IIS Manager GUI after you install to see the “URL Rewrite” icon under the “IIS” section of your web site
    • it has a good wizard for the easy stuff which is all I needed to map “photos/(.*)/” to “photos/index.php?album={R:1}”
    • also under conditionals, input: {REQUEST_FILENAME} => “Is Not a File” & “Is Not a Folder” was crucial to allow the real URLs for direct downloading of images and such to continue working
    • Here’ are all the rewrite rules I needed to apply:
  1. <?xml version="1.0"?>
  2. <configuration>
  3.     <system.webServer>
  5.     <rewrite>
  6.             <rules>
  7.                 <clear/>
  8.                 <rule name="RewriteUserFriendlyURL6" enabled="true" stopProcessing="true">
  9.                     <match url="^page/search/archive/(.*)$"/>
  10.                     <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
  11.                         <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
  12.                         <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
  13.                     </conditions>
  14.                     <action type="Rewrite" url="index.php?p=search&amp;date={R:1}"/>
  15.                 </rule>
  16.                 <rule name="RewriteUserFriendlyURL5" enabled="true" stopProcessing="true">
  17.                     <match url="^page/([0-9]+)/?$"/>
  18.                     <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
  19.                         <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
  20.                         <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
  21.                     </conditions>
  22.                     <action type="Rewrite" url="index.php?page={R:1}"/>
  23.                 </rule>
  24.                 <rule name="RewriteUserFriendlyURL4" enabled="true" patternSyntax="ECMAScript" stopProcessing="true">
  25.                     <match url="^page/(.*?)$"/>
  26.                     <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
  27.                         <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
  28.                     </conditions>
  29.                     <action type="Rewrite" url="index.php?p={R:1}" appendQueryString="true"/>
  30.                 </rule>
  31.                 <rule name="RewriteUserFriendlyURL1" enabled="true" stopProcessing="true">
  32.                     <match url="^(.*?)/?$"/>
  33.                     <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
  34.                         <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
  35.                         <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
  36.                     </conditions>
  37.                     <action type="Rewrite" url="index.php?album={R:1}" appendQueryString="false"/>
  38.                 </rule>
  39.             </rules>
  40.         </rewrite>
  41.         <directoryBrowse enabled="true"/>
  42.     </system.webServer>
  44.   <system.web>
  45.         <compilation targetFramework="4.0" debug="true"/>
  46.         <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>
  47.   </system.web>
  49. </configuration>

Create Self-Signed Cert IIS7

PHP Setup:

  • Map an IIS virtual directory to your zenphoto root
  • browse to http://{your domain}/{zenphoto virtual dir}/setup.php
  • it’ll probably bark about a couple settings you have to make manually… no biggie hopefully
  • you’ll have to reset "World Wide Web Publishing Service" to refresh any PHP settings it tells you to twiddle
  • I had to leave file/folder permissions as "loose (0777)"… all of the stricter settings blocked zenphoto subfolder permissions
  • MySQL settings:
    • root login & password
    • (localhost did NOT work!?!)
    • database name ("zenphoto")
    • table prefix = blank (i preferred to go with a separate database w/o table prefixes)
    • it’ll create the zenphoto database for you with a simple click once you get a successful login to MySQL server working
    • *GO* 🙂
  • i went ahead and let it delete the "zp-coresetup*.php" files
  • set admin username & password
  • You’re in!

ZenPhoto admin page settings:

  • just unzip zpGallerific folder into the zenphotothemes folder
  • Theme’s tab – activate zpGallerific
  • Options tab
    • general subtab – Time zone = Europe/Berlin
    • gallery subtab
      • title = The Andersons
      • description = {blank} (set Gallerific subtitle next)
      • sorty by = filename – descending (works for me because i name all folders "yyyy-mm-dd {description}")
    • image subtab – Full image protection = Unprotected (as long as you don’t really care who gets access, this yields a MAJOR speed boost for page rendering times)
    • theme subtab
      • Albums per page = 9
      • Color = Blue
      • Tagline = Cassidy, Anne, BJ & Friends