Working man’s image attach for publishing workflow

When files are uploaded and attached to a node configured to allow attachments, when the node is viewed, attachments are presented in a table showing the clickable name and size of the attached files. This article shows how to override the default theming for the uploaded attachments table, adding a third column for the visualization of image thumbnails.

When files are uploaded and attached to a node configured to allow attachments, when the node is viewed, attachments are presented in a table showing the clickable name and size of the attached files. This article shows how to override the default theming for the uploaded attachments table, adding a third column for the visualization of image thumbnails.

Use case: In a publishing workflow, just attach a series of images for the editor to choose from.

The great image attach module

The image module itself is something everyone comes back to… because everything is a node! When building a site based on Drupal which includes image handling, a bewildering number of options still abound: one would like the great usability of the Imce module integrated into TinyMCE, for example; but for that you have to pay the price of the images not then being nodes, and not being able to be made available to views, etc. So I practically always wind up using the Image Assist module, which also integrates with the TinyMCE (as a little camera icon) and allows you to create image content types on the fly (with gallery-specific and other categories) while placing the image in a body of text you are editing. Which requires some training, but, apparently in future Drupal versions this problem will be streamlined.

Well, without going into a lot of details on that subject, it happens that in many use cases, for example in the publishing industry, one simply wants to attach images to an article, for example, so that the editor can choose which image to go with.
For this purpose, the image_attach module (bundled with the image module) is very promising. You can choose which content types will have an "Attached images" section, an image content type node is created, and the uploaded image appears as a thumbnail, while both editing and viewing the node.

But… you can only have one per node! Life is full of ugly choices!

Using regular file attachments for images

Well… what about the regular file attachments? That is what one client with the above use case started doing. And then they asked my how come the images "weren’t showing up in the file attachments table." OK… well, for a start the big disadvantage is that images attached as regular file attachments are not going to be nodes, but that didn’t matter for this use case (if it did, perhaps in another article we can show how to accomplish that via interception of the submit operation involving the uploading of files).

So in this article, let’s just deal with the task of showing the images when viewing the node (leaving the more difficult task of altering the form to include the images in the File attachments table during an edit for another time, although it can be done in similar fashion).

Making sure the image attach and file attachments table appear in your theming

By default, image attach and file attachment content is appended to the bottom of each viewed node. But just in case your super duper theming had left them out in a node-content-type.tpl.php file (as was my case), I had to put them back in again manually!

That is done in the following manner:

Code from node-article.tpl.php:





content['image_attach']) {
echo $node -> content['image_attach']['#value'];
}
?>

Overriding the upload attachments theme

Notice how we invoke the theme function instead of calling it directly. This technique allows us to use the Drupal theme override system, which insulates you against future changes, and we are going to avail ourselves of that right now.

In the theme’s template.php file, first copy the theme_upload_attachments($files) function (around line 580 of ./modules/upload.module)

Rename the function to something like mytheme_upload_attachments($files), and at this stage it looks like this:

theme_upload_attachments() from upload.module copied into template.php:


/**
* Displays file attachments in table
*/
function mytheme_upload_attachments($files) {
$header = array(t('Attachment'), t('Size'));
$rows = array();
foreach ($files as $file) {
$file = (object)$file;
if ($file->list && !$file->remove) {
// Generate valid URL for both existing attachments and preview of new attachments (these have 'upload' in fid)
$href = file_create_url((strpos($file->fid, 'upload') === FALSE ? $file->filepath : file_create_filename($file->filename, file_create_path())));
$text = $file->description ? $file->description : $file->filename;
$rows[] = array(l($text, $href), format_size($file->filesize));
}
}
if (count($rows)) {
return theme('table', $header, $rows, array('id' => 'attachments'));
}
}

Now we add the following changes:

The modified mytheme_upload_attachments():


/**
* Displays file attachments in table
*/
function mytheme_upload_attachments($files) {
$header = array(t('Image'), t('Attachment'), t('Size'));
$rows = array();
foreach ($files as $file) {
$file = (object)$file;
if ($file->list && !$file->remove) {
switch ($file -> filemime) {
case 'image/jpeg':
case 'image/gif':
case 'image/png':
$filedir = realpath('.') . '/' . $file -> filepath;
$info = image_get_info($filedir);
// calculate image size
$width = 200;
$height = 200;
// don't scale up
if ($width >= $info['width'] && $height >= $info['height']) {
$width = $info['width'];
$height = $info['height'];
}
else {
$aspect = $info['height'] / $info['width'];
if ($aspect < $height / $width) { $width = (int)min($width, $info['width']); $height = (int)round($width * $aspect); } else { $height = (int)min($height, $info['height']); $width = (int)round($height / $aspect); } } global $base_url; $image = '';
break;
default:
$image=" ";
}
// Generate valid URL for both existing attachments and preview of new attachments (these have 'upload' in fid)
$href = file_create_url((strpos($file->fid, 'upload') === FALSE ? $file->filepath : file_create_filename($file->filename, file_create_path())));
$text = $file->description ? $file->description : $file->filename;
$rows[] = array($image, l($text, $href), format_size($file->filesize));
}
}
if (count($rows)) {
return theme('table', $header, $rows, array('id' => 'attachments'));
}
}

First of all we change the header, to add the first column to display the image thumbnails. Then we add a switch to detect which files are images, and in the case that they are, get the image info (viva realpath()!), calculate the thumbnail size (snippet from the image_scale() function in image.inc from Drupal core) and create the html to be output for the image.

In this case we do a crass hack and let the browser scale the image, which should normally be avoided like the plague, but in this case more than serves the purpose (use case). (If we automatically created an image node for each of the attachments when they were first uploaded, we could avail ourselves here of certain image module functions to create/use/load the thumbnail; or we could use the installed toolkit to create a temporary image file: exercises for the reader!).

This whole problem really cries out for an object-oriented approach to give us an extensible image class in Drupal core, but … that discussion has to be pursued elsewhere… and will be hard work.

So now, at any rate, the editor can choose which of the two photos of striking casino workers in Buenos Aires to use in the article explaining their tremendous and important struggle (perhaps uploading it as the one and only image attach to show it is the chosen one):

Striking casino workers in Buenos Aires January 2008Striking casino workers in Buenos Aires January 2008