Chapter Three LLC

drupal

The Amazing Scene at Drupalcon Szeged

Matt Cheney

Drupalcon Szeged 2008 was the annual European gathering of some of the best Drupal minds of our generation with plenty of excitement and style thrown into the mix. For the event I flew across the pond to represent for Chapter Three and chase down some amazing vegan food throughout Central Europe with Drupal genius Neil Drumm. It was a wonderful time all around and worth the trek from San Francisco through Prague and Vienna to the town of Szeged in southern Hungary.

The conference was kicked off with Dries’ regular State of Drupal presentation where he dropped that there are over 20,000 Drupal 6 installations currently online and laid out his vision for Drupal 7 which included naming Angie “webchick” Byron to be co-maintainer of Drupal 7. Angie is a big fan of automatic testing and improved usability so wait for that to break out big in Drupal 7.

There were a couple great sessions by Sam Boyer about using Panels and using the Panels API where he laid out some of the existing power and the future direction of this very important module (including a Drupal 6 release in the coming weeks). I attended all the talks on Drupal security (a favorite topic of mine), including some particularly neat automatic security auditing approaches demonstrated by Barry Jaspan. The talk on running a Drupal consultancy by Tiffany Farriss (Palantir), Eric Gundersen (Development Seed), and Robert Scales (Raincity Studios) was fantastic and I strongly encourage anyone in this business to watch it a couple times.

Despite the serious time difference, but thanks to the hotel and conference internet I was able to keep up with the Chapter Three business and got to spend some quality time creating a Drupal 5 and Drupal 6 release for Drupal for Firebug, a module and Firefox extension to help with Drupal debugging and development. Rolled a couple of patches, got up to speed with the latest in Drupal 7, and made some additional improvements to Drupal for Firebug during the Code Sprint on the final day.

I met some amazing people at the conference (including a number of European developers I had only spoken with online) and left feeling really pumped and excited about everything Drupal. The future here is quite bright and I look forward to seeing everyone at Drupalcon North America next March.

Drupal Elegance: Not Just For Code Anymore

Matt Cheney

We all love the elegance the Drupal. The code style is clean, the architecture beautiful, and it’s all pretty well thought out. Plus the logo is pretty amazing.

To find a fitting tribute to our favorite content management system, I spoke with a friend from college, the very elegant Lilly Russo, who makes amazing glass mosaics through her business in Oakland, California.

Drupal for Firebug: Firebug talking Drupal

Matt Cheney


Firebug is a hell of a thing and at Chapter Three we use it regularly - as many others do - to analyze and debug our web development work. It can do things like inspect the HTML, manipulate the CSS, execute and debug Javascript, and monitor site performance and download times. It is so great that we wished it spoke Drupal and knew our name - kind of like the cute girl at the dance. Things get a little better when people know your name.

It is in that spirit that that Drupal for Firebug was developed as a way to extend the capabilities of Firebug by mixing in a little Drupal. Drupal for Firebug is both a FireFox Extension and a Drupal Module. To celebrate Drupalcon Szeged there are now releases for both Drupal 5 and Drupal 6 available. Get the extension and download the module, check our the test and demonstration site, and watch the drupal magic happen right in your Firebug window:

With a copy of the module and the Firefox extension installed, a developer can see debug messages, examine the SQL query log (with Devel module), look at how a Drupal page load is handling a site’s users, views, nodes, and forms, and even execute PHP. The goal of the module and extension is to create a framework for centralizing Drupal development and debugging work using power of Firebug. The major features include:

  • More centralization of web development debugging information by moving that information from Drupal page view into separate Firebug window. This helps to provide a clean separation of development debug information from the rest of the site.
  • Clean and external debugging message text and dumpable PHPs arrays and objects which can be displayed in the Firebug debugging window using the handy firep() function where they can be viewed without disrupting the rest of the page flow. There is also a handy “debug message only” search feature which is helpful in locating specific pieces of information in the debugging output.
  • Integration with the wonderful Devel module is available to provide a Firebug accessible SQL query log. Since the Firebug debugging window can be resized, hidden, or broken out as a separate window, it provides additional options (beyond appending the log to the bottom of the screen) for viewing the SQL query log.
  • Complete information about each user and node and view and form that is accessed during a page load is available. This can be invaluable in determining what Drupal items are being referenced and what $op is being performed on them. There is awareness of the original and modified state of the view and form items and even smart color highlighting to tell you when additions, changes, or deletions have happened to the item during the hook_form_alter or hook_views callbacks.

