Hello -

More and more sites are doing a header navigation structure like the one on this website, for example: http://thethemefoundry.com/wordpress/

I'm wondering if there is an easy way to accomplish something like this in Drupal by showing the primarily link and then its description underneath it. I'm sure it involves some php in the template file, and that's why I'm posting...does anyone have a snippet that would work for this or does anyone want to point me in the right direction... I did find this forum topic: http://drupal.org/node/343440. However, I'm unsure of trying it since the date on the topic is so old. Any help could be appreciated. Thanks so much.

Comments

drclaw’s picture

Put this in your theme's template.php file:

/*
 *  An implementation of theme_menu_item_link()
 *
 *  Customized to include the Menu Item description on sub menu items for the primary links
 *
 *  @param $link
 *    array The menu item to render.
 *  @return
 *    string The rendered menu item.
 */

function phptemplate_menu_item_link($link) {
  
  if (empty($link['localized_options'])) {
    $link['localized_options'] = array();
  }
  
  if($link['menu_name'] == 'primary-links' && $link['options']['attributes']['title']){
    // By default the menu system stores the menu item's 'Description' in the title attribute of the link 
    // that is created. We can use this to reformat the link with the description in the output as well
    $link_description = '<p class="primary_link_description">' .
                        check_plain($link['options']['attributes']['title']) . '</p>';
    // Adding a little <span> for css themeing later to the title
    $link['title'] = '<span class="primary_link_title">' . 
                     check_plain($link['title']) . '</span>';
    $link['localized_options']['html'] = TRUE;
    // tack the description on to the end of the link actual link
    return l($link['title'], $link['href'], $link['localized_options']) . $link_description;
  }
  else {
    // Otherwise, we just return the link as normal
    return l($link['title'], $link['href'], $link['localized_options']);
  }
}

It is a bit confusing that the $link['title'] is the link's text and the $link['options']['attributes']['title'] is the description, since they're both named "title" in the end, but that's Drupal for ya!

Presumably you could put anything you want in $link['title'] because it's really just the link text. So, if you want the description to also be a part of the link itself, you could just add it to $link['title']

// Make sure you concatenate so you don't overwrite the original value!
$link['title'] .= '<p class="primary_link_description">' . check_plain($link['options']['attributes']['title']) . '</p>';
return l($link['title'], $link['href'], $link['localized_options']);
crbassett’s picture

Thank you for offering your help. I'll confess I'm not versed in PHP, beyond hacking here and there in snippets and in content templates (contemplate module), so, if you're willing to continue helping me, I'd appreciate it.

I'm using Zen as my theme (actually a sub-theme) called "stephen", so, first of all, I assume (if I remember correctly) I should change the word phptemplate to the name of the theme, so, "stephen_phptemplate_menu_item_link". (I actually tried it both ways, and it didn't work for me either way...

The page.tpl.php file in Zen shows this for printing the primary links:

<?php print theme(array('links__system_main_menu', 'links'), $primary_links,
  array(
    'id' => 'main-menu',
    'class' => 'links clearfix',
  ),
  array(
    'text' => t('Main menu'),
    'level' => 'h2',
    'class' => 'element-invisible',
  ));
?>

Do I need to change that declaration in the page.tpl.php file?

Thanks for your patience. I'll hopefully get this working.

drclaw’s picture

Sorry it took so long to get back to you on this!

I just realized that the method I showed you wasn't necessarily the right way to do this. I used an example from an existing site that I had but it only worked in that case because I was using it for a different use case.

Anyway, the theme system basically gives you a way to override functions (defined in modules) that produce the output of html on your site. All you need to do to override one is find the original, copy it to your template.php file, and change it from theme_function_name to yourthemename_function_name. Theme functions are invoked within drupal using the function theme() (like in your example you posted theme(array('links__system_main_menu', 'links'), $primary_links..... For example you would take the function theme_links, and rename it to stephen_links. Then you would call the function using theme('links') somewhere in your template file.

(Sorry, if my use of phptemplate before was confusing, btw. It should work, but you should read more about the theming system to see why =) )

Okay, so, having said all that, Let's look at the snippet from your template file

print theme(array('links__system_main_menu', 'links'), $primary_links,
  array(
    'id' => 'main-menu',
    'class' => 'links clearfix',
  ),
  array(
    'text' => t('Main menu'),
    'level' => 'h2',
    'class' => 'element-invisible',
  ));

In this case the function theme() is being passed a few different function options. It will first look for theme_links__system_main_menu() but if it doesn't exist, it will fallback to theme_links. Let's use the first one:

function theme_links__system_main_menu($links, $attributes = array('class' => 'links')) {
  global $language;
  $output = '';
  dpm($links);
  if (count($links) > 0) {
    $output = '<ul' . drupal_attributes($attributes) . '>';

    $num_links = count($links);
    $i = 1;

    foreach ($links as $key => $link) {
      $class = $key;

      // Add first, last and active classes to the list of links to help out themers.
      if ($i == 1) {
        $class .= ' first';
      }
      if ($i == $num_links) {
        $class .= ' last';
      }
      if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))
           && (empty($link['language']) || $link['language']->language == $language->language)) {
        $class .= ' active';
      }
      $output .= '<li' . drupal_attributes(array('class' => $class)) . '>';

      if (isset($link['href'])) {
        // This is where the magic happens
        if (isset($link['attributes']['title'])) {
          // By default the menu system stores the menu item's 
          // 'Description' in the title attribute of the link 
          // that is created. We can use this to reformat the 
          // link with the description in the output as well
          $link_description = '<span class="link_description">' .
                              check_plain($link['attributes']['title']) . '</span>';
        }
        // Pass in $link as $options, they share the same keys.
        // Add the link description on to the link
        $output .= l($link['title'], $link['href'], $link) . $link_description;
      }
      else if (!empty($link['title'])) {
        // Some links are actually not links, but we wrap these in <span> for adding title and class attributes
        if (empty($link['html'])) {
          $link['title'] = check_plain($link['title']);
        }
        $span_attributes = '';
        if (isset($link['attributes'])) {
          $span_attributes = drupal_attributes($link['attributes']);
        }
        $output .= '<span' . $span_attributes . '>' . $link['title'] . '</span>';
      }

      $i++;
      $output .= "</li>\n";
    }

    $output .= '</ul>';
  }

  return $output;
}

All I've done here is copy the function theme_links, and renamed it to match the theme() call in your template file.

Also, as a side note, the output from my example isn't the best. It will require further theming using css to make sure it displays the way you want. You can also add some extra html in the output as well if you need to.

Anyway, sorry if this explanation is tough to follow, I'm in a bit of a rush and typed it out quickly. =P Feel free to ask more questions if you need to! Also check this out http://drupal.org/node/341628