user.install

Install, update and uninstall functions for the user module.

File

drupal/core/modules/user/user.install
View source
<?php

/**
 * @file
 * Install, update and uninstall functions for the user module.
 */
use Drupal\Component\Uuid\Uuid;
use Drupal\Core\Language\Language;

/**
 * Implements hook_schema().
 */
function user_schema() {

  // 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,
      ),
      'uuid' => array(
        'description' => 'Unique Key: Universally unique identifier for this entity.',
        'type' => 'varchar',
        'length' => 128,
        'not null' => FALSE,
      ),
      'name' => array(
        'type' => 'varchar',
        'length' => 60,
        'not null' => TRUE,
        'default' => '',
        'description' => 'Unique user name.',
      ),
      'langcode' => array(
        'type' => 'varchar',
        'length' => 12,
        'not null' => TRUE,
        'default' => '',
        'description' => "The {language}.langcode of the user's profile.",
      ),
      '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 ID 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.",
      ),
      'preferred_langcode' => array(
        'type' => 'varchar',
        'length' => 12,
        'not null' => TRUE,
        'default' => '',
        'description' => 'The {language}.langcode that the user prefers for receiving emails and viewing the site.',
      ),
      'preferred_admin_langcode' => array(
        'type' => 'varchar',
        'length' => 12,
        'not null' => TRUE,
        'default' => '',
        'description' => 'The {language}.langcode that the user prefers for viewing administration pages.',
      ),
      'init' => array(
        'type' => 'varchar',
        'length' => 254,
        'not null' => FALSE,
        'default' => '',
        'description' => 'E-mail address used for initial account creation.',
      ),
    ),
    'indexes' => array(
      'access' => array(
        'access',
      ),
      'created' => array(
        'created',
      ),
      'mail' => array(
        'mail',
      ),
    ),
    'unique keys' => array(
      'uuid' => array(
        'uuid',
      ),
      'name' => array(
        'name',
      ),
    ),
    'primary key' => array(
      'uid',
    ),
    'foreign keys' => array(
      'signature_format' => array(
        'table' => 'filter_format',
        'columns' => array(
          'signature_format' => 'format',
        ),
      ),
    ),
  );
  $schema['role_permission'] = array(
    'description' => 'Stores the permissions assigned to user roles.',
    'fields' => array(
      'rid' => array(
        'type' => 'varchar',
        'length' => 64,
        '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['users_data'] = array(
    'description' => 'Stores module data as key/value pairs per user.',
    'fields' => array(
      'uid' => array(
        'description' => 'Primary key: {users}.uid for user.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'module' => array(
        'description' => 'The name of the module declaring the variable.',
        'type' => 'varchar',
        'length' => 204,
        'not null' => TRUE,
        'default' => '',
      ),
      'name' => array(
        'description' => 'The identifier of the data.',
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ),
      'value' => array(
        'description' => 'The value.',
        'type' => 'blob',
        'not null' => FALSE,
        'size' => 'big',
      ),
      'serialized' => array(
        'description' => 'Whether value is serialized.',
        'type' => 'int',
        'size' => 'tiny',
        'unsigned' => TRUE,
        'default' => 0,
      ),
    ),
    'primary key' => array(
      'uid',
      'module',
      'name',
    ),
    'indexes' => array(
      'module' => array(
        'module',
      ),
      'name' => array(
        'name',
      ),
    ),
    'foreign keys' => array(
      'uid' => array(
        'users' => 'uid',
      ),
    ),
  );
  $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' => 'varchar',
        'length' => 64,
        'not null' => TRUE,
        '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,
  ))
    ->execute();
}

/**
 * Creates a user picture image field for the User entity.
 *
 * This is only used in core's standard.install, but is kept as a separate
 * helper function so that other install profiles can reuse it.
 */
