Apr 09 2008
Nik
Apr 09

Just a quick tip for an extra, more accessible theming variable. I have personally found that on nearly all the sites I’ve built, I’ve never had a use for the Mission variable. So it struck me that I could probably use this field to output something else; something relevant to the general workings of the site, for sure though.

So, on this site, I have edited the mission variable and put in the copyright notice that you see at the bottom of the page. I saved the config screen and carried on, thinking that it would just be working – I had tested it out on the front page, and the value was appearing where I had placed the $mission variable in the footer area in my page template. No problems, I thought.

Today, I actually noticed that this was not appearing, and I couldn’t work it out for a while, but I trawled through the phptemplate.engine, and in there is some code that conditionally sets the $mission variable on only the front page – perhaps that’s why it doesn’t get used so much?

Anyway, I opened up the template.php file for my theme, and placed in it the code below, in the _phptemplate_variables bit under case 'page' – see here. Now I have a usable variable across all pages of my site, with the added advantage that this is accessible from the admin interface at “site information”. I guess that the theme settings API in Drupal 6 may alleviate this problem, but for simple things like updating the year (which is contained in my © statement) in D5, this is a potential time saver (and face-saver) for administrators.

<?php
 
// populate the $mission variable on every page so we can use it universally
  // don't check <front>, it's already handled in phptemplate.engine
if (!$vars['is_front']) {
   
$vars['mission'] = filter_xss_admin(theme_get_setting('mission'));
  }
?>
Mar 26 2008
Nik
Mar 26

Today I’m just demonstrating a few simple theme adjustments to comments. Comments in Drupal 5 are “not sexy”, out of the box, so in this short post I’m going to illustrate how to:

  • change the text on “submitted” lines in comments (just a little)
  • add nofollow to username links – unless you’re feeling generous
  • remove the “not verified” marker from anonymous users

<?php
// change the "submitted by" text to "posted by"
// note that you can alter the date display too, by changing the way
// format_date() is called - see http://api.drupal.org/api/function/format_date/5
$vars['submitted'] = t('Posted by !a on @b.',
                     array(
'!a' => theme('username', $vars['comment']),
                    
'@b' => format_date($vars['comment']->timestamp)));
?>

This first snippet belongs in the ‘comment’ section of your _phptemplate_variables function in template.php; I have an example of this here.

Moving on to that “not verified” business, and the question of holding on to your link juice, adding an overridden theme_username() function is the way to go. Here’s some code, which you can also place in your template.php file in your theme. Remember, if you don’t have that file in your theme, you can just create it yourself. I personally recommend Zen as a starting point for theming.

<?php
// we can't use phptemplate_username as this is already declared in that engine
function mytheme_username($object) { // rename according to your theme

  // this basically means "if the user has an account"
 

if ($object->uid && $object->name) {
   
// Shorten the name when it is too long or it will break many tables.
   
if (drupal_strlen($object->name) > 20) {  // obviously you could change this value
     
$name = drupal_substr($object->name, 0, 15) .'...';
    }
    else {
     
$name = $object->name;
    }

    if (

user_access('access user prosites/default/files')) {
     
// we could nofollow the internal links too (authenticated user's pages)
      // but there's not really any point - my site doesn't use membership,
      // so usernames are not highlighted -
      // commented line below would do that, though.
      // $output = l($name, 'user/'. $object->uid,
      //     array('title' => t('View user profile.'), 'rel' => 'nofollow'));
     
$output = l($name, 'user/'. $object->uid,
          array(
'title' => t('View user profile.')));
    }
    else {
     
$output = check_plain($name);
    }
  }
// if we're entering this func, the user is anon (i.e. we want to nofollow them)
 
else if ($object->name) {
    if (
$object->homepage) {
     
// this is where we're nofollow-ing the external links to comment authors' pages
      // we don't really need to use t() here, as rel=nofollow is language independent
     
$output = l($object->name, $object->homepage, array('rel' => 'nofollow'));
    }
    else {
     
$output = check_plain($object->name);
    }
   
// commenting out this line prevents "teh ugly" in the $submitted text
    // $output .= ' ('. t('not verified') .')';
 
}
  else {
   
$output = variable_get('anonymous', t('Anonymous'));
  }
  return
$output;
}
?>

If you’ve got any other simple tips like these, let me know, so I can use & share those too!

Update: This code works fine under Drupal 6 as well – but don’t forget to clear your theme registry when you’ve modified things in your theme!

