HowTo: Change the name display
There has been a long standing issue concerning display names in Drupal (http://drupal.org/node/102679, http://drupal.org/node/188734, http://drupal.org/node/394282). On MothersClick we achieve this through the theme layer. Specifically, we override theme_username() in our theme. In Drupal 5, this is by far the cleanest solution. It isn't ideal because a module, say a Facebook module, can't change the name without forcing the user to alter their theme. Not nice...
User Display API achieves this by applying a patch phptemplate.engine. All calls to theme('username') use an additional parameter to inform the module what display class it is, dictating how to display the result informing the module what context the call to theme('username') is called from. Using this context, you can detect how to display. Again not ideal, but emphasizes my feeling on the issue, its a display issue. So my two example implementations exist in the theme layer. And my solution using Drupal 6 follows the similar "theme". In the words of a Mr. Jeff Eaton "Drupal 6: The Land of Milk and Honey" (http://www.lullabot.com/articles/overriding-theme-functions-in-modules).
Here is a module, called fullname that displays a users full name in place of their username. It does so by, via hook_theme_registry_alter(). First, we need to display the fields to the user and save it away.
<?php
/**
* Implementation of hook_user().
*/
function fullname_user($op, &$edit, &$account, $category = NULL) {
switch($op) {
case 'register':
case 'form':
$form['firstname'] = array(
'#type' => 'textfield',
'#title' => t('First Name'),
'#default_value' => $account->firstname,
'#required' => TRUE,
);
$form['lastname'] = array(
'#type' => 'textfield',
'#title' => t('Last Name'),
'#default_value' => $account->lastname,
'#required' => TRUE,
);
return $form;
break;
}
}
?>Now Im cheating in this case by saving the first name and the last name to the {users}.data field. Not the best practice, probably belongs in a separate table.
So, to the land of Milk and Honey:
<?php
/**
* Implementation of hook_theme_registry_alter().
*/
function fullname_theme_registry_alter(&$theme_registry) {
if (!empty($theme_registry['username']['function'])) {
$previous_function = $theme_registry['username']['function'];
$theme_registry['username']['function'] = 'fullname_username';
$theme_registry['username']['arguments']['previous_function'] =
$previous_function;
}
}
?>So couple of cool tricks in there. First, we override the registry function and replace it with ours. We also add an argument that is the previous function. We can make use of this in our function after we process the name property of the object.
<?php
/**
* theme_username function()
*/
function fullname_username($object = NULL, $previous_function = 'theme_username') {
// want to use a fake object because objects are passed by reference
$fake_object = new stdClass();
// theme_username() expects name, uid, homepage
if (empty($object->firstname)) {
if ($object->uid) {
// go get it.
// because we save the data {users}.data field, we do a user_load()
// but you would do a query against your table here
$object = user_load($object->uid);
$fake_object->name = $object->firstname . ' ' . $object->lastname;
}
else {
// nothing todo here..
$fake_object->name = $object->name;
}
}
else {
$fake_object->name = $object->firstname . ' ' . $object->lastname;
}
$fake_object->uid = $object->uid;
$fake_object->homepage = $object->homepage;
// now call the real theme function. In boring installs its theme_username()
return call_user_func($previous_function, $fake_object);
}
?>We make use of a 'fake' object. I did this because the $object passed into this function is passed by reference, so you do not want to change the actual properties of the object. In this function, you can add the logic you need. Now does it work?
Yes, for the most part. The problem is that not all spots where a name is output is put through theme('username') http://drupal.org/node/192056 . For instance, the Navigation block provided by the users module. It outputs check_plain($account->name). Can't be help here.
So there is my 70 line module as an example to a solution to the problem.
Comments
Not dictation. Only _classification_.
"an additional parameter to inform the module what display class it is, dictating how to display the result"
Sorry, that is wrong. User Display API is not dictating it.
It is using the class to tell where the user will be rendered. So you can decide how it is rendered when it appears "there".
If you don't assign a new output style to some or all classes, nothing happens and all usernames and user pictures are rendered like they were before.
You are overriding any instance of
theme('username'), regardless of where it appears. That may be a valid use-case, but is just a workaround for the current system that provides no context.Try this with your simple override concept and you will end up with either nasty
$_GET['q']orarg(#), or evendebug_backtrace()tests.noted and fixed. Have you
noted and fixed.
Have you looked into using Context or CTools? At least from an implementation perspective it would give some insights on how other people tackle the need for context.
I haven't used CTools, but I know Context is like a big static variable cache. And the context can be set via rules defined in the context_ui interface.
Forms
The node add/edit form also shows usernames, and if you don't want them to appear there, you're going to need to form_alter them out and pop something else in.
theme_username stuff is easy, but core makes it hard to replace usernames completely.
Your right I agree. But I do
Your right I agree. But I do not believe that we need a new field in the users table for a 'display name'. Even in sun's module, that wouldn't work. That module needs a way to determine context.
On MothersClick, we need to determine if the $GLOBALS['user'] has access to the view another users full name. Adding another field makes little sense. Doesn't solve the problem. Calling theme('username') constantly in core and a solution similar to this in your own module would.
Really short but useful
Really short but useful howto.
Thanks.