First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,12 @@
name = "User module form tests"
description = "Support module for user form testing."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,82 @@
<?php
/**
* @file
* Dummy module implementing a form to test user password validation
*/
/**
* Implements hook_menu().
*
* Sets up a form that allows a user to validate password.
*/
function user_form_test_menu() {
$items = array();
$items['user_form_test_current_password/%user'] = array(
'title' => 'User form test for current password validation',
'page callback' => 'drupal_get_form',
'page arguments' => array('user_form_test_current_password',1),
'access arguments' => array('administer users'),
'type' => MENU_SUGGESTED_ITEM,
);
return $items;
}
/**
* A test form for user_validate_current_pass().
*/
function user_form_test_current_password($form, &$form_state, $account) {
$account->user_form_test_field = '';
$form['#user'] = $account;
$form['user_form_test_field'] = array(
'#type' => 'textfield',
'#title' => t('Test field'),
'#description' => t('A field that would require a correct password to change.'),
'#required' => TRUE,
);
$form['current_pass'] = array(
'#type' => 'password',
'#title' => t('Current password'),
'#size' => 25,
'#description' => t('Enter your current password'),
);
$form['current_pass_required_values'] = array(
'#type' => 'value',
'#value' => array('user_form_test_field' => t('Test field')),
);
$form['#validate'][] = 'user_validate_current_pass';
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Test'),
);
return $form;
}
/**
* Submit function for the test form for user_validate_current_pass().
*/
function user_form_test_current_password_submit($form, &$form_state) {
drupal_set_message(t('The password has been validated and the form submitted successfully.'));
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function user_form_test_form_user_profile_form_alter(&$form, &$form_state) {
if (variable_get('user_form_test_user_profile_form_rebuild', FALSE)) {
$form['#submit'][] = 'user_form_test_user_account_submit';
}
}
/**
* Submit function for user_profile_form().
*/
function user_form_test_user_account_submit($form, &$form_state) {
// Rebuild the form instead of letting the process end. This allows us to
// test for bugs that can be triggered in contributed modules.
$form_state['rebuild'] = TRUE;
}

View file

@ -0,0 +1,23 @@
<?php
/**
* @file
* Default theme implementation to present a picture configured for the
* user's account.
*
* Available variables:
* - $user_picture: Image set by the user or the site's default. Will be linked
* depending on the viewer's permission to view the user's profile page.
* - $account: Array of account information. Potentially unsafe. Be sure to
* check_plain() before use.
*
* @see template_preprocess_user_picture()
*
* @ingroup themeable
*/
?>
<?php if ($user_picture): ?>
<div class="<?php print $classes; ?>">
<?php print $user_picture; ?>
</div>
<?php endif; ?>

View file

@ -0,0 +1,33 @@
<?php
/**
* @file
* Default theme implementation to present profile categories (groups of
* profile items).
*
* Categories are defined when configuring user profile fields for the site.
* It can also be defined by modules. All profile items for a category will be
* output through the $profile_items variable.
*
* @see user-profile-item.tpl.php
* where each profile item is rendered. It is implemented as a definition
* list by default.
* @see user-profile.tpl.php
* where all items and categories are collected and printed out.
*
* Available variables:
* - $title: Category title for the group of items.
* - $profile_items: All the items for the group rendered through
* user-profile-item.tpl.php.
* - $attributes: HTML attributes. Usually renders classes.
*
* @see template_preprocess_user_profile_category()
*/
?>
<?php if ($title): ?>
<h3><?php print $title; ?></h3>
<?php endif; ?>
<dl<?php print $attributes; ?>>
<?php print $profile_items; ?>
</dl>

View file

@ -0,0 +1,26 @@
<?php
/**
* @file
* Default theme implementation to present profile items (values from user
* account profile fields or modules).
*
* This template is used to loop through and render each field configured
* for the user's account. It can also be the data from modules. The output is
* grouped by categories.
*
* @see user-profile-category.tpl.php
* for the parent markup. Implemented as a definition list by default.
* @see user-profile.tpl.php
* where all items and categories are collected and printed out.
*
* Available variables:
* - $title: Field title for the profile item.
* - $value: User defined value for the profile item or data from a module.
* - $attributes: HTML attributes. Usually renders classes.
*
* @see template_preprocess_user_profile_item()
*/
?>
<dt<?php print $attributes; ?>><?php print $title; ?></dt>
<dd<?php print $attributes; ?>><?php print $value; ?></dd>

View file

@ -0,0 +1,39 @@
<?php
/**
* @file
* Default theme implementation to present all user profile data.
*
* This template is used when viewing a registered member's profile page,
* e.g., example.com/user/123. 123 being the users ID.
*
* Use render($user_profile) to print all profile items, or print a subset
* such as render($user_profile['user_picture']). Always call
* render($user_profile) at the end in order to print all remaining items. If
* the item is a category, it will contain all its profile items. By default,
* $user_profile['summary'] is provided, which contains data on the user's
* history. Other data can be included by modules. $user_profile['user_picture']
* is available for showing the account picture.
*
* Available variables:
* - $user_profile: An array of profile items. Use render() to print them.
* - Field variables: for each field instance attached to the user a
* corresponding variable is defined; e.g., $account->field_example has a
* variable $field_example defined. When needing to access a field's raw
* values, developers/themers are strongly encouraged to use these
* variables. Otherwise they will have to explicitly specify the desired
* field language, e.g. $account->field_example['en'], thus overriding any
* language negotiation rule that was previously applied.
*
* @see user-profile-category.tpl.php
* Where the html is handled for the group.
* @see user-profile-item.tpl.php
* Where the html is handled for each item in the group.
* @see template_preprocess_user_profile()
*
* @ingroup themeable
*/
?>
<div class="profile"<?php print $attributes; ?>>
<?php print render($user_profile); ?>
</div>

34
modules/user/user-rtl.css Normal file
View file

@ -0,0 +1,34 @@
#permissions td.permission {
padding-left: 0;
padding-right: 1.5em;
}
#user-admin-roles .form-item-name {
float: right;
margin-left: 1em;
margin-right: 0;
}
/**
* Password strength indicator.
*/
.password-strength {
float: left;
}
.password-strength-text {
float: left;
}
div.password-confirm {
float: left;
}
.confirm-parent,
.password-parent {
clear: right;
}
/* Generated by user.module but used by profile.module: */
.profile .user-picture {
float: left;
margin: 0 0 1em 1em;
}

1053
modules/user/user.admin.inc Normal file

File diff suppressed because it is too large Load diff

477
modules/user/user.api.php Normal file
View file

