by Jeff Eaton on March 9, 2010 // Short URL

Customizable Header Images for Your Drupal Theme

Meet your new friend: theme-settings.php

Drupal themes get a number of configurable settings options for free. For example, most provide toggle switches for the search box, site slogans, user pictures, and so on. Similarly, most provide file uploading widgets to add a custom logo or favicon. These settings are easy: Drupal will add them to the theme's configuration page by default, so it takes no extra work. We want to create our own custom setting, however -- one that adds another field to the Theme configuration form. To do that, we'll need to add a new file to the theme: theme-settings.php.

This file's only job is to implement a single Drupal hook: hook_settings(). Its job is to take an array of settings for the theme, and return a custom FormAPI form that allows users to edit them. Drupal will automatically save the values that users enter into it for later retrieval: managing the form itself is this code's only task.

<?php
/**
* Implementation of hook_settings() for themes.
*/
function MYTHEMENAME_settings($settings) {
 
// This ensures that a 'files' directory exists if it hasn't
  // already been been created.
 
file_check_directory(file_directory_path(),
   
FILE_CREATE_DIRECTORY, 'file_directory_path');

 
// Check for a freshly uploaded header image, save it to the
  // filesystem, and grab its full path for later use.
 
if ($file = file_save_upload('header_image',
      array(
'file_validate_is_image' => array()))) {
   
$parts = pathinfo($file->filename);
   
$filename = 'MYTHEMENAME_header_image.'. $parts['extension'];
    if (
file_copy($file, $filename, FILE_EXISTS_REPLACE)) {
     
$settings['header_image_path'] = $file->filepath;
    }
  }

 
// Define the settings-related FormAPI elements.
 
$form = array();
 
$form['header_image'] = array(
   
'#type' => 'file',
   
'#title' => t('Header image'),
   
'#maxlength' => 40,
  );
 
$form['header_image_path'] = array(
   
'#type' => 'value',
   
'#value' => !empty($settings['header_image_path']) ?
     
$settings['header_image_path'] : '',
  );
  if (!empty(
$settings['header_image_path'])) {
   
$form['header_image_preview'] = array(
     
'#type' => 'markup',
     
'#value' => !empty($settings['header_image_path']) ?
         
theme('image', $settings['header_image_path']) : '',
    );
  }
  return
$form;
}
?>

In the code above, loosely adapted from Development Seed's Singular theme, we've defined three specific form elements. The first, header_image, is a file upload widget that lets users post a file: it's pretty straightforward.

The second form element is header_image_path. It's a 'value' field that stores the actual location of the uploaded file on the server. It will never be displayed directly to the user, but because it is present in the form, it will be saved in the theme's settings and can be used later.

The third form element is header_image_preview. If an image path exists, it simply spits out an image tag containing a preview of the uploaded header image.

The only other code is the snippet at the beginning: it checks to see whether the form has just been submitted, along with a file upload. It ensures that the file itself gets saved correctly, and the path is extracted and handled properly.

Teach your theme new tricks: template.php

Now that the settings form has been created, administrators can upload new header images at will. The theme doesn't actually do anything with those fresh images, however: that's something we'll need to add ourselves. We'll do that in the template.php file, where your theme can store custom "Preprocess" functions that prepare variables for use in your HTML templates.

<?php
/**
* Implementation of hook_preprocess_page().
*/
function MYTHEMENAME_preprocess_page(&$variables) {
 
$settings = theme_get_settings('MYTHEMENAME');
  if (!empty(
$settings['header_image_path'])) {
   
$vars['header_image_path'] = $settings['header_image_path'];
  }
  else {
   
$variables['header_image_path'] = path_to_theme().'/head.jpg';
  }
}
?>

This function is pretty straightforward. When the theme's page.tpl.php file is about to be called, rendering the entire page into HTML, this function will fire -- it gets a chance to add or alter the $variables collection that will be passed on to the template.

To make the magic happen, we're calling theme_settings(), a Drupal API function that conveniently retrieves all of the settings that our form allowed site administrators to change. Then we look for the header_image_path setting, and if it exists, we add the custom path to the $variables array that will be handed off to the template. If the setting doesn't exist -- in other words, if the administrator hasn't uploaded a custom image -- we use the default header image that ships with the theme.

Pulling it together

We've added the settings page to our theme, we've added a new $header_image_path variable to the page template's list of pre-built data, and we're ready to rock. All that's left now is printing it out at the proper location in the theme's page.tpl.php file instead of a hard-coded reference to a static header image.

