Jun 23 2008
Jun 23

While working on various projects with drupal I realized that there is really no one definitive place which lists the concepts that you need to become familiar with when dealing with drupal. So I decided that I would put them in one place for people who maybe are experienced php developers who are trying to learn to work with drupal for the first time.

The main important components when working with drupal on a serious project are:

  1. Pages

    A page is a unit that most web developers are most familiar with already. And that is the problem, because when you are creating a website with drupal, the code is not organized in these bite sized chunks. The beauty of the drupal model of work is that drupal separates what you need to write into re-usable chunks of code which can be used on numerous pages, and is not confined to one page in particular.

    That being said, there are still instances where pages are used, but not in the same way as in a website you have to program from scratch. Often times pages are used instead as a means to configure areas of drupal, as admin pages etc.

  2. Blocks

    A block is a small chunk of code which can be placed on the same page as page content, or on a node (which I will explain next). The block is a handy thing because it is easy to put blocks across a large number of pages, and it is also possible to let the admin user configure the visibility of a block based on the user's permission level (called a user role) via the admin interface.

    These properties make blocks powerful in that you can put them in almost any configuration on a page, but not care ahead of time what else is on the page. In effect leaving page design to a designer or site admin.

  3. Nodes

    A node is a very important part of drupal, because it ties in so deeply with the drupal structure. A node is an object in drupal. The node can be displayed and expanded and acted upon by modules. In the drupal module repository there are many modules which will act on nodes allowing for some really cool things.

    The node can be a somewhat difficult concept to grasp, but thinking of it as a procedural object which can be acted upon, displayed, filtered, and restricted as a group. So a grouping of these "nodes" (known as a content type) can be exposed to different user roles. This means that if you want a bunch of pages which do the exact same thing you would create a new content type and then the individual nodes would all have the same structure.

  4. Theming

    Theming is important in drupal as it makes any code which is written and enables the designer to alter the output of that code, by adding tags, markup and css in order to make the website pretty. In this it is important to expose all html to drupal when you are writing it so that a designer can later open up the theme folder and overwrite your theming function adding these ever important tags.

    The theming engine in drupal is quite powerful and it is possible to procedurally theme small subsets of a page consistantly on a website by using this theming technique.

  5. Modules

    A module is where the exciting things happen in drupal. Because as a drupal developer you will want to avoid hacking drupal core as much as possible. This makes upgrading simple and easy, as well as keeping the codebase clean for your modules, making it possible for them all to work together instead of having one module vs. another.

    The module system in drupal again is very powerful because of the node structure, and drupal's ability to edit queries which are written in one area of drupal, or written in one module by another.

  6. Menu

    The menu structure in drupal is also important. This is the area of drupal which exposes certain menu items based on permissions, and also changes based on where you are in your website's tree structure. It is important to use the menu structure on your drupal websites because this structure is much more powerful than doing it by hand. And because of the permission based feature.

  7. Testing

    Going forward into drupal 7 testing is becoming increasingly important in the drupal community. This testing is becoming a part of the core list of modules, but also it is very helpful even in drupal 5 and drupal 6. Unit testing ensures that if you make changes to your code, that it does not effect the code you have already written.

    There has recently been a big push for testing to increase the productivity of coding and reduce the number of errors in the code. Drupal's testing is mostly done with the simpletest module found at http://drupal.org/projects/simpletest.

So this is my broad overview of what concepts a php programmer needs to know when starting to work with drupal. Please let me know if I forgot anything.

Related Posts

Debunking the myth: Content doesn't create itself

Content Management systems are the easy end all solution for creating content for the web, a magical thing which makes it easy to create awesome websites which get tons of visitors, right?
Jun 22 2008
Jun 22

It seems to be a big question in the community whether to develop new web sites using Drupal 5 or Drupal 6.

Read the full post on Millwood Online

Jun 21 2008
Jun 21

Friend Mark Bernstein promotes "software as craft" with the phrase NeoVictorian Computing. Jeremy recalls that "Part of his argument is that software creators have something to learn from the ideals of the arts and crafts movement: the software world is full of soulless bits and bytes, and maybe we would all be a little happier if we embraced handcraft ... During the talk, I remember Bernstein proposed that software creators should sign their work as a painter signs a painting, which is a lovely visual metaphor that I hope to keep around." And Greg Wilson has a book called Beautiful Code.

Happily, I already agree - they're all echoes of my own belief in "code shui", be it XML (a Morbus Rant from 2002 on "why beauty is important in computer file formats") or in code from 2004 ("His style is quite unique. [Morbus' AmphetaDesk] source reads almost like a paper, instead of terse code. He documents his code well and I've thus far found nothing that was very hard to understand. Best of all, its so un-Perl. He doesn't seem to use really clever tricks to do simple things, so the code has been very easy to understand").

Jun 21 2008
Jun 21

People who talk with others on IRC, particularly on #Drupal, will be happy to know that our loving IRC bot, Druplicon, is getting some love. Druplicon, if you don't know, is the IRC bot that idles in many of the Drupal IRC channels. It logs chats, helps you remember facts, and just is there when you feel like giving out the odd botsnack. Its powered by the Bot module, making it integrate with Drupal very nicely.

Anyway, I've been routing through the Bot module API lately and thought it could do much more then what it was currently being used for. Since it's run off of a Drupal website, we could integrate it with one of the many other modules out there. Just think, what could you do with an IRC bot on a Drupal website?

  • Dynamically create "meeting note" nodes when you're having a meeting.
  • Only accept people with Drupal user accounts into a given channel
  • Asterisk to make phone calls from an IRC command prompt.
  • Location module to spit out information about places it notices in a channel
  • Private Message module to send private messages through the website from IRC
  • Twitter to make tweet posts from your chat window.
  • Weather to tell you what it's like outside
  • Pirate module to have instant translations to Pirate-talk.... Yar.

In order to make any of these secure though, it definitely needed an authentication system. You wouldn't want people to make Twitter posts in your name, or take over your helpless IRC bot. So, I put together a small little module called that checks your IRC hostname,and your Drupal username. It worked pretty well, so I thought I'd take it one step further and allow me to make Twitter posts from IRC. Here's the result:

< RobLoach > TwitterBot: twitter Testing the new Twitter Bot Drupal module for IRC Twitter posting!
< TwitterBot > RobLoach: You successfully posted to Twitter.
< NotRobLoach > TwitterBot: twitter I'm posing as Rob Loach!
< TwitterBot > NotRobLoach: You are not authenticated to post on Twitter.

If you don't believe me, check the log! Anyway, posting to Twitter is just the beginning. If you want it, the code is available, and I'll be working with eaton, Morbus, dmitrig01 and cwgordon7 to make Bot module the best it can be. This is the start of some awesome things happening to Druplicon. Having authenticated sessions means we can do some really powerful stuff. I'm particularly looking forward to private messages that send when the recipient returns from being offline. What's on your wish list for Druplicon?

Jun 20 2008
Jun 20

A few days ago I got thinking that I've been using CSS frameworks such as Blueprint quite a lot and that they carry some bloat that makes them less than ideal for smaller projects. It didn't take long until I was creating a new theme, one that was clean and as bloat-free as possible. That theme is Clean theme.

In a couple of sessions I managed to create what I feel is a good solid foundation for building a small Drupal site.

Key features of the Clean theme:

  • Lean, mean base theme
  • Fully XHTML 1.0 Strict and CSS 2.1 valid
  • 3 column collapsible layout
  • Fancy blog and comment submission dates
  • Large base font size
  • Relative font sizes
  • Thorough use of CSS inheritance

I wanted to keep this theme as simple as possible so here's a brief rundown of design decisions that were made to facilitate this:

  • Fixed width, pixel based layout
  • Minimal reset using * { margin:0; padding:0; }
  • Minimal form styling (mostly tidying up)

Rather than trying to be everything to everyone, this theme is more of a base theme. Having said that it's got enough default styles to make it usable right out of the box.

Update: I'm not trying to say that the Drupal Blueprint theme is bloated. It's still an awesome theme and we're going to continue using it for major projects where 100% accuracy is required.

Posted by psynaptic

Jun 20 2008
Jun 20

In Wordpress, unlike in Drupal, terms are not lumped together in posts. Each Wordpress vocabulary has its own “template tag”, and the ones that come out-of-the box are: the_tags(), and the_category(). The following theming tweak is about putting order in Drupal terms before they're output to screen. It you need to break up your terms by vocabulary before you display them, read on.

Here's a Wordpress blog entry:

I will present an all-purpose solution that will print all terms by vocabulary. Each vocabulary list will be wrapped in its own HTML element, and I will use the vocabulary name as a 'label' for each list — a label you will be able to edit in the Administration Section of your Drupal site.


  1. Edit template.php to rebuild your $node->taxonomy array...

  2. ... and re-theme that array into a new $terms variable... and then...

  3. style your terms as needed in style.css.

Say you have two vocabularies, one for “free tagging”, and another for filing posts under sections, like so:

You may not like your free tags to be lumped together with your “Filed under” terms.

You may prefer to see something like this:

Let's get to it.

You will use a prepocess function for your node template. You will add this function to template.php if it has not already been defined. Open your theme template.php file in a text editor, and add the following code (please read the comments):