@ -0,0 +1,477 @@
<?php
/**
* @file
* Hooks provided by the User module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Act on user objects when loaded from the database.
*
* Due to the static cache in user_load_multiple() you should not use this
* hook to modify the user properties returned by the {users} table itself
* since this may result in unreliable results when loading from cache.
*
* @param $users
* An array of user objects, indexed by uid.
*
* @see user_load_multiple()
* @see profile_user_load()
*/
function hook_user_load($users) {
$result = db_query('SELECT uid, foo FROM {my_table} WHERE uid IN (:uids)', array(':uids' => array_keys($users)));
foreach ($result as $record) {
$users[$record->uid]->foo = $record->foo;
}
}
/**
* Respond to user deletion.
*
* This hook is invoked from user_delete_multiple() before field_attach_delete()
* is called and before users are actually removed from the database.
*
* Modules should additionally implement hook_user_cancel() to process stored
* user data for other account cancellation methods.
*
* @param $account
* The account that is being deleted.
*
* @see user_delete_multiple()
*/
function hook_user_delete($account) {
db_delete('mytable')
->condition('uid', $account->uid)
->execute();
}
/**
* Act on user account cancellations.
*
* This hook is invoked from user_cancel() before a user account is canceled.
* Depending on the account cancellation method, the module should either do
* nothing, unpublish content, or anonymize content. See user_cancel_methods()
* for the list of default account cancellation methods provided by User module.
* Modules may add further methods via hook_user_cancel_methods_alter().
*
* This hook is NOT invoked for the 'user_cancel_delete' account cancellation
* method. To react on this method, implement hook_user_delete() instead.
*
* Expensive operations should be added to the global account cancellation batch
* by using batch_set().
*
* @param $edit
* The array of form values submitted by the user.
* @param $account
* The user object on which the operation is being performed.
* @param $method
* The account cancellation method.
*
* @see user_cancel_methods()
* @see hook_user_cancel_methods_alter()
*/
function hook_user_cancel($edit, $account, $method) {
switch ($method) {
case 'user_cancel_block_unpublish':
// Unpublish nodes (current revisions).
module_load_include('inc', 'node', 'node.admin');
$nodes = db_select('node', 'n')
->fields('n', array('nid'))
->condition('uid', $account->uid)
->execute()
->fetchCol();
node_mass_update($nodes, array('status' => 0));
break;
case 'user_cancel_reassign':
// Anonymize nodes (current revisions).
module_load_include('inc', 'node', 'node.admin');
$nodes = db_select('node', 'n')
->fields('n', array('nid'))
->condition('uid', $account->uid)
->execute()
->fetchCol();
node_mass_update($nodes, array('uid' => 0));
// Anonymize old revisions.
db_update('node_revision')
->fields(array('uid' => 0))
->condition('uid', $account->uid)
->execute();
// Clean history.
db_delete('history')
->condition('uid', $account->uid)
->execute();
break;
}
}
/**
* Modify account cancellation methods.
*
* By implementing this hook, modules are able to add, customize, or remove
* account cancellation methods. All defined methods are turned into radio
* button form elements by user_cancel_methods() after this hook is invoked.
* The following properties can be defined for each method:
* - title: The radio button's title.
* - description: (optional) A description to display on the confirmation form
* if the user is not allowed to select the account cancellation method. The
* description is NOT used for the radio button, but instead should provide
* additional explanation to the user seeking to cancel their account.
* - access: (optional) A boolean value indicating whether the user can access
* a method. If access is defined, the method cannot be configured as the
* default method.
*
* @param $methods
* An array containing user account cancellation methods, keyed by method id.
*
* @see user_cancel_methods()
* @see user_cancel_confirm_form()
*/
function hook_user_cancel_methods_alter(&$methods) {
// Limit access to disable account and unpublish content method.
$methods['user_cancel_block_unpublish']['access'] = user_access('administer site configuration');
// Remove the content re-assigning method.
unset($methods['user_cancel_reassign']);
// Add a custom zero-out method.
$methods['mymodule_zero_out'] = array(
'title' => t('Delete the account and remove all content.'),
'description' => t('All your content will be replaced by empty strings.'),
// access should be used for administrative methods only.
'access' => user_access('access zero-out account cancellation method'),
);
}
/**
* Add mass user operations.
*
* This hook enables modules to inject custom operations into the mass operations
* dropdown found at admin/people, by associating a callback function with
* the operation, which is called when the form is submitted. The callback function
* receives one initial argument, which is an array of the checked users.
*
* @return
* An array of operations. Each operation is an associative array that may
* contain the following key-value pairs:
* - "label": Required. The label for the operation, displayed in the dropdown menu.
* - "callback": Required. The function to call for the operation.
* - "callback arguments": Optional. An array of additional arguments to pass to
* the callback function.
*
*/
function hook_user_operations() {
$operations = array(
'unblock' => array(
'label' => t('Unblock the selected users'),
'callback' => 'user_user_operations_unblock',
),
'block' => array(
'label' => t('Block the selected users'),
'callback' => 'user_user_operations_block',
),
'cancel' => array(
'label' => t('Cancel the selected user accounts'),
),
);
return $operations;
}
/**
* Define a list of user settings or profile information categories.
*
* There are two steps to using hook_user_categories():
* - Create the category with hook_user_categories().
* - Display that category on the form ID of "user_profile_form" with
* hook_form_FORM_ID_alter().
*
* Step one builds out the category but it won't be visible on your form until
* you explicitly tell it to do so.
*
* The function in step two should contain the following code in order to
* display your new category:
* @code
* if ($form['#user_category'] == 'mycategory') {
* // Return your form here.
* }
* @endcode
*
* @return
* An array of associative arrays. Each inner array has elements:
* - "name": The internal name of the category.
* - "title": The human-readable, localized name of the category.
* - "weight": An integer specifying the category's sort ordering.
* - "access callback": Name of the access callback function to use to
* determine whether the user can edit the category. Defaults to using
* user_edit_access(). See hook_menu() for more information on access
* callbacks.
* - "access arguments": Arguments for the access callback function. Defaults
* to array(1).
*/
function hook_user_categories() {
return array(array(
'name' => 'account',
'title' => t('Account settings'),
'weight' => 1,
));
}
/**
* A user account is about to be created or updated.
*
* This hook is primarily intended for modules that want to store properties in
* the serialized {users}.data column, which is automatically loaded whenever a
* user account object is loaded, modules may add to $edit['data'] in order
* to have their data serialized on save.
*
* @param $edit
* The array of form values submitted by the user. Assign values to this
* array to save changes in the database.
* @param $account
* The user object on which the operation is performed. Values assigned in
* this object will not be saved in the database.
* @param $category
* The active category of user information being edited.
*
* @see hook_user_insert()
* @see hook_user_update()
*/
function hook_user_presave(&$edit, $account, $category) {
// Make sure that our form value 'mymodule_foo' is stored as
// 'mymodule_bar' in the 'data' (serialized) column.
if (isset($edit['mymodule_foo'])) {
$edit['data']['mymodule_bar'] = $edit['mymodule_foo'];
}
}
/**
* A user account was created.
*
* The module should save its custom additions to the user object into the
* database.
*
* @param $edit
* The array of form values submitted by the user.
* @param $account
* The user object on which the operation is being performed.
* @param $category
* The active category of user information being edited.
*
* @see hook_user_presave()
* @see hook_user_update()
*/
function hook_user_insert(&$edit, $account, $category) {
db_insert('mytable')
->fields(array(
'myfield' => $edit['myfield'],
'uid' => $account->uid,
))
->execute();
}
/**
* A user account was updated.
*
* Modules may use this hook to update their user data in a custom storage
* after a user account has been updated.
*
* @param $edit
* The array of form values submitted by the user.
* @param $account
* The user object on which the operation is performed.
* @param $category
* The active category of user information being edited.
*
* @see hook_user_presave()
* @see hook_user_insert()
*/
function hook_user_update(&$edit, $account, $category) {
db_insert('user_changes')
->fields(array(
'uid' => $account->uid,
'changed' => time(),
))
->execute();
}
/**
* The user just logged in.
*
* @param $edit
* The array of form values submitted by the user.
* @param $account
* The user object on which the operation was just performed.
*/
function hook_user_login(&$edit, $account) {
// If the user has a NULL time zone, notify them to set a time zone.
if (!$account->timezone && variable_get('configurable_timezones', 1) && variable_get('empty_timezone_message', 0)) {
drupal_set_message(t('Configure your <a href="@user-edit">account time zone setting</a>.', array('@user-edit' => url("user/$account->uid/edit", array('query' => drupal_get_destination(), 'fragment' => 'edit-timezone')))));
}
}
/**
* The user just logged out.
*
* Note that when this hook is invoked, the changes have not yet been written to
* the database, because a database transaction is still in progress. The
* transaction is not finalized until the save operation is entirely completed
* and user_save() goes out of scope. You should not rely on data in the
* database at this time as it is not updated yet. You should also note that any
* write/update database queries executed from this hook are also not committed
* immediately. Check user_save() and db_transaction() for more info.
*
* @param $account
* The user object on which the operation was just performed.
*/
function hook_user_logout($account) {
db_insert('logouts')
->fields(array(
'uid' => $account->uid,
'time' => time(),
))
->execute();
}
/**
* The user's account information is being displayed.
*
* The module should format its custom additions for display and add them to the
* $account->content array.
*
* @param $account
* The user object on which the operation is being performed.
* @param $view_mode
* View mode, e.g. 'full'.
* @param $langcode
* The language code used for rendering.
*
* @see hook_user_view_alter()
* @see hook_entity_view()
*/
function hook_user_view($account, $view_mode, $langcode) {
if (user_access('create blog content', $account)) {
$account->content['summary']['blog'] = array(
'#type' => 'user_profile_item',
'#title' => t('Blog'),
'#markup' => l(t('View recent blog entries'), "blog/$account->uid", array('attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => format_username($account)))))),
'#attributes' => array('class' => array('blog')),
);
}
}
/**
* The user was built; the module may modify the structured content.
*
* This hook is called after the content has been assembled in a structured array
* and may be used for doing processing which requires that the complete user
* content structure has been built.
*
* If the module wishes to act on the rendered HTML of the user rather than the
* structured content array, it may use this hook to add a #post_render callback.
* Alternatively, it could also implement hook_preprocess_user_profile(). See
* drupal_render() and theme() documentation respectively for details.
*
* @param $build
* A renderable array representing the user.
*
* @see user_view()
* @see hook_entity_view_alter()
*/
function hook_user_view_alter(&$build) {
// Check for the existence of a field added by another module.
if (isset($build['an_additional_field'])) {
// Change its weight.
$build['an_additional_field']['#weight'] = -10;
}
// Add a #post_render callback to act on the rendered HTML of the user.
$build['#post_render'][] = 'my_module_user_post_render';
}
/**
* Act on a user role being inserted or updated.
*
* Modules implementing this hook can act on the user role object before
* it has been saved to the database.
*
* @param $role
* A user role object.
*
* @see hook_user_role_insert()
* @see hook_user_role_update()
*/
function hook_user_role_presave($role) {
// Set a UUID for the user role if it doesn't already exist
if (empty($role->uuid)) {
$role->uuid = uuid_uuid();
}
}
/**
* Respond to creation of a new user role.
*
* Modules implementing this hook can act on the user role object when saved to
* the database. It's recommended that you implement this hook if your module
* adds additional data to user roles object. The module should save its custom
* additions to the database.
*
* @param $role
* A user role object.
*/
function hook_user_role_insert($role) {
// Save extra fields provided by the module to user roles.
db_insert('my_module_table')
->fields(array(
'rid' => $role->rid,
'role_description' => $role->description,
))
->execute();
}
/**
* Respond to updates to a user role.
*
* Modules implementing this hook can act on the user role object when updated.
* It's recommended that you implement this hook if your module adds additional
* data to user roles object. The module should save its custom additions to
* the database.
*
* @param $role
* A user role object.
*/
function hook_user_role_update($role) {
// Save extra fields provided by the module to user roles.
db_merge('my_module_table')
->key(array('rid' => $role->rid))
->fields(array(
'role_description' => $role->description
))
->execute();
}
/**
* Respond to user role deletion.
*
* This hook allows you act when a user role has been deleted.
* If your module stores references to roles, it's recommended that you
* implement this hook and delete existing instances of the deleted role
* in your module database tables.
*
* @param $role
* The $role object being deleted.
*/
function hook_user_role_delete($role) {
// Delete existing instances of the deleted role.
db_delete('my_module_table')
->condition('rid', $role->rid)
->execute();
}
/**
* @} End of "addtogroup hooks".
*/

102
modules/user/user.css Normal file
View file

@ -0,0 +1,102 @@
#permissions td.module {
font-weight: bold;
}
#permissions td.permission {
padding-left: 1.5em; /* LTR */
}
#permissions tr.odd .form-item,
#permissions tr.even .form-item {
white-space: normal;
}
#user-admin-settings fieldset .fieldset-description {
font-size: 0.85em;
padding-bottom: .5em;
}
/**
* Override default textfield float to put the "Add role" button next to
* the input textfield.
*/
#user-admin-roles td.edit-name {
clear: both;
}
#user-admin-roles .form-item-name {
float: left; /* LTR */
margin-right: 1em; /* LTR */
}
/**
* Password strength indicator.
*/
.password-strength {
width: 17em;
float: right; /* LTR */
margin-top: 1.4em;
}
.password-strength-title {
display: inline;
}
.password-strength-text {
float: right; /* LTR */
font-weight: bold;
}
.password-indicator {
background-color: #C4C4C4;
height: 0.3em;
width: 100%;
}
.password-indicator div {
height: 100%;
width: 0%;
background-color: #47C965;
}
input.password-confirm,
input.password-field {
width: 16em;
margin-bottom: 0.4em;
}
div.password-confirm {
float: right; /* LTR */
margin-top: 1.5em;
visibility: hidden;
width: 17em;
}
div.form-item div.password-suggestions {
padding: 0.2em 0.5em;
margin: 0.7em 0;
width: 38.5em;
border: 1px solid #B4B4B4;
}
div.password-suggestions ul {
margin-bottom: 0;
}
.confirm-parent,
.password-parent {
clear: left; /* LTR */
margin: 0;
width: 36.3em;
}
/* Generated by user.module but used by profile.module: */
.profile {
clear: both;
margin: 1em 0;
}
.profile .user-picture {
float: right; /* LTR */
margin: 0 1em 1em 0; /* LTR */
}
.profile h3 {
border-bottom: 1px solid #ccc;
}
.profile dl {
margin: 0 0 1.5em 0;
}
.profile dt {
margin: 0 0 0.2em 0;
font-weight: bold;
}
.profile dd {
margin: 0 0 1em 0;
}