These same techniques can be used to expose more advanced settings. The NineSixty Robots theme we build in our Advanced Theming DVDs, for example, exposes configuration options for a Twitter-driven slogan rotator. Because we're exposing the header image as a page template variable, other Drupal modules can intercept and modify it. For example, a site's custom module could use the hook_preprocess_page() function to change the header image depending on the section of the site that's being viewed, or the time of day.

Of course, themes don't have to be that complex! Even simple uses of theme settings -- like this swappable header -- can put a lot of personalization power in the hands of site builders.

Jeff Eaton

Senior Digital Strategist

Want Jeff Eaton to speak at your event? Contact us with the details and we’ll be in touch soon.

Comments

Gary

Nitobe Theme Use of phptemplate_settings Function

The Nitobe 960 theme is somewhat similar to your example, but it uses a rotating series of header images, and also features the input of parameters via a form. In the theme-settings.php file Nitobe uses phptemplate_settings( $settings ) rather than nitobe_settings( $settings ).

Is this a case of being able to use either function name?

Reply

eaton

Historical reasons

In previous versions of Drupal, it was common to use phptemplate_hookname() as a pattern when implementing theme functions. As themes have become fuller 'citizens' in the Drupal code ecosystem, though, that's fallen out of style and the themename_hookname() pattern is preferred.

Reply

eaton

True...

...although those modules all use relatively heavy approaches to loading the information. For example, headerimage requires that you set up a new custom node type matching certain criteria, and position a custom block into a particular region in your theme. While that might work for some people, specific settings inside the theme itself can be much easier for new users to figure out.

It's important to note, too, that the technique outlined in the article doesn't have to be header-image-specific. It could be used to upload any sort of image that's outside of Drupal's normal logo/favicon pair.

Reply

Anonymous

You guys are "too" smart to

You guys are "too" smart to the point that you loose focus in what most people like on a website (most people are dumb!) they want lots of photo galleries and videos and post coments on photos. I really hope you guys make a dvd about doing a drupal site with those simple yet atractive functions. The 960 theme has wonderful functions but is too "windows like" when most people enjoy "mac like" what I mean is you can teach people how to create atractive websites and bring a couple of graphic designers into your videos so things get more artistic.

Reply

eaton

Tools vs. End products

Thanks for the thoughts -- at the end of the day, Lullabot tries hard to find the right balance between the extremes you're talking about. We try to focus on helping the people who are building new web sites -- and the people who are customizing Drupal themselves -- get better at what they're doing.

While these kinds of tips may not help end users with no PHP experience who just want a pretty theme, they DO help themers make it easier for those end users to customize themes!

Reply

Anonymous

Isn't the point to not have to code?

I have been trying to learn Drupal for about a month now, and I am dismayed by how much code you need to know to do simple things...I thought sort of the point of a CMS was so that you didn't have to code? So now not only do you have to learn the world of Drupal and how all of its crazy modules interact, but you have to know a bunch of code and how it all interacts as well? Am I missing something here? Or am I just too new at all of this to know what I am bellyaching about?

Reply

wadsofat

You don't have to code

The point of a CMS is so you don't have to code HTML, and for the most part, you don't have to code PHP either unless you want to start doing funky stuff that hasn't been released in a module or theme that's suitable for you yet.

Reply

Charles

How to revert back to theme

How to revert back to theme default header image if site admin doesn't like the upload header image anymore ?

Reply

Anonymous

Thanks for the tips; how do

Thanks for the tips; how do I move margins i.e. the content of page a little bit downwards so as to give room for my background image?

Secondly, is there a way of making the whole page transparent so I can be more free with my links layout? Thanks, once again.

Reply

Anonymous

Drupal 7

Any tips on how to make this work on Drupal 7 with the Zen theme? I keep coming up with this error:

The specified file temporary://image_2.jpg could not be copied, because the destination simple_header_image.jpg is invalid. This is often caused by improper use of file_unmanaged_copy() or a missing stream wrapper.
Notice: Undefined property: stdClass::$filepath in simple_form_system_theme_settings_alter() (line 42 of /home6/resoure0/public_html/drupal/sites/all/themes/simple/theme-settings.php).

I've made these 2 changes for drupal 7 compatibility:
file_check_directory --> file_prepare_directory

function MYTHEMENAME_settings($settings) --> function simple_form_system_theme_settings_alter(&$form, &$form_state)

I admit I don't really know what I'm doing - help would be much appreciated.

Reply