diff --git a/core/core.libraries.yml b/core/core.libraries.yml index c8ab09e..dc81a4a 100644 --- a/core/core.libraries.yml +++ b/core/core.libraries.yml @@ -222,6 +222,15 @@ drupal.progress: - core/jquery - core/drupalSettings +drupal.revealpass: + version: VERSION + js: + misc/revealpass.js: {} + dependencies: + - core/drupal + - core/jquery + - core/jquery.once + drupal.states: version: VERSION js: diff --git a/core/lib/Drupal/Core/Form/FormBuilderInterface.php b/core/lib/Drupal/Core/Form/FormBuilderInterface.php index 5f8b452..193bd02 100644 --- a/core/lib/Drupal/Core/Form/FormBuilderInterface.php +++ b/core/lib/Drupal/Core/Form/FormBuilderInterface.php @@ -158,8 +158,7 @@ public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form * $form_state = new FormState(); * $values['name'] = 'robo-user'; * $values['mail'] = 'robouser@example.com'; - * $values['pass']['pass1'] = 'password'; - * $values['pass']['pass2'] = 'password'; + * $values['pass']['pass'] = 'password'; * $values['op'] = t('Create new account'); * $form_state->setValues($values); * \Drupal::formBuilder()->submitForm('user_register_form', $form_state); diff --git a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php index 0354e25..4e88e2a 100644 --- a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php +++ b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php @@ -177,6 +177,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#attributes' => array('class' => array('username')), ); $form['admin_account']['account']['pass'] = array( + '#title' => $this->t('Password'), '#type' => 'password_confirm', '#required' => TRUE, '#size' => 25, diff --git a/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php b/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php index 9bc32b7..2dd5e7f 100644 --- a/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php +++ b/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php @@ -17,82 +17,30 @@ * * @FormElement("password_confirm") */ -class PasswordConfirm extends FormElement { +class PasswordConfirm extends Password { /** * {@inheritdoc} */ public function getInfo() { $class = get_class($this); - return array( - '#input' => TRUE, - '#process' => array( - array($class, 'processPasswordConfirm'), - ), - '#theme_wrappers' => array('form_element'), + $info = parent::getInfo(); + $info['#process'] = array( + array($class, 'processAjaxForm'), + array($class, 'processPattern'), + array($class, 'processPasswordConfirm'), ); - } - - /** - * {@inheritdoc} - */ - public static function valueCallback(&$element, $input, FormStateInterface $form_state) { - if ($input === FALSE) { - $element += array('#default_value' => array()); - return $element['#default_value'] + array('pass1' => '', 'pass2' => ''); - } + return $info; } /** * Expand a password_confirm field into two text boxes. */ public static function processPasswordConfirm(&$element, FormStateInterface $form_state, &$complete_form) { - $element['pass1'] = array( - '#type' => 'password', - '#title' => t('Password'), - '#value' => empty($element['#value']) ? NULL : $element['#value']['pass1'], - '#required' => $element['#required'], - '#attributes' => array('class' => array('password-field')), - ); - $element['pass2'] = array( - '#type' => 'password', - '#title' => t('Confirm password'), - '#value' => empty($element['#value']) ? NULL : $element['#value']['pass2'], - '#required' => $element['#required'], - '#attributes' => array('class' => array('password-confirm')), - ); - $element['#element_validate'] = array(array(get_called_class(), 'validatePasswordConfirm')); - $element['#tree'] = TRUE; - - if (isset($element['#size'])) { - $element['pass1']['#size'] = $element['pass2']['#size'] = $element['#size']; - } - + $element['#attributes']['class'][] = 'password-field'; + $element['#attributes']['class'][] = 'password-confirm'; + $element['#attached']['library'][] = 'core/drupal.revealpass'; + $element['#attributes']['data-drupal-revealpass'] = TRUE; return $element; } - - /** - * Validates a password_confirm element. - */ - public static function validatePasswordConfirm(&$element, FormStateInterface $form_state, &$complete_form) { - $pass1 = trim($element['pass1']['#value']); - $pass2 = trim($element['pass2']['#value']); - if (!empty($pass1) || !empty($pass2)) { - if (strcmp($pass1, $pass2)) { - $form_state->setError($element, t('The specified passwords do not match.')); - } - } - elseif ($element['#required'] && $form_state->getUserInput()) { - $form_state->setError($element, t('Password field is required.')); - } - - // Password field must be converted from a two-element array into a single - // string regardless of validation results. - $form_state->setValueForElement($element['pass1'], NULL); - $form_state->setValueForElement($element['pass2'], NULL); - $form_state->setValueForElement($element, $pass1); - - return $element; - } - } diff --git a/core/misc/revealpass.js b/core/misc/revealpass.js new file mode 100644 index 0000000..bb42a31 --- /dev/null +++ b/core/misc/revealpass.js @@ -0,0 +1,36 @@ +(function ($, Drupal) { + + "use strict"; + + var showPass = Drupal.t('Show password'); + var hidePass = Drupal.t('Hide password'); + + function revealClickHandle(event) { + var $pass = $(event.data.password); + var $button = $(event.target); + + if ($pass.attr('type') === 'password') { + $pass.attr('type', 'text'); + $button.text(hidePass); + } + else { + $pass.attr('type', 'password'); + $button.text(showPass); + } + } + + function revealLink(index, element) { + var $trigger = $(''); + + $trigger.on('click', {password: element}, revealClickHandle); + $trigger.insertAfter(element); + } + + Drupal.behaviors.revealPass = { + attach: function (context) { + $(context).find('input[type=password][data-drupal-revealpass]') + .once('revealpass') + .each(revealLink); + } + }; +})(jQuery, Drupal); diff --git a/core/modules/contact/src/Tests/ContactPersonalTest.php b/core/modules/contact/src/Tests/ContactPersonalTest.php index 03965ad..07bd3d5 100644 --- a/core/modules/contact/src/Tests/ContactPersonalTest.php +++ b/core/modules/contact/src/Tests/ContactPersonalTest.php @@ -278,8 +278,7 @@ protected function checkContactAccess($response, $contact_value = NULL) { $edit = array( 'name' => $name, 'mail' => $this->randomMachineName() . '@example.com', - 'pass[pass1]' => $pass = $this->randomString(), - 'pass[pass2]' => $pass, + 'pass' => $this->randomString(), 'notify' => FALSE, ); if (isset($contact_value)) { diff --git a/core/modules/dblog/src/Tests/DbLogTest.php b/core/modules/dblog/src/Tests/DbLogTest.php index 9726fff..c07575e 100644 --- a/core/modules/dblog/src/Tests/DbLogTest.php +++ b/core/modules/dblog/src/Tests/DbLogTest.php @@ -258,8 +258,7 @@ private function doUser() { $edit = array(); $edit['name'] = $name; $edit['mail'] = $name . '@example.com'; - $edit['pass[pass1]'] = $pass; - $edit['pass[pass2]'] = $pass; + $edit['pass'] = $pass; $edit['status'] = 1; $this->drupalPostForm('admin/people/create', $edit, t('Create new account')); $this->assertResponse(200); diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php index 63cf76a..d794441 100644 --- a/core/modules/simpletest/src/WebTestBase.php +++ b/core/modules/simpletest/src/WebTestBase.php @@ -996,10 +996,7 @@ protected function installParameters() { 'account' => array( 'name' => $this->rootUser->name, 'mail' => $this->rootUser->getEmail(), - 'pass' => array( - 'pass1' => $this->rootUser->pass_raw, - 'pass2' => $this->rootUser->pass_raw, - ), + 'pass' => $this->root_user->pass_raw, ), // \Drupal\Core\Render\Element\Checkboxes::valueCallback() requires // NULL instead of FALSE values for programmatic form submissions to diff --git a/core/modules/system/src/Tests/Form/FormTest.php b/core/modules/system/src/Tests/Form/FormTest.php index 0197e8a..18d8f47 100644 --- a/core/modules/system/src/Tests/Form/FormTest.php +++ b/core/modules/system/src/Tests/Form/FormTest.php @@ -72,11 +72,9 @@ function testRequiredFields() { $elements['password']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'password'); $elements['password']['empty_values'] = $empty_strings; + // @todo remove element? $elements['password_confirm']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'password_confirm'); - // Provide empty values for both password fields. - foreach ($empty_strings as $key => $value) { - $elements['password_confirm']['empty_values'][$key] = array('pass1' => $value, 'pass2' => $value); - } + $elements['password_confirm']['empty_values'] = $empty_strings; $elements['textarea']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'textarea'); $elements['textarea']['empty_values'] = $empty_strings; @@ -533,7 +531,7 @@ function testDisabledElements() { // All the elements should be marked as disabled, including the ones below // the disabled container. $actual_count = count($disabled_elements); - $expected_count = 41; + $expected_count = 40; $this->assertEqual($actual_count, $expected_count, String::format('Found @actual elements with disabled property (expected @expected).', array( '@actual' => count($disabled_elements), '@expected' => $expected_count, diff --git a/core/modules/user/css/user.module.css b/core/modules/user/css/user.module.css index 0b6afcf..6fbec09 100644 --- a/core/modules/user/css/user.module.css +++ b/core/modules/user/css/user.module.css @@ -16,6 +16,3 @@ width: 0; background-color: gray; } -.password-confirm-match { - visibility: hidden; -} diff --git a/core/modules/user/css/user.theme.css b/core/modules/user/css/user.theme.css index 52413e0..7393426 100644 --- a/core/modules/user/css/user.theme.css +++ b/core/modules/user/css/user.theme.css @@ -26,10 +26,8 @@ background-color: #77b259; } -.password-confirm, .password-field, -.password-strength, -.password-confirm-match { +.password-strength { width: 55%; } diff --git a/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php index 8c14682..d662c7b 100644 --- a/core/modules/user/src/AccountForm.php +++ b/core/modules/user/src/AccountForm.php @@ -119,9 +119,10 @@ public function form(array $form, FormStateInterface $form_state) { // assign a password during registration. if (!$register) { $form['account']['pass'] = array( + '#title' => $this->t('Password'), '#type' => 'password_confirm', '#size' => 25, - '#description' => $this->t('To change the current user password, enter the new password in both fields.'), + '#description' => $this->t('To change the current user password, enter the new password.'), ); // To skip the current password field, the user must have logged in via a @@ -178,9 +179,10 @@ public function form(array $form, FormStateInterface $form_state) { } elseif (!$config->get('verify_mail') || $admin) { $form['account']['pass'] = array( + '#title' => $this->t('Password'), '#type' => 'password_confirm', '#size' => 25, - '#description' => $this->t('Provide a password for the new account in both fields.'), + '#description' => $this->t('Provide a password for the new account.'), '#required' => TRUE, ); } diff --git a/core/modules/user/src/Tests/UserCreateFailMailTest.php b/core/modules/user/src/Tests/UserCreateFailMailTest.php index b7b9618..d9fdc90 100644 --- a/core/modules/user/src/Tests/UserCreateFailMailTest.php +++ b/core/modules/user/src/Tests/UserCreateFailMailTest.php @@ -37,8 +37,7 @@ public function testUserAdd() { $edit = array( 'name' => $name, 'mail' => $this->randomMachineName() . '@example.com', - 'pass[pass1]' => $pass = $this->randomString(), - 'pass[pass2]' => $pass, + 'pass' => $this->randomString(), 'notify' => TRUE, ); $this->drupalPostForm('admin/people/create', $edit, t('Create new account')); diff --git a/core/modules/user/src/Tests/UserCreateTest.php b/core/modules/user/src/Tests/UserCreateTest.php index c906267..e7103cd 100644 --- a/core/modules/user/src/Tests/UserCreateTest.php +++ b/core/modules/user/src/Tests/UserCreateTest.php @@ -94,8 +94,7 @@ public function testUserAdd() { $edit = array( 'name' => $name, 'mail' => $this->randomMachineName() . '@example.com', - 'pass[pass1]' => $pass = $this->randomString(), - 'pass[pass2]' => $pass, + 'pass' => $this->randomString(), 'notify' => $notify, ); $this->drupalPostForm('admin/people/create', $edit, t('Create new account')); diff --git a/core/modules/user/src/Tests/UserEditTest.php b/core/modules/user/src/Tests/UserEditTest.php index 38acdb8..3731b82 100644 --- a/core/modules/user/src/Tests/UserEditTest.php +++ b/core/modules/user/src/Tests/UserEditTest.php @@ -69,18 +69,6 @@ function testUserEdit() { $this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save')); $this->assertRaw(t('The username %name is already taken.', array('%name' => $edit['name']))); - // Check that filling out a single password field does not validate. - $edit = array(); - $edit['pass[pass1]'] = ''; - $edit['pass[pass2]'] = $this->randomMachineName(); - $this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save')); - $this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.'); - - $edit['pass[pass1]'] = $this->randomMachineName(); - $edit['pass[pass2]'] = ''; - $this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save')); - $this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.'); - // Test that the error message appears when attempting to change the mail or // pass without the current password. $edit = array(); @@ -94,8 +82,7 @@ function testUserEdit() { // Test that the user must enter current password before changing passwords. $edit = array(); - $edit['pass[pass1]'] = $new_pass = $this->randomMachineName(); - $edit['pass[pass2]'] = $new_pass; + $edit['pass'] = $new_pass = $this->randomMachineName(); $this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save')); $this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('Password')))); diff --git a/core/modules/user/src/Tests/UserLanguageCreationTest.php b/core/modules/user/src/Tests/UserLanguageCreationTest.php index 3b9c5b1..e359de5 100644 --- a/core/modules/user/src/Tests/UserLanguageCreationTest.php +++ b/core/modules/user/src/Tests/UserLanguageCreationTest.php @@ -55,8 +55,7 @@ function testLocalUserCreation() { $edit = array( 'name' => $username, 'mail' => $this->randomMachineName(4) . '@example.com', - 'pass[pass1]' => $username, - 'pass[pass2]' => $username, + 'pass' => $username, ); $this->drupalPostForm($langcode . '/admin/people/create', $edit, t('Create new account')); @@ -94,8 +93,7 @@ function testLocalUserCreation() { // Set pass_raw so we can login the new user. $user->pass_raw = $this->randomMachineName(10); $edit = array( - 'pass[pass1]' => $user->pass_raw, - 'pass[pass2]' => $user->pass_raw, + 'pass' => $user->pass_raw, ); $this->drupalPostForm($user_edit, $edit, t('Save')); diff --git a/core/modules/user/src/Tests/UserPasswordResetTest.php b/core/modules/user/src/Tests/UserPasswordResetTest.php index 5b61fda..f196519 100644 --- a/core/modules/user/src/Tests/UserPasswordResetTest.php +++ b/core/modules/user/src/Tests/UserPasswordResetTest.php @@ -113,7 +113,7 @@ function testUserPasswordReset() { // Change the forgotten password. $password = user_password(); - $edit = array('pass[pass1]' => $password, 'pass[pass2]' => $password); + $edit = array('pass' => $password); $this->drupalPostForm(NULL, $edit, t('Save')); $this->assertText(t('The changes have been saved.'), 'Forgotten password changed.'); diff --git a/core/modules/user/src/Tests/UserRegistrationTest.php b/core/modules/user/src/Tests/UserRegistrationTest.php index 447a7cb..c262076 100644 --- a/core/modules/user/src/Tests/UserRegistrationTest.php +++ b/core/modules/user/src/Tests/UserRegistrationTest.php @@ -73,15 +73,8 @@ function testRegistrationWithoutEmailVerification() { $edit['name'] = $name = $this->randomMachineName(); $edit['mail'] = $mail = $edit['name'] . '@example.com'; - // Try entering a mismatching password. - $edit['pass[pass1]'] = '99999.0'; - $edit['pass[pass2]'] = '99999'; - $this->drupalPostForm('user/register', $edit, t('Create new account')); - $this->assertText(t('The specified passwords do not match.'), 'Typing mismatched passwords displays an error message.'); - // Enter a correct password. - $edit['pass[pass1]'] = $new_pass = $this->randomMachineName(); - $edit['pass[pass2]'] = $new_pass; + $edit['pass'] = $this->randomMachineName(); $this->drupalPostForm('user/register', $edit, t('Create new account')); $this->container->get('entity.manager')->getStorage('user')->resetCache(); $accounts = entity_load_multiple_by_properties('user', array('name' => $name, 'mail' => $mail)); @@ -95,8 +88,7 @@ function testRegistrationWithoutEmailVerification() { $edit = array(); $edit['name'] = $name = $this->randomMachineName(); $edit['mail'] = $mail = $edit['name'] . '@example.com'; - $edit['pass[pass1]'] = $pass = $this->randomMachineName(); - $edit['pass[pass2]'] = $pass; + $edit['pass'] = $pass = $this->randomMachineName(); $this->drupalPostForm('user/register', $edit, t('Create new account')); $this->assertText(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.'), 'Users are notified of pending approval'); @@ -175,8 +167,7 @@ function testRegistrationDefaultValues() { $edit = array(); $edit['name'] = $name = $this->randomMachineName(); $edit['mail'] = $mail = $edit['name'] . '@example.com'; - $edit['pass[pass1]'] = $new_pass = $this->randomMachineName(); - $edit['pass[pass2]'] = $new_pass; + $edit['pass'] = $this->randomMachineName(); $this->drupalPostForm(NULL, $edit, t('Create new account')); // Check user fields. diff --git a/core/modules/user/src/Tests/UserRolesAssignmentTest.php b/core/modules/user/src/Tests/UserRolesAssignmentTest.php index 79addc2..4f000b5 100644 --- a/core/modules/user/src/Tests/UserRolesAssignmentTest.php +++ b/core/modules/user/src/Tests/UserRolesAssignmentTest.php @@ -53,8 +53,7 @@ function testCreateUserWithRole() { $edit = array( 'name' => $this->randomMachineName(), 'mail' => $this->randomMachineName() . '@example.com', - 'pass[pass1]' => $pass = $this->randomString(), - 'pass[pass2]' => $pass, + 'pass' => $this->randomString(), "roles[$rid]" => $rid, ); $this->drupalPostForm('admin/people/create', $edit, t('Create new account')); diff --git a/core/modules/user/user.js b/core/modules/user/user.js index 45f554c..889f563 100644 --- a/core/modules/user/user.js +++ b/core/modules/user/user.js @@ -9,39 +9,19 @@ Drupal.behaviors.password = { attach: function (context, settings) { var translate = settings.password; - $(context).find('input.password-field').once('password').each(function () { + $(context).find('input[type=password][data-drupal-revealpass]').once('password').each(function () { var passwordInput = $(this); var innerWrapper = $(this).parent(); - var outerWrapper = $(this).parent().parent(); - var passwordDescription; - + var passwordDescription = $('
').hide(); // Add identifying class to password element parent. innerWrapper.addClass('password-parent'); - // Add the password confirmation layer. - outerWrapper.find('input.password-confirm').parent().append('
' + translate.confirmTitle + '
').addClass('confirm-parent'); - var confirmInput = outerWrapper.find('input.password-confirm'); - var confirmResult = outerWrapper.find('div.password-confirm-match'); - var confirmChild = confirmResult.find('span'); - // If the password strength indicator is enabled, add its markup. if (settings.password.showStrengthIndicator) { var passwordMeter = '
' + translate.strengthTitle + '
'; - confirmInput.parent().after('
'); - innerWrapper.append(passwordMeter); - passwordDescription = outerWrapper.find('div.password-suggestions').hide(); + innerWrapper.append(passwordMeter).append(passwordDescription); } - // Check that password and confirmation inputs match. - var passwordCheckMatch = function (confirmInputVal) { - var success = passwordInput.val() === confirmInputVal; - var confirmClass = success ? 'ok' : 'error'; - - // Fill in the success message and set the class accordingly. - confirmChild.html(translate['confirm' + (success ? 'Success' : 'Failure')]) - .removeClass('ok error').addClass(confirmClass); - }; - // Check the password strength. var passwordCheck = function () { if (settings.password.showStrengthIndicator) { @@ -65,20 +45,10 @@ // Update the strength indication text. innerWrapper.find('.password-strength__text').html(result.indicatorText); } - - // Check the value in the confirm input and show results. - if (confirmInput.val()) { - passwordCheckMatch(confirmInput.val()); - confirmResult.css({visibility: 'visible'}); - } - else { - confirmResult.css({visibility: 'hidden'}); - } }; // Monitor input events. passwordInput.on('input', passwordCheck); - confirmInput.on('input', passwordCheck); }); } }; diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 665b43d..fe3da5c 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1300,9 +1300,6 @@ function user_element_info_alter(array &$types) { */ function user_form_process_password_confirm($element) { $password_settings = array( - 'confirmTitle' => t('Passwords match:'), - 'confirmSuccess' => t('yes'), - 'confirmFailure' => t('no'), 'showStrengthIndicator' => FALSE, ); diff --git a/core/themes/seven/css/components/form.css b/core/themes/seven/css/components/form.css index 72980e5..1dacd9a 100644 --- a/core/themes/seven/css/components/form.css +++ b/core/themes/seven/css/components/form.css @@ -171,14 +171,6 @@ textarea.form-textarea { width: auto; } -.form-item .password-suggestions { - float: left; /* LTR */ - clear: left; - width: 100%; -} -[dir="rtl"] .form-item .password-suggestions { - float: right; -} .form-item-pass .description { clear: both; } diff --git a/core/themes/seven/css/theme/install-page.css b/core/themes/seven/css/theme/install-page.css index c43b42e..38bf29f 100644 --- a/core/themes/seven/css/theme/install-page.css +++ b/core/themes/seven/css/theme/install-page.css @@ -41,14 +41,7 @@ .install-configure-form .form-type-password { width: 100%; } - .password-confirm, .password-field { float: none; } - .password-confirm-match { - float: none; - width: auto; - max-width: 100%; - } - }