Hi - I've had a fairly good search for this info, so apologies if I'm asking a common one here...

1. Don't hyperlink "active" menu items?

I'd like Drupal to not hyperlink menu items pointing to the current page. By default, Drupal adds class="active" to active menu items, so it can obviously distiguish. Can anyone tell me how I can use this to cause Drupal to skip the hyperlink on active menu items?

2. How can I best hack the breadcrumbs feature?

On my non-Drupal sites - and these are decreasing daily :) - I tend to implement breadcrumbs like this:

You are here: Home > Site section > This page

I was thinking that I need to hack the contents of $breadcrumb - but perhaps it would be better to leave this alone and just add the bits I need in page.tpl.php? Can anyone advise on the best way to go about this?

Many thanks :)

Comments

jastraat’s picture

Regarding the breadcrumbs, you can use the function below. Just place it in your template.php file for the theme that you're using. It appends the title of the current page to the breadcrumb.

function phptemplate_breadcrumb($breadcrumb) {
  if (!empty($breadcrumb)) {
	//Append title of current page
	$breadcrumb[] = drupal_get_title();
        return ''. implode(' > ', $breadcrumb);
  }
}
nancydru’s picture

The breadcrumb trail should show how you got here, not the current page. My problem is keeping track of how I got here.

Nancy W.
now running 5 sites on Drupal so far
Drupal Cookbook (for New Drupallers)
Adding Hidden Design or How To notes in Your Database

Chill35’s picture

You are correct, Nancy. But some people want to also know where they are. That last bit would not be a link, it would not be clickable.

Caroline
A coder's guide to file download in Drupal
Who am I | Where are we
11 heavens

Mark Nielsen’s picture

I've just tried your snippet - just the job :) Thanks so much.

alynner’s picture

I have found this to be very useful: http://drupal.org/node/93500

it makes breadcrumbs from the url path, if you know php you should be able to customize it further.

alynner

nancydru’s picture

Unless you have multiple ways to get to the same node, as I do.

Nancy W.
now running 5 sites on Drupal so far
Drupal Cookbook (for New Drupallers)
Adding Hidden Design or How To notes in Your Database

vthirteen’s picture

and unless your drupal is not on the root folder (e.g. http://yourdomain.com/drupaldirectory/) and unless you're not using internationalization (e.g. http://yourdrupalsite.com/uk).

in those cases printing the full url is useless cause breadcrumbs will be printed as: "Home > drupaldirectory > ..." or "Home > uk > ..."

Mark Nielsen’s picture

I'm sure you could strip out the bits you didn't need in your breadcrumbs - so long as there was a consistent pattern.

I've done this as a rough workaround for the groups and event modules that used to mess up breadcrumbs.

vthirteen’s picture

i'm no good at php. won't you tell us how you did it?

banglogic’s picture

EDIT: After typing this post, I foolishly went and actually tried just adding the code my site, as-is.
It works! I am still wondering though, is this kind of "hack-ish"? Shouldn't I be using the _phptemplate_variables?

Original post below:
---

Having a breadcrumb sounds like the ideal solution for certain "business brochure" sites.

For my new site -- under construction at http://banglogic.net -- I am struggling with getting breadcrumbs to behave in a consistent manner.

The "Breadcrumb from Path" thread you linked to says it works for 4.6 and 4.7 but does it work for 5.x? If I understand correctly, with 5.x you cannot place custom functions in the template.php file but you can update standard Drupal variables and create new variables by using the _phptemplate_variables function in template.php.

I am thinking I could do one of two things with this code to make it work:

  1. Place it in page.tpl.php instead (which I choose not to do because I like having that file nice and clean, keeping all "evaluative" code elsewhere.
  2. Place it in template.php (as that thread directs) but edit it to be part of the existing _phptemplate_variables function I have in there now. I am assuming that _phptemplate_variables should only be defined once.

Do you have this working with a 5.x based site? If so, how?

Thanks,
Ken Knicker
Bang Logic

Chill35’s picture

There is themable function for the menu items too.

In template.php, add this "override" :

function phptemplate_menu_links($links) {
  $is_active = FALSE;
  if (!count($links)) {
    return '';
  }
  $level_tmp = explode('-', key($links));
  $level = $level_tmp[0];
  $output = "<ul class=\"links-$level\">\n";
  foreach ($links as $index => $link) {
    $output .= '<li';
    if (stristr($index, 'active')) {
      $is_active = TRUE;
      $output .= ' class="active"';
    }
    if ($is_active) {
      $output .= ">". $link['title'] ."</li>\n";
    } else {
      $output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) ."</li>\n";
    }
    $is_active = FALSE;
  }
  $output .= '</ul>';

  return $output;
}

Something like that.

Caroline
A coder's guide to file download in Drupal
Who am I | Where are we
11 heavens

nancydru’s picture

Can you explain what this does - and how?

Nancy W.
now running 5 sites on Drupal so far
Drupal Cookbook (for New Drupallers)
Adding Hidden Design or How To notes in Your Database

vm’s picture

it overrides at theme level, (template.php) the theme function (in this case menu_links), built into the menu.module and alters it to create the desired effect. which in this case, is having the title of the node show (as a non active link) in the breadcrumb trail.

which if you compare with the original in the module or on api.drupal.org you'll see the difference between it and chill35's

which is mainly: (brief summary of the output)

if ($is_active) {
      $output .= ">". $link['title'] ."</li>\n";
    } else {
      $output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) ."</li>\n";
    }
    $is_active = FALSE;
  }

