3

I'm trying to insert a span class around some text in title callback. It passes through a t() function so all the html is escaped.

Is there a way round this?

My code is:

function custom_module_menu() {
  $items = array();

  $items['discussion'] = array(
    'title' => 'Discussion',
    'title callback' => 'custom_module_title',
    'page callback' => 'custom_module_tester1',
    'access callback' => TRUE,
    'menu_name' => 'document-menu',
    'type' => MENU_NORMAL_ITEM,
    'options' => array('attributes' => array('class' => 'discussion')),
    'weight' => 10,
  );

  return $items;
}
function custom_module_title() {
  $discussionnid = custom_module_postexist(3);
  if ($discussionnid) {
    return 'Discussion <span class="discussionnumber">85</span>';
  }
  else {
    return 'Discussion';
  }

}
  • You can do this in your theme as it is theming related. – ram4nd Aug 15 '11 at 11:44
5

The HTML in the title is escaped because template_preprocess_page() gets the title using the following code:

// Construct page title
  if (drupal_get_title()) {
    $head_title = array(strip_tags(drupal_get_title()), variable_get('site_name', 'Drupal'));
  }

drupal_get_title() then contains the following code:

function drupal_get_title() {
  $title = drupal_set_title();

  // during a bootstrap, menu.inc is not included and thus we cannot provide a title
  if (!isset($title) && function_exists('menu_get_active_title')) {
    $title = check_plain(menu_get_active_title());
  }

  return $title;
}

The title is escaped from that function.

As far as I know, the content of <title> should not contain HTML tags; that is the reason it is escaped.

In the W3C specifications for HTML 4 is reported the following text:

Titles may contain character entities (for accented characters, special characters, etc.), but may not contain other markup (including comments). Here is a sample document title:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD>
<TITLE>A study of population dynamics</TITLE>
<!-- other head elements -->
</HEAD>
<BODY>
<!-- document body -->
</BODY>
</HTML>

In Changed Elements it's not reported any changes between HTML 5 and HTML 4 about the <title> tag.

In Drupal 7, you could set the title with drupal_set_title($title, PASS_THROUGH), but the documentation says:

Only set to PASS_THROUGH if you have already removed any possibly dangerous code from $title using a function like check_plain() or filter_xss(). With this flag the string will be passed through unchanged.

PASS_THROUGH is used to avoid the title is filtered twice, not to filter the title at all.

Then, using drupal_set_title() is not possible from the title callback; you should set it in a different place. I would not suggest doing so, though.

  • I was getting confused, title also changes the menu link text as well. I'm only interested in that not the actual page title. I didn't realise page title and link text were the same in hook_menu(). I need to look elsewhere to change the link text. – digital Aug 16 '11 at 14:08
  • You need a way to have a page title that is different from the <head> title. Even if you are able to get the HTML not escaped in the page title, the <head> title would still contain escaped HTML tags. – kiamlaluno Nov 20 '12 at 14:33
  • I believe OP mean the title key in the menu item array, the one that is rendered in the page, not the title HTML attribute. – Gabriel R. Sep 27 '13 at 10:39
  • @GabrielR. The title reported in the menu item array is the title of the page, and becomes part of the HTML title shown in <title>. What I reported in this answer is correct. – kiamlaluno Sep 27 '13 at 11:06
2

You can use

drupal_set_title('your html title',PASS_THROUGH) 

before to render your page.

PASS_THROUGH = Flag for drupal_set_title();, text has already been sanitized.

2

I had a similar situation where I wanted to display the weight of the menu item right next to the link. Since it seems problematic to insert HTML in the link title itself, I chose a different approach:

Add an extra attribute to your links and show it using CSS

$items['discussion'] = array(
  // ... all the other properties
  // Our special attributes:
  'options' => array(
    'attributes' => customAttr()
  ),
);

function customAttr() {
  $discussionnid = custom_module_postexist(3);
  if ($discussionnid) {
    return array(
      'class' => 'discussion',
      'data-discussionnumber' => 85
    );
  }
  else {
    return array(
      'class' => 'discussion'
    );
  }
}

This returns a link like:

<a class="discussion" data-discussionnumber="85" href="yourHref">yourTitle</a>

Now you can add the following CSS:

#document-menu a:after {
  content: attr(data-discussionnumber);
  /* your custom styles */
}

Hope this helps!

1

Define title callback a function to replace t()

e.g.

<?php
function node_menu() {
  $items['node/%node'] = array(
    'title callback' => 'node_page_title',
    'title arguments' => array(1),
    'page callback' => 'node_page_view',
    'page arguments' => array(1),
    'access callback' => 'node_access',
    'access arguments' => array('view', 1),
    'type' => MENU_CALLBACK,
  );
}

/**
* Title callback.
*/
function node_page_title($nid) {
  $node = node_load($nid);
  return "<span>" . $node->title . "</span>";
}
?>
  • I've updated my question with the code I'm using, even though I'm defining a title callback the html is still escaped. – digital Aug 15 '11 at 11:59
0

If you arrived here looking for the answer for drupal 7. (I don't know if it works for drupal 6). You can add to the "options" array "html" => "true". This parameter will be passed to the l() saying to not parse the html tags. Use with precaution, because it's dangerous.

from hook_menu:

"options": An array of options to be passed to l() when generating a link from this menu item. Note that the "options" parameter has no effect on MENU_LOCAL_TASK, MENU_DEFAULT_LOCAL_TASK, and MENU_LOCAL_ACTION items.

and from l():

'attributes': An associative array of HTML attributes to apply to the anchor tag. If element 'class' is included, it must be an array; 'title' must be a string; other elements are more flexible, as they just need to work in a call to drupal_attributes($options['attributes']).

'html' (default FALSE): Whether $text is HTML or just plain-text. For example, to make an image tag into a link, this must be set to TRUE, or you will see the escaped HTML image tag. $text is not sanitized if 'html' is TRUE. The calling function must ensure that $text is already safe.

would be like that:

 function custom_module_menu() {
  $items = array();

  $items['discussion'] = array(
    'title' => 'Discussion',
    'title callback' => 'custom_module_title',
    'page callback' => 'custom_module_tester1',
    'access callback' => TRUE,
    'menu_name' => 'document-menu',
    'type' => MENU_NORMAL_ITEM,
    'options' => array('attributes' => array(
       'class' => 'discussion',                //will be passed to l()
       'html' => true 
       )),
    'weight' => 10,
  );

  return $items;
  }


  function custom_module_title() {
    $discussionnid = custom_module_postexist(3);
    if ($discussionnid) {
      return 'Discussion <span class="discussionnumber">85</span>';
    }
    else {
      return 'Discussion';
    }
  }
  • If you menu item is MENU_LOCAL_TASK anything you specify in 'options' won't be picked up. The only way I could work out to get my options in was using hook_menu_local_tasks_alter() – leon.nk Feb 12 '16 at 6:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.