This is only the initial release of the module and there are plenty of tweaks and improvements to make down the road. The real secret sauce will be figuring out what are the most useful pieces of information to display in the Firebug window and making some important XUL interface improvements to the Firefox Extension to increase usability around the viewing and sorting of debugging data. Think HTML browser style functionality for the User, Form, Node, and View arrays and objects. But for now, at least it speaks our language.

HOWTO: Utilize Drupal 6-style "preprocess" theme architecture in Drupal 5

Josh Koenig

Once of the biggest advances “under the hood” in Drupal 6 is the addition of the preprocess architecture to the theme layer. This is part and parcel with the deeper embedding of template files, and together they render Drupal 6 the most flexible and powerful release yet in terms of theme and design.

For those of us who still do a fair amount of work with the 5.x branch, it’s easy to be envious of those lucky enough to have these new tools at their disposal. But resist that cardinal sin! This quick HOWTO explains how to quickly turn your theme into a node-preprocessing machine, which has great benefits in terms of elegant architecture, and also future-proofing your work for the eventual Drupal 6.0 migration.

Bulding off _phptemplate_vars()

Possibly the most powerful programmatic tool in the Drupal themer’s toolkit in 5.0 is the _phptemplate_vars() function. We’ve talked before about to use this to use different template files under different circumstances. Overall, this function is a great way to make all those minor changes that are needed so that a site can have truly top-notch appearance and user-interface.

However, once your theme gets to be very complex, this function can easily become an overloaded beast of logical branches. Each new node type or special case creating new complexity, and maintaining that as your site grows can quickly become untenable.

Fear not, though. You can also use this function to implement a simple preprocessing architecture that will help you customize the data in all your node types without creating a mass of template files, and set the table for 6.0. Here’s how:

<?php
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
    case
'node':
     
$node = $vars['node']; // handy shorthand
     
$preprocess = 'phptemplate_'. $node->type .'_node_vars';
      if (
function_exists($preprocess)) {
       
call_user_func($preprocess, $vars);
      }
      break;
    case
'page':
    

 
}
}

function
phptemplate_blog_node_vars(&$vars) {
 
// your custom preprocessing here
 
$node = $vars['node']; // handy shorthand
 
drupal_add_css($vars['directory']. '/custom_blog_style.css'); 
 
$vars['submitted'] = t('Blogged by !name on !date', array('!name' => l($node->name, 'user/'. $node->uid), '!date' => format_date($node->created, 'custom', 'm-d-Y')));
}
?>

Note that you don’t need to use phptemplate_ as the prefix here; indeed it may be advisable to use your theme_name.

What this function does is quickly check for the existence of a node-specific preprocessing function, and if it exists passes through the $vars array by reference.

In this case we detect that a blog node is being rendered, and take the opportunity to include a final custom stylesheet form our theme, as well as altering the standard $submitted var.

The possibilities here are endless, and in “enterprise theming” situations where you may be dealing with upwards of 25 (or even 100!) node types, having this kind of programatic structure in your theme is invaluable for keeping things clean, elegant and extensible.

Good luck, and happy drupal theming!

HOWTO: Upgrade an SVN Managed Drupal Installation (without CVS)

Matt Cheney

At Chapter Three we use the Subversion Version Control System to manage our client and internal Drupal projects. When we kick off a new project we roll out the latest version of Drupal, stick it in the SVN repository, and start developing.

As a matter of best practices it is usually a good idea to check Drupal directly out of the Drupal CVS repository which makes upgrading as simple as "cvs update -dP -r DRUPAL-5--X". However, we have a number of sites that were not checked out in this way and this makes updating Drupal sort of a pain. Our SVN management system wants us to update each file individually (so the changes can be versioned), but individually updating each of Drupal core's 300+ files is a little bit tedious.

However, with a little command line wizardry we can quickly download a copy of Drupal and generate a command to copy the new version of each Drupal core file on top of our installation in a way that can be committed up to SVN. To do this we need to wget a copy of Drupal and then use the all powerful find command to detect and copy the relevant files.

# Download a Copy of Drupal, Extract it, and Enter its Directory
wget http://ftp.drupal.org/files/projects/drupal-5.9.tar.gz
gzip -d drupal-5.9.tar.gz
tar xf drupal-5.9.tar
cd drupal-5.9

