Commit 4433a2a2 authored by catch's avatar catch

Issue #1292470 by sun, tim.plunkett, Berdir, moshe weitzman, pcambra, Niklas...

Issue #1292470 by sun, tim.plunkett, Berdir, moshe weitzman, pcambra, Niklas Fiekas, andypost et al: Convert user pictures to Image Field.
parent 3ee3f5ad
......@@ -499,7 +499,11 @@ function update_fix_d8_requirements() {
global $conf;
if (drupal_get_installed_schema_version('system') < 8000 && !update_variable_get('update_d8_requirements', FALSE)) {
// @todo: Make critical, first-run changes to the database here.
// Make sure that file.module is enabled as it is required for the user
// picture upgrade path.
update_module_enable(array('file'));
update_variable_set('update_d8_requirements', TRUE);
}
}
......
......@@ -1599,7 +1599,6 @@ function comment_preview(Comment $comment) {
$comment->name = check_plain($account->name);
$comment->signature = $account->signature;
$comment->signature_format = $account->signature_format;
$comment->picture = $account->picture;
}
elseif (empty($comment->name)) {
$comment->name = config('user.settings')->get('anonymous');
......@@ -1655,7 +1654,14 @@ 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)) : '';
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');
}
else {
$variables['user_picture'] = array();
}
$variables['signature'] = $comment->signature;
$uri = $comment->uri();
......
......@@ -27,6 +27,9 @@ public function buildContent(array $entities = array(), $view_mode = 'full', $la
return $return;
}
// Attach user account.
user_attach_accounts($entities);
parent::buildContent($entities, $view_mode, $langcode);
foreach ($entities as $entity) {
......
......@@ -34,7 +34,7 @@ protected function buildQuery($ids, $revision_id = FALSE) {
$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;
}
......
......@@ -44,12 +44,11 @@ function testCommentPreview() {
// Login as web user and add a signature and a user picture.
$this->drupalLogin($this->web_user);
config('user.settings')->set('signatures', 1)->save();
variable_set('user_pictures', 1);
$test_signature = $this->randomName();
$edit['signature[value]'] = '<a href="http://example.com/">' . $test_signature. '</a>';
$edit['signature[format]'] = 'filtered_html';
$image = current($this->drupalGetTestFiles('image'));
$edit['files[picture_upload]'] = drupal_realpath($image->uri);
$edit['files[user_picture_und_0]'] = drupal_realpath($image->uri);
$this->drupalPost('user/' . $this->web_user->uid . '/edit', $edit, t('Save'));
// As the web user, fill in the comment form and preview the comment.
......
......@@ -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.
......@@ -79,7 +80,7 @@
<?php print render($title_suffix); ?>
<footer>
<?php print $user_picture; ?>
<?php print render($user_picture); ?>
<p class="submitted"><?php print $submitted; ?></p>
<?php
// Indicate the semantic relationship between parent and child comments
......
......@@ -357,6 +357,15 @@ function _update_7000_field_create_instance($field, &$instance) {
'deleted' => 0,
);
// Merge in display defaults.
if (isset($instance['display'])) {
foreach ($instance['display'] as &$display) {
$display += array(
'weight' => 0,
);
}
}
// The serialized 'data' column contains everything from $instance that does
// not have its own column and is not automatically populated when the
// instance is read.
......
......@@ -118,11 +118,15 @@ function field_sql_storage_update_8000(&$sandbox) {
$table_info = array($data_table => $primary_key_data, $revision_table => $primary_key_revision);
foreach ($table_info as $table => $primary_key) {
db_drop_primary_key($table);
db_drop_index($table, 'language');
db_change_field($table, 'language', 'langcode', $field_langcode);
db_add_primary_key($table, $primary_key);
db_add_index($table, 'langcode', $langcode_index);
// Do not attempt to rename the 'language' column for fields that already
// contain it (created during the upgrade before this update function).
if (db_field_exists($table, 'language')) {
db_drop_primary_key($table);
db_drop_index($table, 'language');
db_change_field($table, 'language', 'langcode', $field_langcode);
db_add_primary_key($table, $primary_key);
db_add_index($table, 'langcode', $langcode_index);
}
}
}
}
......@@ -455,6 +455,12 @@ function field_ui_widget_type_options($field_type = NULL, $by_label = FALSE) {
$widget_types = field_info_widget_types();
uasort($widget_types, 'drupal_sort_weight');
foreach ($widget_types as $name => $widget_type) {
if (!isset($widget_type['field_types'])) {
// @todo: TestFieldWidgetMultiple widget is found but the info alter
// hook isn't. Looks like another namespace problem. Ignore this entry
// to allow the tests to pass. See http://drupal.org/node/1836008.
continue;
}
foreach ($widget_type['field_types'] as $widget_field_type) {
// Check that the field type exists.
if (isset($field_types[$widget_field_type])) {
......
......@@ -32,6 +32,19 @@ function setUp() {
$type_name = strtolower($this->randomName(8)) . '_test';
$type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
$this->type = $type->type;
// Create a default vocabulary.
$vocabulary = entity_create('taxonomy_vocabulary', array(
'name' => $this->randomName(),
'description' => $this->randomName(),
'machine_name' => drupal_strtolower($this->randomName()),
'langcode' => LANGUAGE_NOT_SPECIFIED,
'help' => '',
'nodes' => array('article' => 'article'),
'weight' => mt_rand(0, 10),
));
taxonomy_vocabulary_save($vocabulary);
$this->vocabulary = $vocabulary->machine_name;
}
/**
......
......@@ -192,11 +192,11 @@ function testNonInitializedFields() {
* Tests hiding the view modes fieldset when there's only one available.
*/
function testSingleViewMode() {
$this->drupalGet('admin/config/people/accounts/display');
$this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary . '/display');
$this->assertNoText('Use custom display settings for the following view modes', 'Custom display settings fieldset found.');
// This may not trigger a notice when 'view_modes_custom' isn't available.
$this->drupalPost('admin/config/people/accounts/display', array(), t('Save'));
$this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary . '/display', array(), t('Save'));
}
/**
......
......@@ -23,6 +23,10 @@ public function buildContent(array $entities = array(), $view_mode = 'full', $la
if (empty($entities)) {
return $return;
}
// Attach user account.
user_attach_accounts($entities);
parent::buildContent($entities, $view_mode, $langcode);
foreach ($entities as $key => $entity) {
......
......@@ -1148,7 +1148,14 @@ 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)) : '';
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['user_picture'] = array();
}
}
else {
$variables['display_submitted'] = FALSE;
......
......@@ -133,7 +133,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
......@@ -142,7 +141,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;
......
......@@ -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().
......@@ -86,7 +86,7 @@
<?php if ($display_submitted): ?>
<footer>
<?php print $user_picture; ?>
<?php print render($user_picture); ?>
<p class="submitted"><?php print $submitted; ?></p>
</footer>
<?php endif; ?>
......
<?php
/**
* @file
* Definition of Drupal\system\Tests\Upgrade\UserPictureUpgradePathTest.
*/
namespace Drupal\system\Tests\Upgrade;
/**
* Tests upgrading a filled database with user picture data.
*
* Loads a filled installation of Drupal 7 with user picture data and runs the
* upgrade process on it.
*/
class UserPictureUpgradePathTest extends UpgradePathTestBase {
public static function getInfo() {
return array(
'name' => 'User picture upgrade test',
'description' => 'Upgrade tests with user picture data.',
'group' => 'Upgrade path',
);
}
public function setUp() {
$path = drupal_get_path('module', 'system') . '/tests/upgrade';
$this->databaseDumpFiles = array(
$path . '/drupal-7.bare.standard_all.database.php.gz',
$path . '/drupal-7.user_picture.database.php',
);
parent::setUp();
}
/**
* Tests expected user picture conversions after a successful upgrade.
*/
public function testUserPictureUpgrade() {
$this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.');
// Retrieve the field instance and check for migrated settings.
$instance = field_info_instance('user', 'user_picture', 'user');
$file = entity_load('file', $instance['settings']['default_image']);
$this->assertIdentical($instance['settings']['default_image'], $file->id(), 'Default user picture has been migrated.');
$this->assertEqual($file->uri, 'public://user_pictures_dir/druplicon.png', 'File id matches the uri expected.');
$this->assertEqual($file->filename, 'druplicon.png');
$this->assertEqual($file->langcode, LANGUAGE_NOT_SPECIFIED);
$this->assertEqual($file->filemime, 'image/png');
$this->assertFalse(empty($file->uuid));
// Check file usage for the default image.
$usage = file_usage()->listUsage($file);
$field = field_info_field('user_picture');
$this->assertEqual(1, $usage['image']['default_image'][$field['id']]);
$this->assertEqual($instance['settings']['max_resolution'], '800x800', 'User picture maximum resolution has been migrated.');
$this->assertEqual($instance['settings']['max_filesize'], '700 KB', 'User picture maximum filesize has been migrated.');
$this->assertEqual($instance['description'], 'These are user picture guidelines.', 'User picture guidelines are now the user picture field description.');
$this->assertEqual($instance['settings']['file_directory'], 'user_pictures_dir', 'User picture directory path has been migrated.');
$this->assertEqual($instance['display']['default']['settings']['image_style'], 'thumbnail', 'User picture image style setting has been migrated.');
// Verify compact view mode default settings.
$this->drupalGet('admin/config/people/accounts/display/compact');
$this->assertFieldByName('fields[member_for][type]', 'hidden');
// Check the user picture and file usage record.
$user = user_load(1);
$file = file_load($user->user_picture[LANGUAGE_NOT_SPECIFIED][0]['fid']);
$this->assertEqual('public://user_pictures_dir/faked_image.png', $file->uri);
$usage = file_usage()->listUsage($file);
$this->assertEqual(1, $usage['file']['user'][1]);
}
}
......@@ -409,7 +409,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;
}
......
<?php
/**
* @file
* Database additions for user picture tests. Used in UserPictureUpgradePathTest.
*
* This dump only contains data and schema components relevant for user picture
* functionality. The drupal-7.bare.database.php file is imported before
* this dump, so the two form the database structure expected in tests
* altogether.
*/
// Add an image and assign it to uid 1.
$fid = db_insert('file_managed')
->fields(array(
'uri' => 'public://user_pictures_dir/faked_image.png',
'uid' => 1,
'status' => 1,
'filename' => 'faked_image.png',
'filesize' => 1000,
'filemime' => 'image/png',
'timestamp' => 1353542634,
))
->execute();
db_insert('file_usage')
->fields(array(
'fid' => $fid,
'module' => 'user',
'type' => 'user',
'id' => 1,
'count' => 1,
))
->execute();
db_update('users')
->condition('uid', 1)
->fields(array(
'picture' => $fid,
))
->execute();
// Set up variables needed for user picture support.
$deleted_variables = array(
'user_pictures',
'user_picture_default',
'user_picture_dimensions',
'user_picture_file_size',
'user_picture_guidelines',
'user_picture_path',
'user_picture_style',
);
db_delete('variable')
->condition('name', $deleted_variables, 'IN')
->execute();
db_insert('variable')->fields(array(
'name',
'value',
))
->values(array(
'name' => 'user_pictures',
'value' => 'i:1;',
))
->values(array(
'name' => 'user_picture_default',
'value' => 's:51:"sites/default/files/user_pictures_dir/druplicon.png";',
))
->values(array(
'name' => 'user_picture_dimensions',
'value' => 's:7:"800x800";',
))
->values(array(
'name' => 'user_picture_file_size',
'value' => 's:3:"700";',
))
->values(array(
'name' => 'user_picture_guidelines',
'value' => 's:34:"These are user picture guidelines.";',
))
->values(array(
'name' => 'user_picture_path',
'value' => 's:17:"user_pictures_dir";',
))
->values(array(
'name' => 'user_picture_style',
'value' => 's:9:"thumbnail";',
))
->execute();
......@@ -171,39 +171,6 @@ public function form(array $form, array &$form_state, EntityInterface $account)
'#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';
$user_preferred_langcode = $register ? $language_interface->langcode : user_preferred_langcode($account);
$user_preferred_admin_langcode = $register ? $language_interface->langcode : user_preferred_langcode($account, 'admin');
......
......@@ -47,7 +47,11 @@
* "full" = {
* "label" = "User account",
* "custom_settings" = FALSE
* }
* },
* "compact" = {
"label" = "Compact",
"custom_settings" = TRUE
}
* }
* )
*/
......@@ -167,13 +171,6 @@ class User extends Entity {
*/
public $preferred_admin_langcode = LANGUAGE_NOT_SPECIFIED;
/**
* The file ID of the user's picture.
*
* @var integer
*/
public $picture = 0;
/**
* The email address used for initial account creation.
*
......
<?php
/**
* @file
* Definition of Drupal\user\Plugin\views\field\Picture.
*/
namespace Drupal\user\Plugin\views\field;
use Drupal\Core\Annotation\Plugin;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\field\FieldPluginBase;
/**
* Field handler to provide simple renderer that allows using a themed user link.
*
* @ingroup views_field_handlers
*
* @Plugin(
* id = "user_picture",
* module = "user"
* )
*/
class Picture extends FieldPluginBase {
/**
* Overrides Drupal\views\Plugin\views\field\FieldPluginBase::init().
*/
public function init(ViewExecutable $view, &$options) {
parent::init($view, $options);
$this->additional_fields['uid'] = 'uid';
$this->additional_fields['name'] = 'name';
$this->additional_fields['mail'] = 'mail';
}
function element_type($none_supported = FALSE, $default_empty = FALSE, $inline = FALSE) {
if ($inline) {
return 'span';
}
if ($none_supported) {
if ($this->options['element_type'] === '0') {
return '';
}
}
if ($this->options['element_type']) {
return check_plain($this->options['element_type']);
}
if ($default_empty) {
return '';
}
if (isset($this->definition['element type'])) {
return $this->definition['element type'];
}
return 'div';
}
protected function defineOptions() {
$options = parent::defineOptions();
$options['link_photo_to_profile'] = array('default' => TRUE, 'bool' => TRUE);
$options['image_style'] = array('default' => '');
return $options;
}
public function buildOptionsForm(&$form, &$form_state) {
parent::buildOptionsForm($form, $form_state);
$form['link_photo_to_profile'] = array(
'#title' => t("Link to user's profile"),
'#description' => t("Link the user picture to the user's profile"),
'#type' => 'checkbox',
'#default_value' => $this->options['link_photo_to_profile'],
);
if (module_exists('image')) {
$styles = image_styles();
$style_options = array('' => t('Default'));
foreach ($styles as $style) {
$style_options[$style['name']] = $style['name'];
}
$form['image_style'] = array(
'#title' => t('Image style'),
'#description' => t('Using <em>Default</em> will use the site-wide image style for user pictures set in the <a href="!account-settings">Account settings</a>.', array('!account-settings' => url('admin/config/people/accounts', array('fragment' => 'edit-personalization')))),
'#type' => 'select',
'#options' => $style_options,
'#default_value' => $this->options['image_style'],
);
}
}
function render($values) {
if ($this->options['image_style'] && module_exists('image')) {
// @todo: Switch to always using theme('user_picture') when it starts
// supporting image styles. See http://drupal.org/node/1021564
if ($picture_fid = $this->get_value($values)) {
$picture = file_load($picture_fid);
$picture_filepath = $picture->uri;
}
else {
$picture_filepath = variable_get('user_picture_default', '');
}
if (file_valid_uri($picture_filepath)) {
$output = theme('image_style', array('style_name' => $this->options['image_style'], 'path' => $picture_filepath));
if ($this->options['link_photo_to_profile'] && user_access('access user profiles')) {
$uid = $this->get_value($values, 'uid');
$output = l($output, "user/$uid", array('html' => TRUE));
}
}
else {
$output = '';
}
}
else {
// Fake an account object.
$account = entity_create('user', array());
if ($this->options['link_photo_to_profile']) {
// Prevent template_preprocess_user_picture from adding a link
// by not setting the uid.
$account->uid = $this->get_value($values, 'uid');
}
$account->name = $this->get_value($values, 'name');
$account->mail = $this->get_value($values, 'mail');
$account->picture = $this->get_value($values);
$output = theme('user_picture', array('account' => $account));
}
return $output;
}
}
......@@ -26,8 +26,7 @@ public static function getInfo() {
* Test user edit page.
*/
function testUserEdit() {
// Test user edit functionality with user pictures disabled.
variable_set('user_pictures', 0);
// Test user edit functionality.
$user1 = $this->drupalCreateUser(array('change own username'));
$user2 = $this->drupalCreateUser(array());
$this->drupalLogin($user1);
......@@ -37,11 +36,6 @@ function testUserEdit() {
$this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
$this->assertRaw(t('The name %name is already taken.', array('%name' => $edit['name'])));
// Repeat the test with user pictures enabled, which modifies the form.
variable_set('user_pictures', 1);
$this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
$this->assertRaw(t('The name %name is already taken.', array('%name' => $edit['name'])));
// Check that filling out a single password field does not validate.
$edit = array();
$edit['pass[pass1]'] = '';
......
......@@ -185,7 +185,6 @@ function testRegistrationDefaultValues() {
$this->assertEqual($new_user->timezone, variable_get('date_default_timezone'), 'Correct time zone field.');
$this->assertEqual($new_user->langcode, language_default()->langcode, 'Correct language field.');
$this->assertEqual($new_user->preferred_langcode, language_default()->langcode, 'Correct preferred language field.');
$this->assertEqual($new_user->picture, 0, 'Correct picture field.');
$this->assertEqual($new_user->init, $mail, 'Correct init field.');
}
......
......@@ -23,12 +23,7 @@ class UserStorageController extends DatabaseStorageController {
* Overrides Drupal\Core\Entity\DatabaseStorageController::attachLoad().
*/
function attachLoad(&$queried_users, $load_revision = 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) {
......@@ -45,15 +40,6 @@ function attachLoad(&$queried_users, $load_revision = FALSE) {
$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, $load_revision);
......@@ -97,45 +83,7 @@ protected function preSave(EntityInterface $entity) {
}