function user_install_picture_field() {
  $t = get_t();
  $field = array(
    'field_name' => 'user_picture',
    'module' => 'image',
    'type' => 'image',
    'cardinality' => 1,
    'locked' => FALSE,
    'indexes' => array(
      'fid' => array(
        'fid',
      ),
    ),
    'settings' => array(
      'uri_scheme' => 'public',
      'default_image' => FALSE,
    ),
  );
  $field = field_create_field($field);
  $instance = array(
    'field_name' => 'user_picture',
    'entity_type' => 'user',
    'label' => 'Picture',
    'bundle' => 'user',
    'description' => $t('Your virtual face or picture.'),
    'required' => FALSE,
    'settings' => array(
      'file_extensions' => 'png gif jpg jpeg',
      'file_directory' => 'pictures',
      'max_filesize' => '30 KB',
      'alt_field' => 0,
      'title_field' => 0,
      'max_resolution' => '85x85',
      'min_resolution' => '',
      'default_image' => 0,
    ),
  );
  field_create_instance($instance);

  // Assign form display settings for the 'default' view mode.
  entity_get_form_display('user', 'user', 'default')
    ->setComponent('user_picture', array(
    'type' => 'image_image',
    'settings' => array(
      'progress_indicator' => 'throbber',
      'preview_image_style' => 'thumbnail',
    ),
    'weight' => -1,
  ))
    ->save();

  // Assign display settings for the 'default' and 'compact' view modes.
  entity_get_display('user', 'user', 'default')
    ->setComponent('user_picture', array(
    'label' => 'hidden',
    'type' => 'image',
    'settings' => array(
      'image_style' => 'thumbnail',
      'image_link' => 'content',
    ),
  ))
    ->save();
  entity_get_display('user', 'user', 'compact')
    ->setComponent('user_picture', array(
    'label' => 'hidden',
    'type' => 'image',
    'settings' => array(
      'image_style' => 'thumbnail',
      'image_link' => 'content',
    ),
  ))
    ->removeComponent('member_for')
    ->save();
}

/**
 * Implements hook_update_dependencies().
 */
function user_update_dependencies() {

  // Convert user picture to field after the fields and instances are converted
  // to ConfigEntities.
  $dependencies['user'][8011] = array(
    'field' => 8003,
  );
  return $dependencies;
}

/**
 * @addtogroup updates-7.x-to-8.x
 * @{
 */

/**
 * The 'Member for' extra field has moved one level up in the array.
 */
function user_update_8000() {
  $settings = field_bundle_settings('user', 'user');
  if (isset($settings['extra_fields']['display']['summary'])) {
    $settings['extra_fields']['display']['member_for'] = $settings['extra_fields']['display']['summary'];
    unset($settings['extra_fields']['display']['summary']);
    field_bundle_settings('user', 'user', $settings);
  }
}

/**
 * Splits {users}.language field to langcode and preferred_langcode.
 *
 * @see http://drupal.org/node/1454538
 */
function user_update_8001() {

  // The former language field is the language preference of the user. Rename
  // this to preferred_langcode in order to distinguish it from the langcode
  // field common to all entity types, used for identifying the language of the
  // entity itself.
  $preferred_langcode_field = array(
    'type' => 'varchar',
    'length' => 12,
    'not null' => TRUE,
    'default' => '',
    'description' => 'The {language}.langcode that the user prefers for receiving emails and viewing the site.',
  );
  db_change_field('users', 'language', 'preferred_langcode', $preferred_langcode_field);

  // Add the langcode field.
  $langcode_field = array(
    'type' => 'varchar',
    'length' => 12,
    'not null' => TRUE,
    'default' => '',
    'description' => "The {language}.langcode of the user's profile.",
  );
  db_add_field('users', 'langcode', $langcode_field);

  // Since distinguishing the language of the user entity from the user's
  // preferred language is a new feature in Drupal 8, assume that for updated
  // sites, existing user entities are in the user's preferred language.
  db_update('users')
    ->expression('langcode', 'preferred_langcode')
    ->execute();
}

/**
 * Replace serial role IDs with machine name strings.
 */
