background image
HomeRecent PostsDrupalSearchTagsRSSContactAboutAccount
Eric.London's picture

When creating a view you are presented with two types of row styling: fields and node. If you choose node, you have the ability to choose to show the teaser of the node. In certain situations, you might want to choose fields row styling, but also show the teaser version of a field. This can be accomplished by creating a template file for your field and modify its contents with a bit of PHP code.

By clicking on the "Theme: Information" section on the view edit screen, you can see different options you have for overriding a view. In this case, I looked for the theme file that matched the field, name of the view, and the view type:

views-view-field--MYVIEWNAME--block--MYFIELD.tpl.php

Clicking on the link to the left of the theme file name options will show you the default contents of that file. Example: "Field Node: Body (ID: body)"

<?php
// $Id: views-view-field.tpl.php,v 1.1 2008/05/16 22:22:32 merlinofchaos Exp $
/**
  * This template is used to print a single field in a view. It is not
  * actually used in default Views, as this is registered as a theme
  * function which has better performance. For single overrides, the
  * template is perfectly okay.
  *
  * Variables available:
  * - $view: The view object
  * - $field: The field handler object that can process the input
  * - $row: The raw SQL result that can be used
  * - $output: The processed output that will normally be used.
  *
  * When fetching output from the $row, this construct should be used:
  * $data = $row->{$field->field_alias}
  *
  * The above will guarantee that you'll always get the correct data,
  * regardless of any changes in the aliasing that might happen if
  * the view is modified.
  */
?>

<?php print $output; ?>

Create a new file in your theme directory that matches the naming convention and copy the default contents into it. Click on the "Rescan template files" button below the list of options. If everything is working correctly, your newly created theme file will be bold in the list, representing that its now the template file being used. If it's not, there's a chance you named it incorrectly.

In my example, I wanted to show the teaser view of a field and then append a "read more" link to the content. I replaced the contents of my newly created theme file with the following:

<?php
// generate the teaser for this field
$teaser = node_teaser($output);

// output the teaser
print $teaser;

// if the teaser if different from the original output,
// show a more link
if ($teaser!=$output) {
  print
' ' . l('> Read More', 'node/'.$row->nid);
}
?>

If you're like me, you probably use complicated node types with many fields and custom layouts. If that's the case, the default printer-friendly rendering of nodes may not suit you. In this code snippet I'll explain how you can override the templates and how I got to this step.

The first step when making modifications like this is to determine how the html was generated (template, function, callback, etc). Take a look at the query string for a printer friendly page: book/export/html/YOURNODEID. If you check out the book module, you'll see an entry in the book_menu() hook (book/export/%/%) that uses the book_export() page callback. Examining that function leads you to book_export_html(). This function generates the html for printer-friendly layout using the book_export_traverse() function and then calls the theme() function. The theme function allows us to override the theme layer. In this example I'll create a function that follows the templates suggestions guidelines to insert my code...

<?php
// the first argument being passed to theme function is book_export_html, so I'll prefix my function with my theme's name
function MYTHEME_book_export_html($title, $contents, $depth) {
 
// since the node ID is not being passed as an argument,
  // we'll grab it from the query string...
  // load node
 
$node = node_load(arg(3));
   
 
// define a list of node types to override
 
$nodeTypes = array('MYCOMPLICATEDNODETYPE1','MYCOMPLICATEDNODETYPE2');
  if (!
in_array($node->type, $nodeTypes)) {
   
// for all the rest of my node types, I'll use the default template:
   
return theme('MYTHEME_book_export_html', $title, $contents, $depth);
  }

 
// now, you have access to the $node variable and can do what ever you'd like with it
  // note: the drupal_render() function is great for generating the output for a CCK field

 
return "MY NEW AMAZING PRINTER-FRIENDLY HTML";
}
?>

In the above code, the theme registry was overrode by creating a function named: MYTHEME_book_export_html. The last part of this code snippet is returning regular nodes back to the default book template file (book-export-html.tpl.php). In the above code, I mention a theme registry entry called "MYTHEME_book_export_html", which has not been registered yet. We can register a new template using hook_theme()...

<?php
function MYTHEME_theme() {
  return array(
   
// register the default book export template
   
'MYTHEME_book_export_html' => array(
     
// pass it the same arguments, defined in the book_theme() function
     
'arguments' => array('title' => NULL, 'contents' => NULL, 'depth' => NULL),
     
// define the same template as defined in the book_theme() function
     
'template' => 'book-export-html',
     
// since this file does not exist in our theme directory,
      // we'll specify the path of the book module
     
'path' => 'modules/book',
    )
  );
}
?>