# Use Find to Detect Each Local File in Drupal Core and Execute a
# Command to Copy the File to the Live Version of the Website
find * -type f -exec echo cp {} /path/to/www/{} \;   # this just echos the  commands
find * -type f -exec cp {} /path/to/www/{} \;  # this actually runs the commands

To make sure the copy worked we can use the same find command do an individual diff on each file. This will assure us that we successfully updated our website with the version of code we downloaded from Drupal. This command can also be used prior to updating to compare different versions of Drupal to see what core changes are going to be (or have been) made.

find * -type f -exec diff {} /path/to/www/{} \;

HOWTO: Use TinyMCE in a Panel Pane Popup

Matt Cheney

The magic of Panels 2 is just the sort of thing you might expect from a wizard. Users can create pages with flexible and customizable layouts, they can populate those pages through an extensible block system, and they can configure and drag-and-drop those blocks around the page. Check out the demo page.

Sadly, the panels system does not allow its textareas to be used as TinyMCE WYSIWYG areas because of the way javascript and javascript events are handled. The basic problem is that TinyMCE runs on page load and is not set up to be activated on the Panel pane textareas that are created after the fact. This is a problem that hopefully the Drupal 6 version of Panels can resolve, but for Drupal 5.x the following hook_form_alter solution is possible.

First you need to set up a hook_form_alter callback as part of a new custom module or as part of an existing module you are modifying.

function my_module_form_alter($form_id, &$form) {
  switch($form_id) {

Second you need to render a hidden TinyMCE field on each panels editing page. This is not a particular clean way to proceed, but it will ensure that all of the appropriate TinyMCE .js is loaded on each page.

    case 'panels_edit_display':
      $form['tinymce_hidden'] = array(
        '#type' => 'fieldset',
        '#attributes' => array('style' => 'display: none'),
      );
      $form['tinymce_hidden']['tinymce_prerender'] = array(
        '#type' => 'textarea',
      );
      break;

Third you need to assign a special submit handler javascript call to convert all TinyMCE content back into its normal textarea content so it can be appropriately saved when the Panel pane is submitted.

     case 'panels_content_config_form':
       $form['next']['#attributes'] = array('onclick' => 'tinyMCE.triggerSave(true,true);');

Finally each of the textareas that are being rendered on the panel pane page need to have a special enable/disable TinyMCE link assigned to them. This allows users to turn on the TinyMCE functionality if they want. The code belows assumes your default TinyMCE state is off. I am sure there is a better and more generalized way to add these links, but this is a first step.

       // Load a disable or enable link below each textarea
       global $user;
       $enable  = t('enable rich-text');
       $disable = t('disable rich-text');
       $user = user_load(array('uid' => $user->uid));
       $profile = tinymce_user_get_profile($user);
       $status = tinymce_user_get_status($user, $profile);
       $link_text = $status == 'true' ? $disable : $enable;
       foreach($form['configuration'] as $index_raw => $value) {
         $index = str_replace('_','-', $index_raw);
         if ($value['#type'] == 'textarea') {
           $form['configuration'][$index_raw]['#description'] .= "<div><a href=
\"javascript:mceToggle('edit-configuration-$index', 'wysiwyg4-configuration-$index');\" class=\"wysiwyg-editor\" title=\"edit-configuration-\"" . $index . "\" id=\"wysiwyg4-configuration-$index\">$link_text</a></div>";
         } else {
           if (is_array($value)) {
             foreach($form['configuration'][$index_raw] as $index2_raw => $value2) {
               $index2 = str_replace('_', '-', $index2_raw);
               if ($value2['#type'] == 'textarea') {
                 $form['configuration'][$index_raw][$index2_raw]['#description'] .= "<div><a href=\"javascript:mceToggle('edit-configuration-$index-$index2', 'wysiwyg4-configuration-$index-$index2'); \"  class=\"wysiwyg-editor\" title=\"edit-configuration-\"" . $index . '-' . $index2 . "\" id=\"wysiwyg4-configuration-$index-$index2\">$link_text</a></div>";
               }
             }
           }
         }
       }
      break;

And you should be good to go.

  }
}

Making the web friendly again

Robert Wohleb

There are more than a few Drupal modules and 3rd party services on the internet for trying to manage the flood of spam that websites regularly receive. However, many family-friendly websites need to go beyond merely blocking crude ads. They need to also fight off the hordes of forum trolls and instigators.