function user_update_8002() {

  // Change serial rid columns into strings.
  $column = array(
    'type' => 'varchar',
    'length' => 64,
    'not null' => TRUE,
    'description' => 'Primary Key: Unique role ID.',
  );
  db_change_field('role', 'rid', 'rid', $column);
  $column['description'] = 'Foreign Key: {role}.rid.';
  db_change_field('role_permission', 'rid', 'rid', $column);
  $column['description'] = 'Primary Key: {role}.rid for role.';
  db_change_field('users_roles', 'rid', 'rid', $column);

  // Enlarge the role name (label) column.
  $column = array(
    'type' => 'varchar',
    'length' => 255,
    'not null' => TRUE,
    'default' => '',
    'description' => 'Role label.',
  );
  db_change_field('role', 'name', 'name', $column);

  // Remove unique index.
  db_drop_unique_key('role', 'name');

  // Rename the built-in serial role IDs into the hardcoded machine names.
  db_update('role')
    ->fields(array(
    'rid' => DRUPAL_ANONYMOUS_RID,
  ))
    ->condition('rid', 1)
    ->execute();
  db_update('role')
    ->fields(array(
    'rid' => DRUPAL_AUTHENTICATED_RID,
  ))
    ->condition('rid', 2)
    ->execute();
  db_update('role_permission')
    ->fields(array(
    'rid' => DRUPAL_ANONYMOUS_RID,
  ))
    ->condition('rid', 1)
    ->execute();
  db_update('role_permission')
    ->fields(array(
    'rid' => DRUPAL_AUTHENTICATED_RID,
  ))
    ->condition('rid', 2)
    ->execute();
  db_update('users_roles')
    ->fields(array(
    'rid' => DRUPAL_ANONYMOUS_RID,
  ))
    ->condition('rid', 1)
    ->execute();
  db_update('users_roles')
    ->fields(array(
    'rid' => DRUPAL_AUTHENTICATED_RID,
  ))
    ->condition('rid', 2)
    ->execute();
}

/**
 * Create a UUID column for users.
 */
function user_update_8003() {
  $spec = array(
    'description' => 'Unique Key: Universally unique identifier for this entity.',
    'type' => 'varchar',
    'length' => 128,
    'not null' => FALSE,
  );
  $keys = array(
    'unique keys' => array(
      'uuid' => array(
        'uuid',
      ),
    ),
  );

  // Account for sites having the contributed UUID module installed.
  if (db_field_exists('users', 'uuid')) {
    db_change_field('users', 'uuid', 'uuid', $spec, $keys);
  }
  else {
    db_add_field('users', 'uuid', $spec, $keys);
  }
}

/**
 * Moves account settings from variable to config.
 *
 * @ingroup config_upgrade
 */
function user_update_8004() {
  update_variables_to_config('user.settings', array(
    'anonymous' => 'anonymous',
    'user_admin_role' => 'admin_role',
    'user_register' => 'register',
    'user_signatures' => 'signatures',
    'user_cancel_method' => 'cancel_method',
    'user_mail_status_activated_notify' => 'notify.status_activated',
    'user_mail_status_blocked_notify' => 'notify.status_blocked',
    'user_mail_status_cancelled_notify' => 'notify.status_cancelled',
    'user_email_verification' => 'verify_mail',
    'user_password_reset_timeout' => 'password_reset_timeout',
  ));

  // Convert the user.settings:register numeric value to text value.
  $map = array(
    '0' => 'admin_only',
    '1' => 'visitors',
    '2' => 'visitors_admin_approval',
  );
  $config = config('user.settings');
  $user_register = $config
    ->get('register');
  $user_cancel_method = $config
    ->get('cancel_method');
  if (is_numeric($user_register) && isset($map[$user_register])) {
    $config
      ->set('register', $map[$user_register])
      ->save();
  }

  // Convert user.settings:cancel_method numeric value to text value.
  $cancel_map = array(
    '0' => 'user_cancel_block',
    '1' => 'user_cancel_block_unpublish',
    '2' => 'user_cancel_block_reassign',
    '3' => 'user_cancel_block_delete',
  );
  if (is_numeric($user_cancel_method) && isset($cancel_map[$user_cancel_method])) {
    $config
      ->set('cancel_method', ${$cancel_map}[$user_cancel_method])
      ->save();
  }
}

/**
 * Creates a preferred_admin_langcode column.
 */
