Remove Node Lists from Taxonomy Pages in Drupal 7

If you use Drupal, you can’t help but love the Taxonomy module. After all, categorizing content goes hand in hand with creating it or managing it, and Taxonomy gives us a nice, flexible framework, especially when you throw fields into the mix.

Unfortunately, every taxonomy term page comes with a default list of all the content classified with that term. Sometimes you might want this, but sometimes you might not. I’ve run into a few situations in which I needed tighter control over the term page display, like that provided by Views, without wanting to override the entire page.

Here’s the snippet of code I created to deal with those situations. Just put the following in the template.php file of your theme folder:

function THEMENAME_preprocess_page(&$vars){
  if (arg(0) == ‘taxonomy’ && arg(1) == ‘term’ && is_numeric(arg(2))){
    unset($vars['page']['content']['system_main']['nodes']);
    unset($vars['page']['content']['system_main']['pager']);
    unset($vars['page']['content']['system_main']['no_content']);
  }
}

This will remove the default content list, as well as the pager and "No content has been classified with this term" text if either is present. On a default installation, that sets the page back to a blank slate, like an ordinary content page, so you can play around with it from there to your heart's content.

jQuery Default Form Text Function

Often times, in place of labels, designers will choose to put default text in a form field to denote the information that it should contain. This is good design and usability, but it also creates some functional problems. Obviously, the text should disappear when the field is in focus, but what happens if the user clicks away without entering anything, or submits the form before filling in the field?

This handy jQuery function solves those problems. Default text will appear in the field whenever it is empty and not in focus. The user can click it to remove the text, then click away to reveal it again. If the field has been filled out, nothing happens. As a bonus, if the form in question is submitted with default values, those are cleared before the submission takes place to allow for proper validation.


function default_text(selector, text){
  element = $(selector);
  if (element.val() == ''){
    element.val(text);
  }
  element.focus(function(){
    if ($(this).val() == text){
      $(this).val('');
    }
  }).blur(function(){
    if ($(this).val() == ''){
      $(this).val(text);
    }
  }).parents('form').submit(function(){
    if (element.val() == text){
      element.val('');
    }
  });
}

$(document).ready(function(){
  default_text('#your_form_element_id', 'Your default text');
});

How to Enable Ubercart Product Image Zoom with Gallery Thumbnails

In my experience, ecommerce websites are their own beasts, with all sorts of specialized functionality that doesn’t get requested on other websites. One of the most frequent requests, and often times one of the most difficult to fulfill, is enabling product image zoom functionality along with gallery thumbnails that can seamlessly swap out the main image. Here is one way to accomplish that on a Drupal 6 website with Ubercart.

  1. Install the necessary modules. For this solution, you’re going to need CCK, ImageField, ImageCache, and Cloud Zoom (I used to prefer jQZoom, but found it to have less compatibility in WebKit browsers).
  2. Set up your image field. The code provided below is designed to work with the default image handling field creating by Ubercart, but it can work just as well with any CCK image field. Just be sure to swap out the $node->field_image_cache variable with the name of your own field if you’re using something different.
  3. Set up your ImageCache presets. You’re going to need three. Again, the solution below is tailored for use with product images in Ubercart, so I’ve named them accordingly. “product_full” is the main image displayed to the user, “product_zoom” is the larger image shown in the zoom window, and “product_thumbnail” is the gallery thumbnail that allows the user to swap out the main image. You can name these whatever you like; just be sure to change their names in the code as well.
  4. Stop the field from displaying. By default, Cloud Zoom can automatically display the image and zoom window without any need to mess with template files. This is very handy, but it doesn’t support gallery thumbnails, so we’ll need to disable the default behavior. Go into Administer > Content Management > Content Types > Manage fields > Display fields and change the Label, Teaser, and Full node drop-downs for your image field to <Hidden>. Be sure not to click the Exclude boxes, or else the template modifications below will not work properly.
  5. Add this code to your theme file. In the case of Ubercart, this goes in node-product.tpl.php where you’d like the images to appear. If you’re using a different content type, use that in place of product. And again, be sure to switch out the image field variable name or the ImageCache preset names if you’re using something different.
    <?php

    drupal_add_js(drupal_get_path('module', 'cloud_zoom') . '/cloud-zoom/cloud-zoom.1.0.2.min.js');
    drupal_add_css(drupal_get_path('module', 'cloud_zoom') . '/cloud-zoom/cloud-zoom.css');
    if (is_array($node->field_image_cache) && count($node->field_image_cache) > 0 && strlen($node->field_image_cache[0]) > 0){

    // Display the primary image.
    echo l(theme('imagecache', 'product_full', $node->field_image_cache[0]['filepath'], $node->field_image_cache[0]['data']['alt'], $node->field_image_cache[0]['data']['title']), imagecache_create_path('product_zoom', $node->field_image_cache[0]['filepath']), array('attributes' => array('class' => 'cloud-zoom', 'id' => 'zoom1'), 'html' => TRUE));

    // Display the gallery thumbnails.
    $num_images = count($node->field_image_cache);
    if ($num_images > 1){
    for ($i = 0; $i < $num_images; $i++){
    echo '<a class="cloud-zoom-gallery" href="' . base_path() . imagecache_create_path('product_zoom', $node->field_image_cache[$i]['filepath']) . '" rel="useZoom:\'zoom1\', smallImage:\'' . base_path() . imagecache_create_path('product_full', base_path() . $node->field_image_cache[$i]['filepath']) . '\'">' . theme('imagecache', 'product_thumbnail', $node->field_image_cache[$i]['filepath']) . '</a>';
    }
    }
    }

    ?>
  6. Stylize to taste. With a bit of added coding and some CSS, you can make the final product display however you like. For example, I like to put the gallery thumbnails into an unordered list and float them beneath the main image for ease of usability. Your needs may vary.