A few services fill this role, such as the excellent WebPurify web-service (nominal yearly fee). We developed our WebPurify module for Drupal while working with PBS. Being one of THE family-oriented destinations on the Internet, they couldn’t let anything pass that wasn’t “COOKIE MONSTER APPROVED”.

Luckily for us, and Cookie Monster, WebPurify exposes a simple REST API, using the now common XML-over-HTTP paradigm. Their documentation lays out the request and response formats, and even provides a simple PHP example. Our module provides a nice wrapper around the intracies of using this REST interface, and even provides comment filtering right out of the box. The exposed API even lets other Drupal developers use WebPurify in their modules.

HOWTO: Quick jQuery Usability Tip: Automatically Clear/Restore Useful Default Values

Josh Koenig

Just wanted to post this quick trick I’ve been using lately to automagically hide/show useful default text field values (e.g. “Search” in the search box) using jQuery and the ultra-handy Drupal.settings() object.

Here’s the short and sweet copy/pastable jQuery code:

$(document).ready(function(){
  Drupal.settings.inputDefaults = {}
  $("input:text").focus(function() {
    var element = $(this);
    Drupal.settings.inputDefaults[element.attr("id")] = element.val();
    element.val('');
  });
  $("input:text").blur(function() {
    var element = $(this);
    if (element.val() == '') {
      element.val(Drupal.settings.inputDefaults[element.attr("id")]);
    }
  });
});

Basically this quick snippit will add a blank array object (ahh, the joys of moving between js and PHP) to the Drupal.settings object — which is useful for all sorts of great javascript functionality, and is integral to Drupal 6.0’s extended AHAH features; if you don’t already know it, do your self a favor and study up — and automatically fill it with any textarea’s default values when a user clicks/tabs it into focus. This lets us clear the default value, but replace it quickly if the user moves on to another element.

As listed, you probably don’t want this on your site, as it will affect things like editing nodes (e.g. title inputs will go blank when you click on them… not what you necessarily want), but it’s easy to tune this to only hit elments within certain forms since every form in Drupal has a unique #id.

Thinking about this, I decided to tune it up and actually make an extended jQuery function for this so it could be more easily applied to speecific elements like so:

$(document).ready(function(){
  // handle hide/show for text field default values in only one form
  Drupal.settings.input_defaults = {};
  $("#specific-form input:text").clearDefaultText();
});

jQuery.fn.clearDefaultText = function() {
  return this.each(function(){
    var element = $(this);
    Drupal.settings.inputDefaults[element.attr("id")] = element.val();
    element.focus(function() {
      if (element.val() == Drupal.settings.input_defaults[element.attr("id")]) {
        element.val('');
      }
    });
    element.blur(function() {
      if (element.val() == '') {
        element.val(Drupal.settings.inputDefaults[element.attr("id")]);
      }
    });
  });
}

This is a pretty nice little plugin, I think, and it shows just how easy it can be to add nice/reusable UI functionality. Happy Drupaling, and go get ‘em jQuery!

(updated w/slight improvement to jQuery fn)
(updated again w/object style improvements from comments)

Two New Screencasts in the Drupal Dojo

Josh Koenig

Yesterday I ran an impromptu lesson in the Drupal Dojo building on last week’s introduction of Druapl 6.0’s new theme layer enhancements, namely built-in template files and automatic preprocess_functions(). We covered a topic my colleague Matt blogged about a couple weeks ago: using template files to take control of forms, which is a great way to take your UI to the next level by making it much more designer-friendly.

Check the screencast here: Fine-tuning the UI: Theming Forms With Templates In Drupal 6.0. If you’re curious about that technique in version 5, Matt’s blog post is a good place to start.

Drupal Dojo

Also, by popular demand I made a short (6 minute) mini-lesson explaining the virtues of devel.module, again in the 6.0 context with the theme_developer tool featured prominently.

Drupal 6.0: More Designer-Friendly Than Ever!

Josh Koenig

With the release of Drupal 6.0, there have been major steps forward in the theme layer. Two of the most important are the standardization of template files and their associated pre-process functions, and the addition of theme.info files which allow the overriding of whole core stylesheets.

This Sunday I gave a 45 minute overview lesson on these topics for The Drupal Dojo. There will be more later, but in brief I think with this core advance, all that remains for Drupal to be a truly designer-friendly platform is better documentation of best practices.

Check out the screencast for details.

Syndicate content