Improving the file attachments form in Drupal
As I already explained in a previous post, I am very fond of spending time improving things that don't usually get a lot attention from Drupal developers and themers. Even if many of the interfaces shipped by default in Drupal provide a very systematic and procedural environment for administrating the content, you sometimes need to add a bit of gloss to make things prettier and easier to use for your clients. When I start a project, I invariably add a bunch of theme overrides, CSS files and small scripts that help me reach that goal. This time I am going to expose the technique I use to beautify the upload form for file attachments. Here is what it looks like when I am done:

The problem(s)

In my (very) own opinion, the default file attachments form, even if very functional, suffers from a few main problems :
- The fieldset wrapping it is collapsed by default and there is no setting to change this. That's logic from a Drupal point of view, but many people miss it and just have no idea you can upload files to be attached to the content they are editing. I'd add to this that some people don't get the meaning of the "File Attachments" label to start with.
- The form actually looks too much like... a form! The name of the uploaded files are displayed in a form (even if 99% of users won't edit it) and if users wish to remove a file they need to check the "Delete" checkbox and then submit the node's form.
- As a bonus, I would say that some people are disturbed by the fact that they need to select a file and then have to hit the "Attach" button.
My solution
All this lead to users either totally unaware of this feature or struggling with basic operations. Here is what I usually do:
- Make the "File Attachments" fieldset expanded, and customize its label.
- By default just display the file name and add a "Rename" link that turns that title into an editable input.
- Add a "Remove" link that instantly remove the file from display.
- Avoid having the user to click on the Attach button by directly attaching the file after it's been selected.
- Do some minor cosmetic changes, like adding icons depending on the file extension, regrouping all secondary information (file size, file URL...) on a single line displayed below the file name, reformatting the help messages...
The result can be seen in the picture posted at the top of this post.
How to?
By hacking the core upload module of course! More seriously, this is mainly done by overriding theming functions (I am a big fan of overriding pretty much anything I can). Attached to that post, you will find the archive of a folder named "upload" that you can dump in you theme's folder. This folder contains almost everything that you need: CSS and JS files, images... On top of that, you will need to do two things:
- Add the upload.css file to your theme by adding the following line into your theme's .info file:
stylesheets[all][] = upload/upload.css - Add the following overrides to your theme's template.php file (replace "mytheme" by the name of your theme):
That's quite a lot of code: you can find it in the template.php.txt file attached to this post./** * Declare theme's theming functions. */ function mytheme_theme() { return array( // The form ID. 'node_form' => array( 'arguments' => array('form' => NULL), ), ); } /** * Hack a bit the attachment fieldset. */ function mytheme_node_form($form) { drupal_add_js(path_to_theme() .'/upload/upload.js'); $form['attachments']['#collapsible'] = 0; $form['attachments']['#collapsed'] = 0; $form['attachments']['#title'] = t('Attach files to this @type', array('@type' => strtolower(node_get_types('name', $form['#node']->type)))); global $user; $limits = _upload_file_limits($user); $form['attachments']['#description'] = ($limits['resolution'] ? t('Images are larger than %resolution will be resized. ', array('%resolution' => $limits['resolution'])) : '') . t('Files must be smaller than %filesize and have one of the following extensions: %extensions.', array('%filesize' => format_size($limits['file_size']), '%extensions' => $limits['extensions'])); $form['buttons']['#weight'] = 100; return drupal_render($form); } /** * Hack a bit the attachment fieldset. */ function phptemplate_upload_form_new($form) { unset($form['new']['upload']['#title']); unset($form['new']['upload']['#description']); drupal_add_tabledrag('upload-attachments', 'order', 'sibling', 'upload-weight'); return drupal_render($form); } /** * Massive hack of the upload form. */ function phptemplate_upload_form_current(&$form) { drupal_add_tabledrag('upload-attachments', 'order', 'sibling', 'upload-weight'); foreach (element_children($form) as $key) { // Add class to group weight fields for drag and drop. $form[$key]['weight']['#attributes']['class'] = 'upload-weight'; $row = array(''); $output = ''; // Description: we save the URL, remove it as a description and change the size of the input $url = $form[$key]['description']['#description']; unset($form[$key]['description']['#description']); $form[$key]['description']['#size'] = 20; $form[$key]['description']['#attributes'] = array('class' => 'rename'); $output .= drupal_render($form[$key]['description']); // Size & URL $output .= '<span class="details">'. drupal_render($form[$key]['size']) .' - '. $url .'</span>'; $row[] = array( 'data' => $output, 'class' => 'file container-inline' ); // Remove $form[$key]['remove']['#attributes'] = array('class' => 'remove'); $form[$key]['remove']['#suffix'] = ' '. t('Remove'); $row[] = array( 'data' => drupal_render($form[$key]['remove']), 'class' => 'remove container-inline' ); // List $form[$key]['list']['#suffix'] = ' '. t('List'); $row[] = array( 'data' => drupal_render($form[$key]['list']), 'class' => 'list container-inline' ); // Weight $row[] = drupal_render($form[$key]['weight']); // Add the extension as a class for styling $extension = strtolower(substr(strrchr($form[$key]['filename']['#value'], '.'), 1)); $rows[] = array('data' => $row, 'class' => 'draggable mime-'. $extension); } $output = theme('table', array(), $rows, array('id' => 'upload-attachments')); $output .= drupal_render($form); return $output; } /** * Theme the attachments output. */ function phptemplate_upload_attachments($files) { $items = array(); foreach ($files as $file) { $file = (object)$file; if ($file->list && empty($file->remove)) { $extension = strtolower(substr(strrchr($file->filename, '.'), 1)); $href = file_create_url($file->filepath); $text = $file->description ? $file->description : $file->filename; $items[] = array( 'data' => l($text, $href) .' - '. format_size($file->filesize), 'class' => 'mime-'. $extension, ); } } if (count($items)) { return theme('item_list', $items, $title = NULL, $type = 'ul', array('class' => 'attachment-list', 'id' => 'attachments')); } }
Enjoy!

- reply
Submitted by Dries (not verified) on Sat, 2009-06-06 19:23.Very nice -- can we get some of this in core please? Break this up in 3-4 patches, and we'll get those changes in! :)
- reply
Submitted by Bojhan Somers (not verified) on Sat, 2009-06-06 19:33.Hey,
We know this, there are even some issues to it - we just need someone to patch core. Drop by #drupal or #drupal-usability if you need my help.
- reply
Submitted by Frando (not verified) on Sat, 2009-06-06 20:35.This is looking great. If you clean up the code a little and provide a core patch this can likely be included in Drupal 7!
- reply
Submitted by Jeremy Caldwell (not verified) on Sat, 2009-06-06 23:39.I really like how well this functions. Not to mention your tutorial and the files included were super easy to implement. I think I spent maybe 5minutes making the necessary changes to my theme and it improved the file attachment styles and method dramatically. Thanks! Like everyone else, would love to see this sort of functionality in D7 core.
- reply
Submitted by Aaron Couch (not verified) on Sun, 2009-06-07 03:31.this is a great improvement. i tried it locally and it works just as advertised. its great how it also adds the smaller icons to the node itself. very nice touch.
- reply
Submitted by thePanz (not verified) on Mon, 2009-06-08 03:53.This is a great enhancement for file uploading, much 2.0 style! :)
Hope to see your edits in D7 soon!
- reply
Submitted by zevonjunior (not verified) on Mon, 2009-06-08 05:36.This is very helpful - both the code and the tut'. Thank you so much.
- reply
Submitted by Anonymous (not verified) on Tue, 2009-06-09 05:39.Have you submitted core patches yet? It's been all of two days. Nag, nag, nag.... ;-)
- reply
Submitted by hunvreus on Tue, 2009-06-09 06:28.Thanks for the comments guys: I spent the last few days working on a (very painful) D5 to D6 upgrade, but I'll make up some time today to get the first patches uploaded today (the nagging works fine on me).
- reply
Submitted by phoenix (not verified) on Tue, 2009-06-09 22:20.I tested it on a local site and it works like a charm!
thanks for the detailed description and the sample code. This makes the way of uploading files much better.
- reply
Submitted by Anonymous (not verified) on Fri, 2009-06-26 01:24.Actually I feel really stupid. In my site and theme the code didn't work. I'm trying to find out what went wrong but I'm still unsure of the code inside template.php. I never manage to make it work the way I expect. In any case, thank you!
- reply
Submitted by Carolina (not verified) on Mon, 2009-06-29 20:52.Hi, I'm trying to apply this code to my drupal 6.12 installation but I can't make it work. I installed the upload folder inside my theme, created an empty template.php with the code you put, replaced all mytheme to 'my_real_theme', cleared the cache, visited the theme page and I got... nothing. Well, not really: I know that the _theme and _node_form functions are working. But I don't know how to find what is wrong.
I don't want to be a nuisance, really, but can you give me a hint to find the problem? Most of the time a var_dump have helped me, but not this time.
Thank you very much!
- reply
Submitted by Carolina (not verified) on Fri, 2009-07-03 00:03.Well, it took me 4 days to find the problem, but I've learned a lot about the Drupal theme system. The problem was that I'm using the Private module and it seems it was overriding the same functions as me. Once I disabled it, your code worked perfectly. Thank you very much and I apologize again.
- reply
Submitted by iva2k (not verified) on Sat, 2009-07-04 14:23.Just to let everyone know - I've implemented a module (named iTweak Upload) for D6 from this wonderful code example.
Using the module there is no more need to patch/hack any themes.
This module in addition supports comment_upload.module.
@hunvreus
I would like to make you a co-maintainer, if you do not object. Also, I posted your screenshot on the drupal project page. If you have any objections - let me know.
- reply
Submitted by hunvreus on Sun, 2009-07-05 19:16.Hey iva2k, I hadn't seen that comment and sent you a message through Drupal.org before that. Feel free to add me as a co-maintainer as I need to build a patch for Drupal 7 in the next few days.
- reply
Submitted by Anonymous (not verified) on Wed, 2009-07-08 23:23.Using it on a new Drupal 6 site and it works nicely. UI is a lot prettier. Appreciate the effort! Thanks again.
- reply
Submitted by iva2k (not verified) on Thu, 2009-07-23 23:38.@hunvreus
I thought I replied, but it did not show here, so I will try again...
I just noticed your reply, and drupal your message did not get through to me by the way. Anyway, I already added you to itweak_upload CVS back then when I first posted here. You'll have to catch up - since then I pushed it to 2.0 with thumbnails and image gallery view, and to 2.1 yesterday with upload preview. Still, CCK file/imagefield support feature is not done, but that's for 3.0/
- reply
Submitted by Joel Stein (not verified) on Wed, 2009-07-29 21:52.One thing I find particularly cumbersome is copying the URL so I can use it in my posts. It would be nice to just click the url, and using Javascript, have the browser auto-select the whole URL, so I can copy and paste it easier.
In fact, I almost never copy the whole url, but instead just the part following the domain (such as, "/file/document.jpg"). It would be nice to add a configuration setting to be able to choose which part of the URL gets selected.
Just an idea... thanks for the UI improvements!
- reply
Submitted by Anonymous (not verified) on Mon, 2009-08-03 16:31.Hi Man, Where are u come from?
- reply
Submitted by Maxim (not verified) on Tue, 2009-08-11 04:04.Thets nice solution, I'm defiantly going to use it in my projects.
- reply
Submitted by odżywki (not verified) on Sat, 2009-10-03 16:55.Thanks, your website is very helpful
- reply
Submitted by suplementy (not verified) on Mon, 2009-11-02 17:30.Hi. This blog is Drupal platform ?