where the title of the node is being called:

if ($is_active) {
      $output .= ">". $link['title'] ."</li>\n";
    } else {
      $output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) ."</li>\n";
    }

and told not to be an active link, with:
$is_active = FALSE;

Chill35’s picture

Thanks Misundertood!

which in this case, is having the title of the node show (as a non active link) in the breadcrumb trail.

Not in the breadcrumb, no.

My post, Nancy, was in response to one of the 2 original questions : 1. Don't hyperlink "active" menu items?

Caroline
A coder's guide to file download in Drupal
Who am I | Where are we
11 heavens

nancydru’s picture

I probably learn more by posting here than just lurking. I am probably going to have to start learning more about themes soon, so I asked.

Nancy W.
now running 5 sites on Drupal so far
Drupal Cookbook (for New Drupallers)
Adding Hidden Design or How To notes in Your Database

Chill35’s picture

it overrides at theme level, (template.php) the theme function (in this case menu_links), built into the menu.module and alters it to create the desired effect. which in this case, is having the title of the node show (as a non active link) in the breadcrumb trail.

The themable function theme_menu_links is defined in menu.inc, to be precise. And here, I am only changing the active link to plain text. It's the HTML of menu items that I am modifying.

and told not to be an active link, with:
$is_active = FALSE;

Here, I am simply reinitializing the $is_active variable to FALSE before I move on to the next iteration in the foreach loop.

Caroline
A coder's guide to file download in Drupal
Who am I | Where are we
11 heavens

Mark Nielsen’s picture

I'm very grateful for this, Caroline - and for your subsequent explanations. I'll work on your suggestion a bit, as I'm going to enclose active menu items with <strong> tags. But you've made it plain sailing now.

Cheers!

Mark Nielsen’s picture

I've just added your menu over-ride function, Caroline - and found a couple of snags.

It only appears to work on my primary and secondary links. Menus in blocks are still showing up hyperlinked. I'm guessing there's an easy way around this problem?

Also, if you visit a primary link and then visit a secondary link which is a child - the links are not present on either child or parent menu item. This means that you can't use parent items in the primary links menu if you're looking at their children. Hope that makes some sense.

It seems that the menu item that Drupal considers "active" is not necessarily the current page? Is there a way of comparing the current page against each menu item and skipping the <a> tags if there's a match?

Mark
flet.org

Mark Nielsen’s picture

OK. I think I've solved the second problem - the entire active "route" of links being disabled. This modification adds class="active" to all menu items along the "active route" (parent and child), but only removes the link on the menu item which points to the current page.

function phptemplate_menu_links($links) {
  $this_page = menu_get_active_title();
  $is_current = FALSE;
  $is_active = FALSE;
  if (!count($links)) {
    return '';
  }
  $level_tmp = explode('-', key($links));
  $level = $level_tmp[0];
  $output = "<ul class=\"links-$level\">\n";
  foreach ($links as $index => $link) {
    $output .= '<li';
    if ($link['title'] == $this_page) {
      $is_current = TRUE;
    }
    if (stristr($index, 'active')) {
      $is_active = TRUE;
    }
    if ($is_active) {
      $output .= ' class="active"';
    }
    if ($is_current) {
      $output .= "><strong>". $link['title'] ."</strong></li>\n";
    } else {
      $output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) ."</li>\n";
    }
    $is_current = FALSE;
    $is_active = FALSE;
  }
  $output .= '</ul>';
  return $output;
}

