Howto nicely theme Drupal node form

Drupal node form is not the most exciting user experience that Drupal may propose. If you are an experienced user, there is no need to worry, but in most cases, the sites you'll make with Drupal will be stupid end-user oriented.

With some minor custom theme alteration, you can easily provide the exact same form, but more "Wordpress-like", understand here, with a better organization and a more trivial visual schema.

Let's look what's wrong

If you look on the drupal common node form, you'll see that common node form is vertical aligned (see this screenshot). You have to scroll a lot to find the information you need.
Let's try to make it columns aware.

Quick and dirty howto

If you are some mad theming guy for Drupal, you've probably already noticed this, but you can alter every form within your template.php writing a custom phptemplate_preprocess_form_id(&$vars). In our tutorial, we will alter node_form so we'll do a custom phptemplate_preprocess_node_form(&vars).
The fact we have a preprocess_*() function allows us to think we are able to make a custom *.tpl.php template file for our form.

We are going to create these both files, and see the result.

Adding a new node-form.tpl.php template

Let's put a simple node-form.tpl.php which will allow us to declare a three column layout for our node form:


<?php print $form_classes ?>">
  <?php if ($sidebar_left): ?>
   
<?php print drupal_render($sidebar_left); ?>

  <?php endif; ?>

  <?php if ($sidebar_right): ?>
   

<?php print drupal_render($sidebar_right); ?>

  <?php endif; ?>

 

<?php print _phptemplate_form_main_classes($sidebar_left, $sidebar_right); ?>">
    <?php print drupal_render($form); ?>
 

  <?php if ($buttons): ?>
   

<?php print drupal_render($buttons); ?>

  <?php endif; ?>

In this template we define new variables that we'll have to define into $vars variable in the preprocess function, which are sidebar_left, sidebar_right and buttons.

Because we want to make these columns dynamic, I decided to add a simple CSS class wrapper into main div declaration:

<?php
function _phptemplate_form_main_classes($left, $right) {
  if (
$left && $right) {
    return
'form-layout-both';
  }
  else if (
$right) {
    return
'form-layout-right';
  }
  else if (
$left) {
    return
'form-layout-left';
  }
  else {
    return
'form-layout-none';
  }
}
?>

This wrapper will allow us to write specific CSS directives for each use case (1, 2 or 3 columns layout for our form).

Writing the preprocess function

The code I'll expose here originally comes from the openatrium open source project.
It handles the principal node-form.
This code must be write into you template.php file:

<?php
/**
* Implementation of hook_preprocess_node_form().
*/
function phptemplate_preprocess_node_form(&$vars) {
 
$form = $vars['form'];

  $vars['type'] = $form['#node']->type ? $form['#node']->type : '';

  foreach (array('i18n') as $key) {
    if (isset(
$form[$key])) {
     
$vars['sidebar_left'][$key] = $form[$key];
      unset(
$form[$key]);
    }
  }

  foreach (array('taxonomy', 'options') as $key) {
    if (isset(
$form[$key])) {
     
$vars['sidebar_left'][$key] = $form[$key];
      unset(
$form[$key]);
    }
  }

  foreach (array('comment_settings', 'author', 'path', 'menu', 'revision_information') as $key) {
    if (isset(
$form[$key])) {
     
$vars['sidebar_right'][$key] = $form[$key];
      unset(
$form[$key]);
    }
  }

  $vars['buttons'] = $form['buttons'];
  unset(
$form['buttons']); 
 
 
$vars['form'] = $form;
 
$vars['form_classes'] = !empty($vars['sidebar']) ? 'form-with-sidebar' : '';
}
?>

This method spreads arbitrary some data into left and right sidebars. Taxonomy and I18n settings goes to left, all other except title and content goes to right sidebar. When you do this kind of stuff, don't forget to unset moved elements, PHP default behavior is to copy array values instead of using references. If you don't unset moved tree elements, you'll get it generated twice in rendering functions.

The last step, some CSS

Because this is not all magic, you'll have to write few lines of CSS to handle your columns as floating elements:

form div.form-form {
  clear: both;
  display: block;
}

form div.form-sidebar-left {
  width: 210px;
  padding-right: 10px;
  float: left;
}

form div.form-sidebar-right {
  width: 210px;
  padding-left: 10px;
  float: right;
}

form div.form-layout-both {
  margin-right: 220px;
  margin-left: 220px;
}
form div.form-layout-left {
  margin-left: 220px;
}
form div.form-layout-right {
  margin-right: 220px;
}
form div.form-layout-none {
}

form div.form-form fieldset,
form div.form-form fieldset.collapsed {
  background:#FFFFFF url(images/gradient-inner.png) repeat-x scroll 0 0;
  border-bottom-width: 1px;
  border-left-width: 1px;
  border-right-width: 1px;
}

Left and right sidebar are set to 220px, this is an arbitrary value, it's up to you to theme it as you like.

Put this code into a seperate CSS file if you want to keep things encapsulated, and tell your theme .info file to load it. Or just append it to your main CSS file if you are that much lazzy.

When you done all this stuff, clear your cache so your Drupal installation updates its theme registry, and enjoy the result.

You should now see something like this screenshot, these have been made in a earlier stage of testing, some fields might be misplaced.
A two columns form preview is available here.

Generalization

At some point, I wanted to generalize this method to all forms. I did a phptemplate_preprocess_form(&$vars). The fact I was surprised to see that, like the global hook_form_alter() hook, this preprocess function seems to be ran after the drupal_render(), all I could find in $vars was the already generated form. If you want to really alter your forms, you'll have to write one preprocess function per form, and create one template file for each form.

Note that I done a general form.tpl.php file, but when using a custom preprocess function, it won't use the common form template file. For each proprocess function, you must have the corresponding template file.

Conclusion

This method is far away better than write custom module to it with hook_form_alter(), for many reasons, the first one is you don't interact with custom modules code, but only in theme rendering, which at some point avoid some module incompatibilities.

This article is only a proof of concept, it worked well on many installation I did (running latest Drupal 6.x version, which is 6.13 right now). You might encounter some problems with custom modules and themes at some point.

Votre notation : Aucun Moyenne : 5 (2 votes)