Chapter Three LLC

Planet Drupal

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: 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.

  }
}

Sun, Drupal, and Chapter Three

Zack Rosen

In May, Sun Microsystems approached us for assistance in launching a very cool social media site they were building in Drupal. Two months later we launched the Sun Video Learning Exchange, designed to let Sun staff easily publish training videos and resources to their global network of support engineers and customers. From the time of project kick off we had two months to complete two design passes, develop all features, test, and launch the site. Work progressed smoothly much in part to a very agile work process that paired our team with internal Sun developers working side by side to launch the site.

The site is a great showcase of the comprehensive media handling functionality available now in Drupal. Apart from the integration with LimeWire to store and transcode content and integration with Sun’s LDAP directory for authentication, the site is built entirely with very standard Drupal modules including Views, FiveStar, and Community Tags. It was a real pleasure for us to work with Sun to develop the Learning Exchange and we are really looking forward to seeing what else they can do with Drupal.

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)

Doubling Drupal

Zack Rosen

“They’re going to double in a year. That’s insane, in my opinion” Steve Ballmer (Microsoft, CEO), speaking of Google’s rate of growth as an organization

It’s one thing to see the Drupal community’s exponential growth curve plainly plainly graphed. It’s was quite another to walk into a massive conference hall packed with over 800 Drupal developers last week in Boston. It was honestly all a bit overwhelming.

Since I have been a member of the Drupal project, the incredible rate of growth for the community has been a given. When a relatively small piece of the ‘market’ knew what Drupal was, it’s rate of growth seemed more an interesting aspect of the project than a defining characteristic. But as more and more individuals, organizations, and Drupal specialized companies start betting big on the community, the pressure on the project created by this growth will only continue to increase proportionally.

What does this actually mean? Well, in the past few years it has meant:

Despite these changes though, in many ways the Drupal project appears very much the same as it did a few years ago. Yes, every release the software really does get that much better. More patches, better patches, rinse, repeat. But this kind of relatively painless growth is only possible because the open-source method has created an incredible medium of software production that can relatively easily scale to meet the demands of virtually any size project.

But past a certain point the open-source method runs it’s course. The demands of supporting communities, even if they are largely web based, catches up with the code. We clearly reached this point with the Drupal project a little more than a year ago. These new type of challenges the Drupal community has been confronted with in the past year will become the major challenges that the community will face. And as Drupal continues to double, they will double.

But of course, so will Drupal.

Drupalcon Boston Recap: Bringing Guests to the Drupal External Data Party

Matt Cheney

This year’s Drupalcon was the biggest Drupal Party to date with over 800 attendees and many different sponsors (including Chapter Three of course). Drupal rocked Boston all week and gave everyone in the community a chance to catch up and keep abreast of some of the cool new stuff going on in the Drupal World. Plus, there was some scrumpcious and tasty vegetarian food on the scene.

In my Drupalcon rockstar moment, I took the stage and gave a session with Drupal Genius Neil Drumm and Digital Newspaper Extraordinaire Ken Rickard on Using External Data Sources with Drupal (slides here). The general idea is that Drupal is marvelous for content management, but becomes even better when it integrates with data from external sources and Drupal allows a lot of different ways to make that happen.

Ken kicked off our session by laying the groundwork for why using external data is important and transitioned into talking about some of the basic tools (drupal_execute, drupal_http_request, database switching) and techniques (lazy instantiation, hook_search) needed to make it all happen. He has a lot of good experience around using external data sources from his work in the newspaper world and provided a detailed orientation to the 150 odd people who attended our session.

Neil talked about his work with a wonderful organization called Map Light that records and analyzes information about campaign contributions and congressional votes. For that project Neil had to import *millions* of pieces of information and developed a couple modules to assist him in his work. Job Queue is his module that helps to import external data in batches (instead of all at once) and Import Manager allows administrators to monitor and manage the overall import process.

For my part, I concluded the session by discussing a few specific case examples of how to integrate external data into a Drupal website. I started with an example, which was dear to my heart as a librarian and admirer of philosophy, was to use the Swish-E Module (which I maintain on Drupal.org) to full text index a folder of philosophical essays in .PDF format and make them available via the Drupal search. The second example (see below) was probably the coolest and involved some data mining magic to use the Data Miner API to take a Drupal user’s MySpace name and automatically download their picture from MySpace for display on their user profile. This general technique I was calling “Data Enrichment” and could be used to enhance the data a Drupal site has around either users or specific pieces of node content. Careful consideration here needs to be made to respect both the terms of service and privacy of your users. The final example I presented had to do with some client work Chapter Three did involving the migration of 20,000+ pieces of raw HTML content through a cool audit system set up as several Views and managed by the Workflow System. This system seemed to have a lot of practical value for people and I got a lot of questions about it after the session.

All in all, Drupalcon was awesome and we are looking forward to Drupalcon Europe later this year and Drupalcon San Francisco in 2009.

example of using external data

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.

Syndicate content