The first problem still remains though - I'm not sure how to extend this to make it work on the block menus or on "local" menu items (tabs) .

The principle I'm trying to achieve is that no links are to be provided which point to the current page or view.

Mark
flet.org

Chill35’s picture

Why are you adding $is_current ?

Is it because the link is removed on the parent link ? If so, please disregard most of what I'll say next - EXCEPT for the presentational markup and when I talk about tabs.

I would not use presentational markup here such as <strong>. If you want to style that text, as you should, please use a span with a class or id attribute. (I am probably being anal-retentive here, so I apologize.)

Try this (here I have replaced my $is_active with your $is_current, which I prefer) :

function phptemplate_menu_links($links) {
  $is_current = FALSE;
  if (!count($links)) {
    return '';
  }
  $level_tmp = explode('-', key($links));
  $level = $level_tmp[0];
  $output = "<ul class=\"links-$level\">\n";
  foreach ($links as $index => $link) {
    $output .= '<li';
    // HERE WE KNOW IF THE LINK IS ACTIVE OR NOT
    if (stristr($index, 'active')) {
      $is_current = TRUE;
      $output .= ' class="active"';
    }
    if ($is_current) {
      // USE A SPAN HERE, NOT STRONG
      $output .= ">". '<span class="current">' . $link['title'] . "</span></li>\n";
    } else {
      $output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) ."</li>\n";
    }
    // RE_INITIALIZING VARIABLE TO GO TO NEXT ITERATION
    $is_current = FALSE;
  }
  $output .= '</ul>';

  return $output;
}

That should work in the block menus.

Now for the "local" menu items (tabs), you can use this other themable function defined in menu.inc :

- theme_menu_local_task($mid, $active, $primary)

And override it in your phptemplate file. You already got the hang of it now.

Caroline
A coder's guide to file download in Drupal
Who am I | Where are we
11 heavens

Mark Nielsen’s picture

Yes - the reason I used $is_active and $is_current was to do with the links being removed on parent items. I'd like to keep a marker on to the active parent item, without its link being removed.

I think I've got good reason to add strong. It's a way of emphasising - strongly ;) - that this menu item is relevant to the current page.

If I was to simply use a span/class to style it as important, I wouldn't be conveying that importance to users without the stylesheets - which I think would be in contravention of accessibility guidelines.

So I argue that the <strong> in this context does carry meaning, and therefore is semantic, not presentational.

Can you see where I'm coming from? Make any sense?

Mark
flet.org

Chill35’s picture

Yep it does. I see where you're coming from.

:)

Caroline
A coder's guide to file download in Drupal
Who am I | Where are we
11 heavens

Mark Nielsen’s picture

Hi Caroline - your function didn't seem to work for the block menus. I think they are handled by the function theme_menu_item_link(), on line 700 of /includes/menu.inc

I've added the following function to my template.php file which seems to do the job for me:

function phptemplate_menu_item_link($item, $link_item) {
  $this_page = menu_get_active_title();
  if ($item['title'] == $this_page || drupal_is_front_page() && $item['title'] == "Home") {
    return "<strong>". $item['title'] ."</strong></li>\n";
  }
  else {  
    return l($item['title'], $link_item['path'], !empty($item['description']) ? array('title' => $item['description']) : array(), isset($item['query']) ? $item['query'] : NULL);
  }
}

I've also added the check for the home page link - drupal_is_front_page() && $item['title'] == "Home" - to the primary and secondary links function.

So far so good - I just need to track down how to disable the links on the local tasks tabs. Feel free to raise any objections to my coding above though - it works, but perhaps it's not as straighforward as it could be?

Mark
flet.org

Chill35’s picture

No that's perfect, that code.

As far as the tabs, here is the hinted code for an active tab :

<ul class="tabs primary">
<!-- BEGIN:theme_menu_local_task-->
<li class="active"><!-- BEGIN:theme_menu_item_link-->
<a href="/drupal-5.1/node/25" class="active">View</a>
<!-- END:theme_menu_item_link-->
</li>

So it's theme_menu_item_link that needs to be overriden.

Caroline
A coder's guide to file download in Drupal
Who am I | Where are we
11 heavens