How to Add Variations to a Drupal Theme

Recently, I did work for a few clients who needed several very similar websites launched in a single project, each of which using an almost identical (yet subtly different) theme. As I started configuring them on Drupal multi-site installations, it got me thinking: Is there a way to take advantage of the same sort of code reuse within a theme?

There are already options for this, of course, such as sub-themes or the Color module. In my case, however, I decided to try something a little different: I used a custom theme setting to add a CSS class to the body tag, then created the theme variations with pure CSS. Here’s how I did it.

Step One: Set Up the Advanced Theme Setting

In case you’re a Drupal themer who doesn’t know this trick, it’s a life-saver. You can configure your theme with a form to collect custom settings, then use those settings in the theme itself. I like to use this for things like phone numbers that don’t deserve their own block region but need to be configurable by the client nonetheless.

There’s a great Drupal article on advanced theme settings, which I won’t bother repeating. As far as theme variants go, all you have to do is include the following code in the theme_settings.php file of your theme folder:

<?php

function themename_settings($saved_settings){
$defaults = array(
'variant' => 'default'
);
$settings = array_merge($defaults, $saved_settings);

$form['variant'] = array(
'#title' => t('Variant'),
'#type' => 'select',
'#default_value' => array($settings['variant']),
'#options' => array(
'default' => 'Default',
'variant_1' => 'Variant #1',
'variant_2' => 'Variant #2',
'variant_3' => 'Variant #3'
)
);

return $form;
}

This will create a drop-down selection menu on your theme configuration page that allows you to select the desired variant. Be sure to change the keys and values in the #options array to include the CSS class and variant names you want.

Step Two: Hook the Variant Setting into the Template Files

Now that the variant can be defined, it’s time to dynamically include it in your template files. This is accomplished with the theme_get_setting() function. Include the following code at the top of your page.tpl.php file (and any other relevant template files):

<?php
$variant = theme_get_setting('variant');
if ($variant == 'default'){
unset($variant);
}
?>

Then, on the body tag in each template, include code to insert the variant as a CSS class:

<body<?php echo ($variant) ? ' class=" . $variant . "' : ''; ?>>

If you want, you can do other useful things with the $variant variable. For example, I took it a step farther and created image subfolders at theme_folder/images/$variant. That way, if I had images that needed to vary, all I had to do was name the images the same and include $variant in the image src attribute.

Step Three: Add CSS to Customize Each Variant

Once the body is being classed according to the theme variant setting, you can do whatever you like to customize each variant. Simply add CSS to your style.css file in order to tweak the theme’s appearance according to the new body class. For example, you might adjust the font face and color of each variant:

#content { color:#000; font-family:Arial; }
.variant_1 #content { color:#F00; font-family:Helvetica; }
.variant_2 #content { color:#00F; font-family:Verdana; }

Possible Uses

The main use of this technique is to provide a fast, easy way to create minor variations in a single Drupal theme. You might add some seasonal stylization for a holiday variant, for example. Or, as in my case, you might have a few small differences between sites using the same theme and want to keep a common code base for ease of maintenance.

