Rebuilding gallery.chaostangent.com

gallery.chaostangent.com front page
gallery.chaostangent.com is an application for storing and organising images – ostensibly a very simple desire but one I found not catered for by existing web applications when it was first conceived in 2005. The concept was an application that was simple and easy to use while still allowing for a degree of organisation to ensure images weren’t stored in a single “pool”.

With a small, well-defined feature set it seemed like a good time to address some of the issues which had crept in

Background

When I first started developing the application, PHP 5 hadn’t been released for very long and was receiving a mixed reception. Regardless, I started developing using a custom built framework I had cobbled together from scratch – one that would eventually go on to be refined and used in some of my work projects. With the lack of other mature frameworks to compare with, it was rough round the edges and did little more than segment out code into the MVC pattern and even then it wasn’t an entirely clean encapsulation; it was however useful.

The first version had the following functionality:

  • Uploading of images using a form
  • Hierarchical galleries in a folder like tree structure – mirrored in the file structure on the server
  • Hidden galleries for concealing galleries from the frontend
  • User management with access control lists
  • Batch image upload from a folder on the server

The batch image upload was added when it became obvious that using a web form to add multiple images was a tedious and protracted affair. The batch upload allowed a user to transfer files onto the server using whatever method they desire (e.g. FTP) and then specify that folder for trawling. This made adding hundreds of images a breeze, despite being less than optimal or straightforward.

Technical

The application used the “Modified Pre-Order Tree Traversal” mechanism for storing hierarchical data. This provided its own set of problems; for instance: to get the first level descendants of a using only MPTT you have to hit the entire gallery sub-tree, so for the root node this was the entire tree. Using a hybrid approach solves this problem by storing the gallery parent not just the left and right values; this makes descendant selection trivial.

Another technical hurdle was the thumbnail creation. The most commonly used image library within PHP is GD which is really only suitable for smaller images. As it operates within PHP’s memory, larger images cause more memory to be used until the server imposed limit (memory_limit in php.ini) is reached. This causes odd states whereby an image has been uploaded but no way of telling whether the script would time-out or hit the memory limit prior to beginning processing.

The solution to this dilemma was to switch to using the command line tool ImageMagick. As an external executable, PHP’s memory limit is no longer an issue; however ImageMagick comes with its own foibles. This involved lesser travelled areas of image manipulation such as colourspaces and multi-frame images (animated GIFs et. al.).

Problems

For a number of years the application worked admirably, only a small selection of niggles remained:

  • Batch image upload process
  • Odd hidden galleries logic
  • Tedious bulk image deletion
  • Square thumbnails
  • Changing the name of a gallery changed the filesystem folder name which altered the URLs for the images

Since its inception, a selection of PHP frameworks had been released and matured such as Prado, CakePHP and Symfony and the behemoth of Ruby on Rails was dominating development at the time. With a small, well-defined feature set it seemed like a good time to address some of the issues which had crept in.

False starts

Despite improvements to the bespoke framework I used like request routing, several attempts at improving the application met with the problem that rebuilding it didn’t fundamentally improve it and the updated framework didn’t make coding any quicker or simpler, just different. These conclusions made me down tools and re-evaluate the rebuild.

Version 2.0

Over a year later I began work again, this time using the oft mentioned and newly released Zend Framework. I quickly surpassed the functionality milestone I had reached with older versions – mainly due to my focus on individual functionality. I had set up a Subversion repository and disciplined myself into making smaller, more frequent commits rather than monolithic end-of-the-day updates. After some iterative improvements the application retained the core functionality of version 1.0 and most importantly, built upon it:

  • Thumbnails are now proportional – aesthetics are easy to change, thumbnails aren’t
  • Any number of different image versions can be generated e.g. not just thumbnails; parameters are flexible and easy to extend
  • Removed hidden galleries – despite improving the logic, their usefulness was always in question
  • Users are no longer subject to an ACL – designed for small, trustworthy communities rather than sprawling user bases
  • Galleries now separate out their display and filesystem names – each can be changed independently of the other
  • Image uploads are now done using SWFUpload if available
  • Some actions use AJAX if available to enhance usability
  • Galleries can be output in XML and JSON for external processing

With such an array of improvements it wasn’t long before the build was tested and put into place. Despite being incompatible with the previous version, this turned out to be a good time to clean out the cruft and start afresh.

Technical

The first 2.0 build mirrored the code layout of the 1.0 build, so image sizing was done within the controller which made them needlessly large and unwieldy. The second 2.0 build sectioned image sizing out into the models, triggered on database hooks (pre-insert, post-insert etc.). This was initially tricky as the tightly coupled functionality seemed an odd fit until it was massaged into being more generic and loosely coupled.

Image versions are now calculated on-the-fly rather than being stored in the database. Some image versions may or may not exist depending on the settings a user has entered and the image in question – e.g. a thumbnail is set to always be generated, while a blog sized image may only be present if the image is large enough. This granularity of control allows for a variety of usage scenarios.

Using SWFUpload was an early decision: moving away from the server based folder upload was a high priority. Implementing SWFUpload is tricky as it requires a lot of JavaScript upfront to work well; the Flash cookie bug also cropped up, polluting an otherwise pristine authentication system.

The database structure was simplified even further than version 1.0. Images no longer stored the filename of the thumbnail (a result of allowing multiple image versions) or the filesize of the image, dimensions are retained as these would be too costly to calculate on the fly. I originally planned to use triggers for some of the more complex database actions like insertion and deletion within the gallery tree however this isn’t possible as triggers can’t modify the table they’re fired on – this is a general database principal rather than the standard bloody-mindedness usually exhibited by MySQL. I ended up using database functions which reduced the error checking required in the PHP code.

Future

I am aiming to get a release candidate completed soon. I initially stayed away from a public release due to the usage of a custom framework that was used heavily in commercial projects, and the gnawing doubt that it wasn’t yet good enough for exposure to my peers. With these barriers now removed, it remains for me to thoroughly test and document the system and package it; aspects which I take for granted (having access to the database, knowledge of where the ImageMagick executable is etc.) will need to be addressed prior to release. There are other tertiary concerns as well such as a bug tracker, support forums and so forth.

I am still aiming to further refine the existing functionality – primarily the production of image versions and SWFUpload integration. With the ability to format galleries in XML and JSON, it opens up a number of possibilities, including integration with Wordpress’s media navigator or sidebar widgets.