Mark Nielsen’s picture

Cool - I'm just trying to work out what I can compare $item['title'] with to match the active local link...

[later edit] Hmmm... couldn't suss this out, so have gone for an ugly preg_replace workaround instead - see below.

Mark
flet.org

Chill35’s picture

If you want to know which themable function outputs what on the page, you can hack the core temporarily so that comments are written throughout the page as to what function generates what markup.

It is very handy.

I'll explain the trick in details for all.

1. Open the file includes/theme.inc in a text editor.

2. Around line 161, modify the function theme() from this :

function theme() {
  static $functions;
  $args = func_get_args();
  $function = array_shift($args);

  if (!isset($functions[$function])) {
    $functions[$function] = theme_get_function($function);
  }
  if ($functions[$function]) {
    return call_user_func_array($functions[$function], $args);	
  }
}

To this :

function theme() {
  static $functions;
  $args = func_get_args();
  $function = array_shift($args);

  if (!isset($functions[$function])) {
    $functions[$function] = theme_get_function($function);
  }
  if ($functions[$function]) {
    // Modified by {your_name}
    // return call_user_func_array($functions[$function], $args);
    $output = call_user_func_array($functions[$function], $args);
    if (trim($output)) {
      return "<!-- BEGIN:". $functions[$function] . "-->\n" . $output . "\n<!-- END:" . $functions[$function] ."-->\n";
    }
  }
}

When you're done modifying your theme, don't forget to undo this change as it makes the web page heavier to download.

Caroline
A coder's guide to file download in Drupal
Who am I | Where are we
11 heavens

Mark Nielsen’s picture

Thanks so much - I owe you a beer :)

Mark
flet.org

Chill35’s picture

Do you have an old mouse hanging out in a box somewhere ? ;)

Caroline
A coder's guide to file download in Drupal
Who am I | Where are we
11 heavens

Mark Nielsen’s picture

...I'm involved with a computer recycling/reuse project - we've got mice going back to the 80s :) Old enough?

Mark
flet.org

Chill35’s picture

Well, well, I'd love some vintage mouse.

Caroline
Who am I | Where are we
11 heavens

zach harkey’s picture

This is really an awesome tip. My problem is that all my sites reside on a single drupal codebase so something like this would affect all sites. Is there any way to implement this kind of thing through a module? It seems like the dev module would benefit greatly from the ability to toggle comments like this.

-zach
--
harkey design

: z

Mark Nielsen’s picture

Many thanks to the hand-holding from jastraat and Caroline. I thought I'd post the full results, in case helpful for others.

Having said that, I'm not entirely happy with my ugly preg_replace method for stripping the links out of active local tabs - I'd much prefer it if there was a way to simply find out if that link item was an active local tab and not produce the links in the first place... so if anyone's got any ideas here, I'd be right grateful :)

Anyway, here goes:


/***
  * Overrides HTML output for primary and secondary links:
  *  - no link tag is provided on the menu item for the current page
  *  - class="active" is preserved for menu items in the current path
  *  - strong tags are added to the current page menu item, to emphasise it as such.
  */

function phptemplate_menu_links($links) {
  $this_page = menu_get_active_title();
  $is_current = FALSE;
  $is_active = FALSE;
  if (!count($links)) {
    return '';
  }
  $level_tmp = explode('-', key($links));
  $level = $level_tmp[0];
  $output = "<ul class=\"links-$level\">\n";
  foreach ($links as $index => $link) {
    $output .= '<li';
    if ($link['title'] == $this_page) {
      $is_current = TRUE;
    }
    if (stristr($index, 'active')) {
      $is_active = TRUE;
    }
    if ($is_active) {
      $output .= ' class="active"';
    }
    if ($is_current || drupal_is_front_page() && $link['title'] == "Home") {
      $output .= "><strong>". $link['title'] ."</strong></li>\n";
    }
    else {
      $output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) ."</li>\n";
    }
    $is_current = FALSE;
    $is_active = FALSE;
  }
  $output .= '</ul>';
  return $output;
}


/***
  * Overrides the HTML output for all links in block menus:
  *  - no link tag is provided on the menu item for the current page
  *  - strong tags are added to the current page menu item, to emphasise it as such.
  */