function user_update_8005() {
  $spec = array(
    'description' => 'The {language}.langcode that the user prefers for viewing administration pages.',
    'type' => 'varchar',
    'length' => 12,
    'not null' => TRUE,
    'default' => '',
  );
  db_add_field('users', 'preferred_admin_langcode', $spec);
}

/**
 * Moves user mail settings from variable to config.
 *
 * @ingroup config_upgrade
 */
function user_update_8006() {
  update_variables_to_config('user.mail', array(
    'register_admin_created_subject' => 'register_admin_created.subject',
    'register_admin_created_body' => 'register_admin_created.body',
    'register_pending_approval_subject' => 'register_pending_approval.subject',
    'register_pending_approval_body' => 'register_pending_approval.body',
    'register_pending_approval_admin_body' => 'register_pending_approval_admin.body',
    'register_pending_approval_admin_subject' => 'register_pending_approval_admin.subject',
    'register_no_approval_required_subject' => 'register_no_approval_required.subject',
    'register_no_approval_required_body' => 'register_no_approval_required.body',
    'password_reset_subject' => 'password_reset.subject',
    'password_reset_body' => 'password_reset.body',
    'status_activated_subject' => 'status_activated.subject',
    'status_activated_body' => 'status_activated.body',
    'status_blocked_subject' => 'status_blocked.subject',
    'status_blocked_body' => 'status_blocked.body',
    'cancel_confirm_subject' => 'cancel_confirm.subject',
    'cancel_confirm_body' => 'cancel_confirm.body',
    'status_canceled_subject' => 'status_canceled.subject',
    'status_canceled_body' => 'status_canceled.body',
  ));
}

/**
 * Moves login flood settings from variable to config.
 *
 * @ingroup config_upgrade
 */
function user_update_8007() {
  update_variables_to_config('user.flood', array(
    'user_failed_login_identifier_uid_only' => 'uid_only',
    'user_failed_login_ip_limit' => 'ip_limit',
    'user_failed_login_ip_window' => 'ip_window',
    'user_failed_login_user_limit' => 'user_limit',
    'user_failed_login_user_window' => 'user_window',
  ));
}

/**
 * Make *id fields unsigned.
 */
function user_update_8008() {
  db_change_field('authmap', 'uid', 'uid', array(
    'type' => 'int',
    'unsigned' => TRUE,
    'not null' => TRUE,
    'default' => 0,
    'description' => "User's {users}.uid.",
  ));
}

/**
 * Generate a UUID for all users.
 */
function user_update_8009(&$sandbox) {
  if (!isset($sandbox['progress'])) {
    $sandbox['progress'] = 0;

    // The first user id is 0, so it needs to start with -1.
    $sandbox['last'] = -1;
    $sandbox['max'] = db_query('SELECT COUNT(uid) FROM {users} WHERE uuid IS NULL')
      ->fetchField();
  }
  $uids = db_query_range('SELECT uid FROM {users} WHERE uid > :uid AND uuid IS NULL ORDER BY uid ASC', 0, 10, array(
    ':uid' => $sandbox['last'],
  ))
    ->fetchCol();
  update_add_uuids($sandbox, 'users', 'uid', $uids);
  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : $sandbox['progress'] / $sandbox['max'];
}

/**
 * Create user picture field.
 */
