It's hook_link_alter over jQuery or CSS alone

Last year I would have tried to approach this problem with some kind of image/text replacement... This year it's a whole new ballgame.  The Drupal theming layer is like an onion and I continue to pull back the outer pieces and get to better and better stuff.  Maybe it is more like a Parfait (thanks Donkey from Shrek) ;)

So here's what I wanted to do. Drupal gives us a teaser output with comment links.

3 comments  |  2 new comments

Great, awesome, cool... great to have, now I want more... I want an image to replace the word "comments".  I'm not going to rely on css alone... I'm not going to rely on some jQuery (which I love)... I'm going to use hook_link_alter and get it done in the code.

I had a starting point that looks like this...

I am completely familiar with hook_form_alter and have used and abused it to the fullest.  This time I know that hook was not going to work, I needed something else.  I started looking at the comment module and found the comment_link function.  To my surprise it looked a little similar to when I have worked with form_alter.  The 'array' looked like something I could work with...

<code>
        $all = comment_num_all($node->nid);

        if ($all) {
          $links['comment_comments'] = array(
            'title' => format_plural($all, '1 comment', '@count comments'),
            'href' => "node/$node->nid",
            'attributes' => array('title' => t('Jump to the first comment of this posting.')),
            'fragment' => 'comments'
          );

          $new = comment_num_new($node->nid);

          if ($new) {
            $links['comment_new_comments'] = array(
              'title' => format_plural($new, '1 new comment', '@count new comments'),
              'href' => "node/$node->nid",
              'query' => comment_new_page_count($all, $new, $node),
              'attributes' => array('title' => t('Jump to the first new comment of this posting.')),
              'fragment' => 'new'
            );
          }
        }
        else {
          if ($node->comment == COMMENT_NODE_READ_WRITE) {
            if (user_access('post comments')) {
              $links['comment_add'] = array(
                'title' => t('Add new comment'),
                'href' => "comment/reply/$node->nid",
                'attributes' => array('title' => t('Add a new comment to this page.')),
                'fragment' => 'comment-form'
              );
            }
            else {
              $links['comment_forbidden']['title'] = theme('comment_post_forbidden', $node);

</code>
and because it was part of the link function I decided to use hook_link_alter and see what happened.

At Do It With Drupal, over lunch I was talking with two programmers.  Very cool guys who remarked about the ability to pass with reference.  When I looked closely at the hook_link_alter

<code>
function hook_link_alter(&$links, $node) {
  foreach ($links as $module => $link) {
    if (strstr($module, 'taxonomy_term')) {
      // Link back to the forum and not the taxonomy term page
      $links[$module]['href'] = str_replace('taxonomy/term', 'forum', $link['href']);
    }
  }
}
</code>

I noticed that the links were being passed with the '&', which I know understood to mean that I didn't have to end the function with any type of 'return' statement.  That all of the logic and goodness I needed form the links would stay in tack, I should be able to find the parts I needed, in the array, that would create the output I wanted.

So what do I want.

I need to:
A -> find the comments 'word' in the array and change it with an image.
B -> include in the array that HTML is allowed (so I can use an image).
C -> retain the number in front of the bubble (was the word comment).

o.k., if you are a programmer, you are a Drupal guru, all of this seems pretty basic, but for me, using the Devel module, and inside of the hook_link_later using dsm($links) to find/access my array was totally awesome.  I was able to find the arrays I needed (similar to when I do it with forms) and make my changes.  So, if you are a guru don't laugh, if you aren't hopefully this is not only right, but it helps you down the road.  Here's my code...

<code>
function NAMEOFMYTHEME_link_alter(&$links, $node) {
    $pathhere = drupal_get_path('module','wsotheme');
    $cool = '<img src="'.$pathhere.'/comment2.png" height="16" width="17"/>';
        //drupal_set_message('' . print_r($links, TRUE) . '');
        //dsm($links);
    
        $all = comment_num_all($node->nid);
    $new = comment_num_new($node->nid);
    
    if ($links['comment_comments']['title']) {
        $com_comts = $all . ' ' . $cool;
        $links['comment_comments']['html'] = TRUE;
        $links['comment_comments']['title'] = $com_comts;
    }
    if ($links['comment_new_comments']['title']) {
        $comts_new = $new .' new '. $cool;
        $links['comment_new_comments']['html'] = TRUE;
        $links['comment_new_comments']['title'] = $comts_new;
    }
}
</code>

A -> I grabbed the array where "comments" was included from comment_link, then created a variable with my image.
B -> I added the 'html' = TRUE to the array to allow the <img> tag to be included in the link output.
C -> I copyed/reused the $all and $new variables (placed them here to use) to make sure I was getting a count.

After some adjustments and some time to debug I ended up with the code above.
Some of the links that provided me some insight and direction, might also help someone else out there include...

http://www.kinetasystems.com/blog/theming-the-links-variable-in-drupal-n...
http://agaric.com/note/remove-add-comment-links-from-teasers
http://drupal.org/node/551180#comment-1933852

I am happy with the results, and looking forward to using more hook_link_alters when ever I can :); however my discovery is not without some questions.  After I got it working... I started thinking...

1. What is...
<code>
foreach ($links as $module => $link)
</code>
do I need it?

2. Do I really need to re-declare $new and $all?

3. Did I miss anything important?

<em>Feel free to clue me in if you have any thoughts.</em>

Comments

2

I've been trying to sort this out for ages but I haven't got your example working yet. Does the hook_link_alter() go in template.php of your theme? I've read elsewhere that you have to write a custom module to implement hook_form_alter, or use function _phptemplate_variables($hook, $vars = array()) in the theme's template.php and catch the form_alter hook in there. Or was there something else I missed?

I have stuck to the pattern that "hook_" require a module, and "theme_" can live in template.php. There are some cases, where the site has some pre-processing or other theme overrides, that need to stay with the site... i.e. if I change themes, the node title for the content type story, always get's preprocessed and is appended with a date... in this case, I still like to use a module, so it's "independent" of the theme. As usual, there are dozens of ways to do the same thing, that's just how I have been doing it :)