Drupal Tutorials

Building Custom Panel Panes (CTools Content Types) in Drupal 7


Content types (a.k.a. panel panes) are one of CTools' most visible, versatile, and easy to learn APIs. In this tutorial, we're going to build a custom content type (or panel pane depending on what you call it) that display's a twitter user's latest feeds. As with any new CTools content type, you need to have at minimum three files in following structure:

Switching Drupal tpl.php files at will: Old Switchy McTipplefep's Trick


One of the first lessons they teach you at the School of Drupal Arts, Arcane Sciences and Sorcery is that a tpl.php can have dynamically generated wildcards or "suggestions". You can see this every day drupal themes: you can simply use the "node.tpl.php" file if you want only one style for a node. Where as if you want different styles for three node types: blog, story & page, you create the following files:
  • node-page.tpl.php
  • node-story.tpl.php
  • node-blog.tpl.php
Obviously, this pattern exists far beyond nodes. For page.tpl.php, user/register can have its own tpl.php file:
  • page-user-register.tpl.php
Where as user/1/edit will be (yes -- these suggestions will remove numbers... i think... i was hung over that day in class):
  • page-user-edit.tpl.php
This may be fine and dandy if your agenda is either to build a simple drupal theme with a few exceptions, or to build manly Texas sized drupal themes with enough tpl.php files, and duplicate html code to choke a donkey. Old Switchy McTipplefep's trick is for anyone who:
  • wants 3 styles that are shared by 20 blocks
  • wants 4 possible layouts for dozens of pages in a site
  • has 12 node types, but wants them to share 3 styles
