The danger of "q=$1"; or, why you shouldn't ignore Drupal 404 errors.
Drupal makes your life very easy, but that's no excuse for being lazy. While profiling scripts today, I ran into something that, for whatever reason, never clicked before. I was running Xdebug and KCachegrind on some complex pages to get an idea where I could optimize. Xdebug has a profiler built in that creates files digestible by something like KCachegrind. With profiling on, you get a profile dump file for every PHP script you run. You can in-turn open that file with KCachegrind to get some helpful analysis.
Profiling is a great way to expose those problems that I often ignore...
Using Syslog Module and Splunk to Rock Your Reporting World
Drupal's Syslog module is one of the core modules that I find is often overlooked. On a high traffic site, it's generally a good idea to reduce the db traffic as much as possible. Besides, since the database logging doesn't rotate/archive logs, it is only marginally helpful. So, while you need watchdog, it can quickly bite you in the... sorry, bad pun, but you get the idea. The good news is that we can still log all of these helpful messages in a flat file using Syslog and do some insanely cool reporting using Splunk.
Drupal, jQuery and $.ajax() easyness.
So, I've been remiss in my blogging as of late. It's not for a lack of ideas though, so I'm going to jump right back in with something cool that I've been using a lot lately. Once again, I'm just astounded at how insanely easy jQuery makes my life. I've been reading Resig's book on the train this week and the main thing I'm learning is just how important it is to have Javascript frameworks like jQuery. There are so many nuances between major browsers - even the "modern" ones - that it would be ridiculous to try and account for them yourself when these frameworks already do such a great job.
I've written before about doing modals using the popups api and it sounds like there is some goodies in ctools. Though, sometimes you just need laser precision to accomplish one specific task to meet a clients requirement. In these cases, I'm not at all opposed to writing a quick module.
Let's say you need a really simple module that provides a link to trigger something on the back end of your site and returns a response without a page refresh. This type of thing is super common these days and jQuery makes it oh-so-simple to implement.
Read on for the code...
Adding help text to your Drupal forms using the Popups API
It seems fairly often that I put links into Drupal help text. It's a great way to show some extended descriptions or direct people to administrative options. The only annoying thing is that it really disrupts the UX. If you are entering a new node, you risk leaving an unsaved form. If you use target="_blank" then you risk confusion by having multiple windows open. So, after reading Gábor's post on D7UX overlays I decided to try out the Popups API module to solve this annoying problem.
So, now I am absolutely in love with the Popups API. It provides a great way to show contextual help and even let users execute administrative forms without losing their place. For example, I've run into a few situations where I'm using a CCK nodereference field and thought it would be awesome to create the referenced node without leaving the /node/add form. Now it becomes simple - the Popups API allows you to create an elegant javascript popup that doesn't have a negative effect on the UX. The popup can contain any page content on your site - a new node/add form, a custom page, etc. If you want to get crazy, you can even set up the popups to refresh or effect the parent page.
For example click here to leave a comment. [Planet Drupal readers will have to click through to my site to see this example]
The installation isn't too bad, but make sure that you check out the README.txt file that comes with the module. Once you enable the module, you will have to make sure that you have a container <div> around your $content variable in page.tpl.php - this will filter out the extraneous parts of your theme and only show the important parts in the popups. Next you have to specify the selector of that container <div> in your global theme settings. Finally, you need to do one of three things to make it work: 1) enable popup scanning on all pages (located in the admin settings), 2) call popups_add_popups() somewhere in a module callback, 3) add one of the popups classes to your anchor tag. The available popup classes will determine how the popup interacts with the parent page. Again, check out the README.txt for all of the details.
Also, be sure to watch the screencast on the Popups API project page to really see how cool this module is. You can use these popups to show any number of things. For example, you could create a wiki page that your administrators can edit, then link to that node from help text so that editors can maintain their own help text with notes. You could even build administrative views with bulk operations, or link to a user creation form. If you have ever had someone ask: "can I do this without leaving the page?" then this is a great module for you.
The coolest part is that this doesn't even scratch the surface of what you can do with these popups. It's a very exciting time for Drupal UX and I'm totally excited to see these things find their way into core.
Using Drupal's Batch API
I don't know how I went so long without implementing Drupal's Batch API, but finally had a chance to play with it yesterday and had a Drupal Aha! moment. This particular API is very handy and surprisingly easy to implement.
We've been working on a way to synchronize a bunch of development environments using SVN and make it quick to deploy a new install of the site we're working on. The first step was to use the Import/Export module to dump content types, views and taxonomy definitions to text files. This way we could commit them to the repository and quickly build the framework for a fresh install of the site. Just install Drupal, then run the import function for the content types, views and taxonomies.
This was all great until we started exporting complex content types. It turned out that the system would fail if we tried to do more than one at a time, which meant going through the tedious effort of exporting... one... at... a... time.
Batch API to the rescue!
The Batch API module lets you easily set a batch of callbacks that Drupal will run one at a time, thus avoiding script timeouts for laborious tasks. The batch can be naturally set up in your form submit handler, or can be called in any other function by adding one extra line of code.
<?php
// Set up the Batch API
$batch = array('operations' => array(),
'finished' => '',
'title' => t('Exporting to files'),
'init_message' => t('Starting the export'),
'progress_message' => t('Exported @current out of @total'),
'error_message' => t('An error occurred and some or all of the exports have failed.'),
);
?>Add as many functions to the batch queue as you need:
<?php
// Add an array declaring the callback function for your
// batch process and an array of callback arguments
$batch['operations'][] = array('your_batch_function', array($arg1, $arg2));
// add whatever logic you need
$batch['operations'][] = array('another_batch_function', array($arg1, $arg2));
?>Tell the Batch API that you are finished:
<?php
if (!empty($batch['operations'])) {
batch_set($batch);
// batch_process() only needed if not inside a form _submit handler :
batch_process();
}
?>drupal_execute() is not happy
One problem that I ran into along the way was the discovery that drupal_execute() has some compatibility issues with the Batch API. If I tried to use drupal_execute() in my batch callbacks, it would fail. Luckily there was a link to this tip to pause the batch that allowed the batch functions to use drupal_execute() with no complaints.
<?php
function your_batch_function($arg1, $arg2) {
// Pause the batch
$batch =& batch_get();
$ref = $batch;
$batch = null;
// run your code with drupal_execute()
// Unpause the batch
$batch = $ref;
}
?>Batching makes me happy
So, I just got done batch exporting 88 items including 19 super complex content types, 64 views and 4 vocabularies (one containing over 100 terms) and all it only took a few clicks. The Batch API is an amazing time saver. Combining it with some other awesome community tools like Import/Export gives me time to go take a much needed coffee break :)
Dropdown Drupal menus with accessibility-friendly image links
I just finished up the preliminary work on a new site where the client wanted drop down menus and fancy image links for the top-level menu items. The requirements were to use the Drupal menu system to maintain permissions on the links, but also use stylized images for the main menu links. It took a bit of searching, but the solution was pretty elegant thanks to the Drupal community.
The drop downs were easy. I used the Nice Menus module for the first time and immediately fell in love with it. It provides up to 99 blocks to which you can assign Drupal menus. The menus can drop down or fly out, depending upon your design. Plus, you can stylize them to your hearts content to match your theme. So, the only real problem was how to use images for the top level menu.
There are apparently a few ways to solve this problem. My first attempt was to just use CSS to size the menu items, hide the menu text and place a background image for the element. This seemed to be the least obtrusive method for Drupal, but flaws emerged with compatibility and accessibility. Firefox seems to like color: transparent; but IE does not. They both seem to like text-indent: -1000px; but with either of these solutions the links completely disappear when you turn images off :(
Luckily, I stumbled upon this comment which details the use of theme_menu_item_link() to swap the menu title field with the description field and allow it to use HTML for display. After implementing the function in your template.php file, you can type your HTML img tag into the description field, and your hover text into the title field when creating a menu item.
Now, you have an accessibility friendly menu which uses Drupal permissions, image tags with alt text and an anchor tag with title text. You just have to do a little bit more CSS cleanup to get the drop downs to match your theme and you are golden!
Many thanks to the Drupal community on this one!!
Alter Drupal Email Messages
I just had a requirement to add some text to the body of Drupal's outgoing emails. Turns out it's not too tough using hook_mail_alter(). I started out by using var_export() and drupal_set_message() to find out more information about the array that Drupal creates for the message:
<?php
/**
* Implementation of hook_mail_alter().
*/
function your_module_mail_alter(&$message) {
// For example, submit the contact form and you
// will be able to see the $message array
$v = var_export($message, true);
drupal_set_message("message: " . $v);
}
?>The email text is constructed from an array of elements, so adding a new element to the email body is just a matter of adding an element to the body array:
<?php
/**
* Implementation of hook_mail_alter().
*/
function your_module_mail_alter(&$message) {
// Add a link to the user's profile in the message body
// when you submit the site wide contact form:
switch ($message['id']) {
case 'contact_page_mail':
global $user;
if ($user->uid != 0) {
$message['body'][] = 'Profile: [your-site-address]/user/' . $user->uid;
}
break;
}
}
?>You could just as easily use this for a variety of other cases. For example, you could add conditional recipients, do custom logging, or even use the message content as a conditional trigger. Lots of good stuff there.
Adding containers to Drupal form elements
So, I ran into an issue recently where I needed to add a container <div> to stylize my Drupal form elements. The issue was two-fold. One, I wanted to style form elements differently depending upon type, and two, I needed a container <div> that only wraps the form element and not the label.
Problem: I need a container around the <input> tag so that I can make the background of the input transparent, ditch the border then place a background image that is not attached to the <input> tag (there are scrolling issues in IE if the bg is set for the input tag). This way I can end up with a nice rounded input that matches the site theme.
This won't work for me:
<div id="edit-field-address1-0-value-wrapper" class="form-item">
<label for="edit-field-address1-0-value">Address</label>
<input id="edit-field-address1-0-value" class="form-text required text" type="text" value="" size="60" name="field_address1[0][value]"/>
</div>Quick fix in template.php using theme_form_element() (Look for the line "ADD A CONTAINER DIV WITH A CLASS BASED UPON ELEMENT TYPE")
<?php
/**
*implementation of theme_form_element().
*/
function mytheme_form_element($element, $value) {
// This is also used in the installer, pre-database setup.
$t = get_t();
$output = '<div class="form-item"';
if (!empty($element['#id'])) {
$output .= ' id="'. $element['#id'] .'-wrapper"';
}
$output .= ">\n";
$required = !empty($element['#required']) ? '<span class="form-required" title="'. $t('This field is required.') .'">*</span>' : '';
if (!empty($element['#title'])) {
$title = $element['#title'];
if (!empty($element['#id'])) {
$output .= ' <label for="'. $element['#id'] .'">'. $t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) ."</label>\n";
}
else {
$output .= ' <label>'. $t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) ."</label>\n";
}
}
// ADD A CONTAINER DIV WITH A CLASS BASED UPON ELEMENT TYPE
$output .= "<div class = 'round-" . $element['#type'] . "'> " . $value . "</div>\n";
if (!empty($element['#description'])) {
$output .= ' <div class="description">'. $element['#description'] ."</div>\n";
}
$output .= "</div>\n";
return $output;
}
?>Resulting HTML:
<div id="edit-field-address1-0-value-wrapper" class="form-item">
<label for="edit-field-address1-0-value">Address</label>
<div class="round-textfield">
<input id="edit-field-address1-0-value" class="form-text required text" type="text" value="" size="60" name="field_address1[0][value]"/>
</div>
</div>Above you can see the <div class="round-textfield"> wrapping my form element. Now, I am able to style the container div based upon which type of field it is!
Apache Solr + Acquia Search = Rocks!
I finally got around to setting up the Apache Solr module and Acquia Search. Holy cow, it's crazy nice.
- Faceted search - filter by author, content type, author, etc.
- Speling suggestions!
- Crazy relevant
- Crazy fast
So, the Acquians have been hard at work setting up a big cluster of Solr servers in the cloud to get ready for the big launch of Acquia Search. It's cool because they do all of the heavy lifting and Solr maintenance for you. Drupal search is meh, ok but Solr search is insanely fast and relevant. Your site sends an index (securely) to the Acquia servers, then all of your site queries go (securely) to the Acquia servers to retrieve results.
It's even complete with Drupal access API integration. If you are running the Node Access module, or Organic Groups, it will respect your node permissions!
Drupal.org just switched over to it and people are finally using the site search instead of Google - that's saying something! :)

