5 * Install, update and uninstall functions for the user module.
8 use Drupal\Component\Uuid\Uuid
;
9 use Drupal\Core\Language\Language
;
12 * Implements hook_schema().
14 function user_schema() {
15 // The table name here is plural, despite Drupal table naming standards,
16 // because "user" is a reserved word in many databases.
17 $schema['users'] = array(
18 'description' => 'Stores user data.',
24 'description' => 'Primary Key: Unique user ID.',
28 'description' => 'Unique Key: Universally unique identifier for this entity.',
38 'description' => 'Unique user name.',
45 'description' => "The {language}.langcode of the user's profile.",
52 'description' => "User's password (hashed).",
59 'description' => "User's e-mail address.",
66 'description' => "User's default theme.",
73 'description' => "User's signature.",
75 'signature_format' => array(
79 'description' => 'The filter format ID of the signature.',
85 'description' => 'Timestamp for when user was created.',
91 'description' => 'Timestamp for previous time user accessed the site.',
97 'description' => "Timestamp for user's last login.",
104 'description' => 'Whether the user is active(1) or blocked(0).',
110 'description' => "User's time zone.",
112 'preferred_langcode' => array(
117 'description' => 'The {language}.langcode that the user prefers for receiving emails and viewing the site.',
119 'preferred_admin_langcode' => array(
124 'description' => 'The {language}.langcode that the user prefers for viewing administration pages.',
131 'description' => 'E-mail address used for initial account creation.',
135 'access' => array('access'),
136 'created' => array('created'),
137 'mail' => array('mail'),
139 'unique keys' => array(
140 'uuid' => array('uuid'),
141 'name' => array('name'),
143 'primary key' => array('uid'),
144 'foreign keys' => array(
145 'signature_format' => array(
146 'table' => 'filter_format',
147 'columns' => array('signature_format' => 'format'),
152 $schema['role_permission'] = array(
153 'description' => 'Stores the permissions assigned to user roles.',
159 'description' => 'Foreign Key: {role}.rid.',
161 'permission' => array(
166 'description' => 'A single permission granted to the role identified by rid.',
173 'description' => "The module declaring the permission.",
176 'primary key' => array('rid', 'permission'),
178 'permission' => array('permission'),
180 'foreign keys' => array(
183 'columns' => array('rid' => 'rid'),
188 $schema['users_data'] = array(
189 'description' => 'Stores module data as key/value pairs per user.',
192 'description' => 'Primary key: {users}.uid for user.',
199 'description' => 'The name of the module declaring the variable.',
206 'description' => 'The identifier of the data.',
213 'description' => 'The value.',
218 'serialized' => array(
219 'description' => 'Whether value is serialized.',
226 'primary key' => array('uid', 'module', 'name'),
228 'module' => array('module'),
229 'name' => array('name'),
231 'foreign keys' => array(
232 'uid' => array('users' => 'uid'),
236 $schema['users_roles'] = array(
237 'description' => 'Maps users to roles.',
244 'description' => 'Primary Key: {users}.uid for user.',
250 'description' => 'Primary Key: {role}.rid for role.',
253 'primary key' => array('uid', 'rid'),
255 'rid' => array('rid'),
257 'foreign keys' => array(
260 'columns' => array('uid' => 'uid'),
264 'columns' => array('rid' => 'rid'),
273 * Implements hook_install().
275 function user_install() {
276 // Insert a row for the anonymous user.
285 // We need some placeholders here as name and mail are uniques and data is
286 // presumed to be a serialized array. This will be changed by the settings
287 // form in the installer.
291 'name' => 'placeholder-for-uid-1',
292 'mail' => 'placeholder-for-uid-1',
293 'created' => REQUEST_TIME
,
300 * Creates a user picture image field for the User entity.
302 * This is only used in core's standard.install, but is kept as a separate
303 * helper function so that other install profiles can reuse it.
305 function user_install_picture_field() {
309 'field_name' => 'user_picture',
314 'indexes' => array('fid' => array('fid')),
316 'uri_scheme' => 'public',
317 'default_image' => FALSE
,
320 $field = field_create_field($field);
323 'field_name' => 'user_picture',
324 'entity_type' => 'user',
325 'label' => 'Picture',
327 'description' => $t('Your virtual face or picture.'),
330 'file_extensions' => 'png gif jpg jpeg',
331 'file_directory' => 'pictures',
332 'max_filesize' => '30 KB',
335 'max_resolution' => '85x85',
336 'min_resolution' => '',
337 'default_image' => 0,
340 field_create_instance($instance);
342 // Assign form display settings for the 'default' view mode.
343 entity_get_form_display('user', 'user', 'default')
344 ->setComponent('user_picture', array(
345 'type' => 'image_image',
347 'progress_indicator' => 'throbber',
348 'preview_image_style' => 'thumbnail',
354 // Assign display settings for the 'default' and 'compact' view modes.
355 entity_get_display('user', 'user', 'default')
356 ->setComponent('user_picture', array(
360 'image_style' => 'thumbnail',
361 'image_link' => 'content',
365 entity_get_display('user', 'user', 'compact')
366 ->setComponent('user_picture', array(
370 'image_style' => 'thumbnail',
371 'image_link' => 'content',
374 // Additionally, hide 'summary' pseudo-field from compact view mode..
375 ->removeComponent('member_for')
380 * @addtogroup updates-7.x-to-8.x
385 * The 'Member for' extra field has moved one level up in the array.
387 function user_update_8000() {
388 $settings = field_bundle_settings('user', 'user');
389 if (isset($settings['extra_fields']['display']['summary'])) {
390 $settings['extra_fields']['display']['member_for'] = $settings['extra_fields']['display']['summary'];
391 unset($settings['extra_fields']['display']['summary']);
392 field_bundle_settings('user', 'user', $settings);
397 * Splits {users}.language field to langcode and preferred_langcode.
399 * @see http://drupal.org/node/1454538
401 function user_update_8001() {
402 // The former language field is the language preference of the user. Rename
403 // this to preferred_langcode in order to distinguish it from the langcode
404 // field common to all entity types, used for identifying the language of the
406 $preferred_langcode_field = array(
411 'description' => 'The {language}.langcode that the user prefers for receiving emails and viewing the site.',
413 db_change_field('users', 'language', 'preferred_langcode', $preferred_langcode_field);
415 // Add the langcode field.
416 $langcode_field = array(
421 'description' => "The {language}.langcode of the user's profile.",
423 db_add_field('users', 'langcode', $langcode_field);
425 // Since distinguishing the language of the user entity from the user's
426 // preferred language is a new feature in Drupal 8, assume that for updated
427 // sites, existing user entities are in the user's preferred language.
428 db_update('users')->expression('langcode', 'preferred_langcode')->execute();
432 * Replace serial role IDs with machine name strings.
434 function user_update_8002() {
435 // Change serial rid columns into strings.
440 'description' => 'Primary Key: Unique role ID.',
442 db_change_field('role', 'rid', 'rid', $column);
444 $column['description'] = 'Foreign Key: {role}.rid.';
445 db_change_field('role_permission', 'rid', 'rid', $column);
447 $column['description'] = 'Primary Key: {role}.rid for role.';
448 db_change_field('users_roles', 'rid', 'rid', $column);
450 // Enlarge the role name (label) column.
456 'description' => 'Role label.',
458 db_change_field('role', 'name', 'name', $column);
459 // Remove unique index.
460 db_drop_unique_key('role', 'name');
462 // Rename the built-in serial role IDs into the hardcoded machine names.
464 ->fields(array('rid' => DRUPAL_ANONYMOUS_RID
))
465 ->condition('rid', 1)
468 ->fields(array('rid' => DRUPAL_AUTHENTICATED_RID
))
469 ->condition('rid', 2)
472 db_update('role_permission')
473 ->fields(array('rid' => DRUPAL_ANONYMOUS_RID
))
474 ->condition('rid', 1)
476 db_update('role_permission')
477 ->fields(array('rid' => DRUPAL_AUTHENTICATED_RID
))
478 ->condition('rid', 2)
481 db_update('users_roles')
482 ->fields(array('rid' => DRUPAL_ANONYMOUS_RID
))
483 ->condition('rid', 1)
485 db_update('users_roles')
486 ->fields(array('rid' => DRUPAL_AUTHENTICATED_RID
))
487 ->condition('rid', 2)
492 * Create a UUID column for users.
494 function user_update_8003() {
496 'description' => 'Unique Key: Universally unique identifier for this entity.',
502 'unique keys' => array(
503 'uuid' => array('uuid'),
506 // Account for sites having the contributed UUID module installed.
507 if (db_field_exists('users', 'uuid')) {
508 db_change_field('users', 'uuid', 'uuid', $spec, $keys);
511 db_add_field('users', 'uuid', $spec, $keys);
516 * Moves account settings from variable to config.
518 * @ingroup config_upgrade
520 function user_update_8004() {
521 update_variables_to_config('user.settings', array(
522 'anonymous' => 'anonymous',
523 'user_admin_role' => 'admin_role',
524 'user_register' => 'register',
525 'user_signatures' => 'signatures',
526 'user_cancel_method' => 'cancel_method',
527 'user_mail_status_activated_notify' => 'notify.status_activated',
528 'user_mail_status_blocked_notify' => 'notify.status_blocked',
529 'user_mail_status_cancelled_notify' => 'notify.status_cancelled',
530 'user_email_verification' => 'verify_mail',
531 'user_password_reset_timeout' => 'password_reset_timeout',
534 // Convert the user.settings:register numeric value to text value.
538 '2' => 'visitors_admin_approval',
541 $config = config('user.settings');
542 $user_register = $config->get('register');
543 $user_cancel_method = $config->get('cancel_method');
545 if (is_numeric($user_register) && isset($map[$user_register])) {
546 $config->set('register', $map[$user_register])->save();
549 // Convert user.settings:cancel_method numeric value to text value.
551 '0' => 'user_cancel_block',
552 '1' => 'user_cancel_block_unpublish',
553 '2' => 'user_cancel_block_reassign',
554 '3' => 'user_cancel_block_delete',
557 if (is_numeric($user_cancel_method) && isset($cancel_map[$user_cancel_method])) {
558 $config->set('cancel_method', $
$cancel_map[$user_cancel_method])->save();
563 * Creates a preferred_admin_langcode column.
565 function user_update_8005() {
567 'description' => 'The {language}.langcode that the user prefers for viewing administration pages.',
573 db_add_field('users', 'preferred_admin_langcode', $spec);
577 * Moves user mail settings from variable to config.
579 * @ingroup config_upgrade
581 function user_update_8006() {
582 update_variables_to_config('user.mail', array(
583 'register_admin_created_subject' => 'register_admin_created.subject',
584 'register_admin_created_body' => 'register_admin_created.body',
585 'register_pending_approval_subject' => 'register_pending_approval.subject',
586 'register_pending_approval_body' => 'register_pending_approval.body',
587 'register_pending_approval_admin_body' => 'register_pending_approval_admin.body',
588 'register_pending_approval_admin_subject' => 'register_pending_approval_admin.subject',
589 'register_no_approval_required_subject' => 'register_no_approval_required.subject',
590 'register_no_approval_required_body' => 'register_no_approval_required.body',
591 'password_reset_subject' => 'password_reset.subject',
592 'password_reset_body' => 'password_reset.body',
593 'status_activated_subject' => 'status_activated.subject',
594 'status_activated_body' => 'status_activated.body',
595 'status_blocked_subject' => 'status_blocked.subject',
596 'status_blocked_body' => 'status_blocked.body',
597 'cancel_confirm_subject' => 'cancel_confirm.subject',
598 'cancel_confirm_body' => 'cancel_confirm.body',
599 'status_canceled_subject' => 'status_canceled.subject',
600 'status_canceled_body' => 'status_canceled.body',
605 * Moves login flood settings from variable to config.
607 * @ingroup config_upgrade
609 function user_update_8007() {
610 update_variables_to_config('user.flood', array(
611 'user_failed_login_identifier_uid_only' => 'uid_only',
612 'user_failed_login_ip_limit' => 'ip_limit',
613 'user_failed_login_ip_window' => 'ip_window',
614 'user_failed_login_user_limit' => 'user_limit',
615 'user_failed_login_user_window' => 'user_window',
620 * Make *id fields unsigned.
622 function user_update_8008() {
623 db_change_field('authmap', 'uid', 'uid',
629 'description' => "User's {users}.uid.",
635 * Generate a UUID for all users.
637 function user_update_8009(&$sandbox) {
638 if (!isset($sandbox['progress'])) {
639 $sandbox['progress'] = 0;
640 // The first user id is 0, so it needs to start with -1.
641 $sandbox['last'] = -1;
642 $sandbox['max'] = db_query('SELECT COUNT(uid) FROM {users} WHERE uuid IS NULL')->fetchField();
645 $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();
646 update_add_uuids($sandbox, 'users', 'uid', $uids);
648 $sandbox['#finished'] = empty($sandbox['max']) ?
1 : ($sandbox['progress'] / $sandbox['max']);
652 * Create user picture field.
654 function user_update_8011() {
657 // User pictures can only be migrated to the new user picture image field
658 // if Image module is installed.
659 if (!module_exists('image')) {
660 // Install image.module with schema version 8002 as a previous version
661 // would have to create tables that would be removed again.
662 $old_schema = update_module_enable(array('image'), 8002);
663 if ($old_schema['image'] == SCHEMA_UNINSTALLED
) {
664 // If image.module was not installed before, install default
665 // configuration and run the install hook.
666 config_install_default_config('module', 'image');
667 // Inlined version of image_install(), make sure that the styles
669 $directory = update_variable_get('file_default_scheme', 'public') .
'://styles';
670 file_prepare_directory($directory, FILE_CREATE_DIRECTORY
| FILE_MODIFY_PERMISSIONS
);
674 if ($default_image = update_variable_get('user_picture_default', '')) {
675 $picture_directory = update_variable_get('file_default_scheme', 'public') .
'://' .
update_variable_get('user_picture_path', 'pictures');
676 file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY
);
677 $destination = file_stream_wrapper_uri_normalize($picture_directory .
'/' .
drupal_basename($default_image));
678 // The file entity needs to be created manually as entities can not be
679 // created/saved during the upgrade path. Attempt to replicate the behavior
680 // of file_save_data() by updating an eventually existing record for that
682 file_unmanaged_save_data($default_image, $destination, FILE_EXISTS_REPLACE
);
684 db_merge('file_managed')
686 'uri' => $destination,
690 'status' => FILE_STATUS_PERMANENT
,
691 'filename' => drupal_basename($destination),
692 'uuid' => $uuid->generate(),
693 'langcode' => Language
::LANGCODE_NOT_SPECIFIED
,
694 'filesize' => filesize($destination),
695 'filemime' => file_get_mimetype($destination),
696 'timestamp' => REQUEST_TIME
,
699 $default_image_fid = db_query('SELECT fid FROM {file_managed} WHERE uri = :uri', array(':uri' => $destination))->fetchField();
702 // Create the field and instance.
704 'field_name' => 'user_picture',
709 'indexes' => array('fid' => array('fid')),
711 'uri_scheme' => 'public',
712 'default_image' => FALSE
,
715 'type' => 'field_sql_storage',
716 'settings' => array(),
719 _update_7000_field_create_field($field);
722 'field_name' => 'user_picture',
723 'entity_type' => 'user',
724 'label' => 'Picture',
726 'description' => update_variable_get('user_picture_guidelines', ''),
729 'file_extensions' => 'png gif jpg jpeg',
730 'file_directory' => update_variable_get('user_picture_path', 'pictures'),
731 'max_filesize' => update_variable_get('user_picture_file_size', '30') .
' KB',
734 'max_resolution' => update_variable_get('user_picture_dimensions', '85x85'),
735 'min_resolution' => '',
736 'default_image' => !empty($default_image_fid) ?
$default_image_fid : 0,
739 _update_7000_field_create_instance($field, $instance);
741 module_load_install('entity');
742 if (update_variable_get('user_pictures', 0)) {
743 $formatter = 'image';
744 $widget = 'image_image';
747 $formatter = $widget = 'hidden';
750 // Assign form settings for the 'default' form mode.
751 $form_display = _update_8000_entity_get_form_display('user', 'user', 'default');
752 $form_display->set('content.user_picture', array(
755 'progress_indicator' => 'throbber',
756 'preview_image_style' => 'thumbnail',
761 update_config_manifest_add('entity.form_display', array($form_display->get('id')));
763 // Assign display settings for the 'default' and 'compact' view modes.
764 $display = _update_8000_entity_get_display('user', 'user', 'default');
765 $display->set('content.user_picture', array(
767 'type' => $formatter,
769 'image_style' => 'thumbnail',
770 'image_link' => 'content',
775 update_config_manifest_add('entity.display', array($display->get('id')));
777 $display = _update_8000_entity_get_display('user', 'user', 'compact');
778 $display->set('content.user_picture', array(
780 'type' => $formatter,
782 'image_style' => update_variable_get('user_picture_style', ''),
783 'image_link' => 'content',
788 update_config_manifest_add('entity.display', array($display->get('id')));
790 // Add file usage for the default field.
791 if (!empty($default_image_fid)) {
792 db_insert('file_usage')
794 'fid' => $default_image_fid,
796 'type' => 'default_image',
797 'id' => $field['id'],
803 // Update the user bundle settings and hide the member_for extra field.
804 $settings = update_variable_get('field_bundle_settings_user__user');
805 $settings['extra_fields']['display']['member_for']['compact'] = array(
809 update_variable_set('field_bundle_settings_user__user', $settings);
811 // Delete old variables.
812 update_variable_del('user_pictures');
813 update_variable_del('user_picture_path');
814 update_variable_del('user_picture_default');
815 update_variable_del('user_picture_style');
816 update_variable_del('user_picture_dimensions');
817 update_variable_del('user_picture_file_size');
818 update_variable_del('user_picture_guidelines');
822 * Migrate {users}.picture to 'user_picture' image field.
824 function user_update_8012(&$sandbox) {
825 // Initialize total values to process.
826 if (!isset($sandbox['total'])) {
827 $sandbox['total'] = (int) db_query('SELECT COUNT(picture) FROM {users} WHERE picture > 0')->fetchField();
828 $sandbox['processed'] = 0;
831 if ($sandbox['total']) {
832 // Retrieve next 20 rows to migrate.
833 $rows = db_query_range('SELECT uid, picture FROM {users} WHERE picture > 0', 0, 20)->fetchAllKeyed();
834 foreach ($rows as
$uid => $fid) {
835 // Add a row to the field data and revision tables.
836 db_insert('field_data_user_picture')
838 'entity_type' => 'user',
841 'revision_id' => $uid,
842 'langcode' => Language
::LANGCODE_NOT_SPECIFIED
,
844 'user_picture_fid' => $fid,
847 db_insert('field_revision_user_picture')
849 'entity_type' => 'user',
852 'revision_id' => $uid,
853 'langcode' => Language
::LANGCODE_NOT_SPECIFIED
,
855 'user_picture_fid' => $fid,
859 // Update file usage from user to file module.
860 // @see file_field_insert()
861 // Old: file_usage_add($picture, 'user', 'user', $entity->uid);
862 // New: file_usage_add(file_load($item['fid']), 'file', $entity_type, $id);
863 db_update('file_usage')
864 ->condition('fid', $fid)
865 ->condition('module', 'user')
866 ->condition('type', 'user')
867 ->condition('id', $uid)
874 // Set picture column of the migrated users to 0.
879 ->condition('uid', array_keys($rows))
883 $sandbox['processed'] += count($rows);
885 $sandbox['#finished'] = $sandbox['total'] ?
$sandbox['processed'] / $sandbox['total'] : 1;
890 * Deletes {users}.picture field.
892 function user_update_8013() {
893 db_drop_field('users', 'picture');
897 * Create new {users_data} table.
899 function user_update_8014() {
900 // Create the {users_data} table.
901 db_create_table('users_data', array(
902 'description' => 'Stores module data as key/value pairs per user.',
905 'description' => 'Primary key: {users}.uid for user.',
912 'description' => 'The name of the module declaring the variable.',
919 'description' => 'The identifier of the data.',
926 'description' => 'The value.',
931 'serialized' => array(
932 'description' => 'Whether value is serialized.',
939 'primary key' => array('uid', 'module', 'name'),
941 'module' => array('module'),
942 'name' => array('name'),
944 'foreign keys' => array(
945 'uid' => array('users' => 'uid'),
949 // Create backup table for data migration.
950 // Since the origin/owner of individual values in {users}.data is unknown,
951 // other modules need to migrate their existing values from {_d7_users_data}.
952 db_create_table('_d7_users_data', array(
953 'description' => 'Backup of {users}.data for migration.',
956 'description' => 'Primary Key: {users}.uid for user.',
963 'description' => 'The name of the variable.',
970 'description' => 'The serialized value of the variable.',
977 'primary key' => array('uid', 'name'),
978 'foreign keys' => array(
979 'uid' => array('users' => 'uid'),
985 * Move existing {users}.data into {_d7_users_data} migration table.
987 function user_update_8015(&$sandbox) {
988 if (!isset($sandbox['progress'])) {
989 $sandbox['progress'] = 0;
990 // The anonymous user cannot have data, so start with uid 1.
991 $sandbox['last'] = 0;
992 $sandbox['max'] = db_query('SELECT COUNT(uid) FROM {users} WHERE uid > 0')->fetchField();
995 // Process 20 user records at a time. E.g., if there are 10 data keys per user
996 // record, that leads to an insert query with 200 values.
997 $result = db_query_range('SELECT uid, data FROM {users} WHERE uid > :uid ORDER BY uid ASC', 0, 20, array(':uid' => $sandbox['last']))->fetchAllKeyed();
998 $query = db_insert('_d7_users_data')->fields(array('uid', 'name', 'value'));
1000 foreach ($result as
$uid => $data) {
1001 $sandbox['progress']++;
1002 $sandbox['last'] = $uid;
1006 $data = unserialize($data);
1007 if (!empty($data) && is_array($data)) {
1009 foreach ($data as
$name => $value) {
1010 $query->values(array(
1013 'value' => serialize($value),
1022 $sandbox['#finished'] = empty($sandbox['max']) ?
1 : ($sandbox['progress'] / $sandbox['max']);
1026 * Drop {users}.data column.
1028 function user_update_8016() {
1029 db_drop_field('users', 'data');
1033 * Migrate user roles into configuration.
1035 * @ingroup config_upgrade
1037 function user_update_8017() {
1040 $roles = db_select('role', 'r')
1045 foreach ($roles as
$role) {
1046 config('user.role.' .
$role->rid
)
1047 ->set('id', $role->rid
)
1048 ->set('uuid', $uuid->generate())
1049 ->set('label', $role->name
)
1050 ->set('weight', $role->weight
)
1051 ->set('langcode', Language
::LANGCODE_NOT_SPECIFIED
)
1055 update_config_manifest_add('user.role', array_map(function ($role) {
1061 * @} End of "addtogroup updates-7.x-to-8.x".