The magic happens in your theme's $theme_name . '_preprocess_' . $theme function. Below are examples for how a theme named "example" would do it.
function example_preprocess_node(&$vars) {
  $node = $vars['node'];
 // only switch for story type
  switch($node->type) {
    case 'story':
      $vars['template_files'][] = 'node-batman';

function example_preprocess_block(&$vars) {
  // sure why not -- we'll pass our node.tpl.php file to our block
  // That's how tpl pimpin works sometimes
  $vars['template_files'][] = 'node-batman';

A Damn Simple Technique For Making Anything in Drupal Ajaxed*

*well, probably anything that uses drupal.behaviors...

Today, I was doing a few experiments on how to get several giant CCK node forms to load and submit via ajax from a single custom page.When this technique actually worked on the first try, my exact words were "no f#cking s#it..." I'm sure I'm not the first to figure this trick out, but I have had a hard time finding people who've described it. Perhaps everyone besides me figured it out ages ago -- though if that's true, I don't want to know what horrible things have driven some of you to use your current techniques.

This technique seems especially ideal for integrating any existing drupal form (especially giant CCK forms with sortable, multiple value, file fields), and the results into some highly customized drupal based web gizmo. *IT REQUIRES NO PHP* beyond whatever calls you may have to make to get dependent JavaScript files available to your requesting page. This pattern will mostly degrade to drupal's default behavior if JavaScript isn't present.

The stupid simple strategy

We use javascript to take what we want from drupal's default behavior, and throw away everything else.

Below, is all you need to submit a giant CCK form and have it successfully post:

//node form just happens to be the ID shared by every node form
//replace #node-form with any form ID and the results are the same
$('#node-form').submit( function() {
  $.post($(this).attr('action'), $(this).serialize(), function(response) {
  // ...
  // return false prevents the form from submitting regularly ... noob...
  return false;

Even though the user will not be redirected or see any results of this $_POST, jquery will still follow the default redirect to node/$node->nid or return the original form with validation errors -- this is actually a good thing.

$('#node-form').submit( function() {
  $.post($(this).attr('action'), $(this).serialize(), function(response) {
   // why not, lets replace our logo with the resulting node
  return false;

There's a few downsides I see to this technique: for one generating entire pages is totally unnecessarily; since we are simply taking contents of $(.node) from the result's full document object, why load the entire page? The answer is: the page already exists, and its one less menu_callback, loading, or godforbid form behavior altering trick that will cause bugs down the road. Its easy, it works, and all but the minority of websites would need to worry about the performance implications.

Another downside is that since we are using jQuery, we are depending on classes and ids that may eventually change. I think this is a real risk, but i think the risk can mostly be avoided by making intelligent decisions about what selectors you use. Besides, the maintenance hassle is probably still less compared to many of the alternatives.

Below is a working example: load a node "edit" tab, submit, and refresh the node all via ajax.

The code snippet is cute as a button, though its handling of validation is sort of half-assed. Note that the vast majority of code is devoted to simply throwing around the response data, the jquery itself is elementary.

Web Development:

Intro to Drupal 6 Multistep Form Domination Using Chaos Tools

The mere words "multistep form" once gave me a feeling of dread. There are several techniques (arguably hacks) that enable multistep forms in drupal 6. However, if you've ever used them, you'll know that they are a not techniques for the faint of heart.

While Merlinofchaos's multistep form wizard is not for the faint of heart either, I will say I found programming the forms to be fun. The setup takes a bit of focus, but after that, writing the steps is almost too easy.

Chaos Tool's wizard.inc is distinct:

wombat wizard
  1. See that stepper and buttons in the above image? You never will need to micromanage what step your form is on using the wizard. If you set up the form, it figures that sort of stuff out for you. Since it knows about stuff like "$step", its perfectly happy to figure out whether it should display a next, back, or finish button. I like code that works for me.
  2. Every step in a form is a distinct form_id, that has its own #submit, #validate, and(god knows what else can be done with it via form api). Within the steps, you should never need to think any harder than you would writing a simple form with a message, and an email address. Proof can be found below in the actual form arrays, and the submit functions.
  3. There are thousands of ways you can mess up a multistep form -- in a sense, the wizard lets you make your biggest mistakes in one $form_info array, while keeping the majority of the code (the formapi arrays, and form processing/validation functions) in small, easy to understand units that anyone with even intermediate formapi knowledge could work with.
  4. This wizard DOES power a number of complicated multistep forms that you may use every day - the multistep forms in panels in particular. In a way, its just the most recent chapter of Earl Miles vs. Drupal's formapi -- a saga that has gone on since version 4.7. [come to think of it, i think lullabot needs to make a feature film about that epic story]

Interested? Start Here

Here's a Live Demo of the Wombat Deployment tool I wrote that uses the wizard. Only impressive in how easily it was written, and how easily i could write a 3rd, 4th, 5th, or 20 steps following the same pattern [ the subject of future posts are hinted in a rogue modal.inc file in the download.]

To get started building multistep forms, follows these steps.

  1. Download Chaos Tools
  2. Advanced Help. If you start futzing with this, you'll want the docs merlinofchaos wrote on the wizard. They are available through this module only.
  3. My example module, the wombat tool to rule them all!
  4. check back in a few for all the corrections

The full code, and detailed doc on the $form_info array will only be made available to freaks who click the "read more" link.

Content Type Madness (CTM): Symptoms, Treatment, and Prevention

[img_assist|nid=1045|title=|desc=|link=none|align=left|width=100|height=80]Content type madness is a common disease that effects drupal sites of all sizes. It is most frequently seen in drupal 5 and 6 sites, as result of content construction kit.

It is a serious, chronic disease that can result in long term damage to a site's agility, usability , and information architecture.


Drupal Project Movers and Shakers: The D-6000

Highlight: WYSIWYG API Gets Top Spot With 97% Growth (Feb 8th - March 29th)

Ever wondered which modules' userbase was growing fastest?

With a bit of simpleXML, 2 hours of boredom, and drupal.org's usage charts, I can provide an answer. Personally I thought the results were rather interesting.

This list only includes projects that got 6000 downloads or more last week. I picked 6000, because otherwise, ubercart wouldn't show up.

hook_views_query_alter : alter views queries with array keys

While building a product comparison website, I ran into a situation that required a view to sort using one of 2 possible price totals depending on a users profile: lets say "red state", "blue state". The goal was to open up the product comparison tables (order, fields, field names, and filtering questions and all), as well as the underlying data to this companies' staff, so a homebrew wasn't an option (which it shouldn't be, almost 99% of the time).

Luckily, there were only two possible prices for those products, so all I had to do was create two CCK *decimal* fields [ this is important, because text, and integer fields don't properly store or sort decimal points for prices ]. Then, on hook_cron, or a node submit, I updated the two totals based on other itemized fields.

However, how would the view know which column to sort products by based on a flag stored in the users session? There's about 5 ways to do that, so I went with the 1 minute solution:

Set up two sorts in a single view, and implement hook_views_query_alter(&$view, &$query)

function product_compare_views_query_alter(&$view, &$query) {
// fyi, anonymous sessions need a record in the user table of 0 to work at all....
if ($_SESSION['pref']['state_color'] == 'red') {
// we'll have to do the less popular red query
    // which's key is [1] (weighted second in the views sort order interface)
  else {
// otherwise, don't do the hill billy logic at all....


In theory, you could write a custom algorithm that built its own sort queries based on a criteria using this method. You could also probably alter filters, or fields. However, this isn't the only way to have accomplished this -- but its the easiest for me to stomach. Never use this method when existing views features, or view arguments, or even other views hooks may be the proper hammer for this nail.

If I learned anything, its that views a complex beast; flexible like F-22 -- an amazing piece of technology that offers tremendous freedom -- but often that freedom will cause you to crash in the ground if you don't think about what you are doing carefully.

Theme Development With Blueprint

Web Development:

During the drupal.org redesign process, I noticed that Mark Boulton was using blueprint. I decided to give it a spin, and used it on a drupal site for a surgeon who specializes in sports medicine. (heavy use of imagecache, nodequeue, views, books, and nicemenus). Since that project, my stance on blueprint is this:you'll take my CSS framework from cold dead hands.

Is there a drupal theme?

Yes, and its very well made. Offers nothing except a basic grid layout that supports1,2,3 columns, a strong typographical foundation, and the conversions of various drupal elements to the blueprint styles. My "starter theme" of choice. In my humble opinion, the revolutionary advances in drupal 6 make big, bulky starter themes unnecessary.

Blueprint crash course

You don't need to know a whole lot to use blueprint. Everything you absolutely need to know is in the below code example. (with notes below)

  • 1. .container sets width to 950px, and centers the content with auto margins on the side. All grid elements must be within ".container".
  • 2. .clearfix - this does the same thing as having br class="clear" below the floating elements. See the oddly named br element that doesn't need to be there.
  • 2. .span-[x] The width of the column in the grid. Numbers range from 1 to 24. A full row must add up to 24, and can also include prepend-[x], or .append-[x], which share the exact same widths, only add margins, either before or after the column. The final column always needs the class "last" so that it doesn't add its 10px right margin.

And that's really all there is too it. Of course, there are many more features, that let you get perfectly centered borders that don't cause the elements to overflow, some nice typography classes, and other useful stuff that nearly any website needs. I found the best way to learn the framework was to just look at what's going on on the example pages, with firebug. Its all quite self-explanatory.

A Hack for Teaching Drupal


How Do You Teach Drupal Without Inducing Confusion/Boredom Triggered Comas? Here's a rule that has never let me down:

Never interact with the drupal site in any way during a lesson

The person you teach needs to click on every menu link, fill out every form, and as they learn, its your job to start taking off the training wheels. At first, you'll have to point them to where to go to add a new taxonomy term, but make it a point to later ask them to add a taxonomy term, and see if they remember how to find it.

This "hack" works well for two reasons:

Converting a Drupal Site to Straight HTML

Web Development:

When Bad Hosts (Yahoo Small Business) Happen to Good Drupal Sites

Time and again, I work for a client who is stuck with a horrid server environment -- say -- Yahoo Small Business.* Surely you must think that a company like Yahoo -- who employs none other than Rasmus Lerdorf -- would offer a decent environment. That's where you'd be wrong.

Its so bad that Drupal 6 won't even allow itself to ATTEMPT an installation. And with good reason. Consider the facts:


Subscribe to RSS - Drupal Tutorials