Need some help with your project? Contact me

Theming the username link in Drupal 7 - the catch

In this tutorial I’m going to show you how to programatically output a username link on the page. When theming a Drupal site or building a module, you might need to access the user account and print out the username. There is a Drupal theme function made specially for this but it has a catch. So let’s see what that is. If you are interested in how you can alter the usernames before being rendered on the page, check out my other post.

First, let’s think of a use case to make things easier to explain. Say you need to display the node author in a .tpl file other than the node.tpl.php where it usually is. You get the user ID from the node object (uid) and with the help of the user_load() function you get the user object and store it in $account. But what do you do then?

The problem

You could print $account->name, but then you’d output just the name without linking to the user account - a feature you're most likely going to want. Or you could create an tag with a link to node/$account->uid and wrap that around the username. Would be a link, but if site permissions do not allow anonymous users to see user profiles, you’d be linking to pages that give an access denied error. And not to mention that in both cases you’d be outputting unsanitized information so you’d have to run them through check_plain() too. Complicated...

But you can use the Drupal theme_username() function specifically made for this. It sanitizes the information and it creates a link if whoever sees the username has access to view user profiles. In case they don’t, it just outputs the username in plain text. And more...But, there is a catch.

If you take a look on its API page, you can see that you have a bunch of options when using this function. The one requirement is that you pass in the user account to format. But then, you can also add a different name to be replace the username with, some extra text to be appended, another link path, and some HTML attributes.

But not everything works. The API page is misleading as only the user account is accepted by the function. So you can pass as many of the other options I mentioned as you want... they will not work. These variables are already set in the template preprocess function and cannot be overridden with each use of theme_username().

The solution

A workaround I found to this problem is to override yourself the latter function. Just copy it from the API page and make your changes.

Note: If you override this function, your changes will affect all instances in which the function is called on your site.

To circumvent this problem, you can use a variable to check against when you override the function. This way, you condition the new changes to the existence and possible value of this ad hoc variable that you may or may not pass to theme_username() wherever you use it on your site.

Let me explain:

First, when you use the function in a .tpl file or module, you can do something like this:

$username = theme('username', array('account' => $author, 'type' => 'button'));

As you can see, I passed in the user account (as instructed by the API) and a custom variable called type to which I assigned the value button. This is new, coming from me as I want the username here to be displayed as a button.

Next, you override the theme_username() function in your template.php file or module. Below is the original function declaration that you need to copy (don't forget to replace theme with the name of your theme or module).

function theme_username($variables) {
  if (isset($variables['link_path'])) {
    // We have a link path, so we should generate a link using l().
    // Additional classes may be added as array elements like
    // $variables['link_options']['attributes']['class'][] = 'myclass';
    $output = l($variables['name'] . $variables['extra'], $variables['link_path'], $variables['link_options']);
  }
  else {
    // Modules may have added important attributes so they must be included
    // in the output. Additional classes may be added as array elements like
    // $variables['attributes_array']['class'][] = 'myclass';
    $output = '<span' . drupal_attributes($variables['attributes_array']) . '>' . $variables['name'] . $variables['extra'] . '</span>';
  }
  return $output;
}

Now, within the function, you need to check if your variable was passed. If it was, you need to check its value and based on that you can perform various changes - you are not limited to one way of displaying the usernames across your site.

So in our case, we want to check if type was passed and if it is equal to button. If it is, then add a .css class (.button) to the link and an icon before the username. As you can see I am hardcoding this .css class because I do not require a lot of flexibility. But you can of course have the value of the variable added straight to the html.

On the other hand, if the variable type is not passed to the function and consequently does not equal to button, go ahead and do what normally the function does (which means no changes at all).

function yourthemename_username($variables) {
  if (isset($variables['link_path'])) {
    // We have a link path, so we should generate a link using l().
    // Additional classes may be added as array elements like
    // $variables['link_options']['attributes']['class'][] = 'myclass';
    if (isset($variables['type'])) {
      switch ($variables['type']) {
        case 'button' :
          $variables['prefix']['icon'] = '<i class="icon-user"></i> ';
          $variables['link_options']['attributes']['class'][] = 'button';
          $output = l($variables['prefix']['icon'] . $variables['name'] . $variables['extra'], $variables['link_path'], $variables['link_options']);
          break;
      }
    }
    else {
      $output = l($variables['name'] . $variables['extra'], $variables['link_path'], $variables['link_options']);
    }
  }
  else {
    // Modules may have added important attributes so they must be included
    // in the output. Additional classes may be added as array elements like
    // $variables['attributes_array']['class'][] = 'myclass';
    $variables['attributes_array']['class'][] = 'button strapped';
    $output = '<span' . drupal_attributes($variables['attributes_array']) . '><i class="icon-user"></i> ' . $variables['name'] . $variables['extra'] . '</span>';
  }
  return $output;
}

Instead of the switch statement block, I could have used a. php if conditional. But to illustrate an earlier point, using a switch block allows you to make any number of checks and have the username displayed on your site in any number of ways. All you have to do then is pass the right variable to the theme_username() function.

And this is how you customize the output of usernames on your site. I know it sounds a bit complicated for such as small detail but it is not so difficult and you can get some pretty good control overriding this function. I also think though that the Drupal API page should be updated to mention that these variables cannot be passed when using the function as they are automatically replaced in the preprocess phase.

OK, so hope you understood something, if there are any issues, drop a comment.

Comments

It might be worth mentioning this issue http://drupal.org/node/1238094

Maybe readers can help test taht patch and get it committed.

Thanks for mentioning it!

Add new comment

You must have Javascript enabled to use this form.