Cleaning Up Node Edit Forms

Photo of Greg Harvey
Mon, 2009-10-12 10:16By greg

Every time you build a new application in Drupal you *always* have to carefully govern the number of options available to normal site editors, or their heads will explode. (Literally. I've seen it.)

Almost every module you install will put something more on the node forms, until you end up scrolling forever just to reach the Save button. Not to mention most of this stuff you won't want them playing with anyway.

Of course a lot of this "tidying" can be done with roles and user permissions. You can hide most stuff from Joe Editor this way, but sometimes the form is still pretty long and unwieldy. So how do you make prettier node forms?

Step 1, Vertical Tabs!
http://drupal.org/project/vertical_tabs

This cool little utility is core in Drupal 7.x (cue jig for joy here) but in Drupal 6.x it's contrib, so go and install it. Now!

It provides a neat little check-box mechanism on the content type edit form allowing you to specify which fieldsets presented by other modules on the node form are ok to become tabs instead. I check all the boxes. The result, our ugly fat node form becomes a set of pweddy widdle tabs under the content body field. Nice! =)

There are some issues though. I don't know about the new core version, but sometimes modules present their options outside of a fieldset and the Drupal 6.x version of Vertical Tabs can't do much with them. Other times Vertical Tabs simply doesn't recognise a fieldset as one that can become a tab (seems the Twitter module is a case in point). I think this is down to whether the module implements hook_content_extra_fields() properly or not (which, for the record, every Drupal 6.x module putting stuff on the node add/edit forms should - I lecture myself there too).

Then there are custom requirements due to specific client work-flows. For example, my client requested publishing options, URL alias settings and post to Twitter options to all be in one fieldset (or tab, as it now is) because these represented his most common tasks and having to switch tab to do them was a pain. He also wanted a Related Items CCK field (a nodereference field pulling up other content on the site in the same categories) to sit with Taxonomy, again, to reduce the number of clicks and because it feels right that it's there.

Fortunately there's good old hook_form_alter() for just this sort of thing. Here's the code I used to shunt things around and push stuff in to vertical tabs that weren't appearing as tabs already:

/**
* Implementation of hook_form_alter().
*/
function economistconferences_features_form_alter(&$form, $form_state, $form_id) {

/*
* Alter the node form to group things more intuitively
*/
if ($form['#id'] == 'node-form') {

// Move all path options to publishing options
if ($form['path']) {
$form['options']['path'] = $form['path'];
unset($form['path']);
}

// Move all twitter options to publishing options
if ($form['twitter']) {
$form['options']['twitter'] = $form['twitter'];
unset($form['twitter']);
}

// move related items box in with Taxonomy
if ($form['field_related'] && $form['taxonomy']) {
$type = content_types($form['type']['#value']);
$field = content_field_form($form, $form_state, $type['fields']['field_related']);
$form['taxonomy']['field_related'] = $field['field_related'];
unset($form['field_related']);

// make tab title more descriptive
$form['taxonomy']['#title'] = t('Categories & related items');
}

// we need to add a validate function so we can remove our CCK
// field from Taxonomy and put it where it should be in $form_state
$form['#validate'][] = 'economistconferences_features_node_form_validate';

}

}
?>

I'm not going to explain how the hook works, because there's loads of other blog posts and the API docs for that. If you're familiar with the hook already, you'll notice I'm just pushing stuff in to fieldsets that already exist and Vertical Tabs knows about, then removing them from the main form. No rocket science there.

A word of warning. If you're going to engage in this sort of jiggery pokery, you should expect the unexpected. On the whole this just works - the values go in to $form_state as you'd expect and the various modules do their thing - but some modules might expect $form_state to have a specific structure in order to function correctly. An examples of this is Taxonomy.

You'll notice the last bit of my form altering code adds a validate function, which looks like this:

/**
* This additional validate function for the node forms takes
* the Related Items CCK field values out of the Taxonomy
* collection of values and puts it where it should be in the
* $form_state collection, to avoid Taxonomy module errors.
*/
function economistconferences_features_node_form_validate($form, &$form_state) {
$form_state['values']['field_related'] = $form_state['values']['taxonomy']['field_related'];
unset($form_state['values']['taxonomy']['field_related']);
}
?>

If we leave the Related Items CCK field where it is, in $form_state['values']['taxonomy']['field_related'] then the Taxonomy module tries to process it as though it were a vocabulary. We don't want that, but fortunately it only happens on submit, so we have time to do something about it in a validate function, which is exactly what the above function does. Simply takes the CCK values out of the Taxonomy values and puts them back where CCK expects to find them.

You may need to do something similar with the values for some other modules. Taxonomy was the only one I came across, but there will surely be others that require a specific structure in the form values. Organic Groups feels like it might well be another case, given my fun and games with it and node forms in the past.

So, all done, I've tested this and it works. Client is happy, because this simple UI change is a real time-saver. Oh, and Vertical Tabs rocks! Very happy it's now core. =)

Edit: A wee follow-up. Follow this issue for the CCK module's fieldgroup.module, still not core in Drupal 7.x, which would address this in the UI. Please support:
http://drupal.org/node/404528#comment-1801420