realname.module 8.56 KB
Newer Older
1
<?php
2

3 4 5 6
/**
 * @file
 * Provides token-based name displays for users.
 *
7
 * @todo Add a 'view realname' permission enabled by default
8 9 10 11 12 13 14
 * @todo Allow users to login with their real name
 * @todo Disable the username field
 */

/**
 * @defgroup realname Real name API
 */
15 16

/**
17
 * Implements hook_permission().
18
 */
19 20 21 22 23 24
function realname_permission() {
  $permissions['administer realname'] = array(
    'title' => t('Administer Real Name configuration.'),
  );
  return $permissions;
}
25

26
/**
27
 * Implements hook_menu().
28
 */
29 30 31
function realname_menu() {
  $items['admin/config/people/realname'] = array(
    'title' => 'Real name',
32
    'description' => 'Use tokens to configure how user names are displayed.',
33 34 35 36 37
    'page callback' => 'drupal_get_form',
    'page arguments' => array('realname_settings_form'),
    'access arguments' => array('administer realname'),
    'file' => 'realname.admin.inc',
  );
38

39 40 41 42 43 44
  $items['realname/autocomplete'] = array(
    'page callback' => 'realname_autocomplete',
    'access arguments' => array('access user profiles'),
    'type' => MENU_CALLBACK,
  );

45
  return $items;
46 47
}

48 49 50 51 52 53 54 55
/**
 * Implements hook_menu_alter().
 */
function realname_menu_alter(&$items) {
  // Implement realname autocomplete.
  $items['user/autocomplete']['page callback'] = 'realname_autocomplete';
}

56
/**
57
 * Implements hook_username_alter().
58
 */
59
function realname_username_alter(&$name, $account) {
60
  static $in_username_alter = FALSE;
61

62 63 64 65 66 67
  if (empty($account->uid)) {
    // Don't alter anonymous users or objects that do not have any user ID.
    return;
  }

  // Real name was loaded/generated via hook_user_load(), so re-use it.
68
  if (isset($account->realname)) {
69
    if (drupal_strlen($account->realname)) {
70
      // Only if the real name is a non-empty string is $name actually altered.
71 72
      $name = $account->realname;
    }
73
    return;
74
  }
75 76 77 78

  // Real name was not yet available for the account so we need to generate it.
  // Because tokens may call format_username() we need to prevent recursion.
  if (!$in_username_alter) {
79
    $in_username_alter = TRUE;
80 81

    // If $account->realname was undefined, then the user account object was
82 83 84
    // not properly loaded. We must enforce calling user_load().
    if ($realname_account = user_load($account->uid)) {
      realname_username_alter($name, $realname_account);
85
    }
86

87
    $in_username_alter = FALSE;
88
  }
89 90 91
}

/**
92
 * Implements hook_user_load().
93
 */
94 95 96 97
function realname_user_load(array $accounts) {
  $realnames = realname_load_multiple($accounts);
  foreach ($realnames as $uid => $realname) {
    $accounts[$uid]->realname = $realname;
98 99 100 101
  }
}

/**
102
 * Implements hook_user_update().
103
 */
104 105 106
function realname_user_update(array &$edit, stdClass $account, $category) {
  // Since user data may have changed, delete the existing realname.
  realname_delete($account->uid);
107 108
}

109 110 111 112 113 114
/**
 * Implements hook_user_delete().
 */
function realname_user_delete(stdClass $account) {
  realname_delete($account->uid);
}
115

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
/**
 * Implements hook_user_operations().
 */
function realname_user_operations() {
  $operations['realname_update'] = array(
    'label' => t('Update real name'),
    'callback' => 'realname_user_operations_realname_update',
  );
  return $operations;
}

/**
 * Callback function for admin mass generating user real names.
 *
 * @param $uids
 *   An array of user IDs.
 */
function realname_user_operations_realname_update(array $uids) {
  $accounts = user_load_multiple($uids);
  foreach ($accounts as $account) {
136
    realname_delete($account->uid);
137 138 139
  }
}

140 141 142 143 144 145 146 147 148 149 150 151 152
/**
 * Implements hook_action_info().
 */
