Getting It Done, Part 2

I wrote a little while back about using Javascript and Ajax to build a dynamic and interactive interface for a site that would otherwise be flat HTML. My client is using a commercial ecommerce platform, ShopSite 8.1, which has a web-based admin interface for entering info about a product. The application then publishes summary and detail pages for the products. ShopSite uses an embedded database, but you can’t access it. You have to use it as a publish-once or “push” CMS. But my client wants beautiful and interactive pages, so my solution is to set up ShopSite to publish semantic HTML, much of it in div elements I’ve set to display:none, and use Javascript to parse the info in those divs and display it. Also, I can use Ajax to get more information form the server, especially information about artists, as that info isn’t stored in ShopSite but a separate MySQL database I set up.

I mentioned that I had to figure out how to do a few more things. Two of those challenges were:

  • Retrieve path information for files, e.g. images, related to specific products, using Ajax and JSON.
  • Store and retrieve large pieces of information, e.g. long book descriptions, or complicated info, like the captions for many photos associated with a product.

Here is a brief description of how I’ve done these things.

My client sells back issues of a photography magazine. As you might expect, this magazine’s issues contain a lot of photographs, and they’ve designed a front-end interface to guide users to those images by having them a) select an issue, b) select an artist or collection, i.e. and article within the magazine, and then c) select a thumbnail.

I’ve set aside a customizable ShopSite field to hold special IDs for each issue’s article. Every photo that belongs to that article will have a thumb and full-size image file, and each of those images will have a filename based on the article’s ID. So if an article has the ID “hopper_sunsets”, then the first set of image files (thumb and fullsize) will be named “hopper_sunsets_01.jpg/gif/png”. The second set will be named “hopper_sunsets_02.jpg/gif/png”, and so on.

But there’s no way to know, from the info stored in Shopsite, how many sets of images there will be. There’s also no way to know whether the files are JPEGs, PNGs, or GIFs. I could ask my client to stuff filenames into the ShopSite fields, but this would be error prone, and it would require someone to remember, if a file is replaced on the server, to go into ShopSite and find the record and change it. There’s a bandwidth problem, too. Shoving the filename info into ShopSite means that the listing page, which contains information in hidden div elements, will have to contain all the filenames for all the photos in all the issues. There are several dozen issues and some of these issues have over a hundred images.

The better way is to write a little PHP script that accepts an issue ID, looks in the folders where the image files for that issue should be, and then makes a little list of the files’ names, or to be more exact, a list of lists, since each file needs to be associated with one of the article IDs defined for that issue. I’ll call the script with Ajax from the client, just when a new issue’s files are needed.

The PHP script isn’t hard to write, but that comment I just wrote about “list of lists” suggests a fair amount of structure needs to be conveyed back to the client-side. This is where JSON (JavaScript Object Notation) comes in. JSON allows me to structure the filename information as a nested hashes: the issue ID is a key, the article IDs are keys, and the numeric counters (e.g. “_01”) in the filenames are also extracted as keys. The structure is (fairly) human-readable, much more compact than XML would likely be, and it requires no deserialization on the Javascript side: JSON is valid Javascript and as such may be used as an rvalue to an assignment operator with the use of the eval() function. Like this:

photos = eval('(' +ajax_request.responseText+')');

JSON’s syntax rules are fairly succinct, and are explained in an appropriately concise manner on the JSON website. For completeness, here’s an example of how the JSON I’m generating looks to the human eye:

{"35": {
{"01": {"thumb": "35/hopper_sunsets_01.jpg", "full": "35/hopper_sunsets_01.jpg"},
"02": {"thumb": "35/hopper_sunsets_02.jpg", "full": "35/hopper_sunsets_02.jpg"},
"03": {"thumb": "35/hopper_sunsets_03.jpg", "full": "35/hopper_sunsets_03.jpg"} },
{"01": {"thumb": "35/adams_trees_01.jpg", "full": "35/adams_trees_01.jpg"},
"02": {"thumb": "35/adams_trees_02.jpg", "full": "35/adams_trees_02.jpg"},
"03": {"thumb": "35/adams_trees_03.jpg", "full": "35/adams_trees_03.jpg"} },

The “35” is the issue number, the “hopper_sunsets” key is the ID for the first article, the numeric counters follow, so that I can display the images in the order suggested by their names, and finally each set is broken down into “thumb” and “full” values.

The other thing I promised to do was find a way to pull in captions and long descriptions only as they were needed. I don’t want to load every magazines photo’s caption at once, for bandwidth reasons. Instead, I want to use an Ajax call to retrieve them from the server only when they’re needed, i.e. when a new issue’s contents are requested.

Captions are entered into ShopSite as lists of semi-colon-delimited and comma-delimited data:
photo_id_1,Caption for Photo 1;
photo_id_2,Caption for Photo 2;

The photo IDs contain article information too. For example, for the captions for “hopper_sunsets” article, the IDs would be “hopper_sunsets_01”, “hopper_sunsets_02”, etc. Happily, these also happen to match the filenames for the thumb and full-size images for the article up to the dot-extension suffix.

It’s easy enough to parse the caption info and assign captions to photos, but here’s a wrinkle: I mentioned before that I don’t want to include all the caption data in the hidden parts of the file: there are over a thousand captions. Instead, I want to call them up, via Ajax, when they’re needed. But the caption info is kept in ShopSite, and there’s no way to access ShopSite’s data: I can’t write a script to query a database and get those captions. The solution is to use ShopSite’s push strategy for content.

ShopSite can be easily configured to publish a “More Info” page for each product. For issues, I have it publish such a page, and it includes on this page the caption info. On the main magazine page, each issue’s hidden data includes the URL to the More Info page for that issue. When the issue is selected, an Ajax call uses the URL to retrieve the page and get the info that way.

Long description text, on the other hand, doesn’t belong in ShopSite: it’s too long and likely contains HTML, and this makes it awkward to shove into a text field on an admin screen that already contains 20 other controls. The long description for a book should go in a simple HTML file, saved in a folder like “text” and named with the book’s ID. Then, again, my Javascript can issue and Ajax call to try to retrieve the content for display.

That’s a bit more detail that I planned to present, and there are a bunch of other topics related to my project I’d like to write about, but it will have to wait for “Getting It Done, Part 3.”