diff --git a/includes/common.inc b/includes/common.inc index c6686ee0109..adacdee5e1a 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1289,14 +1289,14 @@ function fix_gpc_magic() { * check_plain, to escape HTML characters. Use this for any output that's * displayed within a Drupal page. * @code - * drupal_set_title($title = t("@name's blog", array('@name' => $account->name)), PASS_THROUGH); + * drupal_set_title($title = t("@name's blog", array('@name' => format_username($account))), PASS_THROUGH); * @endcode * * - %variable, which indicates that the string should be HTML escaped and * highlighted with theme_placeholder() which shows up by default as * emphasized. * @code - * $message = t('%name-from sent %name-to an e-mail.', array('%name-from' => $user->name, '%name-to' => $account->name)); + * $message = t('%name-from sent %name-to an e-mail.', array('%name-from' => format_username($user), '%name-to' => format_username($account))); * @endcode * * When using t(), try to put entire sentences and strings in one t() call. @@ -2329,6 +2329,29 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) { return $cache[$langcode][$code][$string]; } +/** + * Format a username. + * + * By default, the passed in object's 'name' property is used if it exists, or + * else, the site-defined value for the 'anonymous' variable. However, a module + * may override this by implementing hook_username_alter(&$name, $account). + * + * @see hook_username_alter() + * + * @param $account + * The account object for the user whose name is to be formatted. + * + * @return + * An unsanitized string with the username to display. The code receiving + * this result must ensure that check_plain() is called on it before it is + * printed to the page. + */ +function format_username($account) { + $name = !empty($account->name) ? $account->name : variable_get('anonymous', t('Anonymous')); + drupal_alter('username', $name, $account); + return $name; +} + /** * @} End of "defgroup format". */ diff --git a/includes/theme.inc b/includes/theme.inc index 32140a62ffb..0f1fde32154 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -1912,12 +1912,17 @@ function template_preprocess_username(&$variables) { else { $variables['uid'] = (int)$account->uid; } - if (empty($account->name)) { - $variables['name'] = variable_get('anonymous', t('Anonymous')); - } - else { - $variables['name'] = $account->name; + + // Set the name to a formatted name that is safe for printing and + // that won't break tables by being too long. Keep an unshortened, + // unsanitized version, in case other preproces functions want to implement + // their own shortening logic or add markup. If they do so, they must ensure + // that $variables['name'] is safe for printing. + $name = $variables['name_raw'] = format_username($account); + if (drupal_strlen($name) > 20) { + $name = drupal_substr($name, 0, 15) . '...'; } + $variables['name'] = check_plain($name); $variables['profile_access'] = user_access('access user profiles'); $variables['link_attributes'] = array(); @@ -1936,12 +1941,6 @@ function template_preprocess_username(&$variables) { $variables['link_options']['html'] = TRUE; // Set a default class. $variables['attributes_array'] = array('class' => array('username')); - // Shorten the name when it is too long or it will break many tables. - if (drupal_strlen($variables['name']) > 20) { - $variables['name'] = drupal_substr($variables['name'], 0, 15) . '...'; - } - // Make sure name is safe for use in the theme function. - $variables['name'] = check_plain($variables['name']); } /** diff --git a/modules/blog/blog.module b/modules/blog/blog.module index 0d4878d48b7..1198f27e2f0 100644 --- a/modules/blog/blog.module +++ b/modules/blog/blog.module @@ -27,7 +27,7 @@ function blog_user_view($account) { $account->content['summary']['blog'] = array( '#type' => 'user_profile_item', '#title' => t('Blog'), - '#markup' => l(t('View recent blog entries'), "blog/$account->uid", array('attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => $account->name))))), + '#markup' => l(t('View recent blog entries'), "blog/$account->uid", array('attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => format_username($account)))))), '#attributes' => array('class' => array('blog')), ); } @@ -60,7 +60,7 @@ function blog_form(stdClass $node, $form_state) { function blog_view(stdClass $node, $build_mode) { if ((bool)menu_get_object()) { // Breadcrumb navigation. - drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t('Blogs'), 'blog'), l(t("!name's blog", array('!name' => $node->name)), 'blog/' . $node->uid))); + drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t('Blogs'), 'blog'), l(t("!name's blog", array('!name' => format_username($node))), 'blog/' . $node->uid))); } return $node; } @@ -72,9 +72,9 @@ function blog_node_view(stdClass $node, $build_mode = 'full') { if ($build_mode != 'rss') { if ($node->type == 'blog' && arg(0) != 'blog' || arg(1) != $node->uid) { $links['blog_usernames_blog'] = array( - 'title' => t("!username's blog", array('!username' => $node->name)), + 'title' => t("!username's blog", array('!username' => format_username($node))), 'href' => "blog/$node->uid", - 'attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => $node->name))), + 'attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => format_username($node)))), ); $node->content['links']['blog'] = array( '#theme' => 'links', diff --git a/modules/blog/blog.pages.inc b/modules/blog/blog.pages.inc index 6936f54be83..a605cfa1c36 100644 --- a/modules/blog/blog.pages.inc +++ b/modules/blog/blog.pages.inc @@ -12,7 +12,7 @@ function blog_page_user($account) { global $user; - drupal_set_title($title = t("@name's blog", array('@name' => $account->name)), PASS_THROUGH); + drupal_set_title($title = t("@name's blog", array('@name' => format_username($account))), PASS_THROUGH); $items = array(); @@ -123,7 +123,7 @@ function blog_feed_user($account) { ->execute() ->fetchCol(); - $channel['title'] = t("!name's blog", array('!name' => $account->name)); + $channel['title'] = t("!name's blog", array('!name' => format_username($account))); $channel['link'] = url('blog/' . $account->uid, array('absolute' => TRUE)); node_feed($nids, $channel); diff --git a/modules/blog/blog.test b/modules/blog/blog.test index 597c549e822..c00a933993d 100644 --- a/modules/blog/blog.test +++ b/modules/blog/blog.test @@ -38,7 +38,7 @@ class BlogTestCase extends DrupalWebTestCase { $this->drupalGet('blog/' . $this->big_user->uid); $this->assertResponse(200); - $this->assertTitle(t("@name's blog", array('@name' => $this->big_user->name)) . ' | Drupal', t('Blog title was displayed')); + $this->assertTitle(t("@name's blog", array('@name' => format_username($this->big_user))) . ' | Drupal', t('Blog title was displayed')); $this->assertText(t('You are not allowed to post a new blog entry.'), t('No new entries can be posted without the right permission')); } @@ -50,8 +50,8 @@ class BlogTestCase extends DrupalWebTestCase { $this->drupalGet('blog/' . $this->own_user->uid); $this->assertResponse(200); - $this->assertTitle(t("@name's blog", array('@name' => $this->own_user->name)) . ' | Drupal', t('Blog title was displayed')); - $this->assertText(t('!author has not created any blog entries.', array('!author' => $this->own_user->name)), t('Users blog displayed with no entries')); + $this->assertTitle(t("@name's blog", array('@name' => format_username($this->own_user))) . ' | Drupal', t('Blog title was displayed')); + $this->assertText(t('@author has not created any blog entries.', array('@author' => format_username($this->own_user))), t('Users blog displayed with no entries')); } /** @@ -139,7 +139,7 @@ class BlogTestCase extends DrupalWebTestCase { $this->drupalGet('node/' . $node->nid); $this->assertResponse(200); $this->assertTitle($node->title[FIELD_LANGUAGE_NONE][0]['value'] . ' | Drupal', t('Blog node was displayed')); - $this->assertText(t('Home ' . $crumb . ' Blogs ' . $crumb . ' @name' . $quote . 's blog', array('@name' => $node_user->name)), t('Breadcrumbs were displayed')); + $this->assertText(t('Home ' . $crumb . ' Blogs ' . $crumb . ' @name' . $quote . 's blog', array('@name' => format_username($node_user))), t('Breadcrumbs were displayed')); // View blog edit node. $this->drupalGet('node/' . $node->nid . '/edit'); @@ -180,7 +180,7 @@ class BlogTestCase extends DrupalWebTestCase { // Confirm the recent blog entries link goes to the user's blog page. $this->clickLink('View recent blog entries'); - $this->assertTitle(t("@name's blog | Drupal", array('@name' => $user->name)), t('View recent blog entries link target was correct')); + $this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), t('View recent blog entries link target was correct')); // Confirm a blog page was displayed. $this->drupalGet('blog'); @@ -191,7 +191,7 @@ class BlogTestCase extends DrupalWebTestCase { // Confirm a blog page was displayed per user. $this->drupalGet('blog/' . $user->uid); - $this->assertTitle(t("@name's blog | Drupal", array('@name' => $user->name)), t('User blog node was displayed')); + $this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), t('User blog node was displayed')); // Confirm a blog feed was displayed. $this->drupalGet('blog/feed'); @@ -199,6 +199,6 @@ class BlogTestCase extends DrupalWebTestCase { // Confirm a blog feed was displayed per user. $this->drupalGet('blog/' . $user->uid . '/feed'); - $this->assertTitle(t("@name's blog", array('@name' => $user->name)), t('User blog feed was displayed')); + $this->assertTitle(t("@name's blog", array('@name' => format_username($user))), t('User blog feed was displayed')); } } diff --git a/modules/contact/contact.module b/modules/contact/contact.module index 89199270377..3ba19a55ed4 100644 --- a/modules/contact/contact.module +++ b/modules/contact/contact.module @@ -174,7 +174,7 @@ function contact_mail($key, &$message, $params) { '!subject' => $params['subject'], '!category' => isset($params['category']['category']) ? $params['category']['category'] : '', '!form-url' => url($_GET['q'], array('absolute' => TRUE, 'language' => $language)), - '!sender-name' => $params['sender']->name, + '!sender-name' => format_username($params['sender']), '!sender-url' => $params['sender']->uid ? url('user/' . $params['sender']->uid, array('absolute' => TRUE, 'language' => $language)) : $params['sender']->mail, ); @@ -194,7 +194,7 @@ function contact_mail($key, &$message, $params) { case 'user_mail': case 'user_copy': $variables += array( - '!recipient-name' => $params['recipient']->name, + '!recipient-name' => format_username($params['recipient']), '!recipient-edit-url' => url('user/' . $params['recipient']->uid . '/edit', array('absolute' => TRUE, 'language' => $language)), ); $message['subject'] .= t('[!site-name] !subject', $variables, array('langcode' => $language->language)); diff --git a/modules/contact/contact.pages.inc b/modules/contact/contact.pages.inc index 72603a39dcb..f3827083d13 100644 --- a/modules/contact/contact.pages.inc +++ b/modules/contact/contact.pages.inc @@ -64,7 +64,7 @@ function contact_site_form($form, &$form_state) { '#type' => 'textfield', '#title' => t('Your name'), '#maxlength' => 255, - '#default_value' => $user->uid ? $user->name : '', + '#default_value' => $user->uid ? format_username($user) : '', '#required' => TRUE, ); $form['mail'] = array( @@ -180,7 +180,7 @@ function contact_personal_form($form, &$form_state, stdClass $recipient) { return drupal_access_denied(); } - drupal_set_title(t('Contact @username', array('@username' => $recipient->name)), PASS_THROUGH); + drupal_set_title(t('Contact @username', array('@username' => format_username($recipient))), PASS_THROUGH); if (!$user->uid) { $form['#attached']['library'][] = array('system', 'cookie'); @@ -196,7 +196,7 @@ function contact_personal_form($form, &$form_state, stdClass $recipient) { '#type' => 'textfield', '#title' => t('Your name'), '#maxlength' => 255, - '#default_value' => $user->uid ? $user->name : '', + '#default_value' => $user->uid ? format_username($user) : '', '#required' => TRUE, ); $form['mail'] = array( diff --git a/modules/openid/openid.pages.inc b/modules/openid/openid.pages.inc index 0091e33fa77..5fef3bf37f7 100644 --- a/modules/openid/openid.pages.inc +++ b/modules/openid/openid.pages.inc @@ -28,7 +28,7 @@ function openid_authentication_page() { * Menu callback; Manage OpenID identities for the specified user. */ function openid_user_identities($account) { - drupal_set_title($account->name); + drupal_set_title(format_username($account)); drupal_add_css(drupal_get_path('module', 'openid') . '/openid.css'); // Check to see if we got a response diff --git a/modules/php/php.module b/modules/php/php.module index c4e2dfb921c..4208acbd0bf 100644 --- a/modules/php/php.module +++ b/modules/php/php.module @@ -105,7 +105,7 @@ print t(\'Welcome visitor! Thank you for visiting.\');
 global $user;
 if ($user->uid) {
-  print t(\'Welcome @name! Thank you for visiting.\', array(\'@name\' => $user->name));
+  print t(\'Welcome @name! Thank you for visiting.\', array(\'@name\' => format_username($user)));
 }
 else {
   print t(\'Welcome visitor! Thank you for visiting.\');
diff --git a/modules/profile/profile.module b/modules/profile/profile.module
index be5c8392ea6..55b39802663 100644
--- a/modules/profile/profile.module
+++ b/modules/profile/profile.module
@@ -202,7 +202,7 @@ function profile_block_view($delta = '') {
     }
 
     if ($output) {
-      $block['subject'] = t('About %name', array('%name' => $account->name));
+      $block['subject'] = t('About %name', array('%name' => format_username($account)));
       $block['content'] = $output;
       return $block;
     }
diff --git a/modules/statistics/statistics.pages.inc b/modules/statistics/statistics.pages.inc
index c676a4f81fd..048b8fa090d 100644
--- a/modules/statistics/statistics.pages.inc
+++ b/modules/statistics/statistics.pages.inc
@@ -83,7 +83,7 @@ function statistics_user_tracker() {
       $rows[] = array(array('data' => t('No statistics available.'), 'colspan' => 3));
     }
 
-    drupal_set_title($account->name);
+    drupal_set_title(format_username($account));
     $build['statistics_table'] = array(
       '#theme' => 'table',
       '#header' => $header,
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index bcb9ec57245..36a06df8dd5 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -1165,7 +1165,7 @@ function hook_mail($key, &$message, $params) {
   $context = $params['context'];
   $variables = array(
     '%site_name' => variable_get('site_name', 'Drupal'),
-    '%username' => $account->name,
+    '%username' => format_username($account),
   );
   if ($context['hook'] == 'taxonomy') {
     $object = $params['object'];
@@ -2664,6 +2664,28 @@ function hook_url_outbound_alter(&$path, &$options, $original_path) {
   }
 }
 
+/**
+ * Alter the username that is displayed for a user.
+ *
+ * Called by format_username() to allow modules to alter the username that's
+ * displayed. Can be used to ensure user privacy in situations where
+ * $account->name is too revealing.
+ *
+ * @param &$name
+ *   The string that format_username() will return.
+ *
+ * @param $account
+ *   The account object passed to format_username().
+ *
+ * @see format_username()
+ */
+function hook_username_alter(&$name, $account) {
+  // Display the user's uid instead of name.
+  if (isset($account->uid)) {
+    $name = t('User !uid', array('!uid' => $account->uid));
+  }
+}
+
 /**
  * @} End of "addtogroup hooks".
  */
diff --git a/modules/toolbar/toolbar.module b/modules/toolbar/toolbar.module
index 4868e78e01b..55f89df12f2 100644
--- a/modules/toolbar/toolbar.module
+++ b/modules/toolbar/toolbar.module
@@ -101,7 +101,7 @@ function toolbar_build() {
   if ($user->uid) {
     $links = array(
       'account' => array(
-        'title' => t('Hello @username', array('@username' => $user->name)),
+        'title' => t('Hello @username', array('@username' => format_username($user))),
         'href' => 'user',
         'html' => TRUE,
       ),
diff --git a/modules/tracker/tracker.pages.inc b/modules/tracker/tracker.pages.inc
index e4cd1d16f1e..75faaa76bb9 100644
--- a/modules/tracker/tracker.pages.inc
+++ b/modules/tracker/tracker.pages.inc
@@ -19,7 +19,7 @@ function tracker_page($account = NULL, $set_title = FALSE) {
       // When viewed from user/%user/track, display the name of the user
       // as page title -- the tab title remains Track so this needs to be done
       // here and not in the menu definition.
-      drupal_set_title($account->name);
+      drupal_set_title(format_username($account));
     }
   }
   else {
diff --git a/modules/user/user.api.php b/modules/user/user.api.php
index aca316ac22e..c1e04ad53f9 100644
--- a/modules/user/user.api.php
+++ b/modules/user/user.api.php
@@ -320,7 +320,7 @@ function hook_user_view($account) {
     $account->content['summary']['blog'] =  array(
       '#type' => 'user_profile_item',
       '#title' => t('Blog'),
-      '#markup' => l(t('View recent blog entries'), "blog/$account->uid", array('attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => $account->name))))),
+      '#markup' => l(t('View recent blog entries'), "blog/$account->uid", array('attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => format_username($account)))))),
       '#attributes' => array('class' => array('blog')),
     );
   }
diff --git a/modules/user/user.module b/modules/user/user.module
index b35d102686a..d8a60d8d339 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -1251,7 +1251,7 @@ function template_preprocess_user_picture(&$variables) {
       $filepath = variable_get('user_picture_default', '');
     }
     if (isset($filepath)) {
-      $alt = t("@user's picture", array('@user' => $account->name ? $account->name : variable_get('anonymous', t('Anonymous'))));
+      $alt = t("@user's picture", array('@user' => format_username($account)));
       if (module_exists('image') && $style = variable_get('user_picture_style', '')) {
         $variables['user_picture'] = theme('image_style', array('style_name' => $style, 'path' => $filepath, 'alt' => $alt, 'title' => $alt, 'attributes' => array(), 'getsize' => FALSE));
       }
@@ -1632,7 +1632,7 @@ function user_uid_optional_to_arg($arg) {
  * Menu item title callback - use the user name.
  */
 function user_page_title($account) {
-  return $account->name;
+  return format_username($account);
 }
 
 /**
diff --git a/modules/user/user.test b/modules/user/user.test
index 1220e6c54b0..67d885722f3 100644
--- a/modules/user/user.test
+++ b/modules/user/user.test
@@ -701,7 +701,7 @@ class UserPictureTestCase extends DrupalWebTestCase {
         // user's profile page.
         $text = t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $test_dim));
         $this->assertRaw($text, t('Image was resized.'));
-        $alt = t("@user's picture", array('@user' => $this->user->name));
+        $alt = t("@user's picture", array('@user' => format_username($this->user)));
         $style = variable_get('user_picture_style', '');
         $this->assertRaw(image_style_url($style, $pic_path), t("Image is displayed in user's edit page"));