16
modules/user/user.info Normal file
View file

@ -0,0 +1,16 @@
name = User
description = Manages the user registration and login system.
package = Core
version = VERSION
core = 7.x
files[] = user.module
files[] = user.test
required = TRUE
configure = admin/config/people
stylesheets[all][] = user.css
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

927
modules/user/user.install Normal file
View file

@ -0,0 +1,927 @@
<?php
/**
* @file
* Install, update and uninstall functions for the user module.
*/
/**
* Implements hook_schema().
*/
function user_schema() {
$schema['authmap'] = array(
'description' => 'Stores distributed authentication mapping.',
'fields' => array(
'aid' => array(
'description' => 'Primary Key: Unique authmap ID.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'uid' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => "User's {users}.uid.",
),
'authname' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => '',
'description' => 'Unique authentication name.',
),
'module' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => '',
'description' => 'Module which is controlling the authentication.',
),
),
'unique keys' => array(
'authname' => array('authname'),
),
'primary key' => array('aid'),
'foreign keys' => array(
'user' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
),
'indexes' => array(
'uid_module' => array('uid', 'module'),
),
);
$schema['role_permission'] = array(
'description' => 'Stores the permissions assigned to user roles.',
'fields' => array(
'rid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'Foreign Key: {role}.rid.',
),
'permission' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => '',
'description' => 'A single permission granted to the role identified by rid.',
),
'module' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => "The module declaring the permission.",
),
),
'primary key' => array('rid', 'permission'),
'indexes' => array(
'permission' => array('permission'),
),
'foreign keys' => array(
'role' => array(
'table' => 'role',
'columns' => array('rid' => 'rid'),
),
),
);
$schema['role'] = array(
'description' => 'Stores user roles.',
'fields' => array(
'rid' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'Primary Key: Unique role ID.',
),
'name' => array(
'type' => 'varchar',
'length' => 64,
'not null' => TRUE,
'default' => '',
'description' => 'Unique role name.',
'translatable' => TRUE,
),
'weight' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The weight of this role in listings and the user interface.',
),
),
'unique keys' => array(
'name' => array('name'),
),
'primary key' => array('rid'),
'indexes' => array(
'name_weight' => array('name', 'weight'),
),
);
// The table name here is plural, despite Drupal table naming standards,
// because "user" is a reserved word in many databases.
$schema['users'] = array(
'description' => 'Stores user data.',
'fields' => array(
'uid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'Primary Key: Unique user ID.',
'default' => 0,
),
'name' => array(
'type' => 'varchar',
'length' => 60,
'not null' => TRUE,
'default' => '',
'description' => 'Unique user name.',
),
'pass' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => '',
'description' => "User's password (hashed).",
),
'mail' => array(
'type' => 'varchar',
'length' => 254,
'not null' => FALSE,
'default' => '',
'description' => "User's e-mail address.",
),
'theme' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => "User's default theme.",
),
'signature' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => "User's signature.",
),
'signature_format' => array(
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'description' => 'The {filter_format}.format of the signature.',
),
'created' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Timestamp for when user was created.',
),
'access' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Timestamp for previous time user accessed the site.',
),
'login' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => "Timestamp for user's last login.",
),
'status' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
'description' => 'Whether the user is active(1) or blocked(0).',
),
'timezone' => array(
'type' => 'varchar',
'length' => 32,
'not null' => FALSE,
'description' => "User's time zone.",
),
'language' => array(
'type' => 'varchar',
'length' => 12,
'not null' => TRUE,
'default' => '',
'description' => "User's default language.",
),
'picture' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => "Foreign key: {file_managed}.fid of user's picture.",
),
'init' => array(
'type' => 'varchar',
'length' => 254,
'not null' => FALSE,
'default' => '',
'description' => 'E-mail address used for initial account creation.',
),
'data' => array(
'type' => 'blob',
'not null' => FALSE,
'size' => 'big',
'serialize' => TRUE,
'description' => 'A serialized array of name value pairs that are related to the user. Any form values posted during user edit are stored and are loaded into the $user object during user_load(). Use of this field is discouraged and it will likely disappear in a future version of Drupal.',
),
),
'indexes' => array(
'access' => array('access'),
'created' => array('created'),
'mail' => array('mail'),
'picture' => array('picture'),
),
'unique keys' => array(
'name' => array('name'),
),
'primary key' => array('uid'),
'foreign keys' => array(
'signature_format' => array(
'table' => 'filter_format',
'columns' => array('signature_format' => 'format'),
),
),
);
$schema['users_roles'] = array(
'description' => 'Maps users to roles.',
'fields' => array(
'uid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'Primary Key: {users}.uid for user.',
),
'rid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'Primary Key: {role}.rid for role.',
),
),
'primary key' => array('uid', 'rid'),
'indexes' => array(
'rid' => array('rid'),
),
'foreign keys' => array(
'user' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
'role' => array(
'table' => 'role',
'columns' => array('rid' => 'rid'),
),
),
);
return $schema;
}
/**
* Implements hook_install().
*/
function user_install() {
// Insert a row for the anonymous user.
db_insert('users')
->fields(array(
'uid' => 0,
'name' => '',
'mail' => '',
))
->execute();
// We need some placeholders here as name and mail are uniques and data is
// presumed to be a serialized array. This will be changed by the settings
// form in the installer.
db_insert('users')
->fields(array(
'uid' => 1,
'name' => 'placeholder-for-uid-1',
'mail' => 'placeholder-for-uid-1',
'created' => REQUEST_TIME,
'status' => 1,
'data' => NULL,
))
->execute();
// Built-in roles.
$rid_anonymous = db_insert('role')
->fields(array('name' => 'anonymous user', 'weight' => 0))
->execute();
$rid_authenticated = db_insert('role')
->fields(array('name' => 'authenticated user', 'weight' => 1))
->execute();
// Sanity check to ensure the anonymous and authenticated role IDs are the
// same as the drupal defined constants. In certain situations, this will
// not be true.
if ($rid_anonymous != DRUPAL_ANONYMOUS_RID) {
db_update('role')
->fields(array('rid' => DRUPAL_ANONYMOUS_RID))
->condition('rid', $rid_anonymous)
->execute();
}
if ($rid_authenticated != DRUPAL_AUTHENTICATED_RID) {
db_update('role')
->fields(array('rid' => DRUPAL_AUTHENTICATED_RID))
->condition('rid', $rid_authenticated)
->execute();
}
}
/**
* Implements hook_update_dependencies().
*/
function user_update_dependencies() {
// user_update_7006() updates data in the {role_permission} table, so it must
// run after system_update_7007(), which populates that table.
$dependencies['user'][7006] = array(
'system' => 7007,
);
// user_update_7010() needs to query the {filter_format} table to get a list
// of existing text formats, so it must run after filter_update_7000(), which
// creates that table.
$dependencies['user'][7010] = array(
'filter' => 7000,
);
// user_update_7012() uses the file API and inserts records into the
// {file_managed} table, so it therefore must run after system_update_7061(),
// which inserts files with specific IDs into the table and therefore relies
// on the table being empty (otherwise it would accidentally overwrite
// existing records).
$dependencies['user'][7012] = array(
'system' => 7061,
);
// user_update_7013() uses the file usage API, which relies on the
// {file_usage} table, so it must run after system_update_7059(), which
// creates that table.
$dependencies['user'][7013] = array(
'system' => 7059,
);
return $dependencies;
}
/**
* Utility function: grant a set of permissions to a role during update.
*
* This function is valid for a database schema version 7000.
*
* @param $rid
* The role ID.
* @param $permissions
* An array of permissions names.
* @param $module
* The name of the module defining the permissions.
* @ingroup update_api
*/
function _update_7000_user_role_grant_permissions($rid, array $permissions, $module) {
// Grant new permissions for the role.
foreach ($permissions as $name) {
db_merge('role_permission')
->key(array(
'rid' => $rid,
'permission' => $name,
))
->fields(array(
'module' => $module,
))
->execute();
}
}
/**
* @addtogroup updates-6.x-to-7.x
* @{
*/
/**
* Increase the length of the password field to accommodate better hashes.
*
* Also re-hashes all current passwords to improve security. This may be a
* lengthy process, and is performed batch-wise.
*/
function user_update_7000(&$sandbox) {
$sandbox['#finished'] = 0;
// Lower than DRUPAL_HASH_COUNT to make the update run at a reasonable speed.
$hash_count_log2 = 11;
// Multi-part update.
if (!isset($sandbox['user_from'])) {
db_change_field('users', 'pass', 'pass', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''));
$sandbox['user_from'] = 0;
$sandbox['user_count'] = db_query("SELECT COUNT(uid) FROM {users}")->fetchField();
}
else {
require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
// Hash again all current hashed passwords.
$has_rows = FALSE;
// Update this many per page load.
$count = 1000;
$result = db_query_range("SELECT uid, pass FROM {users} WHERE uid > 0 ORDER BY uid", $sandbox['user_from'], $count);
foreach ($result as $account) {
$has_rows = TRUE;
// If the $account->pass value is not a MD5 hash (a 32 character
// hexadecimal string) then skip it.
if (!preg_match('/^[0-9a-f]{32}$/', $account->pass)) {
continue;
}
$new_hash = user_hash_password($account->pass, $hash_count_log2);
if ($new_hash) {
// Indicate an updated password.
$new_hash = 'U' . $new_hash;
db_update('users')
->fields(array('pass' => $new_hash))
->condition('uid', $account->uid)
->execute();
}
}
$sandbox['#finished'] = $sandbox['user_from']/$sandbox['user_count'];
$sandbox['user_from'] += $count;
if (!$has_rows) {
$sandbox['#finished'] = 1;
return t('User passwords rehashed to improve security');
}
}
}
/**
* Remove the 'threshold', 'mode' and 'sort' columns from the {users} table.
*
* These fields were previously used to store per-user comment settings.
*/
function user_update_7001() {
db_drop_field('users', 'threshold');
db_drop_field('users', 'mode');
db_drop_field('users', 'sort');
}
/**
* Convert user time zones from time zone offsets to time zone names.
*/
function user_update_7002(&$sandbox) {
$sandbox['#finished'] = 0;
// Multi-part update.
if (!isset($sandbox['user_from'])) {
db_change_field('users', 'timezone', 'timezone', array('type' => 'varchar', 'length' => 32, 'not null' => FALSE));
$sandbox['user_from'] = 0;
$sandbox['user_count'] = db_query("SELECT COUNT(uid) FROM {users}")->fetchField();
$sandbox['user_not_migrated'] = 0;
}
else {
$timezones = system_time_zones();
// Update this many per page load.
$count = 10000;
$contributed_date_module = db_field_exists('users', 'timezone_name');
$contributed_event_module = db_field_exists('users', 'timezone_id');
$results = db_query_range("SELECT uid FROM {users} ORDER BY uid", $sandbox['user_from'], $count);
foreach ($results as $account) {
$timezone = NULL;
// If the contributed Date module has created a users.timezone_name
// column, use this data to set each user's time zone.
if ($contributed_date_module) {
$date_timezone = db_query("SELECT timezone_name FROM {users} WHERE uid = :uid", array(':uid' => $account->uid))->fetchField();
if (isset($timezones[$date_timezone])) {
$timezone = $date_timezone;
}
}
// If the contributed Event module has stored user time zone information
// use that information to update the user accounts.
if (!$timezone && $contributed_event_module) {
try {
$event_timezone = db_query("SELECT t.name FROM {users} u LEFT JOIN {event_timezones} t ON u.timezone_id = t.timezone WHERE u.uid = :uid", array(':uid' => $account->uid))->fetchField();
$event_timezone = str_replace(' ', '_', $event_timezone);
if (isset($timezones[$event_timezone])) {
$timezone = $event_timezone;
}
}
catch (PDOException $e) {
// Ignore error if event_timezones table does not exist or unexpected
// schema found.
}
}
if ($timezone) {
db_update('users')
->fields(array('timezone' => $timezone))
->condition('uid', $account->uid)
->execute();
}
else {
$sandbox['user_not_migrated']++;
db_update('users')
->fields(array('timezone' => NULL))
->condition('uid', $account->uid)
->execute();
}
$sandbox['user_from']++;
}
$sandbox['#finished'] = $sandbox['user_from'] / $sandbox['user_count'];
if ($sandbox['user_from'] == $sandbox['user_count']) {
if ($sandbox['user_not_migrated'] > 0) {
variable_set('empty_timezone_message', 1);
drupal_set_message(format_string('Some user time zones have been emptied and need to be set to the correct values. Use the new <a href="@config-url">time zone options</a> to choose whether to remind users at login to set the correct time zone.', array('@config-url' => url('admin/config/regional/settings'))), 'warning');
}
return t('Migrated user time zones');
}
}
}
/**
* Update user settings for cancelling user accounts.
*
* Prior to 7.x, users were not able to cancel their accounts. When
* administrators deleted an account, all contents were assigned to uid 0,
* which is the same as the 'user_cancel_reassign' method now.
*/
function user_update_7003() {
// Set the default account cancellation method.
variable_set('user_cancel_method', 'user_cancel_reassign');
// Re-assign notification setting.
if ($setting = variable_get('user_mail_status_deleted_notify', FALSE)) {
variable_set('user_mail_status_canceled_notify', $setting);
variable_del('user_mail_status_deleted_notify');
}
// Re-assign "Account deleted" mail strings to "Account canceled" mail.
if ($setting = variable_get('user_mail_status_deleted_subject', FALSE)) {
variable_set('user_mail_status_canceled_subject', $setting);
variable_del('user_mail_status_deleted_subject');
}
if ($setting = variable_get('user_mail_status_deleted_body', FALSE)) {
variable_set('user_mail_status_canceled_body', $setting);
variable_del('user_mail_status_deleted_body');
}
}
/**
* Changes the users table to allow longer e-mail addresses.
*/
function user_update_7005(&$sandbox) {
$mail_field = array(
'type' => 'varchar',
'length' => 254,
'not null' => FALSE,
'default' => '',
'description' => "User's e-mail address.",
);
$init_field = array(
'type' => 'varchar',
'length' => 254,
'not null' => FALSE,
'default' => '',
'description' => 'E-mail address used for initial account creation.',
);
db_drop_index('users', 'mail');
db_change_field('users', 'mail', 'mail', $mail_field, array('indexes' => array('mail' => array('mail'))));
db_change_field('users', 'init', 'init', $init_field);
}
/**
* Add module data to {role_permission}.
*/
function user_update_7006(&$sandbox) {
$module_field = array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => "The module declaring the permission.",
);
// Check that the field hasn't been updated in an aborted run of this
// update.
if (!db_field_exists('role_permission', 'module')) {
// Add a new field for the fid.
db_add_field('role_permission', 'module', $module_field);
}
}
/**
* Add a weight column to user roles.
*/
function user_update_7007() {
db_add_field('role', 'weight', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
db_add_index('role', 'name_weight', array('name', 'weight'));
}
/**
* If 'user_register' variable was unset in Drupal 6, set it to be the same as
* the Drupal 6 default setting.
*/
function user_update_7008() {
if (!isset($GLOBALS['conf']['user_register'])) {
// Set to the Drupal 6 default, "visitors can create accounts".
variable_set('user_register', USER_REGISTER_VISITORS);
}
}
/**
* Converts fields that store serialized variables from text to blob.
*/
function user_update_7009() {
$spec = array(
'type' => 'blob',
'not null' => FALSE,
'size' => 'big',
'serialize' => TRUE,
'description' => 'A serialized array of name value pairs that are related to the user. Any form values posted during user edit are stored and are loaded into the $user object during user_load(). Use of this field is discouraged and it will likely disappear in a future version of Drupal.',
);
db_change_field('users', 'data', 'data', $spec);
}
/**
* Update the {user}.signature_format column.
*/
function user_update_7010() {
// Update the database column to allow NULL values.
db_change_field('users', 'signature_format', 'signature_format', array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
'description' => 'The {filter_format}.format of the signature.',
));
// Replace the signature format with NULL if the signature is empty and does
// not already have a stored text format.
//
// In Drupal 6, "0" (the former FILTER_FORMAT_DEFAULT constant) could be used
// to indicate this situation, but in Drupal 7, only NULL is supported. This
// update therefore preserves the ability of user accounts which were never
// given a signature (for example, if the site did not have user signatures
// enabled, or if the user never edited their account information) to not
// have a particular text format assumed for them the first time the
// signature is edited.
db_update('users')
->fields(array('signature_format' => NULL))
->condition('signature', '')
->condition('signature_format', 0)
->execute();
// There are a number of situations in which a Drupal 6 site could store
// content with a nonexistent text format. This includes text formats that
// had later been deleted, or non-empty content stored with a value of "0"
// (the former FILTER_FORMAT_DEFAULT constant). Drupal 6 would filter this
// content using whatever the site-wide default text format was at the moment
// the text was being displayed.
//
// In Drupal 7, this behavior is no longer supported, and all content must be
// stored with an explicit text format (or it will not be displayed when it
// is filtered). Therefore, to preserve the behavior of the site after the
// upgrade, we must replace all instances described above with the current
// value of the (old) site-wide default format at the moment of the upgrade.
$existing_formats = db_query("SELECT format FROM {filter_format}")->fetchCol();
$default_format = variable_get('filter_default_format', 1);
db_update('users')
->fields(array('signature_format' => $default_format))
->isNotNull('signature_format')
->condition('signature_format', $existing_formats, 'NOT IN')
->execute();
}
/**
* Placeholder function.
*
* As a fix for user_update_7011() not updating email templates to use the new
* tokens, user_update_7017() now targets email templates of Drupal 6 sites and
* already upgraded sites.
*/
function user_update_7011() {
}
/**
* Add the user's pictures to the {file_managed} table and make them managed
* files.
*/
function user_update_7012(&$sandbox) {
$picture_field = array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => "Foreign key: {file_managed}.fid of user's picture.",
);
if (!isset($sandbox['progress'])) {
// Check that the field hasn't been updated in an aborted run of this
// update.
if (!db_field_exists('users', 'picture_fid')) {
// Add a new field for the fid.
db_add_field('users', 'picture_fid', $picture_field);
}
// Initialize batch update information.
$sandbox['progress'] = 0;
$sandbox['last_user_processed'] = -1;
$sandbox['max'] = db_query("SELECT COUNT(*) FROM {users} WHERE picture <> ''")->fetchField();
}
// As a batch operation move the photos into the {file_managed} table and
// update the {users} records.
$limit = 500;
$result = db_query_range("SELECT uid, picture FROM {users} WHERE picture <> '' AND uid > :uid ORDER BY uid", 0, $limit, array(':uid' => $sandbox['last_user_processed']));
foreach ($result as $user) {
// Don't bother adding files that don't exist.
if (file_exists($user->picture)) {
// Check if the file already exists.
$files = file_load_multiple(array(), array('uri' => $user->picture));
if (count($files)) {
$file = reset($files);
}
else {
// Create a file object.
$file = new stdClass();
$file->uri = $user->picture;
$file->filename = drupal_basename($file->uri);
$file->filemime = file_get_mimetype($file->uri);
$file->uid = $user->uid;
$file->status = FILE_STATUS_PERMANENT;
$file = file_save($file);
}
db_update('users')
->fields(array('picture_fid' => $file->fid))
->condition('uid', $user->uid)
->execute();
}
// Update our progress information for the batch update.
$sandbox['progress']++;
$sandbox['last_user_processed'] = $user->uid;
}
// Indicate our current progress to the batch update system. If there's no
// max value then there's nothing to update and we're finished.
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
// When we're finished, drop the old picture field and rename the new one to
// replace it.
if (isset($sandbox['#finished']) && $sandbox['#finished'] == 1) {
db_drop_field('users', 'picture');
db_change_field('users', 'picture_fid', 'picture', $picture_field);
}
}
/**
* Add user module file usage entries.
*/
function user_update_7013(&$sandbox) {
if (!isset($sandbox['progress'])) {
// Initialize batch update information.
$sandbox['progress'] = 0;
$sandbox['last_uid_processed'] = -1;
$sandbox['max'] = db_query("SELECT COUNT(*) FROM {users} u WHERE u.picture <> 0")->fetchField();
}
// Add usage entries for the user picture files.
$limit = 500;
$result = db_query_range('SELECT f.*, u.uid as user_uid FROM {users} u INNER JOIN {file_managed} f ON u.picture = f.fid WHERE u.picture <> 0 AND u.uid > :uid ORDER BY u.uid', 0, $limit, array(':uid' => $sandbox['last_uid_processed']))->fetchAllAssoc('fid', PDO::FETCH_ASSOC);
foreach ($result as $row) {
$uid = $row['user_uid'];
$file = (object) $row;
file_usage_add($file, 'user', 'user', $uid);
// Update our progress information for the batch update.
$sandbox['progress']++;
$sandbox['last_uid_processed'] = $uid;
}
// Indicate our current progress to the batch update system.
$sandbox['#finished'] = empty($sandbox['max']) || ($sandbox['progress'] / $sandbox['max']);
}
/**
* Rename the 'post comments without approval' permission.
*
* In Drupal 7, this permission has been renamed to 'skip comment approval'.
*/
function user_update_7014() {
db_update('role_permission')
->fields(array('permission' => 'skip comment approval'))
->condition('permission', 'post comments without approval')
->execute();
return t("Renamed the 'post comments without approval' permission to 'skip comment approval'.");
}
/**
* Change {users}.signature_format into varchar.
*/
function user_update_7015() {
db_change_field('users', 'signature_format', 'signature_format', array(
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'description' => 'The {filter_format}.format of the signature.',
));
}
/**
* @} End of "addtogroup updates-6.x-to-7.x".
*/
/**
* @addtogroup updates-7.x-extra
* @{
*/
/**
* Update the database to match the schema.
*/
function user_update_7016() {
// Add field default.
db_change_field('users', 'uid', 'uid', array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
));
}
/**
* Update email templates to use new tokens.
*
* This function upgrades customized email templates from the old !token format
* to the new core tokens format. Additionally, in Drupal 7 we no longer e-mail
* plain text passwords to users, and there is no token for a plain text
* password in the new token system. Therefore, it also modifies any saved
* templates using the old '!password' token such that the token is removed, and
* displays a warning to users that they may need to go and modify the wording
* of their templates.
*/
function user_update_7017() {
$message = '';
$tokens = array(
'!site' => '[site:name]',
'!username' => '[user:name]',
'!mailto' => '[user:mail]',
'!login_uri' => '[site:login-url]',
'!uri_brief' => '[site:url-brief]',
'!edit_uri' => '[user:edit-url]',
'!login_url' => '[user:one-time-login-url]',
'!uri' => '[site:url]',
'!date' => '[date:medium]',
'!password' => '',
);
$result = db_select('variable', 'v')
->fields('v', array('name'))
->condition('name', db_like('user_mail_') . '%', 'LIKE')
->execute();
foreach ($result as $row) {
// Use variable_get() to get the unserialized value for free.
if ($value = variable_get($row->name, FALSE)) {
if (empty($message) && (strpos($value, '!password') !== FALSE)) {
$message = t('The ability to send users their passwords in plain text has been removed in Drupal 7. Your existing email templates have been modified to remove it. You should <a href="@template-url">review these templates</a> to make sure they read properly.', array('@template-url' => url('admin/config/people/accounts')));
}
variable_set($row->name, str_replace(array_keys($tokens), $tokens, $value));
}
}
return $message;
}
/**
* Ensure there is an index on {users}.picture.
*/
function user_update_7018() {
if (!db_index_exists('users', 'picture')) {
db_add_index('users', 'picture', array('picture'));
}
}
/**
* Ensure there is a combined index on {authmap}.uid and {authmap}.module.
*/
function user_update_7019() {
// Check first in case it was already added manually.
if (!db_index_exists('authmap', 'uid_module')) {
db_add_index('authmap', 'uid_module', array('uid', 'module'));
}
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/

198
modules/user/user.js Normal file
View file

@ -0,0 +1,198 @@
(function ($) {
/**
* Attach handlers to evaluate the strength of any password fields and to check
* that its confirmation is correct.
*/
Drupal.behaviors.password = {
attach: function (context, settings) {
var translate = settings.password;
$('input.password-field', context).once('password', function () {
var passwordInput = $(this);
var innerWrapper = $(this).parent();
var outerWrapper = $(this).parent().parent();
// Add identifying class to password element parent.
innerWrapper.addClass('password-parent');
// Add the password confirmation layer.
$('input.password-confirm', outerWrapper).parent().prepend('<div class="password-confirm">' + translate['confirmTitle'] + ' <span></span></div>').addClass('confirm-parent');
var confirmInput = $('input.password-confirm', outerWrapper);
var confirmResult = $('div.password-confirm', outerWrapper);
var confirmChild = $('span', confirmResult);
// Add the description box.
var passwordMeter = '<div class="password-strength"><div class="password-strength-text" aria-live="assertive"></div><div class="password-strength-title">' + translate['strengthTitle'] + '</div><div class="password-indicator"><div class="indicator"></div></div></div>';
$(confirmInput).parent().after('<div class="password-suggestions description"></div>');
$(innerWrapper).prepend(passwordMeter);
var passwordDescription = $('div.password-suggestions', outerWrapper).hide();
// Check the password strength.
var passwordCheck = function () {
// Evaluate the password strength.
var result = Drupal.evaluatePasswordStrength(passwordInput.val(), settings.password);
// Update the suggestions for how to improve the password.
if (passwordDescription.html() != result.message) {
passwordDescription.html(result.message);
}
// Only show the description box if there is a weakness in the password.
if (result.strength == 100) {
passwordDescription.hide();
}
else {
passwordDescription.show();
}
// Adjust the length of the strength indicator.
$(innerWrapper).find('.indicator').css('width', result.strength + '%');
// Update the strength indication text.
$(innerWrapper).find('.password-strength-text').html(result.indicatorText);
passwordCheckMatch();
};
// Check that password and confirmation inputs match.
var passwordCheckMatch = function () {
if (confirmInput.val()) {
var success = passwordInput.val() === confirmInput.val();
// Show the confirm result.
confirmResult.css({ visibility: 'visible' });
// Remove the previous styling if any exists.
if (this.confirmClass) {
confirmChild.removeClass(this.confirmClass);
}
// Fill in the success message and set the class accordingly.
var confirmClass = success ? 'ok' : 'error';
confirmChild.html(translate['confirm' + (success ? 'Success' : 'Failure')]).addClass(confirmClass);
this.confirmClass = confirmClass;
}
else {
confirmResult.css({ visibility: 'hidden' });
}
};
// Monitor keyup and blur events.
// Blur must be used because a mouse paste does not trigger keyup.
passwordInput.keyup(passwordCheck).focus(passwordCheck).blur(passwordCheck);
confirmInput.keyup(passwordCheckMatch).blur(passwordCheckMatch);
});
}
};
/**
* Evaluate the strength of a user's password.
*
* Returns the estimated strength and the relevant output message.
*/
Drupal.evaluatePasswordStrength = function (password, translate) {
password = $.trim(password);
var weaknesses = 0, strength = 100, msg = [];
var hasLowercase = /[a-z]+/.test(password);
var hasUppercase = /[A-Z]+/.test(password);
var hasNumbers = /[0-9]+/.test(password);
var hasPunctuation = /[^a-zA-Z0-9]+/.test(password);
// If there is a username edit box on the page, compare password to that, otherwise
// use value from the database.
var usernameBox = $('input.username');
var username = (usernameBox.length > 0) ? usernameBox.val() : translate.username;
// Lose 5 points for every character less than 6, plus a 30 point penalty.
if (password.length < 6) {
msg.push(translate.tooShort);
strength -= ((6 - password.length) * 5) + 30;
}
// Count weaknesses.
if (!hasLowercase) {
msg.push(translate.addLowerCase);
weaknesses++;
}
if (!hasUppercase) {
msg.push(translate.addUpperCase);
weaknesses++;
}
if (!hasNumbers) {
msg.push(translate.addNumbers);
weaknesses++;
}
if (!hasPunctuation) {
msg.push(translate.addPunctuation);
weaknesses++;
}
// Apply penalty for each weakness (balanced against length penalty).
switch (weaknesses) {
case 1:
strength -= 12.5;
break;
case 2:
strength -= 25;
break;
case 3:
strength -= 40;
break;
case 4:
strength -= 40;
break;
}
// Check if password is the same as the username.
if (password !== '' && password.toLowerCase() === username.toLowerCase()) {
msg.push(translate.sameAsUsername);
// Passwords the same as username are always very weak.
strength = 5;
}
// Based on the strength, work out what text should be shown by the password strength meter.
if (strength < 60) {
indicatorText = translate.weak;
} else if (strength < 70) {
indicatorText = translate.fair;
} else if (strength < 80) {
indicatorText = translate.good;
} else if (strength <= 100) {
indicatorText = translate.strong;
}
// Assemble the final message.
msg = translate.hasWeaknesses + '<ul><li>' + msg.join('</li><li>') + '</li></ul>';
return { strength: strength, message: msg, indicatorText: indicatorText };
};
/**
* Field instance settings screen: force the 'Display on registration form'
* checkbox checked whenever 'Required' is checked.
*/
Drupal.behaviors.fieldUserRegistration = {
attach: function (context, settings) {
var $checkbox = $('form#field-ui-field-edit-form input#edit-instance-settings-user-register-form');
if ($checkbox.length) {
$('input#edit-instance-required', context).once('user-register-form-checkbox', function () {
$(this).bind('change', function (e) {
if ($(this).attr('checked')) {
$checkbox.attr('checked', true);
}
});
});
}
}
};
})(jQuery);

4085
modules/user/user.module Normal file

File diff suppressed because it is too large Load diff

601
modules/user/user.pages.inc Normal file
View file

@ -0,0 +1,601 @@
<?php
/**
* @file
* User page callback file for the user module.
*/
/**
* Menu callback; Retrieve a JSON object containing autocomplete suggestions for existing users.
*/
function user_autocomplete($string = '') {
$matches = array();
if ($string) {
$result = db_select('users')->fields('users', array('name'))->condition('name', db_like($string) . '%', 'LIKE')->range(0, 10)->execute();
foreach ($result as $user) {
$matches[$user->name] = check_plain($user->name);
}
}
drupal_json_output($matches);
}
/**
* Form builder; Request a password reset.
*
* @ingroup forms
* @see user_pass_validate()
* @see user_pass_submit()
*/
function user_pass() {
global $user;
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Username or e-mail address'),
'#size' => 60,
'#maxlength' => max(USERNAME_MAX_LENGTH, EMAIL_MAX_LENGTH),
'#required' => TRUE,
'#default_value' => isset($_GET['name']) ? $_GET['name'] : '',
);
// Allow logged in users to request this also.
if ($user->uid > 0) {
$form['name']['#type'] = 'value';
$form['name']['#value'] = $user->mail;
$form['mail'] = array(
'#prefix' => '<p>',
// As of https://www.drupal.org/node/889772 the user no longer must log
// out (if they are still logged in when using the password reset link,
// they will be logged out automatically then), but this text is kept as
// is to avoid breaking translations as well as to encourage the user to
// log out manually at a time of their own choosing (when it will not
// interrupt anything else they may have been in the middle of doing).
'#markup' => t('Password reset instructions will be mailed to %email. You must log out to use the password reset link in the e-mail.', array('%email' => $user->mail)),
'#suffix' => '</p>',
);
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('E-mail new password'));
return $form;
}
/**
* Form validation handler for user_pass().
*
* @see user_pass_submit()
*/
function user_pass_validate($form, &$form_state) {
$name = trim($form_state['values']['name']);
// Try to load by email.
$users = user_load_multiple(array(), array('mail' => $name, 'status' => '1'));
$account = reset($users);
if (!$account) {
// No success, try to load by name.
$users = user_load_multiple(array(), array('name' => $name, 'status' => '1'));
$account = reset($users);
}
if (isset($account->uid)) {
form_set_value(array('#parents' => array('account')), $account, $form_state);
}
else {
form_set_error('name', t('Sorry, %name is not recognized as a user name or an e-mail address.', array('%name' => $name)));
}
}
/**
* Form submission handler for user_pass().
*
* @see user_pass_validate()
*/
function user_pass_submit($form, &$form_state) {
global $language;
$account = $form_state['values']['account'];
// Mail one time login URL and instructions using current language.
$mail = _user_mail_notify('password_reset', $account, $language);
if (!empty($mail)) {
watchdog('user', 'Password reset instructions mailed to %name at %email.', array('%name' => $account->name, '%email' => $account->mail));
drupal_set_message(t('Further instructions have been sent to your e-mail address.'));
}
$form_state['redirect'] = 'user';
return;
}
/**
* Menu callback; process one time login link and redirects to the user page on success.
*/
function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $action = NULL) {
global $user;
// When processing the one-time login link, we have to make sure that a user
// isn't already logged in.
if ($user->uid) {
// The existing user is already logged in. Log them out and reload the
// current page so the password reset process can continue.
if ($user->uid == $uid) {
// Preserve the current destination (if any) and ensure the redirect goes
// back to the current page; any custom destination set in
// hook_user_logout() and intended for regular logouts would not be
// appropriate here.
$destination = array();
if (isset($_GET['destination'])) {
$destination = drupal_get_destination();
}
user_logout_current_user();
unset($_GET['destination']);
drupal_goto(current_path(), array('query' => drupal_get_query_parameters() + $destination));
}
// A different user is already logged in on the computer.
else {
$reset_link_account = user_load($uid);
if (!empty($reset_link_account)) {
drupal_set_message(t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href="!logout">logout</a> and try using the link again.',
array('%other_user' => $user->name, '%resetting_user' => $reset_link_account->name, '!logout' => url('user/logout'))), 'warning');
} else {
// Invalid one-time link specifies an unknown user.
drupal_set_message(t('The one-time login link you clicked is invalid.'), 'error');
}
drupal_goto();
}
}
else {
// Time out, in seconds, until login URL expires. Defaults to 24 hours =
// 86400 seconds.
$timeout = variable_get('user_password_reset_timeout', 86400);
$current = REQUEST_TIME;
// Some redundant checks for extra security ?
$users = user_load_multiple(array($uid), array('status' => '1'));
if ($timestamp <= $current && $account = reset($users)) {
// No time out for first time login.
if ($account->login && $current - $timestamp > $timeout) {
drupal_set_message(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'error');
drupal_goto('user/password');
}
elseif ($account->uid && $timestamp >= $account->login && $timestamp <= $current && $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid)) {
// First stage is a confirmation form, then login
if ($action == 'login') {
// Set the new user.
$user = $account;
// user_login_finalize() also updates the login timestamp of the
// user, which invalidates further use of the one-time login link.
user_login_finalize();
watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $account->name, '%timestamp' => $timestamp));
drupal_set_message(t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
// Let the user's password be changed without the current password check.
$token = drupal_random_key();
$_SESSION['pass_reset_' . $user->uid] = $token;
drupal_goto('user/' . $user->uid . '/edit', array('query' => array('pass-reset-token' => $token)));
}
else {
$form['message'] = array('#markup' => t('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $account->name, '%expiration_date' => format_date($timestamp + $timeout))));
$form['help'] = array('#markup' => '<p>' . t('This login can be used only once.') . '</p>');
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Log in'));
$form['#action'] = url("user/reset/$uid/$timestamp/$hashed_pass/login");
return $form;
}
}
else {
drupal_set_message(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'error');
drupal_goto('user/password');
}
}
else {
// Deny access, no more clues.
// Everything will be in the watchdog's URL for the administrator to check.
drupal_access_denied();
drupal_exit();
}
}
}
/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
user_logout_current_user();
drupal_goto();
}
/**
* Logs the current user out.
*/
function user_logout_current_user() {
global $user;
watchdog('user', 'Session closed for %name.', array('%name' => $user->name));
module_invoke_all('user_logout', $user);
// Destroy the current session, and reset $user to the anonymous user.
session_destroy();
}
/**
* Process variables for user-profile.tpl.php.
*
* @param array $variables
* An associative array containing:
* - elements: An associative array containing the user information and any
* fields attached to the user. Properties used:
* - #account: The user account of the profile being viewed.
*
* @see user-profile.tpl.php
*/
function template_preprocess_user_profile(&$variables) {
$account = $variables['elements']['#account'];
// Helpful $user_profile variable for templates.
foreach (element_children($variables['elements']) as $key) {
$variables['user_profile'][$key] = $variables['elements'][$key];
}
// Preprocess fields.
field_attach_preprocess('user', $account, $variables['elements'], $variables);
}
/**
* Process variables for user-profile-item.tpl.php.
*
* The $variables array contains the following arguments:
* - $element
*
* @see user-profile-item.tpl.php
*/
function template_preprocess_user_profile_item(&$variables) {
$variables['title'] = $variables['element']['#title'];
$variables['value'] = $variables['element']['#markup'];
$variables['attributes'] = '';
if (isset($variables['element']['#attributes'])) {
$variables['attributes'] = drupal_attributes($variables['element']['#attributes']);
}
}
/**
* Process variables for user-profile-category.tpl.php.
*
* The $variables array contains the following arguments:
* - $element
*
* @see user-profile-category.tpl.php
*/
function template_preprocess_user_profile_category(&$variables) {
$variables['title'] = check_plain($variables['element']['#title']);
$variables['profile_items'] = $variables['element']['#children'];
$variables['attributes'] = '';
if (isset($variables['element']['#attributes'])) {
$variables['attributes'] = drupal_attributes($variables['element']['#attributes']);
}
}
/**
* Form builder; edit a user account or one of their profile categories.
*
* @ingroup forms
* @see user_account_form()
* @see user_account_form_validate()
* @see user_profile_form_validate()
* @see user_profile_form_submit()
* @see user_cancel_confirm_form_submit()
*/
function user_profile_form($form, &$form_state, $account, $category = 'account') {
global $user;
// During initial form build, add the entity to the form state for use during
// form building and processing. During a rebuild, use what is in the form
// state.
if (!isset($form_state['user'])) {
$form_state['user'] = $account;
}
else {
$account = $form_state['user'];
}
// @todo Legacy support. Modules are encouraged to access the entity using
// $form_state. Remove in Drupal 8.
$form['#user'] = $account;
$form['#user_category'] = $category;
if ($category == 'account') {
user_account_form($form, $form_state);
// Attach field widgets.
$langcode = entity_language('user', $account);
field_attach_form('user', $account, $form, $form_state, $langcode);
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
if ($category == 'account') {
$form['actions']['cancel'] = array(
'#type' => 'submit',
'#value' => t('Cancel account'),
'#submit' => array('user_edit_cancel_submit'),
'#access' => $account->uid > 1 && (($account->uid == $user->uid && user_access('cancel account')) || user_access('administer users')),
);
}
$form['#validate'][] = 'user_profile_form_validate';
// Add the final user profile form submit handler.
$form['#submit'][] = 'user_profile_form_submit';
return $form;
}
/**
* Form validation handler for user_profile_form().
*
* @see user_profile_form_submit()
*/
function user_profile_form_validate($form, &$form_state) {
entity_form_field_validate('user', $form, $form_state);
}
/**
* Form submission handler for user_profile_form().
*
* @see user_profile_form_validate()
*/
function user_profile_form_submit($form, &$form_state) {
$account = $form_state['user'];
$category = $form['#user_category'];
// Remove unneeded values.
form_state_values_clean($form_state);
// Before updating the account entity, keep an unchanged copy for use with
// user_save() later. This is necessary for modules implementing the user
// hooks to be able to react on changes by comparing the values of $account
// and $edit.
$account_unchanged = clone $account;
entity_form_submit_build_entity('user', $account, $form, $form_state);
// Populate $edit with the properties of $account, which have been edited on
// this form by taking over all values, which appear in the form values too.
$edit = array_intersect_key((array) $account, $form_state['values']);
user_save($account_unchanged, $edit, $category);
$form_state['values']['uid'] = $account->uid;
if ($category == 'account' && !empty($edit['pass'])) {
// Remove the password reset tag since a new password was saved.
unset($_SESSION['pass_reset_'. $account->uid]);
}
// Clear the page cache because pages can contain usernames and/or profile information:
cache_clear_all();
drupal_set_message(t('The changes have been saved.'));
}
/**
* Submit function for the 'Cancel account' button on the user edit form.
*/
function user_edit_cancel_submit($form, &$form_state) {
$destination = array();
if (isset($_GET['destination'])) {
$destination = drupal_get_destination();
unset($_GET['destination']);
}
// Note: We redirect from user/uid/edit to user/uid/cancel to make the tabs disappear.
$form_state['redirect'] = array("user/" . $form['#user']->uid . "/cancel", array('query' => $destination));
}
/**
* Form builder; confirm form for cancelling user account.
*
* @ingroup forms
* @see user_edit_cancel_submit()
*/
function user_cancel_confirm_form($form, &$form_state, $account) {
global $user;
$form['_account'] = array('#type' => 'value', '#value' => $account);
// Display account cancellation method selection, if allowed.
$admin_access = user_access('administer users');
$can_select_method = $admin_access || user_access('select account cancellation method');
$form['user_cancel_method'] = array(
'#type' => 'item',
'#title' => ($account->uid == $user->uid ? t('When cancelling your account') : t('When cancelling the account')),
'#access' => $can_select_method,
);
$form['user_cancel_method'] += user_cancel_methods();
// Allow user administrators to skip the account cancellation confirmation
// mail (by default), as long as they do not attempt to cancel their own
// account.
$override_access = $admin_access && ($account->uid != $user->uid);
$form['user_cancel_confirm'] = array(
'#type' => 'checkbox',
'#title' => t('Require e-mail confirmation to cancel account.'),
'#default_value' => ($override_access ? FALSE : TRUE),
'#access' => $override_access,
'#description' => t('When enabled, the user must confirm the account cancellation via e-mail.'),
);
// Also allow to send account canceled notification mail, if enabled.
$default_notify = variable_get('user_mail_status_canceled_notify', FALSE);
$form['user_cancel_notify'] = array(
'#type' => 'checkbox',
'#title' => t('Notify user when account is canceled.'),
'#default_value' => ($override_access ? FALSE : $default_notify),
'#access' => $override_access && $default_notify,
'#description' => t('When enabled, the user will receive an e-mail notification after the account has been cancelled.'),
);
// Prepare confirmation form page title and description.
if ($account->uid == $user->uid) {
$question = t('Are you sure you want to cancel your account?');
}
else {
$question = t('Are you sure you want to cancel the account %name?', array('%name' => $account->name));
}
$description = '';
if ($can_select_method) {
$description = t('Select the method to cancel the account above.');
foreach (element_children($form['user_cancel_method']) as $element) {
unset($form['user_cancel_method'][$element]['#description']);
}
}
else {
// The radio button #description is used as description for the confirmation
// form.
foreach (element_children($form['user_cancel_method']) as $element) {
if ($form['user_cancel_method'][$element]['#default_value'] == $form['user_cancel_method'][$element]['#return_value']) {
$description = $form['user_cancel_method'][$element]['#description'];
}
unset($form['user_cancel_method'][$element]['#description']);
}
}
// Always provide entity id in the same form key as in the entity edit form.
$form['uid'] = array('#type' => 'value', '#value' => $account->uid);
return confirm_form($form,
$question,
'user/' . $account->uid,
$description . ' ' . t('This action cannot be undone.'),
t('Cancel account'), t('Cancel'));
}
/**
* Submit handler for the account cancellation confirm form.
*
* @see user_cancel_confirm_form()
* @see user_multiple_cancel_confirm_submit()
*/
function user_cancel_confirm_form_submit($form, &$form_state) {
global $user;
$account = $form_state['values']['_account'];
// Cancel account immediately, if the current user has administrative
// privileges, no confirmation mail shall be sent, and the user does not
// attempt to cancel the own account.
if (user_access('administer users') && empty($form_state['values']['user_cancel_confirm']) && $account->uid != $user->uid) {
user_cancel($form_state['values'], $account->uid, $form_state['values']['user_cancel_method']);
$form_state['redirect'] = 'admin/people';
}
else {
// Store cancelling method and whether to notify the user in $account for
// user_cancel_confirm().
$edit = array(
'user_cancel_method' => $form_state['values']['user_cancel_method'],
'user_cancel_notify' => $form_state['values']['user_cancel_notify'],
);
$account = user_save($account, $edit);
_user_mail_notify('cancel_confirm', $account);
drupal_set_message(t('A confirmation request to cancel your account has been sent to your e-mail address.'));
watchdog('user', 'Sent account cancellation request to %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
$form_state['redirect'] = "user/$account->uid";
}
}
/**
* Helper function to return available account cancellation methods.
*
* See documentation of hook_user_cancel_methods_alter().
*
* @return
* An array containing all account cancellation methods as form elements.
*
* @see hook_user_cancel_methods_alter()
* @see user_admin_settings()
* @see user_cancel_confirm_form()
* @see user_multiple_cancel_confirm()
*/
function user_cancel_methods() {
$methods = array(
'user_cancel_block' => array(
'title' => t('Disable the account and keep its content.'),
'description' => t('Your account will be blocked and you will no longer be able to log in. All of your content will remain attributed to your user name.'),
),
'user_cancel_block_unpublish' => array(
'title' => t('Disable the account and unpublish its content.'),
'description' => t('Your account will be blocked and you will no longer be able to log in. All of your content will be hidden from everyone but administrators.'),
),
'user_cancel_reassign' => array(
'title' => t('Delete the account and make its content belong to the %anonymous-name user.', array('%anonymous-name' => variable_get('anonymous', t('Anonymous')))),
'description' => t('Your account will be removed and all account information deleted. All of your content will be assigned to the %anonymous-name user.', array('%anonymous-name' => variable_get('anonymous', t('Anonymous')))),
),
'user_cancel_delete' => array(
'title' => t('Delete the account and its content.'),
'description' => t('Your account will be removed and all account information deleted. All of your content will also be deleted.'),
'access' => user_access('administer users'),
),
);
// Allow modules to customize account cancellation methods.
drupal_alter('user_cancel_methods', $methods);
// Turn all methods into real form elements.
$default_method = variable_get('user_cancel_method', 'user_cancel_block');
foreach ($methods as $name => $method) {
$form[$name] = array(
'#type' => 'radio',
'#title' => $method['title'],
'#description' => (isset($method['description']) ? $method['description'] : NULL),
'#return_value' => $name,
'#default_value' => $default_method,
'#parents' => array('user_cancel_method'),
);
}
return $form;
}
/**
* Menu callback; Cancel a user account via e-mail confirmation link.
*
* @see user_cancel_confirm_form()
* @see user_cancel_url()
*/
function user_cancel_confirm($account, $timestamp = 0, $hashed_pass = '') {
// Time out in seconds until cancel URL expires; 24 hours = 86400 seconds.
$timeout = 86400;
$current = REQUEST_TIME;
// Basic validation of arguments.
if (isset($account->data['user_cancel_method']) && !empty($timestamp) && !empty($hashed_pass)) {
// Validate expiration and hashed password/login.
if ($timestamp <= $current && $current - $timestamp < $timeout && $account->uid && $timestamp >= $account->login && $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid)) {
$edit = array(
'user_cancel_notify' => isset($account->data['user_cancel_notify']) ? $account->data['user_cancel_notify'] : variable_get('user_mail_status_canceled_notify', FALSE),
);
user_cancel($edit, $account->uid, $account->data['user_cancel_method']);
// Since user_cancel() is not invoked via Form API, batch processing needs
// to be invoked manually and should redirect to the front page after
// completion.
batch_process('');
}
else {
drupal_set_message(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'error');
drupal_goto("user/$account->uid/cancel");
}
}
return MENU_ACCESS_DENIED;
}
/**
* Page callback: Displays the user page.
*
* Displays user profile if user is logged in, or login form for anonymous
* users.
*
* @return
* A render array for either a user profile or a login form.
*
* @see user_view_page()
* @see user_login()
*/
function user_page() {
global $user;
if ($user->uid) {
menu_set_active_item('user/' . $user->uid);
return menu_execute_active_handler(NULL, FALSE);
}
else {
return drupal_get_form('user_login');
}
}

View file

@ -0,0 +1,69 @@
(function ($) {
/**
* Shows checked and disabled checkboxes for inherited permissions.
*/
Drupal.behaviors.permissions = {
attach: function (context) {
var self = this;
$('table#permissions').once('permissions', function () {
// On a site with many roles and permissions, this behavior initially has
// to perform thousands of DOM manipulations to inject checkboxes and hide
// them. By detaching the table from the DOM, all operations can be
// performed without triggering internal layout and re-rendering processes
// in the browser.
var $table = $(this);
if ($table.prev().length) {
var $ancestor = $table.prev(), method = 'after';
}
else {
var $ancestor = $table.parent(), method = 'append';
}
$table.detach();
// Create dummy checkboxes. We use dummy checkboxes instead of reusing
// the existing checkboxes here because new checkboxes don't alter the
// submitted form. If we'd automatically check existing checkboxes, the
// permission table would be polluted with redundant entries. This
// is deliberate, but desirable when we automatically check them.
var $dummy = $('<input type="checkbox" class="dummy-checkbox" disabled="disabled" checked="checked" />')
.attr('title', Drupal.t("This permission is inherited from the authenticated user role."))
.hide();
$('input[type=checkbox]', this).not('.rid-2, .rid-1').addClass('real-checkbox').each(function () {
$dummy.clone().insertAfter(this);
});
// Initialize the authenticated user checkbox.
$('input[type=checkbox].rid-2', this)
.bind('click.permissions', self.toggle)
// .triggerHandler() cannot be used here, as it only affects the first
// element.
.each(self.toggle);
// Re-insert the table into the DOM.
$ancestor[method]($table);
});
},
/**
* Toggles all dummy checkboxes based on the checkboxes' state.
*
* If the "authenticated user" checkbox is checked, the checked and disabled
* checkboxes are shown, the real checkboxes otherwise.
*/
toggle: function () {
var authCheckbox = this, $row = $(this).closest('tr');
// jQuery performs too many layout calculations for .hide() and .show(),
// leading to a major page rendering lag on sites with many roles and
// permissions. Therefore, we toggle visibility directly.
$row.find('.real-checkbox').each(function () {
this.style.display = (authCheckbox.checked ? 'none' : '');
});
$row.find('.dummy-checkbox').each(function () {
this.style.display = (authCheckbox.checked ? '' : 'none');
});
}
};
})(jQuery);

2636
modules/user/user.test Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,131 @@
<?php
/**
* @file
* Builds placeholder replacement tokens for user-related data.
*/
/**
* Implements hook_token_info().
*/
function user_token_info() {
$types['user'] = array(
'name' => t('Users'),
'description' => t('Tokens related to individual user accounts.'),
'needs-data' => 'user',
);
$types['current-user'] = array(
'name' => t('Current user'),
'description' => t('Tokens related to the currently logged in user.'),
'type' => 'user',
);
$user['uid'] = array(
'name' => t('User ID'),
'description' => t("The unique ID of the user account."),
);
$user['name'] = array(
'name' => t("Name"),
'description' => t("The login name of the user account."),
);
$user['mail'] = array(
'name' => t("Email"),
'description' => t("The email address of the user account."),
);
$user['url'] = array(
'name' => t("URL"),
'description' => t("The URL of the account profile page."),
);
$user['edit-url'] = array(
'name' => t("Edit URL"),
'description' => t("The URL of the account edit page."),
);
$user['last-login'] = array(
'name' => t("Last login"),
'description' => t("The date the user last logged in to the site."),
'type' => 'date',
);
$user['created'] = array(
'name' => t("Created"),
'description' => t("The date the user account was created."),
'type' => 'date',
);
return array(
'types' => $types,
'tokens' => array('user' => $user),
);
}
/**
* Implements hook_tokens().
*/
function user_tokens($type, $tokens, array $data = array(), array $options = array()) {
$url_options = array('absolute' => TRUE);
if (isset($options['language'])) {
$url_options['language'] = $options['language'];
$language_code = $options['language']->language;
}
else {
$language_code = NULL;
}
$sanitize = !empty($options['sanitize']);
$replacements = array();
if ($type == 'user' && !empty($data['user'])) {
$account = $data['user'];
foreach ($tokens as $name => $original) {
switch ($name) {
// Basic user account information.
case 'uid':
// In the case of hook user_presave uid is not set yet.
$replacements[$original] = !empty($account->uid) ? $account->uid : t('not yet assigned');
break;
case 'name':
$name = format_username($account);
$replacements[$original] = $sanitize ? check_plain($name) : $name;
break;
case 'mail':
$replacements[$original] = $sanitize ? check_plain($account->mail) : $account->mail;
break;
case 'url':
$replacements[$original] = !empty($account->uid) ? url("user/$account->uid", $url_options) : t('not yet assigned');
break;
case 'edit-url':
$replacements[$original] = !empty($account->uid) ? url("user/$account->uid/edit", $url_options) : t('not yet assigned');
break;
// These tokens are default variations on the chained tokens handled below.
case 'last-login':
$replacements[$original] = !empty($account->login) ? format_date($account->login, 'medium', '', NULL, $language_code) : t('never');
break;
case 'created':
// In the case of user_presave the created date may not yet be set.
$replacements[$original] = !empty($account->created) ? format_date($account->created, 'medium', '', NULL, $language_code) : t('not yet created');
break;
}
}
if ($login_tokens = token_find_with_prefix($tokens, 'last-login')) {
$replacements += token_generate('date', $login_tokens, array('date' => $account->login), $options);
}
if ($registered_tokens = token_find_with_prefix($tokens, 'created')) {
$replacements += token_generate('date', $registered_tokens, array('date' => $account->created), $options);
}
}
if ($type == 'current-user') {
$account = user_load($GLOBALS['user']->uid);
$replacements += token_generate('user', $tokens, array('user' => $account), $options);
}
return $replacements;
}