diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index 83ddd30..7507bf6 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -190,7 +190,7 @@ function _batch_progress_page_nojs() {
// the error message.
ob_start();
$fallback = $current_set['error_message'] . '
' . $batch['error_message'];
- $fallback = theme('maintenance_page', array('content' => $fallback, 'show_messages' => FALSE));
+ $fallback = theme('maintenance_page', array('content' => $fallback, 'show_messages' => TRUE));
// We strip the end of the page using a marker in the template, so any
// additional HTML output by PHP shows up inside the page rather than below
diff --git a/core/modules/comment/comment.entity.inc b/core/modules/comment/comment.entity.inc
index cb4d6d6..3c17843 100644
--- a/core/modules/comment/comment.entity.inc
+++ b/core/modules/comment/comment.entity.inc
@@ -106,7 +106,7 @@ class CommentStorageController extends EntityDatabaseStorageController {
$query->addField('n', 'type', 'node_type');
$query->innerJoin('users', 'u', 'base.uid = u.uid');
$query->addField('u', 'name', 'registered_name');
- $query->fields('u', array('uid', 'signature', 'signature_format', 'picture'));
+ $query->fields('u', array('uid', 'signature', 'signature_format'));
return $query;
}
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 0302913..5fa7817 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -1048,6 +1048,17 @@ function comment_build_content(Comment $comment, Node $node, $view_mode = 'full'
}
/**
+ * Implements hook_entity_prepare_view().
+ *
+ * Load user account objects into comment entities.
+ */
+function comment_entity_prepare_view($entities, $type) {
+ if ($type == 'comment') {
+ user_attach_accounts($entities);
+ }
+}
+
+/**
* Adds reply, edit, delete, etc. links, depending on user permissions.
*
* @param Comment $comment
@@ -2122,7 +2133,12 @@ function template_preprocess_comment(&$variables) {
$variables['changed'] = format_date($comment->changed);
$variables['new'] = !empty($comment->new) ? t('new') : '';
- $variables['user_picture'] = theme_get_setting('toggle_comment_user_picture') ? theme('user_picture', array('account' => $comment)) : '';
+ $variables['user_picture'] = array();
+ if (theme_get_setting('toggle_comment_user_picture')) {
+ // To change user picture settings (e.g., image style), edit the 'compact'
+ // view mode on the User entity.
+ $variables['user_picture'] = user_view($comment->account, 'compact');
+ }
$variables['signature'] = $comment->signature;
$uri = entity_uri('comment', $comment);
diff --git a/core/modules/comment/comment.tpl.php b/core/modules/comment/comment.tpl.php
index e94e6e4..be7d20f 100644
--- a/core/modules/comment/comment.tpl.php
+++ b/core/modules/comment/comment.tpl.php
@@ -20,7 +20,8 @@
* - $permalink: Comment permalink.
* - $submitted: Submission information created from $author and $created during
* template_preprocess_comment().
- * - $user_picture: The comment author's picture from user-picture.tpl.php.
+ * - $user_picture: The comment author's picture. Use render($user_picture) to
+ * print it.
* - $signature: Authors signature.
* - $status: Comment status. Possible values are:
* unpublished, published, or preview.
@@ -68,7 +69,7 @@
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 5e1c3b3..b77ac3a 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1253,6 +1253,17 @@ function node_build_content(Node $node, $view_mode = 'full', $langcode = NULL) {
}
/**
+ * Implements hook_entity_prepare_view().
+ *
+ * Load user account objects into node entities.
+ */
+function node_entity_prepare_view($entities, $type) {
+ if ($type == 'node') {
+ user_attach_accounts($entities);
+ }
+}
+
+/**
* Page callback: Generates an array which displays a node detail page.
*
* @param Drupal\node\Node $node
@@ -1359,7 +1370,11 @@ function template_preprocess_node(&$variables) {
if (variable_get('node_submitted_' . $node->type, TRUE)) {
$variables['display_submitted'] = TRUE;
$variables['submitted'] = t('Submitted by !username on !datetime', array('!username' => $variables['name'], '!datetime' => $variables['date']));
- $variables['user_picture'] = theme_get_setting('toggle_node_user_picture') ? theme('user_picture', array('account' => $node)) : '';
+ $variables['user_picture'] = array();
+ if (theme_get_setting('toggle_node_user_picture')) {
+ // To change user picture settings (e.g. image style), edit the 'compact' view mode on User entity.
+ $variables['user_picture'] = user_view($node->account, 'compact');
+ }
}
else {
$variables['display_submitted'] = FALSE;
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index 868acbd..d98e370 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -414,7 +414,6 @@ function node_preview(Node $node) {
// user ID 0 denotes the anonymous user.
if ($user = user_load_by_name($node->name)) {
$node->uid = $user->uid;
- $node->picture = $user->picture;
}
else {
$node->uid = 0; // anonymous user
@@ -423,7 +422,6 @@ function node_preview(Node $node) {
elseif ($node->uid) {
$user = user_load($node->uid);
$node->name = $user->name;
- $node->picture = $user->picture;
}
$node->changed = REQUEST_TIME;
diff --git a/core/modules/node/node.tpl.php b/core/modules/node/node.tpl.php
index c3db5ae..a53bdf4 100644
--- a/core/modules/node/node.tpl.php
+++ b/core/modules/node/node.tpl.php
@@ -10,7 +10,7 @@
* or print a subset such as render($content['field_example']). Use
* hide($content['field_example']) to temporarily suppress the printing of a
* given element.
- * - $user_picture: The node author's picture from user-picture.tpl.php.
+ * - $user_picture: The node author's picture. Use render() to print it.
* - $date: Formatted creation date. Preprocess functions can reformat it by
* calling format_date() with the desired parameters on the $created variable.
* - $name: Themed username of node author output from theme_username().
@@ -88,7 +88,7 @@
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index aac0c3d..eb982fe 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -393,7 +393,7 @@ function system_theme_settings($form, &$form_state, $key = '') {
// Some features are not always available
$disabled = array();
- if (!variable_get('user_pictures', 0)) {
+ if (!user_picture_enabled()) {
$disabled['toggle_node_user_picture'] = TRUE;
$disabled['toggle_comment_user_picture'] = TRUE;
}
diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
index bd0d9b9..a57a599 100644
--- a/core/modules/user/lib/Drupal/user/UserStorageController.php
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.php
@@ -25,12 +25,7 @@ class UserStorageController extends EntityDatabaseStorageController {
* Overrides EntityDatabaseStorageController::attachLoad().
*/
function attachLoad(&$queried_users, $revision_id = FALSE) {
- // Build an array of user picture IDs so that these can be fetched later.
- $picture_fids = array();
foreach ($queried_users as $key => $record) {
- if ($record->picture) {
- $picture_fids[] = $record->picture;
- }
$queried_users[$key]->data = unserialize($record->data);
$queried_users[$key]->roles = array();
if ($record->uid) {
@@ -47,15 +42,6 @@ class UserStorageController extends EntityDatabaseStorageController {
$queried_users[$record->uid]->roles[$record->rid] = $record->name;
}
- // Add the full file objects for user pictures if enabled.
- if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
- $pictures = file_load_multiple($picture_fids);
- foreach ($queried_users as $entity) {
- if (!empty($entity->picture) && isset($pictures[$entity->picture])) {
- $entity->picture = $pictures[$entity->picture];
- }
- }
- }
// Call the default attachLoad() method. This will add fields and call
// hook_user_load().
parent::attachLoad($queried_users, $revision_id);
@@ -100,46 +86,7 @@ class UserStorageController extends EntityDatabaseStorageController {
}
}
- if (!empty($entity->picture_upload)) {
- $entity->picture = $entity->picture_upload;
- }
- // Delete the picture if the submission indicates that it should be deleted
- // and no replacement was submitted.
- elseif (!empty($entity->picture_delete)) {
- $entity->picture = 0;
- file_usage_delete($entity->original->picture, 'user', 'user', $entity->uid);
- file_delete($entity->original->picture);
- }
-
if (!$entity->isNew()) {
- // Process picture uploads.
- if (!empty($entity->picture->fid) && (!isset($entity->original->picture->fid) || $entity->picture->fid != $entity->original->picture->fid)) {
- $picture = $entity->picture;
- // If the picture is a temporary file, move it to its final location
- // and make it permanent.
- if (!$picture->status) {
- $info = image_get_info($picture->uri);
- $picture_directory = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');
-
- // Prepare the pictures directory.
- file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY);
- $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $entity->uid . '-' . REQUEST_TIME . '.' . $info['extension']);
-
- // Move the temporary file into the final location.
- if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) {
- $picture->status = FILE_STATUS_PERMANENT;
- $entity->picture = file_save($picture);
- file_usage_add($picture, 'user', 'user', $entity->uid);
- }
- }
- // Delete the previous picture if it was deleted or replaced.
- if (!empty($entity->original->picture->fid)) {
- file_usage_delete($entity->original->picture, 'user', 'user', $entity->uid);
- file_delete($entity->original->picture);
- }
- }
- $entity->picture = empty($entity->picture->fid) ? 0 : $entity->picture->fid;
-
// If the password is empty, that means it was not changed, so use the
// original password.
if (empty($entity->pass)) {
diff --git a/core/modules/user/user-picture.tpl.php b/core/modules/user/user-picture.tpl.php
deleted file mode 100644
index a33d266..0000000
--- a/core/modules/user/user-picture.tpl.php
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
diff --git a/core/modules/user/user-profile.tpl.php b/core/modules/user/user-profile.tpl.php
index 940d47f..4bf9d5c 100644
--- a/core/modules/user/user-profile.tpl.php
+++ b/core/modules/user/user-profile.tpl.php
@@ -8,12 +8,10 @@
* 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['member_for'] 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.
+ * such as render($content['field_example']).
+ * 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.
*
* Available variables:
* - $user_profile: An array of profile items. Use render() to print them.
diff --git a/core/modules/user/user-rtl.css b/core/modules/user/user-rtl.css
index 642c943..07a402d 100644
--- a/core/modules/user/user-rtl.css
+++ b/core/modules/user/user-rtl.css
@@ -27,8 +27,7 @@ div.password-confirm {
clear: right;
}
-/* Generated by user.module but used by profile.module: */
-.profile .user-picture {
+.profile .field-name-field-user-picture {
float: left;
margin: 0 0 1em 1em;
}
diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc
index 8de4fb9..fec1f33 100644
--- a/core/modules/user/user.admin.inc
+++ b/core/modules/user/user.admin.inc
@@ -342,80 +342,6 @@ function user_admin_settings() {
'#title' => t('Enable signatures.'),
'#default_value' => variable_get('user_signatures', 0),
);
- // If picture support is enabled, check whether the picture directory exists.
- if (variable_get('user_pictures', 0)) {
- $picture_path = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');
- if (!file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY)) {
- form_set_error('user_picture_path', t('The directory %directory does not exist or is not writable.', array('%directory' => $picture_path)));
- watchdog('file system', 'The directory %directory does not exist or is not writable.', array('%directory' => $picture_path), WATCHDOG_ERROR);
- }
- }
- $picture_support = variable_get('user_pictures', 0);
- $form['personalization']['user_pictures'] = array(
- '#type' => 'checkbox',
- '#title' => t('Enable user pictures.'),
- '#default_value' => $picture_support,
- );
- drupal_add_js(drupal_get_path('module', 'user') . '/user.js');
- $form['personalization']['pictures'] = array(
- '#type' => 'container',
- '#states' => array(
- // Hide the additional picture settings when user pictures are disabled.
- 'invisible' => array(
- 'input[name="user_pictures"]' => array('checked' => FALSE),
- ),
- ),
- );
- $form['personalization']['pictures']['user_picture_path'] = array(
- '#type' => 'textfield',
- '#title' => t('Picture directory'),
- '#default_value' => variable_get('user_picture_path', 'pictures'),
- '#size' => 30,
- '#maxlength' => 255,
- '#description' => t('Subdirectory in the file upload directory where pictures will be stored.'),
- );
- $form['personalization']['pictures']['user_picture_default'] = array(
- '#type' => 'textfield',
- '#title' => t('Default picture'),
- '#default_value' => variable_get('user_picture_default', ''),
- '#size' => 30,
- '#maxlength' => 255,
- '#description' => t('URL of picture to display for users with no custom picture selected. Leave blank for none.'),
- );
- if (module_exists('image')) {
- $form['personalization']['pictures']['settings']['user_picture_style'] = array(
- '#type' => 'select',
- '#title' => t('Picture display style'),
- '#options' => image_style_options(TRUE),
- '#default_value' => variable_get('user_picture_style', ''),
- '#description' => t('The style selected will be used on display, while the original image is retained. Styles may be configured in the Image styles administration area.', array('!url' => url('admin/config/media/image-styles'))),
- );
- }
- $form['personalization']['pictures']['user_picture_dimensions'] = array(
- '#type' => 'textfield',
- '#title' => t('Picture upload dimensions'),
- '#default_value' => variable_get('user_picture_dimensions', '85x85'),
- '#size' => 10,
- '#maxlength' => 10,
- '#field_suffix' => ' ' . t('pixels'),
- '#description' => t('Pictures larger than this will be scaled down to this size.'),
- );
- $form['personalization']['pictures']['user_picture_file_size'] = array(
- '#type' => 'number',
- '#title' => t('Picture upload file size'),
- '#default_value' => variable_get('user_picture_file_size', '30'),
- '#size' => 10,
- '#maxlength' => 10,
- '#min' => 0,
- '#field_suffix' => ' ' . t('KB'),
- '#description' => t('Maximum allowed file size for uploaded pictures. Upload size is normally limited only by the PHP maximum post and file upload settings, and images are automatically scaled down to the dimensions specified above.'),
- );
- $form['personalization']['pictures']['user_picture_guidelines'] = array(
- '#type' => 'textarea',
- '#title' => t('Picture guidelines'),
- '#default_value' => variable_get('user_picture_guidelines', ''),
- '#description' => t("This text is displayed at the picture upload form in addition to the default guidelines. It's useful for helping or instructing your users."),
- );
$form['email_title'] = array(
'#type' => 'item',
@@ -1030,4 +956,3 @@ function user_admin_role_delete_confirm_submit($form, &$form_state) {
drupal_set_message(t('The role has been deleted.'));
$form_state['redirect'] = 'admin/people/permissions/roles';
}
-
diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php
index 1b8a253..ec24eac 100644
--- a/core/modules/user/user.api.php
+++ b/core/modules/user/user.api.php
@@ -333,12 +333,17 @@ function hook_user_logout($account) {
* @see hook_entity_view()
*/
function hook_user_view($account, $view_mode, $langcode) {
- $account->content['user_picture'] = array(
- '#markup' => theme('user_picture', array('account' => $account)),
- '#weight' => -10,
+ if (!isset($account->content['summary'])) {
+ $account->content['summary'] = array();
+ }
+ $account->content['summary'] += array(
+ '#type' => 'user_profile_category',
+ '#title' => t('History'),
+ '#attributes' => array('class' => array('user-member')),
+ '#weight' => 5,
);
- $account->content['member_for'] = array(
- '#type' => 'item',
+ $account->content['summary']['member_for'] = array(
+ '#type' => 'user_profile_item',
'#title' => t('Member for'),
'#markup' => format_interval(REQUEST_TIME - $account->created),
);
diff --git a/core/modules/user/user.css b/core/modules/user/user.css
index bbfeb83..69d4ad6 100644
--- a/core/modules/user/user.css
+++ b/core/modules/user/user.css
@@ -78,12 +78,11 @@ div.password-suggestions ul {
width: 36.3em;
}
-/* Generated by user.module but used by profile.module: */
.profile {
clear: both;
margin: 1em 0;
}
-.profile .user-picture {
+.profile .field-name-field-user-picture {
float: right; /* LTR */
margin: 0 1em 1em 0; /* LTR */
}
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index ce11aaf..b025116 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -218,12 +218,6 @@ function user_schema() {
'default' => '',
'description' => 'The {language}.langcode that the user prefers for receiving emails and viewing the site.',
),
- '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,
@@ -243,7 +237,6 @@ function user_schema() {
'access' => array('access'),
'created' => array('created'),
'mail' => array('mail'),
- 'picture' => array('picture'),
),
'unique keys' => array(
'name' => array('name'),
@@ -399,5 +392,96 @@ function user_update_8001() {
}
/**
+ * Create user picture field.
+ */
+function user_update_8002() {
+ user_create_picture_field();
+}
+
+/**
+ * Migrate user pictures to Field API.
+ */
+function user_update_8003() {
+ $results = db_select('users', 'u')
+ ->fields('u', array('uid', 'picture'))
+ ->condition('picture', 0, '>')
+ ->execute()
+ ->fetchAllKeyed();
+ if ($uids = array_keys($results)) {
+ $accounts = user_load_multiple($uids);
+ foreach ($results as $uid => $fid) {
+ if ($file = file_load($fid)) {
+ $edit['field_user_picture'][LANGUAGE_NONE][0] = (array) $file;
+ user_save($accounts[$uid], $edit);
+ }
+ }
+ }
+}
+
+/**
* @} End of "addtogroup updates-7.x-to-8.x"
+ * The next series of updates should start at 9000.
*/
+
+/**
+ * Creates a "User picture" image field for the User entity.
+ */
+function user_create_picture_field() {
+ $field = array(
+ 'field_name' => 'field_user_picture',
+ 'module' => 'image',
+ 'type' => 'image',
+ 'cardinality' => 1,
+ 'locked' => FALSE,
+ 'indexes' => array('fid' => array('fid')),
+ 'settings' => array(
+ 'uri_scheme' => 'public',
+ 'default_image' => FALSE,
+ ),
+ 'storage' => array(
+ 'type' => 'field_sql_storage',
+ 'settings' => array(),
+ ),
+ );
+ _update_7000_field_create_field($field);
+
+ $instance = array(
+ 'field_name' => 'field_user_picture',
+ 'entity_type' => 'user',
+ 'label' => 'User Picture',
+ 'bundle' => 'user',
+ 'description' => st('Upload an image to go with this user account.'),
+ 'required' => FALSE,
+ 'settings' => array(
+ 'file_directory' => 'pictures',
+ 'file_extensions' => 'png gif jpg jpeg',
+ 'max_filesize' => '',
+ 'max_resolution' => '',
+ 'min_resolution' => '',
+ 'alt_field' => 'User picture',
+ 'title_field' => '',
+ ),
+ 'widget' => array(
+ 'module' => 'image',
+ 'type' => 'image_image',
+ 'settings' => array(
+ 'progress_indicator' => 'throbber',
+ 'preview_image_style' => 'medium',
+ ),
+ 'weight' => -1,
+ ),
+ 'display' => array(
+ 'default' => array(
+ 'label' => 'hidden',
+ 'type' => 'image',
+ 'settings' => array('image_style' => 'medium', 'image_link' => 'content'),
+ ),
+ 'compact' => array(
+ 'label' => 'hidden',
+ 'type' => 'image',
+ 'settings' => array('image_style' => 'thumbnail', 'image_link' => 'content'),
+ ),
+ ),
+ );
+ _update_7000_field_create_instance($field, $instance);
+}
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index e6fa2dd..8fb32be 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -51,7 +51,7 @@ function user_help($path, $arg) {
$output .= '' . t('User roles and permissions') . '';
$output .= '' . t('Roles are used to group and classify users; each user can be assigned one or more roles. By default there are two roles: anonymous user (users that are not logged in) and authenticated user (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 Roles page. After creating roles, you can set permissions for each role on the Permissions page. 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'))) . '';
$output .= '' . t('Account settings') . '';
- $output .= '' . t('The Account settings page 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'))) . '';
+ $output .= '' . t('The Account settings page 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 adapt the text for the e-mail messages that are sent automatically during the user registration process.', array('@accounts' => url('admin/config/people/accounts'))) . '';
$output .= '';
return $output;
case 'admin/people/create':
@@ -104,10 +104,6 @@ function user_module_invoke($type, &$edit, $account) {
*/
function user_theme() {
return array(
- 'user_picture' => array(
- 'variables' => array('account' => NULL),
- 'template' => 'user-picture',
- ),
'user_profile' => array(
'render element' => 'elements',
'template' => 'user-profile',
@@ -167,6 +163,10 @@ function user_entity_info() {
'label' => t('User account'),
'custom settings' => FALSE,
),
+ 'compact' => array(
+ 'label' => t('Compact'),
+ 'custom settings' => TRUE,
+ ),
),
),
);
@@ -199,6 +199,45 @@ function user_label($entity_type, $entity) {
}
/**
+ * Populate $entity->account for each prepared entity.
+ *
+ * Called by hook_entity_prepare_view() implementations.
+ *
+ * @param array $entities
+ * The entities keyed by entity ID.
+ */
+function user_attach_accounts(array $entities) {
+ $uids = array();
+ foreach ($entities as $entity) {
+ $uids[] = $entity->uid;
+ }
+ $uids = array_unique($uids);
+ $accounts = user_load_multiple($uids);
+ $anonymous = drupal_anonymous_user();
+ foreach ($entities as $id => $entity) {
+ if (isset($accounts[$entity->uid])) {
+ $entities[$id]->account = $accounts[$entity->uid];
+ }
+ else {
+ $entities[$id]->account = $anonymous;
+ }
+ }
+}
+
+/**
+ * Returns whether this site supports the default user picture feature.
+ *
+ * This approach preserves compatibility with node/comment templates. Alternate
+ * user picture implementations (e.g., Gravatar) should provide their own
+ * add/edit/delete forms and populate the 'picture' variable during the
+ * preprocess stage.
+ */
+function user_picture_enabled() {
+ $instances = field_info_instances('user', 'user');
+ return isset($instances['field_user_picture']) && !$instances['field_user_picture']['deleted'];
+}
+
+/**
* Implements hook_field_info_alter().
*/
function user_field_info_alter(&$info) {
@@ -355,6 +394,191 @@ function user_load_by_name($name) {
}
/**
+ * Save changes to a user account or add a new user.
+ *
+ * @param $account
+ * (optional) The user object to modify or add. If you want to modify
+ * an existing user account, you will need to ensure that (a) $account
+ * is an object, and (b) you have set $account->uid to the numeric
+ * user ID of the user account you wish to modify. If you
+ * want to create a new user account, you can set $account->is_new to
+ * TRUE or omit the $account->uid field.
+ * @param $edit
+ * An array of fields and values to save. For example array('name'
+ * => 'My name'). Key / value pairs added to the $edit['data'] will be
+ * serialized and saved in the {users.data} column.
+ *
+ * @return
+ * A fully-loaded $user object upon successful save or FALSE if the save failed.
+ *
+ * @todo D8: Drop $edit and fix user_save() to be consistent with others.
+ */
+function user_save($account, $edit = array()) {
+ $transaction = db_transaction();
+ try {
+ if (!empty($edit['pass'])) {
+ // Allow alternate password hashing schemes.
+ require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'core/includes/password.inc');
+ $edit['pass'] = user_hash_password(trim($edit['pass']));
+ // Abort if the hashing failed and returned FALSE.
+ if (!$edit['pass']) {
+ return FALSE;
+ }
+ }
+ else {
+ // Avoid overwriting an existing password with a blank password.
+ unset($edit['pass']);
+ }
+ if (isset($edit['mail'])) {
+ $edit['mail'] = trim($edit['mail']);
+ }
+
+ // Load the stored entity, if any.
+ if (!empty($account->uid) && !isset($account->original)) {
+ $account->original = entity_load_unchanged('user', $account->uid);
+ }
+
+ if (empty($account)) {
+ $account = new stdClass();
+ }
+ if (!isset($account->is_new)) {
+ $account->is_new = empty($account->uid);
+ }
+ // Prepopulate $edit['data'] with the current value of $account->data.
+ // Modules can add to or remove from this array in hook_user_presave().
+ if (!empty($account->data)) {
+ $edit['data'] = !empty($edit['data']) ? array_merge($account->data, $edit['data']) : $account->data;
+ }
+
+ // Invoke hook_user_presave() for all modules.
+ user_module_invoke('presave', $edit, $account);
+
+ // Invoke presave operations of Field Attach API and Entity API. Those APIs
+ // require a fully-fledged and updated entity object. Therefore, we need to
+ // copy any new property values of $edit into it.
+ foreach ($edit as $key => $value) {
+ $account->$key = $value;
+ }
+ field_attach_presave('user', $account);
+ module_invoke_all('entity_presave', $account, 'user');
+
+ if (is_object($account) && !$account->is_new) {
+ // Do not allow 'uid' to be changed.
+ $account->uid = $account->original->uid;
+ // Save changes to the user table.
+ $success = drupal_write_record('users', $account, 'uid');
+ if ($success === FALSE) {
+ // The query failed - better to abort the save than risk further
+ // data loss.
+ return FALSE;
+ }
+
+ // Reload user roles if provided.
+ if ($account->roles != $account->original->roles) {
+ db_delete('users_roles')
+ ->condition('uid', $account->uid)
+ ->execute();
+
+ $query = db_insert('users_roles')->fields(array('uid', 'rid'));
+ foreach (array_keys($account->roles) as $rid) {
+ if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
+ $query->values(array(
+ 'uid' => $account->uid,
+ 'rid' => $rid,
+ ));
+ }
+ }
+ $query->execute();
+ }
+
+ // Delete a blocked user's sessions to kick them if they are online.
+ if ($account->original->status != $account->status && $account->status == 0) {
+ drupal_session_destroy_uid($account->uid);
+ }
+
+ // If the password changed, delete all open sessions and recreate
+ // the current one.
+ if ($account->pass != $account->original->pass) {
+ drupal_session_destroy_uid($account->uid);
+ if ($account->uid == $GLOBALS['user']->uid) {
+ drupal_session_regenerate();
+ }
+ }
+
+ // Save Field data.
+ field_attach_update('user', $account);
+
+ // Send emails after we have the new user object.
+ if ($account->status != $account->original->status) {
+ // The user's status is changing; conditionally send notification email.
+ $op = $account->status == 1 ? 'status_activated' : 'status_blocked';
+ _user_mail_notify($op, $account);
+ }
+
+ // Update $edit with any interim changes to $account.
+ foreach ($account as $key => $value) {
+ if (!property_exists($account->original, $key) || $value !== $account->original->$key) {
+ $edit[$key] = $value;
+ }
+ }
+ user_module_invoke('update', $edit, $account);
+ module_invoke_all('entity_update', $account, 'user');
+ }
+ else {
+ // Allow 'uid' to be set by the caller. There is no danger of writing an
+ // existing user as drupal_write_record will do an INSERT.
+ if (empty($account->uid)) {
+ $account->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
+ }
+ // Allow 'created' to be set by the caller.
+ if (!isset($account->created)) {
+ $account->created = REQUEST_TIME;
+ }
+ $success = drupal_write_record('users', $account);
+ if ($success === FALSE) {
+ // On a failed INSERT some other existing user's uid may be returned.
+ // We must abort to avoid overwriting their account.
+ return FALSE;
+ }
+
+ // Make sure $account is properly initialized.
+ $account->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+
+ field_attach_insert('user', $account);
+ $edit = (array) $account;
+ user_module_invoke('insert', $edit, $account);
+ module_invoke_all('entity_insert', $account, 'user');
+
+ // Save user roles.
+ if (count($account->roles) > 1) {
+ $query = db_insert('users_roles')->fields(array('uid', 'rid'));
+ foreach (array_keys($account->roles) as $rid) {
+ if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
+ $query->values(array(
+ 'uid' => $account->uid,
+ 'rid' => $rid,
+ ));
+ }
+ }
+ $query->execute();
+ }
+ }
+ // Clear internal properties.
+ unset($account->is_new);
+ unset($account->original);
+ // Clear the static loading cache.
+ entity_get_controller('user')->resetCache(array($account->uid));
+
+ return $account;
+ }
+ catch (Exception $e) {
+ $transaction->rollback();
+ watchdog_exception('user', $e);
+ throw $e;
+ }
+}
+
+/**
* Verify the syntax of the given name.
*/
function user_validate_name($name) {
@@ -391,25 +615,24 @@ function user_validate_name($name) {
}
/**
- * Validates an image uploaded by a user.
+ * Validates a user's email address.
*
- * @see user_account_form()
+ * Checks that a user's email address exists and follows all standard
+ * validation rules. Returns error messages when the address is invalid.
+ *
+ * @param $mail
+ * A user's email address.
+ *
+ * @return
+ * If the address is invalid, a human-readable error message is returned.
+ * If the address is valid, nothing is returned.
*/
-function user_validate_picture(&$form, &$form_state) {
- // If required, validate the uploaded picture.
- $validators = array(
- 'file_validate_is_image' => array(),
- 'file_validate_image_resolution' => array(variable_get('user_picture_dimensions', '85x85')),
- 'file_validate_size' => array(variable_get('user_picture_file_size', '30') * 1024),
- );
-
- // Save the file as a temporary file.
- $file = file_save_upload('picture_upload', $validators);
- if ($file === FALSE) {
- 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'))));
+function user_validate_mail($mail) {
+ if (!$mail) {
+ return t('You must enter an e-mail address.');
}
- elseif ($file !== NULL) {
- $form_state['values']['picture_upload'] = $file;
+ if (!valid_email_address($mail)) {
+ return t('The e-mail address %mail is not valid.', array('%mail' => $mail));
}
}
@@ -578,45 +801,6 @@ function user_permission() {
}
/**
- * Implements hook_file_download().
- *
- * Ensure that user pictures (avatars) are always downloadable.
- */
-function user_file_download($uri) {
- if (strpos(file_uri_target($uri), variable_get('user_picture_path', 'pictures') . '/picture-') === 0) {
- $info = image_get_info($uri);
- return array('Content-Type' => $info['mime_type']);
- }
-}
-
-/**
- * Implements hook_file_move().
- */
-function user_file_move($file, $source) {
- // If a user's picture is replaced with a new one, update the record in
- // the users table.
- if (isset($file->fid) && isset($source->fid) && $file->fid != $source->fid) {
- db_update('users')
- ->fields(array(
- 'picture' => $file->fid,
- ))
- ->condition('picture', $source->fid)
- ->execute();
- }
-}
-
-/**
- * Implements hook_file_predelete().
- */
-function user_file_predelete($file) {
- // Remove any references to the file.
- db_update('users')
- ->fields(array('picture' => 0))
- ->condition('picture', $file->fid)
- ->execute();
-}
-
-/**
* Implements hook_search_info().
*/
function user_search_info() {
@@ -693,7 +877,6 @@ function user_user_view($account) {
*
* @see user_account_form_validate()
* @see user_validate_current_pass()
- * @see user_validate_picture()
* @see user_validate_mail()
*/
function user_account_form(&$form, &$form_state) {
@@ -842,34 +1025,6 @@ function user_account_form(&$form, &$form_state) {
'#format' => isset($account->signature_format) ? $account->signature_format : NULL,
);
- // Picture/avatar.
- $form['picture'] = array(
- '#type' => 'fieldset',
- '#title' => t('Picture'),
- '#weight' => 1,
- '#access' => (!$register && variable_get('user_pictures', 0)),
- );
- $form['picture']['picture'] = array(
- '#type' => 'value',
- '#value' => isset($account->picture) ? $account->picture : NULL,
- );
- $form['picture']['picture_current'] = array(
- '#markup' => theme('user_picture', array('account' => $account)),
- );
- $form['picture']['picture_delete'] = array(
- '#type' => 'checkbox',
- '#title' => t('Delete picture'),
- '#access' => !empty($account->picture->fid),
- '#description' => t('Check this box to delete your current picture.'),
- );
- $form['picture']['picture_upload'] = array(
- '#type' => 'file',
- '#title' => t('Upload picture'),
- '#size' => 48,
- '#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', '')),
- );
- $form['#validate'][] = 'user_validate_picture';
-
if (module_exists('language') && language_multilingual()) {
$languages = language_list();
@@ -1215,55 +1370,6 @@ function user_format_name($account) {
}
/**
- * Process variables for user-picture.tpl.php.
- *
- * The $variables array contains the following arguments:
- * - $account: A user, node or comment object with 'name', 'uid' and 'picture'
- * fields.
- *
- * @see user-picture.tpl.php
- */
-function template_preprocess_user_picture(&$variables) {
- $variables['user_picture'] = '';
- if (variable_get('user_pictures', 0)) {
- $account = $variables['account'];
- if (!empty($account->picture)) {
- // @TODO: Ideally this function would only be passed file objects, but
- // since there's a lot of legacy code that JOINs the {users} table to
- // {node} or {comments} and passes the results into this function if we
- // a numeric value in the picture field we'll assume it's a file id
- // and load it for them. Once we've got user_load_multiple() and
- // comment_load_multiple() functions the user module will be able to load
- // the picture files in mass during the object's load process.
- if (is_numeric($account->picture)) {
- $account->picture = file_load($account->picture);
- }
- if (!empty($account->picture->uri)) {
- $filepath = $account->picture->uri;
- }
- }
- elseif (variable_get('user_picture_default', '')) {
- $filepath = variable_get('user_picture_default', '');
- }
- if (isset($filepath)) {
- $alt = t("@user's picture", array('@user' => user_format_name($account)));
- // If the image does not have a valid Drupal scheme (for eg. HTTP),
- // don't load image styles.
- if (module_exists('image') && file_valid_uri($filepath) && $style = variable_get('user_picture_style', '')) {
- $variables['user_picture'] = theme('image_style', array('style_name' => $style, 'uri' => $filepath, 'alt' => $alt, 'title' => $alt));
- }
- else {
- $variables['user_picture'] = theme('image', array('uri' => $filepath, 'alt' => $alt, 'title' => $alt));
- }
- if (!empty($account->uid) && user_access('access user profiles')) {
- $attributes = array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE);
- $variables['user_picture'] = l($variables['user_picture'], "user/$account->uid", $attributes);
- }
- }
- }
-}
-
-/**
* Preprocesses variables for theme_username().
*
* Modules that make any changes to variables like 'name' or 'extra' must insure
@@ -2335,8 +2441,6 @@ function user_view_page($account) {
*
* When viewing a user profile, the $page array contains:
*
- * - $page['content']['user_picture']:
- * User's rendered picture.
* - $page['content']['member_for']:
* Contains the default "Member for" profile data for a user.
* - $page['content']['#account']:
@@ -3386,6 +3490,9 @@ function user_form_process_password_confirm($element) {
/**
* Implements hook_node_load().
+ *
+ * @todo: Deprecated by node_entity_prepare_view(). Update code that depends on
+ * these properties.
*/
function user_node_load($nodes, $types) {
// Build an array of all uids for node authors, keyed by nid.
@@ -3394,19 +3501,20 @@ function user_node_load($nodes, $types) {
$uids[$nid] = $node->uid;
}
- // Fetch name, picture, and data for these users.
- $user_fields = db_query("SELECT uid, name, picture, data FROM {users} WHERE uid IN (:uids)", array(':uids' => $uids))->fetchAllAssoc('uid');
+ // Fetch name and data for these users.
+ $user_fields = db_query("SELECT uid, name, data FROM {users} WHERE uid IN (:uids)", array(':uids' => $uids))->fetchAllAssoc('uid');
// Add these values back into the node objects.
foreach ($uids as $nid => $uid) {
$nodes[$nid]->name = $user_fields[$uid]->name;
- $nodes[$nid]->picture = $user_fields[$uid]->picture;
$nodes[$nid]->data = $user_fields[$uid]->data;
}
}
/**
* Implements hook_image_style_delete().
+ *
+ * @todo Obsolete?
*/
function user_image_style_delete($style) {
// If a style is deleted, update the variables.
@@ -3416,6 +3524,8 @@ function user_image_style_delete($style) {
/**
* Implements hook_image_style_save().
+ *
+ * @todo Obsolete?
*/
function user_image_style_save($style) {
// If a style is renamed, update the variables that use it.
diff --git a/core/modules/user/user.test b/core/modules/user/user.test
index c911088..7d484e9 100644
--- a/core/modules/user/user.test
+++ b/core/modules/user/user.test
@@ -168,7 +168,6 @@ class UserRegistrationTestCase extends DrupalWebTestCase {
$this->assertEqual($new_user->timezone, variable_get('date_default_timezone'), t('Correct time zone field.'));
$this->assertEqual($new_user->langcode, language_default()->langcode, t('Correct language field.'));
$this->assertEqual($new_user->preferred_langcode, language_default()->langcode, t('Correct preferred language field.'));
- $this->assertEqual($new_user->picture, 0, t('Correct picture field.'));
$this->assertEqual($new_user->init, $mail, t('Correct init field.'));
}
@@ -258,6 +257,42 @@ class UserRegistrationTestCase extends DrupalWebTestCase {
}
}
+class UserAttachTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Embedded user on node page.',
+ 'description' => 'Verify that author details are shown on node and comment render.',
+ 'group' => 'User',
+ );
+ }
+
+ function testUserViewOnNodePage() {
+ $this->user = $this->drupalCreateUser();
+ $this->drupalLogin($this->user);
+ $edit = array(
+ 'mail' => $this->user->mail,
+ );
+ $test_image = current($this->drupalGetTestFiles('image'));
+ $edit['files[field_user_picture_' . LANGUAGE_NONE . '_0]'] = drupal_realpath($test_image->uri);
+ $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
+
+ $node = $this->drupalCreateNode(array('type' => 'article'));
+ // Set display preferences so pictures are enabled for this content type.
+ variable_set('theme_settings', array('toggle_node_user_picture' => TRUE));
+ variable_set('node_submitted_' . $node->type, TRUE);
+ $this->drupalGet('node/' . $node->nid);
+ $this->assertRaw('field-name-field-user-picture', 'User picture is shown on node render.');
+
+ // Disable picture for nodes and enable for comments.
+ variable_set('theme_settings', array('toggle_comment_user_picture' => TRUE));
+
+ $edit = array();
+ $edit['comment_body[' . LANGUAGE_NONE . '][0][value]'] = $this->randomString();
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertRaw('field-name-field-user-picture', 'User picture is shown on comment render.');
+ }
+}
+
class UserValidationTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
diff --git a/core/themes/bartik/css/style-rtl.css b/core/themes/bartik/css/style-rtl.css
index 90638eb..19ed3f6 100644
--- a/core/themes/bartik/css/style-rtl.css
+++ b/core/themes/bartik/css/style-rtl.css
@@ -84,9 +84,9 @@ ul.tips {
/* ----------------- Content ------------------ */
-.submitted .user-picture img {
+.submitted .field-name-field-user-picture img {
float: right;
- margin-left: 5px;
+ margin-left: 20px;
margin-right: 0;
}
.field-type-taxonomy-term-reference .field-label {
@@ -105,7 +105,7 @@ ul.tips {
/* ----------------- Comments ----------------- */
-.comment .user-picture img {
+.comment .field-name-field-user-picture img {
margin-right: 0;
}
.comment .attribution {
diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css
index 7ab3d4e..651cfc3 100644
--- a/core/themes/bartik/css/style.css
+++ b/core/themes/bartik/css/style.css
@@ -595,10 +595,9 @@ h1#page-title {
color: #68696b;
margin-bottom: -5px;
}
-.submitted .user-picture img {
+.submitted .field-name-field-user-picture img {
float: left; /* LTR */
- height: 20px;
- margin: 1px 5px 0 0; /* LTR */
+ margin: 1px 20px 0 0; /* LTR */
}
.field-type-taxonomy-term-reference {
margin: 0 0 1.2em;
@@ -630,7 +629,7 @@ h1#page-title {
text-align: right;
}
.field-type-image img,
-.user-picture img {
+.field-name-field-user-picture img {
margin: 0 0 1em;
}
ul.links {
@@ -651,7 +650,7 @@ ul.links {
.comment h2.title {
margin-bottom: 1em;
}
-.comment div.user-picture img {
+.comment .field-name-field-user-picture img {
margin-left: 0; /* LTR */
}
.comment {
@@ -1029,7 +1028,7 @@ div.messages {
/* -------------- User Profile -------------- */
-.profile .user-picture {
+.profile .field-name-field-user-picture {
float: none;
}
diff --git a/core/themes/bartik/templates/comment.tpl.php b/core/themes/bartik/templates/comment.tpl.php
index 0b70594..8361a3e 100644
--- a/core/themes/bartik/templates/comment.tpl.php
+++ b/core/themes/bartik/templates/comment.tpl.php
@@ -20,7 +20,6 @@
* - $permalink: Comment permalink.
* - $submitted: Submission information created from $author and $created
* during template_preprocess_comment().
- * - $picture: Authors picture.
* - $signature: Authors signature.
* - $status: Comment status. Possible values are:
* unpublished, published, or preview.
@@ -64,7 +63,7 @@
-
+
-
+
diff --git a/profiles/standard/standard.install b/profiles/standard/standard.install
index 30a943b..70a13bf 100644
--- a/profiles/standard/standard.install
+++ b/profiles/standard/standard.install
@@ -273,12 +273,6 @@ function standard_install() {
// Don't display date and author information for "Basic page" nodes by default.
variable_set('node_submitted_page', FALSE);
- // Enable user picture support and set the default to a square thumbnail option.
- variable_set('user_pictures', '1');
- variable_set('user_picture_dimensions', '1024x1024');
- variable_set('user_picture_file_size', '800');
- variable_set('user_picture_style', 'thumbnail');
-
// Allow visitor account creation with administrative approval.
variable_set('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
@@ -402,6 +396,18 @@ function standard_install() {
);
field_create_instance($instance);
+
+ module_load_install('user');
+ user_create_picture_field();
+
+ // Remove 'summary' pseudo-field from compact view mode on the User entity.
+ $bundle_settings = field_bundle_settings('user', 'user');
+ $bundle_settings['extra_fields']['display']['member_for']['compact'] = array(
+ 'visible' => FALSE,
+ 'weight' => 10,
+ );
+ field_bundle_settings('user', 'user', $bundle_settings);
+
// Enable default permissions for system roles.
$filtered_html_permission = filter_permission_name($filtered_html_format);
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access content', 'access comments', $filtered_html_permission));