Feb 28 2008
Nik
Feb 28

This snippet of code gives a brief example of how to rewrite components of the $links variable to make them prettier :) Specifically, here I’m overwriting the link generated by the Forward module. You can see the result below: the little envelope icon labelled “Email”. Normally, this would just say “Forward this page”, which is a bit… well, it could be better. Obviously, it’s nice to be able to change these things to taste.

There are two ways to achieve this result: using theme code in template.php, or inside of a helper module. First, I’ll discuss the module approach.

The helper module method is what I had originally used. It’s a little neater, in that you code it once and forget about it, and it doesn’t clutter up template.php’s _phptemplate_variables function, which can easily become bloated with code.

In the module, I’ve added a function to implement Drupal’s hook_link_alter() function. Here’s the code to do it:

<?php
function mymodule_link_alter(&$node, &$links) {
  foreach (
$links as $module => $link) {   // iterate over the $links array
    //drupal_set_message(print_r($links)); // uncomment to display your $links array

    // check if this element is the forward module's link
   

if ($module == 'forward_links') {
     
$title = t('Email this page to a friend');    // change the title to suit
     
$path = path_to_theme() . '/images/email.png' // make an image path

      // now update the links array
      // set the title to some html of the image and choice of link text
     

$links[$module]['title'] = theme('image', $path, $title, $title) . ' Email'; // let's set some attributes on the link
     
$links[$module]['attributes'] = array(
       
'title' => $title,
       
'class' => 'forward-page',
       
'rel' => 'nofollow',
      );
// this must be set, so that l() interprets the image tag correctly
     
$links[$module]['html'] = TRUE;
    }
  }
}
?>

Ok so really, this ought to be done in the theme layer. Like I said, it’s perhaps not as compact and neat, but here’s the code. It’s mostly the same, but note a couple of additions and changes: firstly, we are not changing $links – this is a pre-rendered string by the time it gets to the template.php. We need to get to the original goodies! Hence, we use $vars[&#039;node&#039;]-&gt;links[module-name][field-name].

Secondly, note that because we have now altered the value of one of the original links’ values, does not mean that the node’s $links is correct. This is the bit that caught me out! We must now regenerate the $links variable using the theme_links() function, as per the last line of code below. This mimics what phptemplate.engine does in core.

<?php
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
    case
'node':
      foreach (
$vars['node']->links as $module => $link) {
        if (
$module == 'forward_links') {
         
$title = t('Email this page to a friend');
         
$path = path_to_theme() . '/images/email.png';
         
$vars['node']->links[$module]['title'] =
             
theme('image', $path, $title, $title) . ' Email';
         
$vars['node']->links[$module]['attributes'] =
              array(
'title' => $title, 'class' => 'forward-page', 'rel' => 'nofollow');
         
$vars['node']->links[$module]['html'] = TRUE;
        }
      }
     
$vars['links'] = theme('links', $vars['node']->links,
                                           array(
'class' => 'links inline'));
      break;
  }
}
?>

You can achieve this effect for anything that’s in the $links array. On this page (below), you can see the link I’ve described here, another for print-friendly pages and also a themed comment link.

Feb 23 2008
Nik
Feb 23

Quite frequently I’ve been asked about putting images into site “sections”, depending on path or menu trail. Look up, that “Blog” image is what I’m talking about. It’s on all blog related pages. So, here goes – it’s nice to be able to finally offer this information here.

The first main chunk of code attempts to get a menu item and build an image link from that. The second chunk assumes failure of the first and tries again using a partial path method.

If all nodes on your site have menu entries, you can use that piece of code independently. Likewise, if all your nodes can be identified by the first bit of the path, the second chunk will stand alone.

I have got a mixture of the two on this site. A lot of the entries have menu entries, but the blog and portfolio section do not. Therefore, the image links on those sections are powered by the second chunk.

Note: this code expects to find sites/default/files of the GIF type in a directory ‘images/sections’ within my own theme directory. It also will only pick up sites/default/files that have names which are all lower case. In the case of menu entries that contain spaces, those will be replaced with hyphens, so if the menu link is “Site Map”, the image name will have to be “site-map.gif”. Path-based is really dependant on how you are using aliases (e.g. your pathauto.module setup) and isn’t really inside the scope of this article. You’ll have to figure that out yourself.

