What is the proper filter to add html to a post / page title?

I want to use Myanmar Unicode text on my blog. Unfortunatly, pseudo-Unicode Myanmar fonts are common. So, I am writing a plugin that will suround all Myanmar phrases with HTML span tags, and use css to select the proper font.

Everything works fine for content, comments, etc. However, after adding HTML using the the_title hook, that HTML gets escaped, and then included in the title attribute of the link tag in the recent posts section. In addition, I’m seeing the raw HTML tags in the post name on my admin post screen.

Read More

How should I go about solving this problem? I prefer to do everything from within my plugin, not change the theme files or something.

Here is my code:

add_filter( 'the_title', 'addThemSpans');
function addThemSpans($theTitle) {
  // functionality to add span tags to $theTitle so that a title that looks like this...
    // LatinTextHere ကကကကက​ MoreLatinText
  // becomes this...
    // LatinTextHere <span class="myText">ကကကကက</span> MoreLatinText
    // in the final output.
  return $theModifiedTitle;
}

As it is now, the title attribute for the link in the recent posts sidebar widget is set to:

LatinTextHere &lt;span class='myText'&gt;ကကကကက&lt;/span&gt;<span class='myText'>ကကကကက</span> MoreLatinText

Which is not right.

Related posts

Leave a Reply

4 comments

  1. the_title is the correct hook. You can confirm that by checking the source.

    What you are doing is a bit odd, but it makes sense. Clever solution but I don’t think it was expected. The problem you having seems to be with this line:

    http://core.trac.wordpress.org/browser/tags/3.5/wp-includes/default-widgets.php#L574

    <a href="<?php the_permalink() ?>" title="<?php echo esc_attr( get_the_title() ? get_the_title() : get_the_ID() ); ?>"><?php if> (get_the_title() ) the_title(); else the_ID(); ?></a>
    

    And there is no hook that will help you. The widget grabs the title with get_the_title and doesn’t pass it through anything that you can manipulate.

    I think you may need to rethink your approach.

    1. If this is a situation where you can edit the theme you can remove your filter before the sidebars runs and add it back afterwards. I think that is the most straightforward way to do it but may not be possible in you case.

    2. You could also write your own recent posts widget.

    3. You could manipulate your fonts with Javascript– not sure it that would be robust enough for you.

    You might be able to check for an $instance variable in your filte and sort out whether you are dealing with a widget or not.

    add_filter( 'the_title', 'addThemSpans');
    function addThemSpans($theTitle) {
      global $instance;
      var_dump($instance); // see if this works.
      // functionality to add span tags to $theTitle so that a title that looks like this...
        // LatinTextHere ကကကကက​ MoreLatinText
      // becomes this...
        // LatinTextHere <span class="myText">ကကကကက</span> MoreLatinText
        // in the final output.
      return $theModifiedTitle;
    }
    

    I don’t know if that will work or not, but if so it will give you something to switch on and you won’t have to edit the theme. Edit: Tested. Sadly, doesn’t work.

  2. So, what’s happening is that the_title_attribute(), which outputs the title attribute in the Recent Posts widget links, calls get_the_title(); thus, your the_title filter is being applied to the string returned by the_title_attribute().

    One idea would be to ensure that get_the_title is being called from within the main query loop (caveat: if you use WP_Query() loops, this approach would not work for them).

    Perhaps something like this (untested).

    Edit

    This won’t work as-written. If you could find some way to grab the current $query object from inside of get_the_title(), then you could do something like this:

    function wpse82297_filter_the_title( $title ) {
        // Need some way to identify the current query object
        $query = '';
        // If this isn't the main query, return
        if ( ! $query->is_main_query() ) {
            return $title;
        } else {
            // Your $title filtering goes here
            return $modified_title;
        }
    }
    add_filter( 'the_title', 'wpse82297_filter_the_title' );
    

    So, this would prevent your filter from being applied to Nav Menu items, Recent Posts Widgets, etc. – anything except for the main query loop.

    I’ll leave this here as an answer, in case anyone can think of a way to identify the current $query object.

  3. Ok, I thought I’d post my final solution up here in case anyone else has the same problem. Basically, I have to add new hooks to my theme files, and then send the modified post title to those hooks.

    My plugin code now looks like:

    add_filter( 'the_html_safe_title', 'addThemSpansToPostTitles' );
    function addThemSpansToPostTitles() {
      global $post;
      echo addThemSpans($post->post_title);
      // This is the same function I listed in the question.
    }
    

    Then, in the theme template files, where previously it was:

    <?php the_title(); ?>
    

    I change it to:

    <?php do_action( 'the_html_safe_title' ); ?>
    

    This is less than ideal for at least two reasons:

    1. The titles in places such as the Recent Posts widget don’t get the spans inserted, meaning that, depending on the viewer browser, font settings, and installed fonts, the viewer will still see mangled text in those locations.

    2. This makes it much harder to share the plugin with other Myanmar language bloggers who may not have the technical skills to modify their themes, and may not want to use my theme.

    Attention wordpress developers: I have seen a significant number of people online wishing they could include HTML in their post and page titles. Allowing us to do so makes lots of sense, not only for specialty situations like mine, but for users who want to change their titles in other ways. Please consider changing future wordpresses to allow HTML in titles.

  4. You can convert them back simply using htmlspecialchars(), which is a native PHP function.

    From the manual:

    The translations performed are:

    • '&' (ampersand) becomes '&amp;'
    • '"' (double quote) becomes '&quot;' when ENT_NOQUOTES is not set.
    • "'" (single quote) becomes '&#039;' (or &apos;) only when ENT_QUOTES is set.
    • '<' (less than) becomes '&lt;'
    • '>' (greater than) becomes '&gt;'

    Anyway, I wouldn’t do this if you don’t know every user personally (and know that they care about internet security and don’t reuse passwords) or this is for yourself only. As you convert back those characters, you’re not save if anyone escapes your string and does funky stuff in your installation.