function phptemplate_menu_item_link($item, $link_item) {
  $this_page = menu_get_active_title();
  if ($item['title'] == $this_page || drupal_is_front_page() && $item['title'] == "Home" || $active) {
    return "<strong>". $item['title'] ."</strong></li>\n";
  }
  else {
    return l($item['title'], $link_item['path'], !empty($item['description']) ? array('title' => $item['description']) : array(), isset($item['query']) ? $item['query'] : NULL);
  }
}


/***
  * Overrides the HTML output for links in "local task" menus:
  *  - link tag is stripped out of active local tab.
  *  - strong tags added to active local tab to emphasise this as the current action. 
  */

function phptemplate_menu_local_task($mid, $active, $primary) {
  if ($active) {
    // regular expression to strip out html tags from the menu item link
    $active_link = preg_replace ("/(<\/?)(\w+)([^>]*>)/e", "", menu_item_link($mid));
    return '<li class="active"><strong>'. $active_link ."</strong></li>\n";
  }
  else {
    return '<li>'. menu_item_link($mid) ."</li>\n";
  }
}


/***
  * Overrides the default breadcrumbs HTML, to make it a "you are here" inidcator:
  *  - "Home" is added if we're on the homepage
  *  - The current page is added at the end, although not hyperlinked. 
  */

function phptemplate_breadcrumb($breadcrumb) {
  if (drupal_is_front_page()) {
    return 'Home';
  }
  if (!empty($breadcrumb)) {
    //Append title of current page
    $breadcrumb[] = drupal_get_title();
    return ''. implode(' > ', $breadcrumb);
  }
}

Any suggestions for improvements welcome.

Mark
flet.org

mnapthine’s picture

Hi Guys, some great advice on this page and this is one of the first issues I needed to address with Drupal, however having used this snippet I did notice that the rendering of nested list elements was slightly off, on closer inspection the page no longer validated which was why the links where rendering strangely in Firefox.

Well the problem resides in the following snippet:

return "<strong>". $item['title'] ."</strong></li>\n";

As you can see there is a cheeky little closing list tag in there which as far as I can see has no business being there so remove that and your away. Doesn't it feel better having a valid page? :-)

Really useful stuff though so thanks a lot!

pabsid’s picture

And if wanted without “Home”, will be like this (Drupal 5.x) in template.php

function theme_breadcrumb($breadcrumb) {
if (!empty($breadcrumb)) {
     $breadcrumb[] = drupal_get_title(); //Append title of current page
     array_shift($breadcrumb); //gets rid of "Home" 
     return '<div class="breadcrumb">'. implode(' :: ', $breadcrumb) .'</div>';
   }
 }
eMPee584’s picture

Right now for our club (http://www.muntu-afrika.de) I am modifying the garland theme and I wanted to get rid of the home crap too, but also change the quotes a bit so now it looks like f.e
» Verwalten » Strukturierung ›
i.e. leaving out the home link.. after trying out appending the title to it I recognized it looked stupidly duplicated so I just added a › to the crumb. code in my template.php:

function phptemplate_breadcrumb($breadcrumb) {
  array_shift($breadcrumb);
  if (!empty($breadcrumb)) {
    $breadcrumb_temp = '&raquo; '.implode(' &raquo; ', $breadcrumb)." ›";
    return '<div class="breadcrumb">'. $breadcrumb_temp .'</div>';
  }
}
mrgoltra’s picture

good info

Tistur’s picture

Me too

lias’s picture

thanks for the explaination walk through and code

Mark Nielsen’s picture

I discovered a tricky problem with the above theme functions which led to block menus not working properly. However, I found a fix - so both the problem and the solution are addressed here:
http://drupal.org/node/171529

I think these functions are approaching stability now, so when I get around to it I'll try and add a book page somewhere about this. It's also feeding into an high-accessibility theme I'm working on, which I imagine will be available for Drupal 5 shortly after Drupal 6 is released :/ *sigh* :D

Mark
flet.org

Mark Nielsen’s picture

I have to say, that after a few months of using these template snippets, I'm not sure I'd recommend the non-hyperlinking of "active" menu items.

My method strips link tags based on matching the link text with the current page title, and also if the link contains an "active" class. This has caused me a number of problems though, including:

  • If two menu links have the same name but point to different places, they'll both be inactive.
  • Page "tabs" in tracker and views can be messed up, with more than link being stripped, making some pages inaccessible.

All in all, I'm going to keep working on this, but I don't think it's production ready yet.

Mark
flet.org