bricolage

Creating a CCK Field module with an "invisible" widget

Posted November 19, 2010

Most CCK Field modules have a widget where the user adds information upon creating a node which is then saved with the node. For the recent Etherpad module I wrote, I needed an “invisible” widget which saved with each new node some information from the field definition as well as autogenerated information. As I didn’t any documentation on how to do this, I thought I’d document it here quickly.

The first thing you do is define your database columns for your field in hook_field_settings.

<?php
function etherpad_field_settings($op, $field) {
  switch ($op) {
    // Code removed.
    case 'database columns':
      return array(
        'etherpad_url' => array('type' => 'varchar', 'length' => 1024, 'not null' => FALSE,),
        'etherpad_text' => array('type' => 'text', 'not null' => TRUE, 'size' => 'big'),
        'attributes' => array('type' => 'text', 'size' => 'medium', 'not null' => FALSE),
      );
  }
}
?>

Next, you define your widget form inside hook_widget. Some tutorials I saw suggest you define your widget form in hook_elements/hook_process. I did that at first but decided against it as a) I never got it to work and b) it just adds needless complexity. Generally you’ll just want to define your widget in hook_widgets.

Two really important things here to get your “invisible” widget to work correctly. First, you must name your form keys the same as you named your database column names in hook_field_settings. This tripped me up for a long time. CCK saves data by magic (you never explicitly save anything from a widget) and this is the key to getting the incantation to take. Second, using the ”value” field type was the key to creating an “invisible” field and getting my data saved correctly.

<?php
/**
 * Implementation of hook_widget().
 */
function etherpad_widget(&$form, &$form_state, $field, $items, $delta = 0) {
  $element['etherpad_url'] = array(
    '#type' => 'value',
    '#value' => (isset($items[$delta]['etherpad_url']) && !empty($form['nid']['#value'])) ? $items[$delta]['etherpad_url'] : $field['etherpad_url'] . etherpad_generate_padid($field['etherpad_url']),
  );
  $element['etherpad_text'] = array(
    '#type' => 'value',
    '#value' => (isset($items[$delta]['etherpad_text']) && !empty($form['nid']['#value'])) ? $items[$delta]['etherpad_text'] : "default value for now until we have a function to generate one",
  );
  $element['attributes'] = array(
    '#type' => 'value',
    '#value' => (isset($items[$delta]['attributes']) && !empty($form['nid']['#value'])) ? $items[$delta]['attributes'] : serialize($field['attributes']),
  );
  // Used so that hook_field('validate') knows where to
  // flag an error in deeply nested forms.
  if (empty($form['#parents'])) {
    $form['#parents'] = array();
  }
  $element['_error_element'] = array(
    '#type' => 'value',
    '#value' => implode('][', array_merge($form['#parents'], array('value'))),
  );

  return $element;
}
?>

And that’s it! Read this and you’ll save yourself hours of frustration :)

One other note, the Devel module’s “reinstall module” function is very useful as you’ll be reinstalling the module often to reset the database w/ your changes. Enable the Devel block to access it.

This documentation doesn’t cover most of what you’ll need to know to write a CCK module. I relied heavily on the following tutorials.

Tagged with drupal | etherpad

Subscribe to get updated on new posts!

Kyle's profile picKyle Mathews lives and works in Seattle building useful things. You should follow him on Twitter. Currently exploring what's next and open to consulting.