by Jeff Eaton on June 26, 2009 // Short URL

The Great Pretender: Making your data act like a field

These days, almost every major Drupal site is using CCK, the module that lets you add custom fields to any content type. Among other things, CCK lets administrators rearrange a node type's contents using a simple drag and drop interface. In the past, this only worked for fields that CCK itself managed. If you worked with a custom module that altered a node's content, it was up to you to manage its position in the node content.

Now, though, it's possible for any module to tie into CCK's field management page to control the positioning of custom content. The key is hook_content_extra_fields(), and in this article we'll show you how to use it.

To demonstrate this technique, we'll fix something in Drupal that normally can't be re-ordered: the contextual links that go below each node, like "Add a comment" and "Bookmark this." While a Drupal theme can tweak the location of those links, there's no way to move them around from the administrative UI, and no easy way to position those links between two CCK fields.

The first step is to use hook_nodeapi() to create a new entry in the $node->content array that contains the rendered links. $node->content is a collection of data that's ultimately used to build the $content variable used by themes when printing a node. Data inside of $node->content can be easily tweaked and reordered by any module.

/**
* Implementation of hook_nodeapi().
*/
function link_mover_nodeapi(&$node, $op, $teaser, $page) {
  if ($op == 'view') {
    $links = module_invoke_all('link', 'node', $node, $teaser);
    drupal_alter('link', $links, $node);

    if (!empty($links)) {
      $output = theme('links', $links, array('class' => 'links inline'));
      $weight = content_extra_field_weight($node->type, 'links');

      $node->content['links'] = array(
        '#weight' => !empty($weight) ? $weight : 100,
        '#value' => $output,
      );
    }
  }
}

One of the key lines in that function is the call to content_extra_field_weight(). It's a utility function provided by the CCK module that returns the current 'weight' of a given item in relation to other parts of a node's content. If CCK isn't keeping track of the item we ask about, it will return a zero -- the 'default' weight of an item. If that happens, we substitute 100, so that the links will fall to the bottom of the node's content by default. How, though, can we get CCK to handle our new "links" element?

/**
* Implementation of hook_content_extra_fields.
*/
function link_mover_content_extra_fields() {
  $extras['links'] = array(
    'label' => t('Node links'),
    'description' => t('Links displayed when a node is viewed.'),
    'weight' => 100,
  );
  return $extras;
}

hook_content_extra_fields() is provided by CCK as well; it gives modules a chance to tell it what items they have that need to be considered when reordering a node's component fields. In it, we just need to return an array defining the name, description, and default weight of our item. Once we've done that, visiting the CCK 'Manage Fields' page for a given content type will give us the following:

Ta-da! CCK now lets us reorder the node links like any other field. There's only one piece left, though: removing the default $links variable so that the theme won't print it out in addition to our reorder-able version. That's easy enough, using hook_preprocess_node().

/**
* Implementation of hook_preprocess_node().
*/
function link_mover_preprocess_node(&$vars) {
  unset($vars['links']);
}

Once that's in place (and we've cleared the cache to ensure Drupal recognizes the new preprocess function), everything should work fine. Below is a screenshot of the final results after moving the 'Links' item above the node's body and other fields. I've also attached a zip file containing the sample code. Feel free to tweak it and experiment -- CCK is immensely popular, and tying into its configuration forms is a great way to make things easier for a site's administrators.

Jeff Eaton

Senior Digital Strategist

Want Jeff Eaton to speak at your event? Contact us with the details and we’ll be in touch soon.

Comments

Larry Garfield

Release?

So are you going to release link_mover as an actual module? You should. :-) It seems like a perfectly self-contained little utility, and a useful one to boot.

Reply

eaton

Possibly...

...But then I'd also have to support it! ;-) In all seriousness, that was my plan initially, but I realized that it was only a few dozen lines long and might make a more interesting example article. Perhaps a 'fleshed out' version that can control more of the default node variables like 'Submitted by' would make an interesting release...

Reply

agentrickard

Sounds like a nice project

Sounds like a nice project to hand to an eager developer who wants to get more involved with Drupal.

Maybe the next time someone bugs you for a feature in IRC, you can ask them to inherit and release the code.

/me is _not_ volunteering.

Reply

Anonymous

hook_preprocess_node

Thanks for this very useful Just one question: What's hook_preprocess_node ? I can't find it on the drupal API. Did you mean template_preprocess_node? Or is that hook implemented by a contrib module?

Reply

eaton

hook_preprocess_node()

template_preprocess_node() is very close -- it's the default implementation of hook_preprocess_node(). Basically, any time Drupal is about to use a tpl.php file to output some HTML, it calls 'preprocess' hooks for that template to allow other modules to get data ready. Usually this means sanitizing data for safe printing, formatting things like the 'Submitted by' line, and so on. It also, though, lets modules (like this one) change or remove variables that would otherwise be present in the tpl.php file. In this case, we're nuking the $links variable that node.tpl.php normally spits out.

Reply

Tom Geller

How about moving the Body? Location?

If I remember correctly, neither the Body nor the Location form (via Location module) can be moved. Could this trick be applied to make those items movable?

And could it all be packaged up into a module so non-coders like me have a shot at it? :)

Cheers,

--Tom

Reply

Benjamin Melançon

Great tutorial

Eldorado Superfly is a module that takes control of the "Submitted by..." text, node links, user pictures, and comments. It moves them to $node->content, where they can be re-ordered on CCK's Manage Fields tab. However, this means they are no longer under control of the theme. Whether this is a good thing or a bad thing is a question best debated late at night during Drupalcon.

Sign me up for the "Debate Eldorado Superfly" session, some hotel, Paris, 2:30 a.m...

Reply

Sean

Very cool :) LOL at the

Very cool :) LOL at the name, good to see it describes the module accurately.

Wonder if it'd be possible to make each individual Node Link movable so you could move "add new comment" to the top while keeping "Read more" at the bottom.

- Sean

Reply

Stephen

Thanks for the details

Nice little module for those that need this control and have never understood why we never had it.

Thanks for explaining the code in the module -- a great help to budding module developers as we can see just how simple a powerful module can be.

Reply

Steve

Anyone know how to use this without editing every module?

Well this helps me get a step closer to fix the order of fields that are on my node edit pages.

I'm wondering if anyone has yet to figure out how to implement this without going through every module that one is using that doesn't already make use of this hook.

Seems like there should be a way to take an array of fields and fieldsets that are being used for given node and add them in to the CCK manage fields.

If anyone has any ideas on doing this, I would really appreciate it!

Thanks in advance!
Steve

Reply

Anonymous

Display Part

Any way for the CCK display settings to work on the 'links' so we would be able to disable the links for certain types of post?

Reply

Nancy Wichmann

Fabulous!

Fabulous! But now that CCK knows about the fields, shouldn't Views be seeing them? How about other modules, such as Node Export?

Reply