function realname_action_info() {
  $info['realname_action_realname_update'] = array(
    'type' => 'user',
    'label' => t('Update real name'),
    'configurable' => FALSE,
  );

  return $info;
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
/**
 * Implements hook_rules_action_info().
 */
function realname_rules_action_info() {
  $items['user_realname_update'] = array(
    'label' => t('Update real name'),
    'base' => 'realname_action_realname_update',
    'parameter' => array(
      'account' => array(
        'type' => 'user',
        'label' => t('User'),
        'save' => FALSE,
      ),
    ),
    'group' => t('User'),
    'access callback' => 'rules_user_integration_access',
  );
  return $items;
}

173 174 175 176 177 178 179
/**
 * Action callback to update a user's realname.
 */
function realname_action_realname_update($account, $context = array()) {
  realname_delete($account->uid);
}

180 181 182 183 184 185 186
/**
 * Implements hook_views_api().
 */
function realname_views_api() {
  return array('api' => 3);
}

187 188 189 190 191
/**
 * @addtogroup realname
 * @{
 */

192
/**
193
 * Loads a real name.
194
 *
195 196
 * @param $uid
 *   A user account object.
197
 * @return
198
 *   The user's generated real name.
199
 */
200 201 202 203
function realname_load(stdClass $account) {
  $realnames = realname_load_multiple(array($account->uid => $account));
  return reset($realnames);
}
204

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
/**
 * Loads multiple real names.
 *
 * @param $accounts
 *   An array of user account objects keyed by user ID.
 * @return
 *   An array of real names keyed by user ID.
 */
function realname_load_multiple(array $accounts) {
  $realnames = &drupal_static(__FUNCTION__, array());

  if ($new_accounts = array_diff_key($accounts, $realnames)) {
    // Attempt to fetch realnames from the database first.
    $realnames += db_query("SELECT uid, realname FROM {realname} WHERE uid IN (:uids)", array(':uids' => array_keys($new_accounts)))->fetchAllKeyed();

    // For each account that was not present in the database, generate its
    // real name.
    foreach ($new_accounts as $uid => $account) {
223
      if (!isset($realnames[$uid])) {
224
        $realnames[$uid] = realname_update($account);
225
      }
226
    }
227 228
  }

229
  return array_intersect_key($realnames, $accounts);
230 231
}

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
/**
 * Update the realname for a user account.
 *
 * @param $account
 *   A user account object.
 *
 * @see hook_realname_pattern_alter()
 * @see hook_realname_alter()
 * @see hook_realname_update()
 */
function realname_update($account) {
  // Get the default pattern and allow other modules to alter it.
  $pattern = variable_get('realname_pattern', '[user:name-raw]');
  drupal_alter('realname_pattern', $pattern, $account);

  // Perform token replacement on the real name pattern.
248
  $realname = token_replace($pattern, array('user' => $account), array('clear' => TRUE, 'sanitize' => FALSE));
249 250 251 252 253 254

  // Remove any HTML tags.
  $realname = strip_tags(decode_entities($realname));

  // Remove double spaces (if a token had no value).
  $realname = preg_replace('/ {2,}/', ' ', $realname);
255 256

  // The name must be trimmed to 255 characters before inserting into the database.
257
  $realname = truncate_utf8(trim($realname), 255);
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277

  // Allow other modules to alter the generated realname.
  drupal_alter('realname', $realname, $account);

  // Save to the database and the static cache.
  db_merge('realname')
    ->key(array('uid' => $account->uid))
    ->fields(array(
      'realname' => $realname,
      'created' => REQUEST_TIME,
    ))
    ->execute();

  // Allow modules to react to the realname being updated.
  module_invoke_all('realname_update', $realname, $account);

  $account->realname = $realname;
  return $realname;
}

278
/**
279
 * Delete a real name.
280
 *
281 282
 * @param $uid
 *   A user ID.
283
 */
284 285
function realname_delete($uid) {
  return realname_delete_multiple(array($uid));
286 287 288
}

/**
289 290 291 292
 * Delete multiple real names.
 *
 * @param $uids
 *   An array of user IDs.
293
 */
294 295 296
function realname_delete_multiple(array $uids) {
  db_delete('realname')->condition('uid', $uids, 'IN')->execute();
  drupal_static_reset('realname_load_multiple');
297
  entity_get_controller('user')->resetCache($uids);
298 299 300
}

/**
301
 * Delete all real names.
302
 */
303 304 305
function realname_delete_all() {
  db_delete('realname')->execute();
  drupal_static_reset('realname_load_multiple');
306
  entity_get_controller('user')->resetCache();
307
}
308 309 310 311

/**
 * @} End of "addtogroup realname".
 */
312 313 314 315 316 317 318 319 320 321 322 323

/**
 * Menu callback; Retrieve a JSON object containing autocomplete suggestions
 * for existing users based on their generated real names.
 */
function realname_autocomplete($string = '') {
  $matches = array();

  if ($string) {
    $query = db_select('users', 'u');
    $query->leftJoin('realname', 'rn', 'u.uid = rn.uid');
    $query->fields('u', array('uid'));
324 325 326 327
    $name_like = db_or();
    $name_like->condition('rn.realname', db_like($string) . '%', 'LIKE');
    $name_like->condition('u.name', db_like($string) . '%', 'LIKE');
    $query->condition($name_like);
328 329 330 331 332 333
    $query->range(0, 10);
    $uids = $query->execute()->fetchCol();
    $accounts = user_load_multiple($uids);

    foreach ($accounts as $account) {
      $matches[$account->name] = format_username($account);
334 335 336
      if ($matches[$account->name] != $account->name) {
        $matches[$account->name] .= ' (' . check_plain($account->name) . ')';
      }
337 338 339 340 341
    }
  }

  drupal_json_output($matches);
}