Okay; in order to not crowd up _phptemplate_variables(), I add just this one line of code in template.php inside that function (under ‘page’ – see here for details):

<?php
$vars
['section_link'] = get_section_link();
?>

Then, elsewhere in that file, this code:

<?php
function get_section_link() {
 
// MENU - attempt to make a section link from a menu item, for this page
  // get active menu trail into an array
 
$menu_items = _menu_get_active_trail(); // $menu_items[1] is the top parent of our menu container, e.g. primary links
  // this gets the required menu item into an array
 
$link_array = menu_item_link($menu_items[1], FALSE); // whip out spaces and make the name lower case
 
$section_name = strtolower($link_array['title']);
 
$section_name = str_replace(' ', '-', $section_name);

  if (

$section_link = render_link($section_name)) {
    return
$section_link;
  }
// PATH - if we've not returned, we couldn't make a valid link from menu
  // let's try a path approach instead?
 
if (module_exists('path')) { // dependency for drupal_get_path_alias $sections = array(); // an empty array to collect stuff in

    // get all the top level links in the primary nav (id of 2) into a array
   

$primary_nav = menu_primary_links(1, 2); // iterate over the array and pull out the top level paths
   
foreach ($primary_nav as $menu) { // get the first element of the aliased path for this menu item
     
$path_element = explode('/', drupal_get_path_alias($menu['href'])); // put the first chunk of each path onto an array
     
$sections[] = $path_element[0];
    }
// get the aliased path for the page we're on
   
$section = explode('/', drupal_get_path_alias($_GET['q']));
   
$section_name = $section[0]; // if the path matches a nav item, create a section image
   
if (in_array($section_name, $sections)) {
      if (
$section_link = render_link($section_name)) {
        return
$section_link;
      }
    }
  }
}

function

render_link($section_name) {
 
// construct the image's path (mine are GIFs stored in a subdir of my theme)
 
$image_path = path_to_theme() . '/images/sections/' . $section_name . '.gif'; // make some text for the image's alt & title tags (SEO, accessibility)
 
$image_alt = $section_name . t( ' section');
 
$image_title = $section_name . t( ' section link'); // render image html using theme_image (returns NULL if file doesn't exist)
 
$section_image = theme('image', $image_path, $image_alt, $image_title); // if the image rendered ok, render link using above variables
 
return ($section_image) ? l($section_image, $link_array['href'],
      array(
'title' => $image_title), NULL, NULL, FALSE, TRUE) : NULL;
}
?>

Then finally in page.tpl.php (and any other page templates) we can use the variable in the “Drupal Way”, and print our variable where we like!

<?php if ($section_link): ?>
  <div id="sectionTitle">
    <?php print $section_link; ?>
  </div>
<?php endif; ?>

Feb 11 2008
Nik
Feb 11

I sometimes run into people on the Drupal IRC channels that have a theming issue they just can’t fathom – dysfunctional CSS. Some poor guy has overridden a core CSS class, but his styles just don’t work. I’ve been there myself before and I know it can be very frustrating.

Rather than showering your CSS with !important tags, here is an alternative – remove the offending style file altogether. You can then copy the style information into your own theme, remove any bits that you don’t want and alter it as you see fit.

Let’s see how we get rid of those sites/default/files and regain control of our CSS. Put some code similar to this in the “page” section of your template.php file’s _phptemplate_variables function (see this example).

<?php
// get all the current css information into an array
$css = drupal_add_css();// copy stuff you want to keep from these sites/default/files into your theme's style.css
// or maybe make a separate file for that and @import it into that file

// now we can ditch unwanted core css sites/default/files from the array and they won't be included

unset($css['all']['module']['modules/user/user.css']);
unset(
$css['all']['module']['modules/node/node.css']);// and now, removing the css sites/default/files of some contributed modules
// I'm putting them into an array to save space and code repetition
$rm[] = drupal_get_path('module','content').'/content.css';
$rm[] = drupal_get_path('module','devel').'/devel.css';
$rm[] = drupal_get_path('module','gotcha').'/gotcha.css';// now we can remove the contribs from the array
foreach ($rm as $key => $value) {
  unset(
$css['all']['module'][$value]);
}
// now place the remaining css sites/default/files back into the template variable for rendering
$vars['styles'] = drupal_get_css($css);
?>

You should now be able to see that the CSS sites/default/files have disappeared from the head of your document – a pretty drastic step, but it’s pretty much guaranteed…!

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web