function user_update_8011() {
  global $user;

  // User pictures can only be migrated to the new user picture image field
  // if Image module is installed.
  if (!module_exists('image')) {
    $old_schema = module_enable(array(
      'image',
    ));
    if ($old_schema['image'] == SCHEMA_UNINSTALLED) {

      // Install image.module with schema version 8002 as a previous version
      // would have to create tables that would be removed again.
      update_set_schema('image', 8002);

      // Inlined version of image_install(), make sure that the styles
      // directory exists.
      $directory = update_variable_get('file_default_scheme', 'public') . '://styles';
      file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
    }
  }
  if ($default_image = update_variable_get('user_picture_default', '')) {
    $picture_directory = update_variable_get('file_default_scheme', 'public') . '://' . update_variable_get('user_picture_path', 'pictures');
    file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY);
    $destination = file_stream_wrapper_uri_normalize($picture_directory . '/' . drupal_basename($default_image));

    // The file entity needs to be created manually as entities can not be
    // created/saved during the upgrade path. Attempt to replicate the behavior
    // of file_save_data() by updating an eventually existing record for that
    // file.
    file_unmanaged_save_data($default_image, $destination, FILE_EXISTS_REPLACE);
    $uuid = new Uuid();
    db_merge('file_managed')
      ->key(array(
      'uri' => $destination,
    ))
      ->fields(array(
      'uid' => $user->uid,
      'status' => FILE_STATUS_PERMANENT,
      'filename' => drupal_basename($destination),
      'uuid' => $uuid
        ->generate(),
      'langcode' => Language::LANGCODE_NOT_SPECIFIED,
      'filesize' => filesize($destination),
      'filemime' => file_get_mimetype($destination),
      'timestamp' => REQUEST_TIME,
    ))
      ->execute();
    $default_image_fid = db_query('SELECT fid FROM {file_managed} WHERE uri = :uri', array(
      ':uri' => $destination,
    ))
      ->fetchField();
  }

  // Create the field and instance.
  $field = array(
    'id' => 'user_picture',
    'module' => 'image',
    'type' => 'image',
    'cardinality' => 1,
    'locked' => FALSE,
    'indexes' => array(
      'fid' => array(
        'fid',
      ),
    ),
    'settings' => array(
      'uri_scheme' => 'public',
      'default_image' => FALSE,
    ),
  );
  _update_8003_field_create_field($field);
  $instance = array(
    'id' => 'user.user.user_picture',
    'entity_type' => 'user',
    'label' => 'Picture',
    'bundle' => 'user',
    'description' => update_variable_get('user_picture_guidelines', ''),
    'required' => FALSE,
    'settings' => array(
      'file_extensions' => 'png gif jpg jpeg',
      'file_directory' => update_variable_get('user_picture_path', 'pictures'),
      'max_filesize' => update_variable_get('user_picture_file_size', '30') . ' KB',
      'alt_field' => 0,
      'title_field' => 0,
      'max_resolution' => update_variable_get('user_picture_dimensions', '85x85'),
      'min_resolution' => '',
      'default_image' => !empty($default_image_fid) ? array(
        $default_image_fid,
      ) : array(),
    ),
  );
  _update_8003_field_create_instance($field, $instance);
  module_load_install('entity');
  if (update_variable_get('user_pictures', 0)) {
    $formatter = 'image';
    $widget = 'image_image';
  }
  else {
    $formatter = $widget = 'hidden';
  }

  // Assign form settings for the 'default' form mode.
  $form_display = _update_8000_entity_get_form_display('user', 'user', 'default');
  $form_display
    ->set('content.user_picture', array(
    'type' => $widget,
    'settings' => array(
      'progress_indicator' => 'throbber',
      'preview_image_style' => 'thumbnail',
    ),
    'weight' => -1,
  ))
    ->save();

  // Assign display settings for the 'default' and 'compact' view modes.
  $display = _update_8000_entity_get_display('user', 'user', 'default');
  $display
    ->set('content.user_picture', array(
    'label' => 'hidden',
    'type' => $formatter,
    'settings' => array(
      'image_style' => 'thumbnail',
      'image_link' => 'content',
    ),
    'weight' => 0,
  ))
    ->save();
  $display = _update_8000_entity_get_display('user', 'user', 'compact');
  $display
    ->set('content.user_picture', array(
    'label' => 'hidden',
    'type' => $formatter,
    'settings' => array(
      'image_style' => update_variable_get('user_picture_style', ''),
      'image_link' => 'content',
    ),
    'weight' => 0,
  ));
  $display
    ->set('content.member_for', array(
    'visible' => FALSE,
  ));
  $display
    ->save();

  // Add file usage for the default field.
  if (!empty($default_image_fid)) {
    db_insert('file_usage')
      ->fields(array(
      'fid' => $default_image_fid,
      'module' => 'image',
      'type' => 'default_image',
      'id' => $field['uuid'],
      'count' => 1,
    ))
      ->execute();
  }

  // Delete old variables.
  update_variable_del('user_pictures');
  update_variable_del('user_picture_path');
  update_variable_del('user_picture_default');
  update_variable_del('user_picture_style');
  update_variable_del('user_picture_dimensions');
  update_variable_del('user_picture_file_size');
  update_variable_del('user_picture_guidelines');
}