Anything more than that and you’re probably better off using one of the aforementioned techniques, such as sub-theming or the Color module. It’s really just a matter of how different your variations are going to be.

Why Never to Launch a Site on Friday Afternoon

Imagine sitting in mission control as a rocket is launched into space. The countdown initiates. “10, 9, 8…” The boosters engage. The astronaut comes over the com to confirm final checks. “7, 6, 5…” Everything is a green light. The launch crew sits on the edge of their seats. “4, 3, 2…” The moment is finally upon us, and then… quitting time. Just as the rocket is about to launch, everyone gets up from their desks and heads home for the weekend.

Sounds pretty strange, doesn’t it? Why would anyone do something so reckless? Doesn’t it make more sense to give launch the time and attention it deserves? After all, if everyone walks away right before lift off, they may miss a critical moment that could make or break the whole operation.

That, ladies and gentlemen, is exactly what happens when you try to launch a website on a Friday afternoon. You initiate the countdown and walk away, naively trusting that everything will go smoothly. No verification of success. No post-launch QA. You just push the button and go home for the weekend.

You’d think this would be common sense. You’d think any good web development company would know better than to do it like this. Regrettably, you’d be wrong. This has happened at every web shop I’ve worked in, not just once, but often. Clients have been allowed to say the word “Go” at the worst possible moments, thinking it’s as simple as pushing a button and letting everything magically work out.

The thing is, clients don’t know any better. They don’t do this for a living. It’s the job of their web development team to explain that launching a website is a non-trivial process that takes time and attention, that launching without a human being present to fix things when they inevitably go wrong means they’re stuck with a broken website all weekend, that unexpected glitches must be factored in, and that it’s a bad idea for their company and their brand to do otherwise. Anything less is reckless.

The alternative, of course, is a broken website that languishes for days while clients gnash their teeth, pull their hair out, and make angry phone calls at 3:00am because their brand new website isn’t working right. This hurts not only the client’s business, but the web shop’s business, too.

So the next time you’re working with a client who insists on launching late on Friday (or you happen to be that client), do everyone a favor. Stop, breathe, and ask if it can wait until Monday. 99% of the time, it can, and as I’ve said, it really, really should, for everyone’s sake.

Code HTML Email Templates by Breaking the Rules

If you’re anything like me, the first time someone told you to make an email template, you thought, “Piece of cake!” After all, emails use HTML (or the types that needs templates do, at any rate), and HTML is a cinch, right?

As it turns out, you’d be wrong. HTML is a cinch, but the truth is, the more you know about proper HTML coding standards, the harder it is to make a functional email template. That’s because the only way to write them is to break all of those pretty rules you spent so long mastering. Here’s the breakdown:

Why are the rules set this way? I blame the proliferation of spam and the lack of standards between email clients. HTML is great, but it gives spammers too much control, and the knee-jerk response is to strip out everything until you’re left with… well, this. Don’t believe me? Try testing your email template on the dozens (probably hundreds) of email clients in use. And you thought cross-browser compatibility testing was bad…

So, blasphemy though it may be to us coding purists, these are the standards you have to keep in mind when coding email templates. As a rule of thumb, just break every rule in the book and you’ll be on the right track. And if that makes you feel dirty, you can always read a good book on HTML 5 for absolution. ;)

Elsewhere: So You’ve Got a Drupal Website… Now What? (Part Two: Learning the Lingo)

I just posted So You’ve Got a Drupal Website… Now What? (Part Two: Learning the Lingo) on ClickOptimize.com:

In this series, I walk you through the basics of using your shiny new Drupal website. In part one, I explained what Drupal is and why it’s awesome. We’ll get into working with Drupal in the next section. Before we can walk the Drupal walk, however, we need to learn to talk the Drupal talk.

Read the whole article on ClickOptimize.com.

Elsewhere: So You’ve Got a Drupal Website… Now What? (Part One: What is Drupal)

I just posted So You’ve Got a Drupal Website… Now What? (Part One: What is Drupal) on ClickOptimize.com.

It’s shiny. It’s new. And it’s totally mind-boggling how much you can do with it. The only trouble is, you have no idea where to start. In this series, I’ll walk you through all the useful bits and bobs in your new Drupal site, how to use them, and all of the situationally useful extra stuff you can safely ignore 99% of the time.

Read the whole article on ClickOptimize.com.