* Override or insert PHPTemplate variables into the node template.
function phptemplate_preprocess_node(&$vars) {
  // If we have any terms...
  if ($vars['node']->taxonomy) {
    // Let's iterate through each term.
    foreach ($vars['node']->taxonomy as $term) {
      // We will build a new array where there will be as many
      // nested arrays as there are vocabularies
      // The key for each nested array is the vocabulary ID.     
      $vocabulary[$term->vid]['taxonomy_term_'. $term->tid]  = array(
        'title' => $term->name,
        'href' => taxonomy_term_path($term),
        'attributes' => array(
          'rel' => 'tag', 
          'title' => strip_tags($term->description),
    // Making sure vocabularies appear in the same order.
    ksort($vocabulary, SORT_NUMERIC);
    // We will get rid of the old $terms variable.
    // And build a new $terms.
    foreach ($vocabulary as $vid => $terms) {
      // Getting the name of the vocabulary.
      $name = taxonomy_vocabulary_load($vid)->name;
      // Using the theme('links', ...) function to theme terms list.
      $terms = theme('links', $terms, array('class' => 'links inline'));
      // Wrapping the terms list.
      $vars['terms'] .= '<div class="vocabulary taxonomy_vid_';
      $vars['terms'] .= $vid;
      $vars['terms'] .= '">';
      $vars['terms'] .= $name;
      $vars['terms'] .= ':&nbsp;';
      $vars['terms'] .= $terms;
      $vars['terms'] .= '</div>';

Here is what the preprocess function does essentially:

The new HTML generated from print $terms (in node.tpl.php) is shown in this Firebug screen capture:

Creating PHPTemplate variables to pass on to your node.tpl.php template

As Opusoid recommends in a comment below, you can add each vocabulary to a new variable, that you pass on to your node template. In node.tpl.php, you can output each vocabulary variable wherever you wish. It gets super easy to place each vocabulary list in a precise location in the node: above it, below it, wherever you want. Brilliant, Opusoid, thank you!

Ordering vocabularies by weight — Improved version by manuee

Manuee modified the code snippet above to order vocabularies by weight. This is something you'll most likely prefer to do. After all, that's what weight is all about: presentation order. His code snippet is in his comment below. Thanks manuee!

Does that work in Drupal 5?

This solution will work in Drupal 6 only. Of course, there's an equivalent method for Drupal 5, and if someone asks for it I will provide it.

In Drupal 5, there was a function called taxonomy_get_vocabulary($vid) .This function has been renamed in Drupal 6 to taxonomy_vocabulary_load($vid). It has been renamed probably to bring naming consistency between functions that load objects, such as node_load() and user_load(). The function returns the vocabulary object matching the vocabulary ID $vid. The vocabulary object contains 'name' and 'description' properties, both of which you can use in your theme.

CSS styling

You may need to style your terms if you want them to appear on the same line, like so (these are rules added to the Garland theme style.css file):

 * Terms styling rules
.vocabulary {
  display: inline-block;
  padding-right: 1.5em;
.terms {
  float: none;

Last edited by Caroline Schnapp about 4 years ago.

Jun 20 2008
Jun 20

There is a discussion going on in php land about introducing closures and lambda functions, there was even a discussion on haskell-cafe about it (chx strikes again ;). About time, I would say. Having this functionality is a bonus. Having it implemented badly or half-arsed is going to do more damage than help. This is a short summary of what do I understand from the rfc and what do I think about it.

Anonymous functions aka lambda functions

The proposed syntax is

function & (parameters) { body }

No automatic variable capture. That is no variables from the parent scope are visible in the lambda function. So far so good, this is consistent with the default behaviour of php functions - you need to use global (in the process of becoming deprecated) or $GLOBALS to gain access to the global variables.

Closures and the newly proposed lexical keyword

That is something I'm not sure about. Although it is consistent with the current usage of global, with the combination of lambda functions it seems like a redundant syntax noise. The only explanation for me is to keep the runtime simpler, by requiring the explicit imports. Fair enough, although this verbosity does bug me a lot. It imports the variable by reference, similar to global. The consistency is a good thing. But consider the following trivial example:

for( $i = 1;  $i < 10;  $i++ ) 
  $a[] = function() { lexical $i; print $i;};
//What will it print?

As I understand the proposal and php (I might be wrong) the result of all of the $a[...]() functions will be 10. Which is definitely not what is intended - we can't refer to the same code in different contexts. But this is one of the most common uses for closures - capture the current state for later use! Erm. Yeah, sure, you could possibly work around it with explicit copy, but why? You lose update, true, but when do you actually need update? Which case is more 'natural'?

Can't one do something like:

$i = 1;
//highly illegal, i'll get locked up for this
$lambda = function ( $i &= $i ) { $i = 10; };

when you want to be able to update the parent variable? True &= is ugly. It will make the closures patch far more intrusive. It is true that the same trick - using defaults for arguments in a lambda will work the other way around. The code based on the rfc.

$i = 1;
for( $i = 1; $i < 10; $i++ ) {
  $lambdas['ref'][] = function () { lexical $i; print $i; };
  $lambdas['copy'][] = function ( $i = $i ) { print $i; };
//prints 9
//prints 3

But what is the more common use, especially existing exaples from other languages, and what is more natural in php? Least surprise?

The proposed interaction with classes reinforces my feeling of a half-baked proposal. Why automatically import $this, while not doing it for other variables. IMHO using references rather than copies is not thought through properly. Why do you need to make the $this automatic? Why do you need to use the class/object part of the scope, and make the anonymous function essentially an anonymous method, while not importing the whole scope and prune only the used parts at compile or run time? Why not something like:

class someclass {
   function one( ) {
       return function () { lexical $this; ... }
   function another( ) {
       //errrhhm - this might cause problems... 
       //since the inner $this is a copy, not a reference...
       return function ( $this = $this ) { ... }

Will it cause too much trouble to alpha convert a lambda, and leave it as simple as possible? Turning

$i = 1;
$x = function( $y ) { lexical $i; ... }

at compile time into the equivalent

$i = 1;
function lambda_xxxxgenname( $y, $i = $i ) { lexical $i; }

. Unfortunately this implies copy semantics of lexical and will have unexpected effects with variable function arguments use.

The problem probably lies with how do you do it in a way so that it doesn't change existing php semantics. I don't grok the php internals enough to actually do it, but I feel these questions are important.

OK, it is an RFC, now it is the time to discuss. It is obvious that there will be a need for a compromise, since changing too much will make a majour mess in an already delicate runtime.

As a side note, proper lambdas will help clean up the phptemplate trickery in drupal. Now, it does some hairy acrobatic acts to achieve somewhat similar functionality.

update: added the side by side comparison code for the copy versus reference(using the current rfc) semantics (not sure about the default arguments part)

And a challenge - can you define the Y combinator in php with lambdas & closures?

Jun 18 2008
Jun 18

You want to add quick edit links to the teaser view of your nodes — one link to edit the node, and another to delete it, and you want these links to be shown only to users with the 'administer nodes' permission. Alternatively, you may want these links to be viewed only by the user with uid (user ID) 1.

First Solution

  1. Edit node.tpl.php to add an unordered list of 2 links, contained within a div with a class name (e.g. 'quick-edit-links') and...

  2. style these links in your theme's style.css file.

Say you would like anyone who's logged-in and who has 'administer nodes' permission to see these two additional links below the teaser of every node: Edit [content-type-name] and Delete [content-type-name], like so:

Let's get to it.

In your theme folder, your file node.tpl.php may look like this before the edit (I am only providing a snippet of the template here):

<?php if ($links): ?>
  <div class="links"><?php print $links; ?></div>
<?php endif; ?>

Below the above code, you will add your new 'quick edit' links like so:

<?php if ($teaser && user_access('administer nodes')): ?>
  <?php $content_type_name = node_get_types('name', $node); ?>
  <?php $quick_links['quick-edit'] = array('title' => 'Edit ' . $content_type_name, 'href' => 'node/' . $nid . '/edit'); ?>
  <?php $quick_links['quick-delete'] = array('title' => 'Delete ' . $content_type_name, 'href' => 'node/' . $nid . '/delete'); ?>
  <div class="links quick-edit-links">
    <?php print theme('links', $quick_links, array('class' => 'links inline')); ?>
<?php endif; ?>

In the above snippet, you build a new array of links, $quick_links, and you theme that array with the function theme('links', ...).

You may prefer to write the markup yourself, using the Drupal function l(), like so:

<?php if ($teaser && user_access('administer nodes')): ?>
<?php $content_type_name = node_get_types('name', $node); ?>
  <div class="links quick-edit-links">
    <ul class="links inline">
      <li class="first quick-edit"><?php print l('Edit ' . $content_type_name, 'node/' . $nid . '/edit'); ?></li>
      <li class="last quick-delete"><?php print l('Delete ' . $content_type_name, 'node/' . $nid . '/delete'); ?></li>
<?php endif; ?>

Second Solution

In template.php, using a preprocess function, add 2 new links to $node->links and re-theme (like a re-rince) $node->links to overwrite the existing $links variable. And... style as needed.

Let me show you what I mean. You may want to add the quick edit links next to the other node links, like so:

To achieve this, in your theme's template.php file, you will add a prepocess function for your node template. Open your theme template.php file in a text editor, and add the following code:

* Override or insert PHPTemplate variables into the node template.
function phptemplate_preprocess_node(&$vars) {
// If we are in teaser view and have administer nodes permission
if ($vars['teaser'] && user_access('administer nodes')) {
// get the human-readable name for the content type of the node
$content_type_name = node_get_types('name', $vars['node']);
// making a back-up of the old node links...
$links = $vars['node']->links;
// and adding the quick edit link
$links['quick-edit'] = array(
'title' => 'Edit ' . $content_type_name,
'href' => 'node/' . $vars['nid'] . '/edit',
// and then adding the quick delete link
$links['quick-delete'] = array(
'title' => 'Delete ' . $content_type_name,
'href' => 'node/' . $vars['nid'] . '/delete',
// overwriting the $links variable with our new links THEMED
$vars['links'] = theme('links', $links, array('class' => 'links inline'));

Here's what you've done:

The first solution will work in Drupal 5 as well. Both solutions work only for PHPTemplate-powered themes. With the first solution, it's easy to place the additional links anywhere in the node view.

If you want your quick edit links to be viewed only by the all-mighty Administrator, in your file node.tpl.php, you will replace this code...

&& user_access('administer nodes')

... with that one:

&& ($user->uid == 1)

The variable $user is part of what's called in themespeak the default baseline variables. The baseline variables are available to all template files, as they are generated by the preprocess function template_preprocess(). No need to refer to the oft-used global $user in your template file (although that will work fine as well). Just use the baseline variable $user (it's less code). However, in the preprocess function defined above, to make use of the baseline variable $user, you'll need to write $var['user'].

&& ($var['user']->uid == 1)

You can find more information on preprocess functions (new to Drupal 6) in this other article. Preprocess functions are used to pass additional variables to template files, or to modify the variables already passed to them. In our second solution, we modified the variable $links, which is a themed representation of the node links.

A little variation

Showing these links to people with 'administer nodes' permission is quite restrictive: chances are only a handful of people — maybe one person — have access to the Administration section for content.

You may have a web site with many contributors. Some will have 'edit' permission to some or all of the site content, with or without a right to 'delete'. When the situation is relatively complex, and you do want quick edit links whenever possible, you can rely on the Drupal's function node_access($op, $node, $account = NULL) as Roger López points out in this comment. The preprocess function rewritten with the use of node_access() becomes:

* Override or insert PHPTemplate variables into the node template.
function phptemplate_preprocess_node(&$vars) {
// If we're in teaser view and have right to update(edit) the node
if ($vars['teaser'] && node_access('update', $vars['node'])) {
// get the human-readable name for the content type of the node
$content_type_name = node_get_types('name', $vars['node']);
// making a back-up of the old node links...
$links = $vars['node']->links;
// and adding the quick edit link
$links['quick-edit'] = array(
'title' => 'Edit ' . $content_type_name,
'href' => 'node/' . $vars['nid'] . '/edit',
// and then adding the quick delete link
    // if we have the right to delete the node
if (node_access('delete', $vars['node'])) {
$links['quick-delete'] = array(
'title' => 'Delete ' . $content_type_name,
'href' => 'node/' . $vars['nid'] . '/delete',
// overwriting the $links variable with our new links THEMED
$vars['links'] = theme('links', $links, array('class' => 'links inline'));

The function node_access($op, $node, $account = NULL) determines whether the current user (OR the user with the specified $account) has the right to perform a given operation on a specified node. If he has the right to perform the operation, the function returns TRUE, if not it returns FALSE. The operations, given by the flag $op, are 'view', 'update' (to edit the node), and 'delete'.

Take note of this: if the node was last saved with an input format not permitted to the user (e.g. say it has been edited by the Administrator who saved it in the 'PHP code' input format), the user won't be able to edit his node anymore — for obvious security reasons. In the node_access() function, you'll find this:

// If the node is in a restricted format, disallow editing.
if ($op == 'update' && !filter_access($node->format)) {
  return FALSE;

Last edited by Caroline Schnapp about 5 years ago.

Jun 18 2008
Jun 18

You want to print the content type name along with the 'submitted' info. This will be particularly useful for those who have created custom content types, with or without CCK.


  1. Edit node.tpl.php to add the content type name within a span with a class name (e.g. 'content-type-name') and...

  2. style that span element in your theme's style.css file.

In the following example, the human-readable name for the content type is 'News', and the content type is displayed to the right of the 'submitted' info. Let's try and achieve that.

Your file node.tpl.php may look like this before the edit (I am only providing a snippet of the template here):

<?php if ($submitted): ?>
    <span class="submitted"><?php print $submitted; ?></span>
<?php endif; ?>

If it does, then you will change the above snippet to that:

<?php if ($submitted): ?>
  <span class="submitted"><?php print $submitted; ?> &mdash; <span class="content-type-name"><?php print node_get_types('name', $node); ?></span></span>
<?php endif; ?>

You may want to exclude a certain content type. For example, you may not want 'page' to appear in page nodes. Whenever you want to exclude one content type, you will do like so:

<?php if ($submitted): ?>
  <span class="submitted"><?php print $submitted; ?><?php print ($node->type != 'page') ? ' &mdash; <span class="content-type-name">' . node_get_types('name', $node) . '</span>' : ''; ?></span>
<?php endif; ?>

When you want to exclude more than one content type, say 'story' and 'page', you will do like so:

<?php if ($submitted): ?>
  <span class="submitted"><?php print $submitted; ?><?php print (!in_array($node->type, array('story', 'page'))) ? ' &mdash; <span class="content-type-name">' . node_get_types('name', $node) . '</span>' : ''; ?></span>
<?php endif; ?>

This will work in Drupal 5 as well. Again, this solution works only for PHPTemplate-powered themes.

You need to provide the machine-readable names for content types you wish to exclude, because $node->type is used in the condition, and it is the machine-readable name for the content type. Why are we comparing machine-readable names? Simply to avoid database queries. There is one database query we cannot avoid in our solution, it is the one query we execute to print the human-readable name for the content type.

Possible variations: place the content type information somewhere else in the node; display the content type information only in the teaser; etc.

The machine-readable name of a content type is usually not the same as its human-readable name. Sometimes the difference is only in capitalization:

We used the Drupal function node_get_types() in our solution.

The function can be used in so many ways that it gets rather confusing, as webchick points out here. The function can be used in these seven different ways:

node_get_types('type', $node);
node_get_types('type', $node->type);
node_get_types('name', $node);
node_get_types('types', NULL, TRUE);
node_get_types('module', $node);

When we use the function like so:

node_get_types('name', $node);

... the returned value is a string, and it is the human-readable name for the content type of the $node object.

By the way, the $node object is available in all these templates:

  • comment.tpl.php
  • node.tpl.php
  • page.tpl.php when a node is displayed on its dedicated page, ie: at node/x

In Drupal 7, there will be a new function to get the human-readable name for the content type of a node:

$name = node_get_name($node);

Until then, we'll make-do-and-mend with node_get_types().

Last edited by Caroline Schnapp about 5 years ago.

Jun 18 2008
Jun 18

You want the comment style to be different for comments added by the author of a node — who's commenting on his own node. For example, you may want to highlight the node's author's comments, so that any visitor skimming through the comments will easily differentiate them from other comments.


  1. Edit comment.tpl.php to add a new class (let's say 'comment-by-author-of-post') that...

  2. you can style in your theme's style.css.

Your file comment.tpl.php may look like this before the edit (I am only providing a snippet of the template here):

<div class="comment<?php print ($comment->new) ? ' comment-new' : ''; print ' '. $status ?> clear-block">

If it does, then you will change the above snippet to that:

<div class="comment<?php print ($comment->new) ? ' comment-new' : ''; print ($comment->uid == $node->uid) ? ' comment-by-author-of-post' : ''; print ' '. $status ?> clear-block">

You could now add these two rules to your style.css file, for example:

 * Comment styling rules
.comment-by-author-of-post {
  border: 1px solid #ccc;
  background-color: #eee; 
/* reset to default values when previewing */
.preview .comment-by-author-of-post {
  border: none;
  background-color: transparent;

This will work in Drupal 5 as well. No, it won't. The $node object is not passed to the comment.tpl.php template. Read the comments below for the Drupal 5 recipe. This solution works only for PHPTemplate-powered themes. If your theme does not provide a template file for comments, copy the one from modules/comment/ to your theme folder, and edit it. And yes, the $node object is available in comment.tpl.php. It is the node the comment is attached to. Possible variations: special-style all comments authored by those who have the 'admin' role; special-style anonymously-submitted comments; etc.


($a == 11) ? 'eleven' : 'not eleven'

The above code snippet returns a value. If $a equals 11, the return value is 'eleven'; otherwise, the return value is 'not eleven'. You can print the return value of a ternary comparison operator like so:

print ($a == 11) ? 'eleven' : 'not eleven';

Notice that I added both 'print' and a semicolon in the above snippet.

From php.net: “The expression (expr1) ? (expr2) : (expr3) evaluates to expr2 if expr1 evaluates to TRUE, and expr3 if expr1 evaluates to FALSE.” The ternary comparison operator is used a lot in Drupal templates. In our solution we printed ' comment-by-author-of-post' — in the class attribute of the comment div — if and only if the comment author was the same guy/girl as the author of the node the comment was attached to. And we printed nothing otherwise, ie: ''. (These '' are two SINGLE quotes.) We added this:

<?php print ($comment->uid == $node->uid) ? ' comment-by-author-of-post' : ''; ?>

Last edited by Caroline Schnapp about 5 years ago.

Jun 14 2008
Jun 14

As a programmer with absolutely no design skills, and markup skills that haven't quite made it into the web 2.0 era, I always collaborate with others when building a new site from scratch, or when embarking on a redesign or any substantial UI changes. Usually I act as the themer, transforming finished markup into a set of templates.

Getting Drupal friendly markup from the start makes the task of theming much, much easier.

The theme guide is a fantastic reference for those with a basic understanding of PHP. But if your markup partner doesn't program, or if they don't work on Drupal sites full-time, it's overkill.

This document contains the information from the theme guide that I think is most relevant for a markup expert who doesn't know PHP and doesn't have in-depth experience with Drupal, along with some practical examples of markup from this site.

Drupal Basics

It's helpful to define some terms, especially those that are relevant for theming.

Page A full page, including persistent theme elements (header/footer), module-specific elements, and content Block A small block of content that may be reused on multiple pages is a block. Each block has a unique ids. Blocks are assigned to a region on the page, and can be restricted to appear only on certain pages. Node The main block of content on a page - like a blog post or a forum post - is usually a node. Each nodes has a unique id, which appears in the URL for the page containing that node. Content Type Each node has a content type. Page is usually used for static content. Story is usually used for blog posts. Module Modules can provide pages, blocks, and content types to a drupal site. Each module has a unique name. Path The URL for a particular page. A module usually inserts it's name into the path. For example, the forum module provides a page that lists all forums. The path for that page is /forum.


A page is built out of a number of template files.

  • page.tpl.php is the main template, containing header and footer and includes for all the other template files
  • block.tpl.php is a wrapper for block content.
  • node.tpl.php is a wrapper for node content.
  • comment.tpl.php is a wrapper for comments (replies to nodes).
  • box.tpl.php is used as a wrapper for non-block content in a couple of places.

As an example, imagine a page with 3 blocks (header, footer, and navigation), a single node, and 5 comments. In this case, the markup in page.tpl.php will be included once, block.tpl.php 3 times (once for each block), node.tpl.php once, and comment.tpl.php 5 times (once for each comment).

CSS hooks

Drupal's default themes provide a ton of information in the id and class attributes. Module name, content type, node id and block id are usually assigned as ids of divs. The div that contains a node has class="node". The div that contains a block has class="block". The div the contains the content of a node or block has class="content".

Each form in the site has a unique name that is part of the form's id (for example, the user login form is id="form-user-login".

Various sub-parts of the node template (error messages, related links, breadcrumbs, etc.) have class names or ids.

Navigation menus, lists, and tables all have a standard set of classes and ids.

This allows one to customize layout based on all of the above criteria.

It's possible to write code to insert other information into a class or id attribute (username, logged in status, day of the week, whatever) in order to make it easy to change the layout based on whatever you need to.

Template Overrides

The template files can be overridden depending on the path, module, or unique id of the item being themed. The various templates have different options for override.

  • page.tpl.php can be overridden by module. page-forum.tpl.php will be used for the forum module instead of page.tpl.php. page-front.tpl.php is used on the front page.
  • node.tpl.php can be overridden by module or content type, for example page-story.tpl.php
  • block.tpl.php can be overriden by module or by block id, for example block-search.tpl.php or block-1.tpl.php

It's possible to write code to override templates on other criteria.


These examples are all from this site and the abac theme that it uses. It's also helpful to look at drupal.org, which uses the core bluemarine theme (or something close to it).

Sometimes it's helpful to choose a base theme with your markup partner, so that an agreed upon set of classes and ids can be used by both parties. Sometimes it's enough for them to know the main drupal conventions, and that as long as they are consistent in their use of classes for layout-specific functions, it will be easy for the themer provide those classes in their templates.

Navigation Menu

<div class="headMenu">
<ul class="links primary-links"><li class="first last menu-1-1-2-active"><a href="http://twtitw.firebus.com/blog/3" class="menu-1-1-2-active">Firebus's Blog</a></li>

First and lass classes are provided, primary-links is the name of the menu block, the menu item (menu-1-1-2-active) describes the place in the menu system of this link, and its active status.

It would be nice to have active as a separate class!

Search box

<div class="search">
<form action="/search/node" method="post" id="search-theme-form">
<div id="search" class="container-inline">
<div class="form-item">
<input type="text" maxlength="128" name="search_theme_form_keys" id="edit-search-theme-form-keys" size="15" value="" title="Enter the terms you wish to search for." class="form-text" />
<input type="submit" name="op" id="edit-submit" value="Search" class="form-submit" />

The enclosing div with class="search" names the module.

The form has a unique id.

Form elements have class form-item, and each input element has a class the reflects input type and a unique id. So can style all form elements, all text inputs, or this specific text input, in CSS.

Node and various other bits

<div class="breadcrumb"><a href="http://twtitw.firebus.com/">Home</a> › <a href="http://twtitw.firebus.com/blog/3">Firebus's Blog</a></div>


<h1 class="with-tabs">Favorite Games</h1>
<ul class="tabs primary">
<li class="active"><a href="http://twtitw.firebus.com/node/21" class="active">View</a></li>
<li><a href="http://twtitw.firebus.com/node/21/edit">Edit</a></li>

Node title and tabs list (usually only privileged users have tabs for a node, although logged in users see tabs on user profile edit forms).

<div id="node-21" class="node">
<div class="picture"><a href="http://twtitw.firebus.com/user/3" title="View user profile."><img src="http://twtitw.firebus.com/files/pictures/picture-3.jpg" alt="firebus's picture" title="firebus's picture" /></a></div>
<span class="submitted">Wed, 04/30/2008 - 11:46 — <a href="http://twtitw.firebus.com/user/3" title="View user profile.">firebus</a></span>

There's a unique id for this node, as well as a general class. If for some (bad) reason you wanted to style a specific node in CSS you can.

The next div is the user display with a picture, username, profile link, and date of submission.

<div class="content">

The actual content of the node, pulled from the database.

<div class="clear-block clear">
<div class="meta">
<div class="terms"><ul class="links inline"><li class="first taxonomy_term_6"><a href="http://twtitw.firebus.com/taxonomy/term/6" rel="tag" title="" class="taxonomy_term_6">Games</a></li>
<li class="last taxonomy_term_3"><a href="http://twtitw.firebus.com/taxonomy/term/3" rel="tag" title="" class="taxonomy_term_3">Self-Reference</a></li>
<div class="links"><ul class="links inline"><li class="first blog_usernames_blog"><a href="http://twtitw.firebus.com/blog/3" title="Read firebus's latest blog entries." class="blog_usernames_blog">firebus's blog</a></li>
<li class="last comment_add"><a href="http://twtitw.firebus.com/comment/reply/21#comment-form" title="Share your thoughts and opinions related to this posting." class="comment_add">Add new comment</a></li>

Links to categories, and links to add comments.


<div id="comments"><h2 class="comments">Comments</h2><a id="comment-207"></a>
<div class="comment odd">
<div class="clear-block">
<span class="submitted">Sun, 05/11/2008 - 23:36 — Anonymous</span>
<h3><a href="http://twtitw.firebus.com/node/21#comment-207" class="active">you forgot one.</a></h3>
<div class="content">

We've also got a general class and a unique id for comments.

There are some layout-specific classes like "clear-block", and we're providing odd and even classes in case you want alternating backgrounds.

<div class="links"><ul class="links"><li class="first comment_delete"><a href="http://twtitw.firebus.com/comment/delete/207" class="comment_delete">delete</a></li>
<li class="comment_edit"><a href="http://twtitw.firebus.com/comment/edit/207" class="comment_edit">edit</a></li>
<li class="last comment_reply"><a href="http://twtitw.firebus.com/comment/reply/21/207" class="comment_reply">reply</a></li>

The comment also has some links

<div class="indented"><a id="comment-210"></a>
<div class="comment even">

The next comment starts with indentation specified, as it's a reply to the previous comment.

Jun 12 2008
Jun 12

Now that's a pretty tag cloud! I guess I bookmark Drupal-related stuff a lot.

Jun 11 2008
Jun 11

It has recently come to my attention that I was the only one on drupal.org that had "awesomeness" as an interest. My complaints on irc sparked the discussion: what does the word "awesomeness" mean? Is it even a word? And why is Drupal pure awesomeness?

So I compiled a list of definitions of "Awesomeness" from the internet:

So yes, everyone, awesomeness is a real word. So now I am definitely allowed to say it as much as I want. And, please join me in my quest to have http://drupal.org/profile/interest/awesomeness list as many users as possible!

Jun 11 2008
Jun 11

I am now half way through the Lullabot Drupal training here in Toronto.

Read the full post on Millwood Online

Jun 11 2008
Jun 11

I recently started using a Mac for web development (I don't love it but that's another story). At the recommendation of several friends I'm using MAMP instead of the native Apache, PHP, and MySQL; they said it was much easier to set up. Yesterday, I discovered that MAMP's PHP install does not come with the OpenSSL extension so, for example, you cannot visit HTTPS sites from within PHP. Searching the web revealed several posts from people with the same problem but no solutions. I have no idea why MAMP does not ship with the openssl extension but, it turns out, it is very easy to create yourself. Here's how:

1. Build the PHP openssl extension (or, if you are really lazy, just download the one I built).

Download PHP from php.net. I'm using MAMP 1.6 (haven't bothered to upgrade) which uses PHP 5.2.1 which, it turns out, does not build properly on MacOS X 10.5 Leopard. So I used PHP 5.2.5 instead which works fine; this is also the version that MAMP 1.7 uses. Newer versions of 5.2 would probably also work.

Using Terminal, extract, configure, and build PHP, specifically requesting a *shared* openssl library:

% tar -xzf php-5.2.5.tar.gz
% cd php-5.2.5
% ./configure --with-openssl=shared
% make

2. Install the PHP openssl extension

From the same directory in which you ran make:

cp modules/openssl.so /Applications/MAMP/bin/php5/lib/php/extensions/no-debug-non-zts-20050922/

The date in the final directory name will depend on which version of MAMP/PHP you have installed.

3. Edit /Applications/MAMP/conf/php5/php5.ini.

Search for lines that contain "extension=" and add the line


That's it!

Attachment Size openssl.so 80.46 KB
Jun 09 2008
Jun 09

For those of you who don't know what Disqus is, it's a web service that provides a slick enhancement to comments in websites. Usually when you visit a website, you see a discussion going. This discussion usually just takes place on that website. What you're left with is a bunch of different websites with a bunch of different discussions going on. Disqus rethinks this philosophy by bringing all of those different discussions together.

If you're making a comment on a website that uses Disqus, you'll be able to not only do cool things like make posts from your mobile device, reply to threads through email or make voice comments, you'll also be able to see other discussions going on about the same thing from other websites. You can track where you're discussions are taking place, and get email updates when replies are made. You can also subscribe to a user's comments, export your posts, or import new sets of comments. In essence, Disqus is rethinking how discussions are taking place on multiple different websites.

Although the Drupal comment module is pretty nice, it didn't do exactly what I wanted on my own site. Disqus seemed like a nice alternative, so I put together a Disqus Drupal module, which implements the Disqus web service using Drupal. It allows you to add a discussions on any node type (although we could easily extend this to any taxonomy item, any view, etc). The Disqus guys are working hard on adding import features, as well as extending their API, so there are plans to take advantage of those with this module later on, but this is a good start. If you're interested in trying it out, feel free to make a comment on this post saying where you plan to use Disqus. Enjoy the Disqus Drupal module, and feel free to add any additional ideas you may have!

Jun 06 2008
Jun 06

6th Jun

Come on people, STOP IT! You know who you are. "Subscribing" to an issue on a drupal.org project (can we guess which ones, people?) simply fills the thread full of useless comments, cluttering up any valid discussion enough to make any maintainer cry.

So, again, please stop. Bookmark the issue. Use RSS. Visit the Contributed Module Status group on groups.drupal.org, and help maintain the current branches status list - get some links to the relevant issues in there for us all to "subscribe" to!

And of course, if you really want the issue to enter into your "My Issues" queue, then contribute something more worthwhile than "subscribing" or "..."


Jun 05 2008
Jun 05

I bought a new laptop at the start of this year, and since then I've experienced the "privilege" pile of festering camel dung that is being a user of Windows Vista. As with most things in Vista, installing Drupal and Apache is finickier than it used to be, back on XP. When I first went through the process, I encountered a few particularly weird little gotchas, and I scribbled them down for future reference. Here are some things to look out for, when the inevitable day comes in which you too will shine the light of Drupal upon the dark and smelly abyss of Vista:

  1. Don't use the stop / start / restart Apache controls in the start menu (start > programs > Apache > control), as they are unreliable; use services.msc insetad (start > run > "services.msc").
  2. Don't edit httpd.conf through the filesystem — use the 'edit httpd.conf' icon in the start menu instead (start > programs > Apache > configure), as otherwise your saved changes may not take effect.
  3. If you're seeing the error message "http request status - fails" on Drupal admin pages, then try editing your 'c:\windows\system32\drivers\etc\hosts' file, and taking out the IPv6 mapping of localhost, as this can confuse the Windows mapping of to localhost (restart for this to take effect).
  4. Don't use Vista! If, however, you absolutely have no choice, then refer to steps 1-3.
Jun 04 2008
Jun 04

I've talked about Blueprint here in the past because I use it here and on konigi. Fans of Drupal and Blueprint will be happy to hear that Ted Serbinski has released a Drupal theme that utilizes the Blueprint CSS Framework. Keep in mind that this is for fixed-width layouts only.

Among the features announced:

  • normalizes Drupal’s CSS to be consistent
  • properly aggregates all blueprint CSS files into a single file when this setting is enabled
  • put scripts at bottom of page for nice performance gains
  • flexible layout, from 1 to 3 columns, based on where you configure your blocks to show (left, center, right)
  • SEO optimization without the need for heavy modules and additional queries per page
  • better forum icons
  • improved forum display and performance
  • prevents duplicate form submissions with jQuery
  • shows the # of comments below a node since Drupal doesn’t do this by default (usability)
  • highlight any comments by the author of the node
  • adds a “you need to login / register” box below all comments if you can’t add a comment (usability)
  • supports conditional comment subjects, if the setting is off it won’t show a chopped off title of the comment
  • uses CSSEdit comments for grouping of styles
Jun 04 2008
Jun 04

Last night I put together a simple module for Drupal to implement the XRDS-Simple spec. It is based loosely on the xrds-simple wordpress plugin.

XRDS-Simple is an important piece of the DiSo project. From the XRDS-Simple spec::

XRDS-Simple provides a format and workflow for the discovery of resources metadata, and other linked resources. As web services continue to grow, applications utilize a wider range of web services and resources across multiple providers. XRDS-Simple allows providers to document their resources in a machine-readable way, which can be automatically discovered by consumer applications.

So, check it out: http://drupal.org/project/xrds_simple .

Jun 04 2008
Jun 04

Last night I put together a simple module for Drupal to implement the XRDS-Simple spec. It is based loosely on the xrds-simple wordpress plugin.

XRDS-Simple is an important piece of the DiSo project. From the XRDS-Simple spec::

XRDS-Simple provides a format and workflow for the discovery of resources metadata, and other linked resources. As web services continue to grow, applications utilize a wider range of web services and resources across multiple providers. XRDS-Simple allows providers to document their resources in a machine-readable way, which can be automatically discovered by consumer applications.

So, check it out: http://drupal.org/project/xrds_simple .

Jun 02 2008
Jun 02

A lot of people have been eager for a Drupal 5 version of Shadowbox so I thought I'd spread the word that there is now a Shadowbox 5.x-1.x-dev version available! You can download it from the Shadowbox project page.

There were originally problems getting Shadowbox to work with earlier version of jquery_update but those issues have now been solved thanks to the jquery_update maintainers. The Shadowbox 5.x module depends on jquery_update 5.x-2.x-dev with a minimum release date of 2008-May-31.

Some may ask why there isn't an official release available for Shadowbox so I'll explain. The functionality of the module is very basic at the moment, you have to inject the rel attribute into the theme layer manually. Stella Power posted a great article entitled Drupal “Lightbox” Type Module Comparsion. In her comparison Stella notes that Shadowbox doesn't yet integrate with any other Drupal modules, therefore it wouldn't be right to say Shadowbox is at 1.0 just yet. The plan for the future is to integrate with other modules as tightly as Lightbox2 and Thickbox modules do (priorities include imagefield, imagecache, image).

So the crux of the matter is, We're happy to use Shadowbox in a production environment even though the module is still in development status. At this early stage the module needs testing by the community, so if you find any bugs be sure to report them in the issue queue.

Posted by psynaptic

Jun 01 2008
Jun 01

Top-10 Countdown

Open Source Success


How to ensure a winning implementation on your campus.

Nate AngellNate Angell co-directs a collaborative team that offers services and technologies across a range of teaching, learning, research, and communication needs at Portland State University, Oregon’s largest, most diverse, and only urban public university. Angell’s current focus is on integrating Portland State’s academic and web communication technologies on an open source platform. He is an active member of open source communities including Sakai, Open Source Portfolio, and the web content management platform Drupal. Angell and members of Portland State’s team regularly help other campuses and institutions implement enterprise open source technologies. Here, Angell offers a number of intelligent ways to help open source implementations succeed.


Engage deeply with the community surrounding open source technology.

  • Take control of your destiny and help shape technology roadmaps.
  • Share early and often: Active participation raises your stature.
  • It’s not only for coders: The community needs a range of skills.

Remember, any IT environment can take advantage of open source.

  • Literally any campus—regardless of its IT resources—can succeed.
  • If you need help, vendors offer various levels of support and hosting.

Understand how change happens at your institution.

  • History repeats: Look at past attempts at change on your campus.
  • Take heed where your plan d'es not match institutional culture.

Build the right implementation team.

  • Who can speak effectively with decision-makers and those who influence them?
  • Who can translate technical issues for non-technical audiences?
  • Bring potential conflict inside: Engage critical voices directly.

Leverage drivers of open source adoption on your campus.

  • Prioritize and amplify factors that engage existing passions.
  • Consider a Trojan horse: a smaller, more innocuous goal to start.

Communicate effectively—it’s crucial to your implementation’s success.

  • Plan how to communicate with everyone affected by your implementation.
  • Speak about the larger vision the implementation will enable.
  • Stay on message: Every activity is an opportunity to communicate your vision.

Think outside the box for training and support.

  • Cell phones are a global phenomenon.
  • Integrate training and support: Support calls are training opportunities.
  • Mix it up: Offer resources in a variety of formats, levels, and schedules.

Plan for assessment in every stage and activity of your implementation.

  • Good assessment is not an afterthought; start early.
  • Measure progress toward your goals; don’t merely evaluate tools.
  • Surveys are two-way communications: Use them to support your message.

Stage your success.

  • Pilot everything—from technologies to communications—with increasingly
    larger groups.
  • Think of pilots as dress rehearsals (implementation practice), not as bake-offs
    (technology selection).
  • Champions are made, not born: Use pilots to transform users.

Keep telling the story.

  • Don’t miss a chance to help people understand your vision.
  • Everyone, from front-line user support to your CIO, should speak about the
    change new tools will enable—the tools are means to other ends.
  • People will buy in to visions of change—not just changing technology.
May 31 2008
May 31

In a weeks time I will be sat on a plane to Canada.

Read the full post on Millwood Online

May 31 2008
May 31

Shopping.com API Update

On May 14th, 2008 Shopping.com released the new version (version 3) of their API, the Shopping.com API, commonly called the SDC API. If you were a Shopping.com API version 2 user you will be very happy to see such changes as:

  • Unified Schema - category, products, offers, reviews and hybrid results are all in one unified xml schema.
  • Single Calls - in version 2 if you needed store offers on a category call you would have to make another call, per product sometimes, burning up lots of your 250,000 limit. Due to the unified schema and new filter options you just need to make one call.
  • Sorting products, offers, and reviews is much more efficient now. No more multiple calls to get a sorted result.
  • Version 2 was plaqued with parameters that didn't make sense when first read, like tp, befid, sam, np, nm and more. Version 3, the parameters are very easy to understand and you know what is happening in the query due to the renaming of the parameters. IE, showProductOffers, showProductSpecs, showProductReviews etc.
  • Java developers have more help this time around with the SDK kit, a set of Java classes to develop a working product much quicker.
  • There is good documentation, this time around, for the XML Schema, Use Cases, and REST Option Reference.

If you were a Shopping.com API version 2 developer or user you will welcome these changes, sooner or later. Shopping.com is going to give all partners 1 year to upgrade to the new version. At first, I thought this was too much to ask but after reviewing the lengthy white paper I changed my mind. A developer familiar with the old Shopping.com API will reconize right away the benefits from the new schema. A big thank you to the Shopping.com API engineers!

Drupal Shopping module

The module (never was released to the public) developed for Camera Prices in 2005 was built for version 2 of the Shopping.com API and coded for Drupal 4.3. Very outdated. A few months ago I began blogging about the Shopping.com API, releasing this Drupal Shopping module to the community, and what to expect from the module.

Upto a few weeks ago I was about 65% of the way through building this module, before version 3 of SDC API was announced. Since I am not finished with the SDC version 2 of the module I decided not to support Version 2 and work towards version 3 instead. Unfortunately the changes are significant and require a rewrite of the "request" and "response" parts of the module. The XML Schema has changed as well which requires all sorts of rule changes within the module code as well module settings.

Last week I began planning the module architecture. As of today I have finished the "request" part of the Shopping module. This weekend I look forward to testing it and getting the "response" code built up. I am finding developing this module for SDC Version 3 is going much quicker than I expected.

LinkedIn Group - You're Invited

I have started a LinkedIn Group for those interested in the Shopping.com API and this Drupal Shopping module. Feel free to join to network with others or just to receive updates. As of today there are 9 members in the group, which I hope a few will do beta tests once the module is available. :)

Shopping Module Website

In the coming months I will be launching a website to introduce the module, provide a demo, and show how to make mashups with the Shopping module and othe
r great Drupal modules. Stay tuned!

May 30 2008
May 30

The SUSE Linux guys just launched a new service called SUSE Studio. With it, you can create an operating system running SUSE, and your own checklist of packages. If you watch the screencast, you see that he creates a Linux distribution that has PHP and MySQL running out of the box.

This seems like the perfect solution for creating a Drupal Operating System that's ready for both Drupal development and runtime right when you install it on your machine. All we'd have to do is have the Drupal packages ready!

May 29 2008
May 29

I tweaked Peter's geonames cck and got the filter working. So just quickly jotting down some of the points before I forget.

Creating a filter is quite simple; however, you need to be careful with one thing - Views uses the first column for filtering. So when you build a cck field, you need to design the table structure beforehand.

1. Table structure

table is defined in hook_field_settings($op, $field) and $op == 'database columns'
geonames cck's table structure was initially like this:

$columns = array (

'name' => array('type' => 'varchar', 'length' => 250, 'not null' => TRUE),

'geonameId' => array('type' => 'varchar', 'length' => 20, 'not null' => TRUE),

'country' => array('type' => 'varchar', 'length' => 2, 'not null' => TRUE),

'lat' => array('type' => 'float', 'length' => 10, 'not null' => FALSE),

'lng' => array('type' => 'float', 'length' => 10, 'not null' => FALSE),

. . . . .

The field I needed to search through was 'country', so I needed to change the order i.e. moving the 'country' field to the beginning of the array. This change incurred modifying the order of columns in existing tables and an update script will need to be prepared for upgrade. So it's best to get this right at the very beginning.

2. Filter

Filter is defined in the same hook but $op == 'filters'. The initial filter looked like this:

return array(

'country' => array(

'name' => t('Country'),

'list' => array(geoapi_countries()),

'list-type' => 'select',

'multiple' => true,

'operator' => 'views_handler_operator_andor',

. . . . .

When I searched nodes in a view with this filter, Views threw the following SQL query to the DB:

SELECT node.nid FROM node node

LEFT JOIN content_type_story node_data_field_destination ON node.vid = node_data_field_destination.vid

LEFT JOIN node n ON node.nid = n.nid

WHERE ((n.uid = 1 OR 1 = 1))

AND ( (node.type IN ('story'))

AND (node_data_field_destination.field_destination_country AND 'GB') )

LIMIT 0, 10

... and it didn't work. It was because, in the second line from the bottom, it is using AND operand. This needs to be '=' in order to get the result.

The problem was the operator defined in the filter, namely 'views_handler_operator_andor'. It needs to be 'views_handler_operator_eqneq'. After changing the operator, it threw the query I wanted (as shown below) and the filter worked fine.

. . . . .

AND (node_data_field_destination.field_destination_country = 'GB') )

LIMIT 0, 10

3. notes

  • always go to admin/build/views/tools and clear views cache after changing the code related to views. The change will not take effect otherwise
  • I tried using 'list-type'->'list' instead and did not force single. in this case, the argument changed slightly (the second argument got square brackets before '=' like this: filter1[]=JP). This sent out the 'correct' SQL query with = instead of AND. selecting more than one country name did not work.
May 29 2008
May 29

"I need to fix just this one last thing."

So, what do you do when you have 5 large-ish projects about to start in the next couple of days, you have paperwork to do, you have conference calls to attend, and cats to feed? You make a new Drupal 6 theme for Drupal.org, what else?

Apparently that is what I do rather. This is how Refresco, my new Drupal 6 theme, came about. I started poking at it about 7am, thinking I would just change a thing or two about a theme I was toying with contributing and get to work on another project, and before I knew it it was 11pm and I had submitted it. Not quite sure if anything else happened during the day...

A Very Cool Theme

Ok, I am biased, but Refresco is a killer theme. I designed it to make good use of space. For example I have a section up top to display messages that the system will on occasion give you. Well that space is currently occupied by the search bar, but are you searching when you are reading a Drupal message? Nope, so they share the space.

How about your secondary menu area, do you need to see that all the time? Not if you don't have secondary links for that page, so it just hides, same with breadcrumbs, side columns, and even one of the horizontal regions.

In all this template has 6 actual regions, and a couple of other areas that work as needed.

screenshot of refresco

One of the things I like to find when I use a template is that it is easy to modify, so that is how I make my templates, easy to change. I put all of the background color information at the top of the style sheet so that it can be gotten to easily and try to use no graphics if I can help it. I didn't use any graphics for layout or design in either of these themes, so when you change the CSS the whole look changes and you don't have to go sifting through the Photoshop jungle to fix a rounded corner or change a background graphic.

Drupal 6(mostly) Theming Notes

Some things that I need to brush up on for Drupal 6 theming (or theming in general perhaps), which I may implement more fully in Refresco and other D6 themes later:

  • theme-settings.php - Using this file and a little knowledge that I couldn't be bothered to gather in my mad rush to create the theme, I could have set the search box to be switched on, as well as the mission statement, slogan, logo, or any other normal theme setting as far as I can tell
  • Passing variables to the theme - seems like if I put a conditional statement in my preprocess function in template.php that my $vars['variable_I_want_to_set'] would just plain not get set. Not sure why. MUST figure this one out
  • The secondary links menu block is only for a menu you create or use under Menus that is named secondary links, it will NOT display your secondary links from your primary links menu, or any other menu that is filling the "secondary links" role under Menus -> Settings - maybe this is just as appropriate for D5?
  • theme('links', $primary_links) works, theme('links', $primary_links, '') does not :)

I Had a Title Here Just a Minute Ago?!?

One other gotcha, and it's one that I think also happens in Drupal 5, is that the front page didn't display my $title, because it was displaying a teaser, and not a full page. To address this I had to use the following code prepended to my node.tpl.php file:

  <?php if ($page == 0): ?>
    <h2 class="title">
      <a href="http://brauerranch.com/<?php print $node_url; ?>"><?php print $title; ?></a>
  <?php endif; ?>

This code ensures that if I was viewing full pages, it wouldn't print the title twice, but it did produce my title nicely on the front page, and gives a nice title link to the full content.

Other Lessons Learned

It's actually really cool that after contributing my first theme, Ranch, just a couple of days ago, that I also wrote up a handbook page called "CVS quick-start guide for theme maintainers" to help me remember how to do it, and to help others along the theme contribution path. Anyhow, I say it's cool because last night I was probably the first user of that document.

It needs a little improvement, but overall, it took me less than 1/2 hour to contribute this theme, as opposed to the several hours it took the first time. Practice makes perfect I guess.

I will be making some improvements to that handbook page, especially mentioning Drupal 6 tagging/branching specifics, as well as the formats for the $Id$ and that they should be included in your source files.

Well, I think that's enough for now. I have to get going to work, right after I tweak just this one thing...

May 29 2008
May 29

I had an interesting e-mail exchange yesterday with Chris Messina and a handful of folks from the DiSo project about "DiSo for Drupal". For those of you who haven't heard of it DiSo is:

DiSo (dee • zoh) is an umbrella project for a group of open source implementations of these distributed social networking concepts. or as Chris puts it: “to build a social network with its skin inside out”.

See, Chris recently started a new job working on DiSo full-time at Vidoop. With the announcements of Facebook connect and Google's Friend Connect, there is a battle raging for control of your identity and your relationships. DiSo, in many respects, is the free open answer for the rest of the internet. It combines several free, open standards that already exist in the wild like OpenID, OAuth, and Microformats for exchanging identity and "friend" information.

So, Chris reached out a handful of us Drupal folks about getting on board. The good news is: we, the Drupal community, are already well on our way:

The big holes at the moment (from a DiSo perspective) are XRDS-Simple support and better support for microformats - specifically XFN.

From the list of Drupal modules above, you may notice that this is an area of interest of mine :-P I look forward to working with the rest of the DiSo project and the Drupal community on this stuff!

May 28 2008
May 28

Today LinuxTag 2008 started in Berlin, Germany. LinuxTag is Europe's leading exhibition on Linux and Open Source software and will go on until Saturday (May 31, 2008) at Berlin's Messezentrum unter dem Funkturm.

There is a Drupal booth (no. 217) located in Halle 7.2a organized by the Drupal User Group Berlin. And it looks like that:

Drupal Stand Linuxtag 2008

We introduced quite a few people to Drupal who said they will give it a try and also got some nice feedback from LinuxTag's organization staff, not only because of the cool Drupal shirts we were wearing.

You can find more information on Drupal at LinuxTag Berlin 2008 in German on the group's website and on the new be Drupal website set up to promote Drupal related events and activity in and around Berlin.

If you are an Open Source enthusiast and happen to be in Berlin, don't let the chance pass by to visit this event where many people volunteer to promote such diverse projects as Drupal, MPlayer, Horde, Asterisk, Zope — who where so kind to give away some free beer and Brezeln to celebrate their 10th anniversary today — and many more, not to forget the many flavors of Linux.

May 28 2008
May 28

Contributing is about giving. People do so for many reasons but at it's core it is about giving.

For me, it is not now nor has it ever been about 'getting back'. It has not been about reputation, karma, fame. It has been about solving my needs and helping others solve theirs in a way that encouraged them to share so I too could learn in kind.

Many years ago, authorship was turned off on Drupal.org book pages to encourage this selfless and unconditional giving. At the time the revision tag was fairly useless because, without the diff module, making a comparison is a lot of work. It allowed someone to revert content if a mistake was made. All revisions are is a record of the changes in content of a given node. A way to see what changes were made by whom. It has helped us prevent vandalism and allowed us to see our own mistakes and learn from them that others caught. It is a tool, nothing more, nothing less.

Back to giving. Many years ago, in the early days of documentation, we had the author displayed on handbook pages. This actually caused a lot of problems;

  • Should they change the author name on editing? Minor edits? Major edits? What defines major?
  • It has an author, I should send them an email asking if I can edit the page.
  • I should wait
  • The author knows more then I ever will I am unworthy to edit the page.
  • I can send support requests to them (and get angry that they don't respond)
  • I am afraid

But it's important to acknowledge your contributors. There is the checkbox on the user profile page, but anyone can check that. So to solve this a page was added about contributors. Over time, tools changed and we were able to enable visibility of things that were not so useful before. Revisions only became somewhat useful after we added diff module. Revisions are not a way to display author information, just know who worked on the page last You shared your knowledge with the community and now the community gets to play with it. Revisions is a tool to track changes. What were made. It is useful, how did someone modify my contributed content? Ahhh, that is a better way to phrase it. Revisions help recovering from vandalism (which is rare but does happen). Revisions are not a way to achieve / obtain status and ownership of content. The content belongs to the community. Over time, this page has drifted a little deeper then it should perhaps in the menu tree. That should be changing in the near future, there is an outline floating around for the /about book I hope to get to soonish. But we have left out a group. It's an important group, the folks who spend time editing things, from one comma, to one word to entire pages, these people are unsung heroes without which our content would suffer. Chx created an editors page for me so we can credit these folks too.

One of the other things we had to define early on is who are we writing for? In the early days we tried to write for everyone. In the end we achieved little to nothing but frustration. Writing for everyone has just not been possible. So we defined our audience. We write for users (http://drupal.org/node/21781). Not end users, but for users, those people who build sites. it is the job of the site implementor to provide tools and documentation to their community. The more we get powerful tools into core the better, but this is a seperate thing from writing documentation on Drupal.org.

Drupal is complexity hidden. What it allows is someone like me, with little php knowledge at all, to do is phenomenal. Few can know all of Drupal or it's capabilities. What a lot of the top tier developers and site builders do is contribute back. They do very complex things and share how. As a result, when they get stuck on something, they can ask and even if it's not a complete solution, they can get back a direction that allows them to solve this need. This process also works for the new folks or the folks who build from others work. Everytime you help someone else, you learn something. It's how you pay back those who helped you.

Dries has made a statement several times, with Drupal you can eliminate the web master. It already does this but you have to pay attention. A traditional webmaster is nothing more then a secretary (http://en.wikipedia.org/wiki/Secretary). Really. Their typewriter is a web site but what they do is take content, format it and post it to a site. I found Drupal because I was tired of being that secretary. I gave several people control of their sites content. Not the site itself, but the content. That is powerful beyond belief. And this is because of Drupal 4.4. Drupal keeps getting better.

Giving is also powerful. I don't give because I earn something, I give because the return is far more then the contribution.

May 26 2008
May 26

The past week has been a big one, for the small but growing Sydney Drupal Users' Group. Last week, on Tuesday through Thursday (May 20-22), we manned the first-ever Drupal stall at the annual Sydney CeBIT Expo. CeBIT is one of the biggest technology shows in Australia — and the original CeBIT in Germany is the biggest exhibition in the world. For three days, we helped "spread the word" by handing out leaflets, running lives demos, and talking the talk to the Expo's many visitors.

Ryan and James at CeBIT 2008

Ryan and James at CeBIT 2008

Drupal demo laptops at CeBIT 2008

Drupal demo laptops at CeBIT 2008

The Sunday before (May 18), we also arranged a full-day get-together at the University of Sydney, as a warm-up for CeBIT: there were a few informal presentations, and we got some healthy geeked-up discussion happening.

Drupal May 2008 meetup at Sydney Uni

Drupal May 2008 meetup at Sydney Uni

Dinner and drinks after the May 2008 Drupal meetup

Dinner and drinks after the May 2008 Drupal meetup

Thanks to everyone who travelled to Sydney for the meetup and Expo, from places far and wide. Kudos to Michael and Alan from CaignWebs in Brisbane; to Simon Roberts from Melbourne; and especially to the fine folks from Catalyst IT in New Zealand, who hopped over the ditch just to say hello. Many thanks also to everyone who helped with organising the past week's events. Along with everyone (mentioned above) who visited from other cities, the events wouldn't have happened without the help of Ashley, of Drew, and particularly of Ryan.

I gave a presentation at the Sunday meetup, entitled: "Drupal's System Requirements: Past, Present and Future". I talked about the minimum PHP5 and MySQL5 version requirements that will be introduced as of Drupal 7, and the implications of this for Drupal's future. The presentation sparked an interesting discussion on PDO and on Objest-Oriented-ness. You can find my slides below. Other presentations included: "Drupal's Boost module (by Simon); "The Drupy (Drupal in Python) project" (by the Catalyst guys); and "The Drupal Sydney Community" (by Ryan).

May 25 2008
May 25

The holiday weekend provided us with the time to whip up some Drupal.org goodness. Herre are a few of the things that are going on this weekend:

  • Pat committed the Ranch theme. The theme, which is very similar to the theme we use here, is simple and clean. Best of all it comes out of the box with valid markup and css and seems to look good in a wide variety of browsers
  • In addition to committing the theme Pat did a great write-up on the dance with CVS on drupal.org which describes the process of committing a theme to drupal.org from the perspective of a new committer. He then worked the write-up some more and turned the prose into a handbook page at drupal.org to help others along with the same process.
  • Meanwhile Dan whipped up the Drupal 6 Forum Thread module which marks the first version of the module for Drupal 6
  • And we gathered up several contributed patches for the Light Fantastic theme. The changes should make the module work better with Drupal's CSS aggregation as well as bring it into compliance with the needs to have better tracking of the files for folks using CVS.

All and all not too bad for the first half of a long weekend.

  • Sitewide Tags:
May 25 2008
May 25

Extra Extra - New Theme Created for Drupal Contributions Repository!

Ok maybe it is not a big deal to most people, but because it was my first Drupal project, it really is big news for me. So big that I am still riding high on my pink Drupal drop shaped cloud. See, yesterday I officially released a new theme for Drupal 5 (soon to make it 6 as well) called Ranch, and put it out there for all to download and use.

Ranch started out life based on a theme that I had put together using NiftyCorners for a client of ours, which I had built upon Zen. It is nothing like the former site, aside from being valid and clean, and very rounded. It is pretty nice, with a very clean CSS based layout, 2 columns with a Mission Statement area, primary and secondary links, breadcrumbs, as well as sidebar for whatever your heart desires.

I chose to use Zen mostly because everyone was talking about it, and because it was supposed to be easier to create a theme from than by doing it from scratch. I don’t really agree with that last part, though I do like how Zen does many things, especially their CSS where they lay out most of the selectors so that when you are using FireBug to change or fix something, you can actually alter it (using the empty CSS tag that is provided) right there, quickly and easily. Very nice.

I think for my next theme however, that I will borrow the CSS, and do the rest from scratch. In the end I deleted hundreds of lines of code that I was never going to use and just sort of seemed like bloat.

Giving it to Drupal

Yesterday I finished (ok, stopped) cleaning, tweaking, and perfecting my theme. I might be a little bit... “particular” about things being just right, so it is sometimes a challenge for me to say something is good enough. Anyhow, after finishing up Ranch, it was time to create a new project for it on Drupal.org, and to create a release so that others could easily download and use it.

This is easier said than done. Here is a breakdown of what needed to be done, and a little of the mistakes I made trying to do it.

CVS Account on Drupal.org

The very first thing I needed to do was to request CVS access so that I could contribute to Drupal.org. I did this by going to drupal.org/cvs-application and filing out the application as well as I could. I have it on good authority that if you have a good theme that doesn’t look like crap, that you could probably lorem ipsum your application with a link to the theme and you may actually get approved :) Ok, lorem ipsum might not go over, but you might be able to get away with Dr. Suessing it. Haiku would be going to far.

Read a LOT - Export - Login - Add - CVS is FUN!

Next thing I had to do was find a bunch of information on Drupal.org on how to correctly CVS, make releases, branch, tag, etc. Luckily this is all very well documented and I had in person help from Josh Brauer, but it was still a bit of work. What it all boils down to are the next several lines of bash history:

mkdir ~/Sites/contrib
cd ~/Sites/contrib/
export CVSROOT=:pserver:[email protected]:/cvs/drupal-contrib
cvs login
cvs checkout -l contributions/themes

WARNING - Don't use cvs checkout -l contributions/modules

Right here I was led astray by another document, and accidentally used contributions/modules instead of contributions/themes. This of course is a theme, so this didn't work. So I edited it back to where I removed everything, and started over with the contributions/themes. Also, make note in this next section that the -l is REALLY important

cd contributions/themes/
cp -R /Library/WebServer/Documents/client/drupal-5.7/sites/default/themes/ranch ranch
cd ranch/
ls -al

Yep, everything is there (including a couple of things that I don’t want there, which tosses me a few issues later)

cd ..
cvs add ranch
cvs add ranch/*
cvs commit -m "Initial commit of the Ranch theme, a standards compliant CSS theme with easy to change color."

This is the initial commit, which created the “HEAD” of my project. Neat! Too bad it’s screwed up at this point, but we fix it later. Next is were we create a branch called DRUPAL-5 - can’t have a project release without this!

cd ..
cvs tag -b DRUPAL-5 themes/ranch
cd themes/ranch/
cvs update -dP -r DRUPAL-5

Here is where I was saying it was screwed up. See, CVS isn’t recursive, BUT it will add directories that are in the current directory. So, what ended up happening is that it added my images directory, and my sourcefiles directories. So here is me fumbling around trying to make it right. Actually, suffice to say that I remove, cvs delete, recreate, re-checkout, start from scratch, do all sorts of crap until I figure out that this is what I have to do:

cd ../../..
rm -Rf contributions/
cvs checkout -d ranch contributions/themes/ranch
cd ranch
cvs delete images
cvs delete sourcefiles
cvs commit -m "removing unused directories"
cvs status

As Josh says “CVS Status is your BFF!”

cd ..
rm -Rf ranch/
cvs checkout -r DRUPAL-5 -d ranch contributions/themes/ranch/
cd ranch/
cvs tag DRUPAL-5--1-0

This is where I made my first release tag - This makes the whole package thing possible. Done with CVS for now, YAY!

Make The Project

Now that I had gotten so much pleasure (not to mention knowledge) from my CVS time with Drupal, it was time to actually create my Ranch project. I did this by going to Drupal.org, logging in, clicking on Create Content on the right sidebar, and selecting Project.

Before I got too far on that though, I needed to make a good screenshot for my project page. I followed these guidelines: http://drupal.org/node/11637

After making the nifty screenshot, I finished filling out my Project page, then I went and created a release, which you do by going to the link in the right sidebar called My projects, finding my project amongst the very long list of one, and clicking on “Add release” under the Project links.

From there I selected the DRUPAL-5--1-0 CVS Identifier (I think, I don’t have a history on this part and being an old fart, my addled brain doesn’t remember exactly) and hit Next.

It was pretty self explanatory after that. Then, after waiting just a minute, I checked, and voila my project page was complete. That’s about the time I was starting to say things like Woot! a whole lot, and dancing and basically making a spectacle of myself. It wasn’t pretty. At least I was happy though.

And that is how I made my first Drupal project.

Pat Teglia - CrashTest_

May 21 2008
May 21

The setup

Recently, I did some pro-bono consulting work for a client. As part of our agreement I made it clear that I would retain the rights to the code I wrote. The reason I did this is because I wanted to be able to give back to the community, and now I am going to do that. The work was made up of six tasks, ranging from trivial to complex. I intend to write six articles based on these tasks. This is the first.

The environment

The site I was working on was built with Drupal 5. You should already be familiar with developing themes and modules in Drupal to get the most out of these articles. For more information, you should use the resources below:

Many of the tasks required changing settings on the Drupal adminstration pages. Code for the tasks was placed in either a custom module, or the site's theme. For the purposes of my articles, the site will be mysite.com and all the code in the examples would be placed in two folders:

  • sites/all/modules/mysite
  • sites/all/themes/mysite

Again, if you need an introduction to creating custom modules or themes, you should use the links above.

Task One: Restricting access by email domain

The customer only wanted employees from their company to have access to the site. However, they didn't want to have to put every employee in by hand. The solution to these conflicting specifications was that anyone could try to register for the site, but the registration would only accept users with an email address that had the same domain as the customer. In other words, a user could complete registration if their email was [email protected] but not if there email was [email protected].

Step One: Turning off anonymous access

The first thing to do was to turn off anonymous access to the site. This was done through Drupal's administrative interface:

  1. Log in to http://mysite.com/user as the administrator.
  2. Click on “Administer” on the "Navigation" menu.
  3. Click on the “Access control” link under “User management”.
  4. Turn off “access content” under “node module” for "anonymous".
  5. Only "authenticated users" should have permission to “access content”.
  6. Click on the “Save permissions” button on the bottom of the page.

Step Two: Allowing registration for mysite.com employees only

  1. Log in to http://mysite.com/user as the administrator.
  2. Click on “Administer” on the "Navigation" menu.
  3. Click on the “User settings” link under “User management”.
  4. Click on the radio button labeled “Visitor can create accounts and no administrator approval is required.”
  5. Make sure that the check box labeled “Require e-mail verification when a visitor creates an account” is checked.
  6. Add text to the “User registration guidelines:” text area.
  7. Click on the “Save configuration” button at the bottom of the page.
  8. Last, but definitely not least, in order to make sure that only mysite.com employees can register, the function below was added to sites/all/modules/mysite/mysite.module:

function mysite_user($op, &$edit, &$account, $category = null) {
$op) {
      if (
$edit['form_id'] == "user_register") {
        if (!
preg_match("/[email protected]$/i", $edit['mail'])) {
t("Only valid mysite.com email accounts are allowed."));
// end case "validate"
} // end switch($op)
} // end function mysite_user()

This function implements hook_user(). It looks for the "validate" operation and that the form that is being validated is "user_register". Then it checks the email address with a regular expression. If the email address does not end with "mysite.com" it returns the form with an error.


In addition, this code was added to sites/all/themes/mysite/style.css

.messages {
  margin: 0.5em;
  padding: 0.5em;
  border: 1px solid #009900;
  color: #009900;


Registration was tested with invalid and valid email addresses.

More tasks

This concludes the first task. Look for the next article to appear shortly.

May 21 2008
May 21

21st May

Just a quickie - embedding views in PHP snippets etc. Sometimes blocks or panels don't quite cut the mustard and we need to directly insert a view via some PHP.

I started off here - the method I've used before to achieve this. There's another howto on Innovating Tomorrow. However, views_build_view, and indeed theme_view, are both no longer part of Views...

After a bit of searching I found mention of a new function; its also mentioned in the Views 2 documentation (work in progress).

So, the resulting PHP is wonderfully simple:

print views_embed_view($view_name, $display_id = 'default');

I was tearing my beard out, so I hope this helps someone.

May 20 2008
May 20

In the Drupal content management system, a "node teaser" is small bit of content used to encourage you to "read more" of the post. Drupal can set the teaser to the entire length of the post (typically used for blogs where you don't need extra click-through), or can automatically generate the teaser to a specific character length. In the past, you could also manually generate teasers by including <--break--> in the node's body. In Drupal 6, manual teaser definition has been improved with JavaScript wizardry, along with a new checkbox: "Show summary in full view".

But there's a small problem with the use of the word "summary". Generally, when a Drupal teaser is included in the node's full view, it's because it's the introduction of the node itself, not necessarily a teaser or summary of the entire body. Over at gamegrene.com, a node's teaser is, in fact, a summary of the node, and is also displayed on the full view itself. It's not the first paragraph of the article but, rather, is styled differently to provide an overview of what you'll be reading. IBM uses the same model at their developerWorks.

If you placed a "summary" at the beginning of the node's body, unstyled, readability would tend to suffer - you'd have the summary (node teaser), and then, theoretically, the introduction (node body), with no clear indication that two different types of content, with two different purposes, are being served.

As I've been working on moving Gamegrene to Drupal 6 (in time for Dungeon and Dragons 4th Edition, coming June 7th), I had to solve the problem of: how do I theme the teaser differently than the body inside node.tpl.php? By the time the template gets the node data, only $body and $content exist; $content only contains the teaser (for list views) or body (for full views). The teaser never exists in a node's full view as its own variable.

To solve this and get the same view as seen on IBM's developerWorks, I used themename_preprocess_node() to detect if a teaser has been manually set and that the "Show summary in full view" checkbox has NOT been enabled. When that checkbox is checked, Drupal automatically adds the teaser to the node's $body (or $content) - it treats the teaser as the introduction to the post, not an actual summary of what you're reading:

function phptemplate_preprocess_node(&$variables) {
  // we like to display teasers on the node view pages in a different style,
  // but only if they were NOT set to "show summary on full view" (which seems
  // backward, but the implication with that checkbox is that the teaser is
  // PART of the node's body, instead of an actual summary of the entire
  // node's body). if a node's unbuilt body starts with <!--break-->, then
  // a teaser has been manually set, and "show summary" is not checked.
  if ($variables['page'] == TRUE) { // only do this on full page views.
    $node = node_load($variables['nid']); // we reload the node because
    // by the time it gets here <!--break--> has already been filtered out.
    // this if logic stolen from node.module's node_teaser_include_verify().
    if (strpos($node->body, '<!--break-->') === 0) {
      $variables['style_teaser_differently'] = TRUE;
      $variables['teaser'] = check_markup($node->teaser, $node->format, FALSE);

Note that the extra node_load() is nothing to worry about - since the node has already been loaded earlier in this execution, node_load() will happily return a cached version, saving us any performance concerns.

Now, it's just a matter of displaying it in node.tpl.php:

<?php if ($style_teaser_differently) { ?>
  <div class="node-summary"><?php print $teaser; ?></div>
<?php } ?>

Comments and concerns? Note that, for my particular needs, I wanted this entirely in a theme - I'm not changing data or its structure, merely its display, so doing this sort of stuff in hook_nodeapi() with a module's overhead would be a little much.

May 20 2008
May 20

In the first of the Drupal Seo series I went through basic Drupal SEO with no additional modules installed. Now we are going to move onto SEO Statistics and Analysis and how to set yourself up to win, things get a bit more in-depth (not a lot) as you will need to now install a couple of modules and connect up some external services.

In order to know if your getting its right, when SEO'ing Drupal then you have to set yourself up correctly. So that you can monitor whats happening and analyse why, so that you can make sure that Google et al are seeing your site the way you intend.

To start with you need to decide on two things

1) Your Keywords (this is not dealt with in this article, there is loads on the web about doing this), this is the basis of all SEO, get it wrong and you can end up wasting an awful lot of time and money

2) Your KPI's (Key Performance Indicators) are imperative to establish right at the start, you can evolve these as move forward however you have to at least have the basics in place when you start in earnest.

Here as some baseline SEO KPI's that might be useful:

  • Keyword to Conversion Ratios

  • Ranking By Target Keyword

  • Traffic

  • Traffic by keyword

  • Conversions to sale or leads

  • Duration on visit

  • Referring Sites

  • Referring Search engines

  • PageViews

  • Geo locations

  • Bounce Rate

  • PPC V Natural Traffic

  • Site errors (404's etc.)

You need to tailor these to your own business requirements as they massively change dependant on your site and business requirements.

Basic External tools that you need to use:

  • Google Webmaster tools

  • A must have for analysing the way that Google sees your site and content. Make sure that you do the basics:

    • Verify your site

    • Set your geographic target (Google is slow to use this but eventually does)

    • Opt into image search unless you have a good reason not too

    • If you have the option set your crawl rate to the optimum

    • Add the site map

    • Verify your robots.txt

    • I recommend that you check webmaster tools at least once a week

  • Google Alerts (very useful for seeing how fast you get indexed)

  • Add alerts for all your primary keywords

  • Keyword Ranking monitoring

  • There are loads of tools for this I think keyword elite is pretty good

  • Google analytics / Awstats / Another web statistics analytics applications

  • This is a real case of personal preference / commercial situation, whichever you choose you should setup you KPI Targets / Goals in the application so that they are easily track-able.

  • On page analysis tool e.g. webceo

  • This will help you to understand what search engines are looking for, however a word of warning don't over use it. Its should only be used for guidance, don't destroy your content to optimise it in a way that webceo says is perfect, remember you want to appeal to users so its more important that its perfect for users than search engines.

  • FireFox + add-ons

  • Don't try to use IE for SEO, you just wasting time and effort

  • FireFox add-ons

    • yellow pipe

    • Gets a spider view of your page, very handy

    • firebug

    • Great for analysing pages issues in headers etc.

    • SEO Quake

    • Has a great range of useful tools

    • Web Developer

    • Too many reasons to count for having this installed

  • Xenu

  • Great link analysis tool for checking your site,Run on a regular basis

Basic Drupal SEO modules you need

  • http://drupal.org/project/xmlsitemap

  • This automatically creates an xml sitemap for submission and allows you to authenticate your site with the primary search engines. Make sure you review the sitemap that it generates before your submit it, and tailor it to be what you need.

  • Choose an Statistics package, current options include, if your starting out for now Google analytics is probably easiest

Regular SEO Maintenace Tasks

  • Capture your own onsite searchs, so that you know what specific things people were looking for and make sure your optimise for frequently search terms
  • Track errors 404's etc. and fix the problems, you should use both your local statistics packaage and google webmaster tools for finding issues
  • Add keywords from your analytics package to your keyword list for monitoring, frequently you will find that users are finding you for keywords that you have not optimised for, track these and optimise appropriately

Later in the series we will get into much more indepth analysis of your drupal site and some very detailed statistics.

Bookmark/Search this post with

May 20 2008
May 20

One of my recent projects has been to help move a great Drupal theme forward. Light Fantastic began life as a Summer of Code project in 2007. Amila Sampath, aka lucksy, put together a theme that is great. There are some things remaining to be done, but at the very least there is now a development tar file available for download. It has been a couple of months since most of my work with it so I'll have to go back and revisit the changes that are necessary to polish it off. I've got some ideas for customizing the theme kicking around in the noggin but we'll see how much time those get in the days coming up. If you like the looks of it be sure to download a copy and report what you find.

  • Sitewide Tags:


About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web