/**
 * Migrate {users}.picture to 'user_picture' image field.
 */
function user_update_8012(&$sandbox) {

  // Initialize total values to process.
  if (!isset($sandbox['total'])) {
    $sandbox['total'] = (int) db_query('SELECT COUNT(picture) FROM {users} WHERE picture > 0')
      ->fetchField();
    $sandbox['processed'] = 0;
  }
  if ($sandbox['total']) {

    // Retrieve next 20 rows to migrate.
    $rows = db_query_range('SELECT uid, picture FROM {users} WHERE picture > 0', 0, 20)
      ->fetchAllKeyed();
    foreach ($rows as $uid => $fid) {

      // Add a row to the field data and revision tables.
      db_insert('field_data_user_picture')
        ->fields(array(
        'entity_type' => 'user',
        'bundle' => 'user',
        'entity_id' => $uid,
        'revision_id' => $uid,
        'langcode' => Language::LANGCODE_NOT_SPECIFIED,
        'delta' => 0,
        'user_picture_fid' => $fid,
      ))
        ->execute();
      db_insert('field_revision_user_picture')
        ->fields(array(
        'entity_type' => 'user',
        'bundle' => 'user',
        'entity_id' => $uid,
        'revision_id' => $uid,
        'langcode' => Language::LANGCODE_NOT_SPECIFIED,
        'delta' => 0,
        'user_picture_fid' => $fid,
      ))
        ->execute();

      // Update file usage from user to file module.
      // @see file_field_insert()
      // Old: file_usage_add($picture, 'user', 'user', $entity->uid);
      // New: file_usage_add(file_load($item['fid']), 'file', $entity_type, $id);
      db_update('file_usage')
        ->condition('fid', $fid)
        ->condition('module', 'user')
        ->condition('type', 'user')
        ->condition('id', $uid)
        ->fields(array(
        'module' => 'file',
      ))
        ->execute();
    }

    // Set picture column of the migrated users to 0.
    db_update('users')
      ->fields(array(
      'picture' => 0,
    ))
      ->condition('uid', array_keys($rows))
      ->execute();

    // Report status.
    $sandbox['processed'] += count($rows);
  }
  $sandbox['#finished'] = $sandbox['total'] ? $sandbox['processed'] / $sandbox['total'] : 1;
}

/**
 * Deletes {users}.picture field.
 */
function user_update_8013() {
  db_drop_field('users', 'picture');
}

/**
 * Create new {users_data} table.
 */
function user_update_8014() {

  // Create the {users_data} table.
  db_create_table('users_data', array(
    'description' => 'Stores module data as key/value pairs per user.',
    'fields' => array(
      'uid' => array(
        'description' => 'Primary key: {users}.uid for user.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'module' => array(
        'description' => 'The name of the module declaring the variable.',
        'type' => 'varchar',
        'length' => 204,
        'not null' => TRUE,
        'default' => '',
      ),
      'name' => array(
        'description' => 'The identifier of the data.',
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ),
      'value' => array(
        'description' => 'The value.',
        'type' => 'blob',
        'not null' => FALSE,
        'size' => 'big',
      ),
      'serialized' => array(
        'description' => 'Whether value is serialized.',
        'type' => 'int',
        'size' => 'tiny',
        'unsigned' => TRUE,
        'default' => 0,
      ),
    ),
    'primary key' => array(
      'uid',
      'module',
      'name',
    ),
    'indexes' => array(
      'module' => array(
        'module',
      ),
      'name' => array(
        'name',
      ),
    ),
    'foreign keys' => array(
      'uid' => array(
        'users' => 'uid',
      ),
    ),
  ));

  // Create backup table for data migration.
  // Since the origin/owner of individual values in {users}.data is unknown,
  // other modules need to migrate their existing values from {_d7_users_data}.
  db_create_table('_d7_users_data', array(
    'description' => 'Backup of {users}.data for migration.',
    'fields' => array(
      'uid' => array(
        'description' => 'Primary Key: {users}.uid for user.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'name' => array(
        'description' => 'The name of the variable.',
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ),
      'value' => array(
        'description' => 'The serialized value of the variable.',
        'type' => 'blob',
        'not null' => FALSE,
        'size' => 'big',
        'serialize' => TRUE,
      ),
    ),
    'primary key' => array(
      'uid',
      'name',
    ),
    'foreign keys' => array(
      'uid' => array(
        'users' => 'uid',
      ),
    ),
  ));
}

