HowTo: Replace node title with a related image
I took over a static site, designed with Dreamweaver. The site design and layout were beautiful, and one of my goals for converting the site to Drupal was to maintain as much of the site's visual appeal as possible. One of the visual elements were sharp graphic titles on each page. And so began my quest to figure out how to replace Drupal's text title with these image versions. I tried several methods before coming across the excellent article above (this page's parent), which gave me the following idea (much of the code is lifted directly from that article).
Overview: basic concept
A node on your site has a title, like "Bob's Antiques & Junk", and you want to display the related image file "bobs_antiques_junk.gif" instead of this text title when displaying the node as a page. Furthermore, you want the original title as alt text on the image, and you want "graceful degradation", so the original text title is displayed if the "related image" file does not exist.
What this does is essentially make the Title field of the nodes into an special image selector - if you use a title with an associated image, the image will appear, otherwise, the text title will appear - sweet!
Requirements:
- You are using a template theme.
- You are not averse to a little php.
All code goes into your template.php file.
NOTE: Ignore the opening and closing PHP tags in here. They are just for display and should not be added to your file.
For background info on template.php and the _phptemplate_variables function, see:
- template.php: Overriding other theme functions http://drupal.org/node/11811
- Adding function call to _phptemplate_variables http://drupal.org/node/207841
- HowTo: merge multiple _phptemplate_variables functions http://drupal.org/node/152426
Step 1: convert title to related filename
<?php
// Prepare some general title text for use as a file name.
// Remove special HTML characters, trim whitespace, convert to lower-case
// replace spaces with underscores.
function clean_title($string) {
$cleanString = htmlspecialchars_decode( strtolower(trim($string)), ENT_QUOTES );
$cleanString = str_replace(array("& ", "'"), '', $cleanString);
return str_replace(' ', '_', $cleanString );
}
?>This handy little function will take a title, like "Bob's Antiques & Junk" and convert it to a nice base filename: "bobs_antique_junk". Since Drupal will convert quotes and ampersands to "special characters" (e.g., $#039; or $amp;), it strips those out first. Then does a simple replace to get rid of apostrophe's and ampersands - common in titles. Finally, it replaces remaining spaces with underscores.
This function can be customized to convert titles to filenames however you see fit. Depending on the titles on your site, you may want to extend the substitutions made here. For example, use a regex to match and replace patterns (e.g., preg_replace("/[^a-zA-Z0-9]/", "", $string) ) - this is a little slower, but more general purpose.
Step 2: making the substitution - code snippets
We need both the file system path (to check if the file exists) and the URL (to form the image tag) for the title image. Note: substitute the correct extension and path to your title images - mine are '.gif' files that live in "files/images/titles".
<?php
// convert title to suitable filename and derive both file system path and URL.
$titleFile = clean_title($vars['title']) . '.gif';
$titleImage = base_path() . "files/images/titles/" . $titleFile;
$titleURL = "http://" . $_SERVER['SERVER_NAME'] . $titleImage;
$titlePath = $_SERVER['DOCUMENT_ROOT'] . $titleImage;
?>Only perform the substitution if the "related image" can be found. Use $vars['title'] as the alt text for the image tag, and then put the image tag back in to $vars['title'].
<?php
// Determine if a suitable replacement image for title can be found, and make substitution.
if ( file_exists($titlePath) ) {
$newTitle = '<img alt=\'' . $vars['title'] .'\' src=\'' . $titleURL .'\' />';
$vars['title'] = $newTitle;
}
}
?>A few refinements:
Restrict application of the substitution - see the parent article, which explains this nicely.
<?php
// Substitute title only for nodes...
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1));
// ... of one of the listed types.
if (in_array($node->type, array('page'))) {
// convert title to suitable filename and derive both file system path and URL.
...
?>Save original title text for use in head and breadcrumb.
<?php
$vars['breadcrumb_title'] = $vars['title'];
$vars['head_title'] = $vars['title'];
?>Step 3: putting it all together
In Drupal 5
<?php
function _phptemplate_variables($hook, $vars = array()) {
switch ($hook) {
case 'page':
// substitute node title with an image, if a suitable replacement can be found.
// save original title text for use in head and breadcrumb.
$vars['breadcrumb_title'] = $vars['title'];
$vars['head_title'] = $vars['title'];
// Substitute title only for nodes...
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1)); // (expensive, unless arg(1) is already loaded, which is should be at this point.)
// ... of one of the listed types - add node types to process to the array.
if (in_array($node->type, array('page'))) {
// convert title to suitable filename and derive both file system path and URL.
$titleFile = clean_title($vars['title']) . '.gif';
$titleImage = base_path() . "files/images/titles/" . $titleFile;
$titleURL = "http://" . $_SERVER['SERVER_NAME'] . $titleImage;
$titlePath = $_SERVER['DOCUMENT_ROOT'] . $titleImage;
// $vars['title'] = $titleURL; // DEBUG - see what's being produced
// Determine if a suitable replacement image for title can be found, and make substitution.
if ( file_exists($titlePath) ) {
$newTitle = '<img alt=\'' . $vars['title'] .'\' src=\'' . $titleURL .'\' />';
$vars['title'] = $newTitle;
}
}
}
break;
}
return $vars;
}
?>In Drupal 6
<?php
// afc/template.php
// Prepare some general title text for use as a file name.
// Remove special HTML characters, trim whitespace, convert to lower-case
// repace spaces with underscores.
function clean_title($string)
{
$replace_chars = array("&", ":", ",", "'");
$cleanString = htmlspecialchars_decode( strtolower(trim($string)), ENT_QUOTES );
$cleanString = str_replace($replace_chars, "", $cleanString);
return str_replace(' ', '_', $cleanString );;
}
function mytheme_preprocess_page(&$variables) {
// substitute node title with an image, if a suitable replacement can be found.
// save original title text for use in head and breadcrumb.
$variables['breadcrumb_title'] = $variables['title'];
$variables['head_title'] = $variables['title'];
// Substitute title only for nodes...
$titleFile = clean_title($variables['title']) . '.png';
$titleImage = base_path() . path_to_theme() . "/images/titles/" . $titleFile;
$titleURL = "http://" . $_SERVER['SERVER_NAME'] . $titleImage;
$titlePath = $_SERVER['DOCUMENT_ROOT'] . $titleImage;
//$variables['title'] = $titleURL; // DEBUG - see what's being produced
// Determine if a suitable replacement image for title can be found, and make substitution.
if ( file_exists($titlePath) ) {
$newTitle = '<img alt="' . $variables['title'] .'" src="' . $titleURL .'" />';
$variables['title'] = $newTitle;
}
}
?>
Can this also work in a view?
This is a nice script and works very well.
I have a teaserlist in a view where I would also like to have the images for the titles.
Is this possible?
I have no idea where to change the script to get that working..
Yes, its possible
This script specifically changes the page title:
<?phpswitch ($hook) {
case 'page':
?>
To change the node titles on a teaser list, you need a similar scheme for the 'node' hook.
Best bet is to encapsulate the replace title function (you can download the code and some instructions to install it here: http://lasqueti.ca/books/design-notes/image-titles )
Then do this in phptemplate_variables():
<?php
switch ($hook) {
case 'page':
/ ...
$vars['text_title'] = $vars['title']; // save off text title.
$vars['title'] = title_to_image($vars['title']);
/...
break;
case 'node':
/...
$vars['text_title'] = $vars['title'];
if ( $vars['teaser'] ) {
$vars['title'] = title_to_image($vars['title']);
}
// ...
?>
You may need to modify your node template a little too - can't recall.
good luck.
Editing the template.php
Editing the template.php script and adding to server makes the site disappear.....any suggestions?
I cut and pasted code from
I cut and pasted code from above and came up with the following:
<?php
/*
Functions for replacing title with image
*/
// Prepare some general title text for use as a file name.
// Remove special HTML characters, trim whitespace, convert to lower-case
// repace spaces with underscores.
function clean_title($string)
{
$cleanString = htmlspecialchars_decode( strtolower(trim($string)), ENT_QUOTES );
$cleanString = str_replace(array("& ", "'"), '', $cleanString);
return str_replace(' ', '_', $cleanString );;
}
function title_to_image($title)
{
$ext = '.gif';
$path = 'files/images/titles/';
// convert title to suitable filename
// and derive the file system path to that imaginary file.
$titleFile = clean_title($title) . $ext;
$titleImage = base_path() . $path . $titleFile;
$titlePath = $_SERVER['DOCUMENT_ROOT'] . $titleImage;
// Determine if a suitable replacement image for title can be found,
// and, if so, make substitution with the related URL.
if ( file_exists($titlePath) ) {
$titleURL = "http://" . $_SERVER['SERVER_NAME'] . $titleImage;
$newTitle = '<img alt=\'' . $title .'\' title=\'' . $title .'\' src=\'' . $titleURL .'\' />';
return $newTitle;
}
else { // no suitable image file could be found - return old title.
return $title;
}
}
function _phptemplate_variables($hook, $vars = array()) {
switch ($hook) {
case 'page':
// substitute node title with an image, if a suitable replacement can be found.
// save original title text for use in head and breadcrumb.
$vars['breadcrumb_title'] = $vars['title'];
$vars['head_title'] = $vars['title'];
// Substitute title only for nodes...
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1)); // (expensive, unless arg(1) is already loaded, which is should be at this point.)
// ... of one of the listed types - add node types to process to the array.
if (in_array($node->type, array('page'))) {
// convert title to suitable filename and derive both file system path and URL.
$titleFile = clean_title($vars['title']) . '.gif';
$titleImage = base_path() . "files/images/titles/" . $titleFile;
$titleURL = "http://" . $_SERVER['SERVER_NAME'] . $titleImage;
$titlePath = $_SERVER['DOCUMENT_ROOT'] . $titleImage;
// $vars['title'] = $titleURL; // DEBUG - see what's being produced
// Determine if a suitable replacement image for title can be found, and make substitution.
if ( file_exists($titlePath) ) {
$newTitle = '<img alt=\'' . $vars['title'] .'\' src=\'' . $titleURL .'\' />';
$vars['title'] = $newTitle;
}
}
}
case 'node':
$vars['text_title'] = $vars['title'];
if ( $vars['teaser'] ) {
$vars['title'] = title_to_image($vars['title']);
}
break;
}
return $vars;
}
?>
I put it in template.php and it worked..
$title of a view?
This comes so close to exactly what I'm looking for. Does anyone know how to extend this to views (the title of the view, not the titles of the nodes in the view) and to the contact form at /contact?
Try CSS image replacement
I'd look at using a CSS image replacement technique for views and other objects that are more difficult to mess with. Assuming you have some good identifying information in the body tag of each page on your site (like the Zen theme provides), you can do something like this in your style.css file:
.page-body-class h1.title {background: url(page-title-image.png) no-repeat;
height: 60px;
text-indent: -999em;
overflow: hidden;
}
I am running Drupal 6
I am running Drupal 6 and can't get this code to work. Where do the image files get stored? Thanks.
Working in Drupal 6.6
Add the following code to your themes page template file (page.tpl.php) at the top of the file.
The image files will be looked for in the images/titles dir at the base of the Drupal install.
Un-comment the DEBUG line to see what the full path name is if the images are not found.
<?php
$titleFile = clean_title($title) . '.png';
$titleImage = base_path() . "/images/titles/" . $titleFile;
$titleURL = "http://" . $_SERVER['SERVER_NAME'] . $titleImage;
$titlePath = $_SERVER['DOCUMENT_ROOT'] . $titleImage;
// Determine if a suitable replacement image for title can be found, and make substitution.
if ( file_exists($titlePath) ) {
$newTitle = '<img alt="' . $title .'" src="' . $titleURL .'" />';
$title = $newTitle;
} else {
# $title = $title . $titlePath; #DEBUG
}
?>
You also need to add the clean_title function, I put that in the template.php file
<?php// Prepare some general title text for use as a file name.
// Remove special HTML characters, trim whitespace, convert to lower-case
// repace spaces with underscores.
function clean_title($string)
{
$replace_chars = array("&", ":", ",", "'");
$cleanString = htmlspecialchars_decode( strtolower(trim($string)), ENT_QUOTES );
$cleanString = str_replace($replace_chars, "", $cleanString);
return str_replace(' ', '_', $cleanString );;
}
?>
Another way of control over $title, $submitted and $terms
I placed this code into node.tpl.php and it works like a charm.
You have two options. One is to add a leading underscore "_" to the page title, which prevents the page title from being displayed.
Next, if you also or optionally append a trailing underscore to the page title, the $submitted and $terms info will not be displayed.
FYI, I am using the Wabi theme with this code:
<!-- New code below to print the node title when it DOES NOT -->
<!-- begin with an underscore -->
<?php if(substr($title,0,1) != "_") { ?>
<h2 class="title">
<a href="<?php print $node_url?>">
<?php print $title?></a></h2> <?php
} ?>
<!-- New code below to print the $submitted and $terms when $title it DOES NOT -->
<!-- have a trailing underscore -->
<?php if(substr($title,-1,1) != "_") { ?>
<span class="submitted">
<?php print $submitted?></span>
<span class="taxonomy">
<?php print $terms?></span> <?php
} ?>
-Videoman
No Code, Just CSS
This little video podcast will show how to accomplish this without PHP changes.
http://www.mustardseedmedia.com/podcast/episode1
Home page
Something to keep in mind. Check the home page to make sure the title image works there.
If you are promoting a node to front page, the title script will not work. Go to Site Configuration > Site Information and set a default front page.
Can this also work with block title ?
How to set up this Snippet, to get that working ?
Thanks