Now that we've registered the "new" template (and rebuilt the theme registry) all of our standard nodes will use the original book template file and our custom node types will be modified as defined.

Eric.London's picture

The Devel module has a great feature called Theme Developer which makes theming a breeze. When enabled, you'll see a box in the bottom left of your browser window, themer info. This functionality helps answer the question: how do I change the way this piece of my theme looks? If you enable the themer info checkbox and moveover your theme, you'll noticed that various html structures will be highlighted in red, very much like the essential Firefox plugin, Firebug.

Let's say you wanted to redesign the appearance of the search form in your theme. If you click on search form using the themer info, you'll get a window full of useful information, similar to this:

This window tells you which functions and templates are used to create the html, and which functions and templates you can define to override the themable output. Check out the section called "File used". It should tell you that the search form uses it's own template file found here: modules/search/search-theme-form.tpl.php. In this case, we're in luck. You can simply copy that file into your theme directory and modify it as necessary.

In other situations, a function is used instead of a template tpl.php file. If you use the themer info tool and click on the navigation menu you'll notice a function was used (theme_menu_tree):

To modify the themable output, you'll have to create a function in your template.php called YOURTHEME_menu_tree. Before you go any further, read the API documentation for the function your are replacing. For this example, browse to: http://api.drupal.org/api/function/theme_menu_tree. Since Drupal will be passing a predefined set of arguments to our new function, it's best practice to use the same number and name of arguments that Drupal uses. Check out the sections in the API documentation called definition and code. You'll see that the theme_menu_tree function accepts one argument ($tree):

<?php
theme_menu_tree
($tree);
?>

So, in your template.php file, define your new function. The first thing you should do it get acquainted with the structure of the arguments and what the function returns (array, object, html, etc). In the code below, I'm using the PHP function print_r to show the structure of the argument. Now, you can modify to your heart's content.

<?php
function MYTHEME_menu_tree($tree) {
  print
"<pre>" . print_r($tree, true) . "</pre>";
}
?>

Now, you should be on your way to overriding the themable output of any piece of your site.

Eric.London's picture

I created a CCK node that contained a field used to store information not very user friendly. I wanted the data to show up correctly on a calendar view, so I added some code to my theme to override the display functionality of the node. I put the following function in my template.php file:

<?php
function MYTHEME_calendar_calendar_node($node, $type) {
 
$fieldName = 'node_data_field_MYFIELDNAME_field_MYFIELDNAME_value';
  if (
strlen($node->fields[$fieldName])) {
   
$node->fields[$fieldName] = 'MYNEWHTML';
  }
  return
theme_calendar_calendar_node($node, $type);
}
?>

Here is a quick and powerful way to override the theme engine to get the Views module to display your content anyway you'd like. First, create a view that provides a page view. Choose "List View" as the view type. Make sure you add all the fields you'd like to include in this view. Add your filters and sort criteria as necessary. Save your view and make sure it's working. Go to the URL you assigned to the page view. You should see a <ul> list of the fields you chose to include.

Next, go to the theme wizard page (admin/build/views/wizard). Choose your view from the list and choose "simple list" as the theme type. Click on the Select Theme Type button. On the next page, you'll see two textareas full of code. Copy and past the top code into you template.php file. As for the bottom code, create a new file in your theme directory called "views-list-YOURVIEWNAME.tpl.php".

If you reload your page view, it should look the same. Now, you can alter those two code segments to get the view to appear the way you'd like. Changing the "views-list-YOURVIEWNAME.tpl.php" file will alter how each node is themed.

For example, you could use this technique to create a different table layout. The default table layout included in the views module will display a node's data in each <tr>, separating each field in a <td>. Locate the following code in you template.php file:

<?php
if ($items) {
  return
theme('item_list', $items);
}
?>

You could replace the "item_list theme type with something like this...

<?php
return "<table><tr>" . implode("", $items) . "</tr></table>";
?>

You'll also have to open up the "views-list-YOURVIEWNAME.tpl.php" file and enclose the entire contents in a <td></td>. In this case, you could also add some conditional code to segment the rows into 3 columns, etc.

Syndicate content