Tag-based Gallery in MODX CMS

Jul 12, 2015

Tag-based Gallery in MODX CMS

Do not tell Jason Coward about this post! I'm about to show you how I did something naughty in MODX, of which, he would surely disapprove.

Doing It Wrong, for the Right Reasons?

A friend was undergoing a site redesign and had very a specific requirement: a photo gallery heavily based on tags.

It's a collection of maybe 150 images, that will grow slowly. A piece of cake to develop, but for the fact that none of the (many) robust gallery solutions in the MODX ecosystem can handle tagging at the level they wanted to do it.

Yes it's true that both Gallery and moreGallery, and actually even MIGX to a degree, can support tags—but they wanted tagging on steroids. Only two options would fulfill their very specific needs:

  1. A custom gallery component with pumped-up tagging features—perhaps an extension of one of the above mentioned Extras.
  2. Tagger, the powerful tagging platform by fellow MODX staffer John Peca.

The first option wasn't feasible due to the investment required—this was a side project for a friend.

Option 2 was easy (in MODX) and low-cost, but had a dark side:

Tagger must be used with MODX Resources. This means each gallery image will need to be managed via a Resource, which many would argue, isn't the "right" way to do it. It would make gallery management a tad clumsy.

This was explained thoroughly to the person responsible for ongoing gallery management, but she adamantly preferred the tagging power over the convenience of a "real" gallery component.

Two Wrongs Make a Right?

Despite feeling guilty for what would surely be considered abusive hackery of both MODX and Tagger, I decided to take the challenge whole-heartedly, because it actually gave me an opportunity to try out something I find interesting: image processing on the document save event in MODX.

I made a dedicated MODX Template with 2 TVs: "image" and "crop". The first facilitates the User picking an image file to manage, and the second exposes the zoom-crop options of the magical pthumb Snippet.

Form Customization rules were employed to simplify the Resource Edit View.

Customized Resource Edit View

Then I wrote a Plugin that, "OnDocFormSave", runs pthumb on the image with a set of pre-configured options for each of 3 different crop sizes, along with the chosen zoom-crop option. The resulting cached thumbnail file URLs are saved in the Resource's "properties" field. (See below for gist.) This isn't a new idea btw—it's been discussed in the forums.

It was shockingly easy, because pthumb does all the hard work of checking for a valid source file, processing the image, and caching the results—with a unique hashed filename for each set of options.

A Tagger "group" was assigned to the photo Template, and configured for all the tagging joy it affords. The User can add an obscene amount of tags—Tagger was made for performance and tested with up to a million tags!

A dedicated Collections container Resource was setup to provide a highly customized grid view of the gallery. Thus all image Resources will be direct children of this gallery container Resource. A custom Snippet renders the small-sized thumbnail for each Resource, in the Collections grid. It actually looks somewhat like a "real" gallery component.

Custom Collections Grid View

Naughty or Nice?

Next came the front-end. Using Tagger with getResources returned filtered results, but getting the thumbnail paths from the Resource properties required a custom Snippet in the getResources tpl Chunk.

// Options
$id = $modx->getOption('id', $scriptProperties, $modx->resource->get('id'));
...

// Get stuff
$res = $modx->getObject('modResource', $id);
$thumbs = $res->getProperty('thumbs', 'resourceimage');

// Do stuff
...

!Note the use of:

$modx->getObject()

That meant additional database queries. My first thought, to mitigate this, was to write a custom listing Snippet, without all the power of getResources but with the properties attribute handled natively. Then again, why start doing things "properly" at this stage?

I remembered Bob Ray's perfectly valid argument for using the "introtext" Resource field to store processed data, and since we were already hiding the field from erroneous editing with Form Customization, it seemed too easy (and too naughty) not to do it.

So I refactored the Plugin, and the new version of the Snippet in the getResources tpl Chunk simply extracts a value from a JSON string that's readily available:

The final Plugin gist is here:

I applied a custom Content Editor permissions policy, and disabled "quick update" features, to further protect the introtext field.

Santa Claus is Comin' to Town

The final result was exactly what they wanted: a single, big-ass gallery with all their images in a flat hierarchy. A massive tag cloud delivers filtered results per User input. Thumbnails are pre-generated to keep things snappy, and the absence of TV queries helps too.

getPage even caches the filtered results, speeding up common requests. Let me reiterate this was not a work of creativity. The amount of code used to accomplish this was minimal. It's only that MODX, and the multitude of flexible Extras that are available, facilitate meeting these oddball-type requirements with ease. A huge shout-out to the MODX developers who have laboured on the CMS, and the many Extras mentioned here.