Attach Files to Taxonomy Terms in Drupal

From what I can tell, there are plenty of ways to attach files to nodes in Drupal, and even a few ways to attach them to users, but none that I could find to attach them to taxonomy terms (images through the Taxonomy Image module, for sure, but not files in general). My guess is that there just aren’t enough people who need to do so.

In a recent project, however, I found myself needing just that. The client in question was using a vocabulary of fonts that had to be associated with their respective font files for JavaScript. After beating my head against the problem for awhile, I managed to figure out a solution. Instead of attaching a file directly to the taxonomy term, I assigned the same vocabulary to a new content type to hold the attachment. Here’s how it works:

  1. Go to Administer > Site building > Modules and make sure the core Taxonomy and Upload modules are enabled.
  2. Go to Administer > Content management > Content types and click the link at the bottom to Add a new content type.
  3. Give your new content type an intuitive name (e.g., Font File) and enable attachments under Workflow settings. Configure the other settings as you see fit and hit Save.
  4. Go to Administer > Content management > Taxonomy and edit the vocabulary for which the file attachments are intended (e.g., Fonts). Check the box next to your new content type and click Save.

Great! Now you have a way of associating a term with a file, albeit indirectly through a node. How you use that information will, of course, vary. In my case, the font files needed to be populated into a drop-down list of font choices for use by JavaScript. Here’s how you might go about accessing the attachment from the database:

<?php

// Fetch the file path of the most recently uploaded file attached to a node that shares the given term
$attachment_path = db_result(db_query("

SELECT filepath FROM {files} f JOIN {upload} u ON f.fid = u.fid JOIN {node} n ON u.vid = n.vid JOIN {term_node} tn ON n.vid = tn.vid WHERE n.type = '%s' AND tn.tid = %d ORDER BY f.timestamp DESC LIMIT 1

", $NODE_TYPE_NAME, $TERM_ID));

?>

Of course, the best solution would be to create a custom module to handle all of this. In my case, there simply wasn’t enough time for that. I invite some enterprising developer to invalidate my technique by creating a module that allows file attachments on taxonomy terms. Until then, I hope someone finds my roundabout hack useful.

7 Signals of Comment Spam

Recently, I’ve found myself giving the same advice to several of my clients, many of whom are still getting into the swing of blogging. All too often, they’re uncertain about whether or not to mark a comment as spam. Caught between the desire to avoid spam but foster legitimate conversation, they come to me, and these are the seven signals I tell them to look out for:

  1. Links to Spammy Sites. The biggest red flag should always be links provided by the commenter. Visit them and you can usually figure out pretty quickly if the commenter is working an angle. I’ve marked well-thought-out, on-topic comments as spam before because they linked to sites I didn’t care to be associated with.
  2. Lots of Links. There are very few situations I’ve ever seen in which a comment with more than two or three links isn’t spam. If the comment is chock full of them, or, worse, is nothing but a list of links, you should hear alarm bells ringing.
  3. Overuse of Keywords. Perhaps one of the most obvious cues is when a comment seems like little more than a list of keywords. This may be blatant, or the keywords may be hidden within the comment, at the end of the comment, or in the commenter’s name. This sort of spam is easy to catch, but you have to be willing to give your comments more than a cursory glance.
  4. Off-topic. While not a clear signal in and of itself, the topic of a comment can nevertheless be an important clue. Ask yourself what the commenter is talking about. Does it flow from the article in a natural way, or is it marginally related at best? Does it unambiguously mention anything from the article? The more it strays from the topic at hand, the more likely it’s copied-and-pasted junk.
  5. Complimentary. Make no mistake; spammers will play to your ego if they think it will get their comments posted. Beware comments that pay too much respect to your work. They may just be buttering you up to compromise your better judgment.
  6. Irregular Size. Comments vary in length, but extremely short or long comments beg greater scrutiny.
  7. Poor Grammar. This isn’t to say that ordinary commenters don’t have atrocious grammar some of the time. However, it’s been my experience that most spammers have terrible grammar, even to the point of being nonsensical. Whether this is because English isn’t their native language, the comment is computer-generated, or some other reason, I couldn’t say. Whatever the case, it’s something to keep an eye out for.

Remember; a comment may have one or more of these features and still be perfectly legitimate. When in doubt, ask yourself this: What value does this comment provide to my readers? If you find yourself on the low or negative end of the spectrum, toss the comment without a second thought.