/**
 * Move existing {users}.data into {_d7_users_data} migration table.
 */
function user_update_8015(&$sandbox) {
  if (!isset($sandbox['progress'])) {
    $sandbox['progress'] = 0;

    // The anonymous user cannot have data, so start with uid 1.
    $sandbox['last'] = 0;
    $sandbox['max'] = db_query('SELECT COUNT(uid) FROM {users} WHERE uid > 0')
      ->fetchField();
  }

  // Process 20 user records at a time. E.g., if there are 10 data keys per user
  // record, that leads to an insert query with 200 values.
  $result = db_query_range('SELECT uid, data FROM {users} WHERE uid > :uid ORDER BY uid ASC', 0, 20, array(
    ':uid' => $sandbox['last'],
  ))
    ->fetchAllKeyed();
  $query = db_insert('_d7_users_data')
    ->fields(array(
    'uid',
    'name',
    'value',
  ));
  $has_values = FALSE;
  foreach ($result as $uid => $data) {
    $sandbox['progress']++;
    $sandbox['last'] = $uid;
    if (empty($data)) {
      continue;
    }
    $data = unserialize($data);
    if (!empty($data) && is_array($data)) {
      $has_values = TRUE;
      foreach ($data as $name => $value) {
        $query
          ->values(array(
          'uid' => $uid,
          'name' => $name,
          'value' => serialize($value),
        ));
      }
    }
  }
  if ($has_values) {
    $query
      ->execute();
  }
  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : $sandbox['progress'] / $sandbox['max'];
}

/**
 * Drop {users}.data column.
 */
function user_update_8016() {
  db_drop_field('users', 'data');
}

/**
 * Migrate user roles into configuration.
 *
 * @ingroup config_upgrade
 */
function user_update_8017() {
  $uuid = new Uuid();
  $roles = db_select('role', 'r')
    ->fields('r')
    ->execute()
    ->fetchAll();
  foreach ($roles as $role) {
    config('user.role.' . $role->rid)
      ->set('id', $role->rid)
      ->set('uuid', $uuid
      ->generate())
      ->set('label', $role->name)
      ->set('weight', $role->weight)
      ->set('langcode', Language::LANGCODE_NOT_SPECIFIED)
      ->save();
  }
}

/**
 * @} End of "addtogroup updates-7.x-to-8.x".
 */

Functions

Namesort descending Description
user_install Implements hook_install().
user_install_picture_field Creates a user picture image field for the User entity.
user_schema Implements hook_schema().
user_update_8000 The 'Member for' extra field has moved one level up in the array.
user_update_8001 Splits {users}.language field to langcode and preferred_langcode.
user_update_8002 Replace serial role IDs with machine name strings.
user_update_8003 Create a UUID column for users.
user_update_8004 Moves account settings from variable to config.
user_update_8005 Creates a preferred_admin_langcode column.
user_update_8006 Moves user mail settings from variable to config.
user_update_8007 Moves login flood settings from variable to config.
user_update_8008 Make *id fields unsigned.
user_update_8009 Generate a UUID for all users.
user_update_8011 Create user picture field.
user_update_8012 Migrate {users}.picture to 'user_picture' image field.
user_update_8013 Deletes {users}.picture field.
user_update_8014 Create new {users_data} table.
user_update_8015 Move existing {users}.data into {_d7_users_data} migration table.
user_update_8016 Drop {users}.data column.
user_update_8017 Migrate user roles into configuration.
user_update_dependencies Implements hook_update_dependencies().