diff -u b/core/core.libraries.yml b/core/core.libraries.yml --- b/core/core.libraries.yml +++ b/core/core.libraries.yml @@ -250,15 +250,6 @@ - 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: @@ -269,6 +260,15 @@ - core/jquery - core/drupalSettings +drupal.password-unmask: + version: VERSION + js: + misc/password-unmask.js: {} + dependencies: + - core/drupal + - core/jquery + - core/jquery.once + drupal.states: version: VERSION js: reverted: --- b/core/lib/Drupal/Core/Render/Element/PasswordReveal.php +++ /dev/null @@ -1,50 +0,0 @@ - 'password_reveal', - * '#title' => t('Password'), - * '#size' => 25, - * ); - * @endcode - * - * @see \Drupal\Core\Render\Element\Password - * - * @FormElement("password_reveal") - */ -class PasswordReveal extends Password { - - /** - * {@inheritdoc} - */ - public function getInfo() { - $info = parent::getInfo(); - $info['#process'][] = [static::class, 'processPasswordReveal']; - - return $info; - } - - /** - * Adds the attributes needed to add password strength and the reveal button. - */ - public static function processPasswordReveal(&$element, FormStateInterface $form_state, &$complete_form) { - $element['#attached']['library'][] = 'core/drupal.revealpass'; - $element['#attributes']['class'][] = 'password-field'; - $element['#attributes']['class'][] = 'js-password-field'; - $element['#attributes']['data-drupal-revealpass'] = TRUE; - $element['#attributes']['data-drupal-password-strength'] = TRUE; - - return $element; - } - -} reverted: --- b/core/misc/revealpass.es6.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @file - * Creates a reveal password link on password inputs. - */ - -(function ($, Drupal) { - const hidePass = Drupal.t('Hide password'); - const showPass = Drupal.t('Show password'); - - /** - * Reveal click handle that either shows or hides the password. - * - * @param {jQuery.Event} event - * The jQuery event. - */ - function revealClickHandle(event) { - const $pass = $(event.data.password); - const $button = $(event.target); - - if ($pass.attr('type') === 'password') { - $pass.attr('type', 'text'); - $button.text(hidePass); - } - else { - $pass.attr('type', 'password'); - $button.text(showPass); - } - } - - /** - * Create reveal button and bind event. - * - * @param {number} index - * The index in the loop, as provided by `jQuery.each` - * @param {HTMLElement} element - * The password input form element. - */ - function revealLink(index, element) { - const $trigger = $(``); - - $trigger.on('click', { password: element }, revealClickHandle); - $trigger.insertAfter(element); - } - - /** - * Initialize reveal links. - * - * @type {Drupal~behavior} - */ - Drupal.behaviors.revealPass = { - attach(context) { - $(context).find('input[type=password][data-drupal-revealpass]') - .once('revealpass') - .each(revealLink); - }, - }; -}(jQuery, Drupal)); reverted: --- b/core/misc/revealpass.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @file - * Creates a reveal password link on password inputs. - */ - -(function ($, Drupal) { - - 'use strict'; - - var showPass = Drupal.t('Show password'); - var hidePass = Drupal.t('Hide password'); - - /** - * Reveal click handle that either shows or hides the password. - * - * @param {jQuery.Event} event - * The jQuery event. - */ - 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); - } - } - - /** - * Create reveal button and bind event. - * - * @param {number} index - * The index in the loop, as provided by `jQuery.each` - * @param {HTMLElement} element - * The password input form element. - */ - function revealLink(index, element) { - var $trigger = $(''); - - $trigger.on('click', {password: element}, revealClickHandle); - $trigger.insertAfter(element); - } - - /** - * Initialize reveal links. - * - * @type {Drupal~behavior} - */ - Drupal.behaviors.revealPass = { - attach: function (context) { - $(context).find('input[type=password][data-drupal-revealpass]') - .once('revealpass') - .each(revealLink); - } - }; - -})(jQuery, Drupal); diff -u b/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml --- b/core/modules/system/tests/modules/form_test/form_test.routing.yml +++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml @@ -522,8 +522,8 @@ _access: 'TRUE' -form_test.password_reveal: +form_test.password_unmask: path: '/form-test/password-reveal' defaults: - _form: '\Drupal\form_test\Form\FormTestPasswordReveal' + _form: '\Drupal\form_test\Form\FormTestPasswordUnmask' requirements: _access: 'TRUE' reverted: --- b/core/modules/system/tests/modules/form_test/src/Form/FormTestPasswordReveal.php +++ /dev/null @@ -1,43 +0,0 @@ - $this->t('Password One'), - '#type' => 'password_reveal', - '#size' => 25, - ]; - $form['password_two'] = [ - '#title' => $this->t('Password Two'), - '#type' => 'password_reveal', - '#size' => 25, - ]; - return $form; - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - } - -} diff -u b/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php --- b/core/modules/user/src/AccountForm.php +++ b/core/modules/user/src/AccountForm.php @@ -124,7 +124,7 @@ if ($config->get('password_type_reveal')) { $form['account']['pass'] = [ '#title' => $this->t('Password'), - '#type' => 'password_reveal', + '#type' => 'password_unmask', '#size' => 25, '#description' => $this->t('To change the current user password, enter the new password.'), ]; @@ -176,7 +176,7 @@ if ($config->get('password_type_reveal')) { $form['account']['pass'] = [ '#title' => $this->t('Password'), - '#type' => 'password_reveal', + '#type' => 'password_unmask', '#size' => 25, '#description' => $this->t('Provide a password for the new account.'), '#required' => TRUE, diff -u b/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php --- b/core/modules/user/src/AccountSettingsForm.php +++ b/core/modules/user/src/AccountSettingsForm.php @@ -170,7 +170,8 @@ ]; $form['registration_cancellation']['user_password_type_reveal'] = [ '#type' => 'checkbox', - '#title' => $this->t('Use password reveal, not password confirm'), + '#title' => $this->t('Enable show password button'), + '#description' => $this->t('New users will be able to unmask their password to reveal what they have typed. With this setting enabled, users will not be required to enter a password twice, in two separate fields, to confirm during registration.'), '#default_value' => $config->get('password_type_reveal'), ]; $form['registration_cancellation']['user_cancel_method'] = [ diff -u b/core/modules/user/tests/src/Functional/UserEditTest.php b/core/modules/user/tests/src/Functional/UserEditTest.php --- b/core/modules/user/tests/src/Functional/UserEditTest.php +++ b/core/modules/user/tests/src/Functional/UserEditTest.php @@ -121,12 +121,13 @@ $this->assertNoFieldChecked('edit-status-0'); $this->assertFieldChecked('edit-status-1'); - // Test editing the user with a password_reveal field. + // Test editing the user with a password_unmask field. $config->set('password_type_reveal', TRUE)->save(); $config->set('password_strength', TRUE)->save(); $this->drupalGet("user/" . $admin_user->id() . "/edit"); - $this->assertRaw('Password strength:', 'The password strength indicator is displayed.'); + $this->assertRaw(t('Password strength:'), 'The password strength indicator is displayed.'); + $this->assertFieldByXPath('//input[@type="password"][@name="pass"]', NULL, 'The password field is displayed as masked.'); $edit = []; $edit['pass'] = $this->randomMachineName(); reverted: --- b/core/modules/user/user.js +++ a/core/modules/user/user.js @@ -153,4 +153,4 @@ indicatorClass: indicatorClass }; }; +})(jQuery, Drupal, drupalSettings); \ No newline at end of file -})(jQuery, Drupal, drupalSettings); interdiff impossible; taking evasive action reverted: --- b/core/modules/user/user.module +++ a/core/modules/user/user.module @@ -1285,9 +1285,6 @@ if (isset($types['password_confirm'])) { $types['password_confirm']['#process'][] = 'user_form_process_password_confirm'; } - if (isset($types['password_reveal'])) { - $types['password_reveal']['#process'][] = 'user_form_process_password_reveal'; - } } /** @@ -1330,44 +1327,6 @@ return $element; } -/** - * Form element process handler for client-side password validation. - * - * This #process handler is automatically invoked for 'password_reveal' form - * elements to add the JavaScript to display and hide the password and string - * translations for dynamic password validation. - */ -function user_form_process_password_reveal($element) { - $password_settings = [ - 'confirmTitle' => '', - 'showStrengthIndicator' => FALSE, - ]; - - if (\Drupal::config('user.settings')->get('password_strength')) { - $password_settings['showStrengthIndicator'] = TRUE; - $password_settings += [ - 'strengthTitle' => t('Password strength:'), - 'hasWeaknesses' => t('To make your password stronger:'), - 'tooShort' => t('Make it at least 12 characters'), - 'addLowerCase' => t('Add lowercase letters'), - 'addUpperCase' => t('Add uppercase letters'), - 'addNumbers' => t('Add numbers'), - 'addPunctuation' => t('Add punctuation'), - 'sameAsUsername' => t('Make it different from your username'), - 'weak' => t('Weak'), - 'fair' => t('Fair'), - 'good' => t('Good'), - 'strong' => t('Strong'), - 'username' => \Drupal::currentUser()->getAccountName(), - ]; - } - - $element['#attached']['library'][] = 'user/drupal.user'; - $element['#attached']['drupalSettings']['password'] = $password_settings; - - return $element; -} - /** * Implements hook_modules_uninstalled(). */ unchanged: --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1286,6 +1286,9 @@ function user_element_info_alter(array &$types) { if (isset($types['password_confirm'])) { $types['password_confirm']['#process'][] = 'user_form_process_password_confirm'; } + if (isset($types['password_unmask'])) { + $types['password_unmask']['#process'][] = 'user_form_process_password_unmask'; + } } /** @@ -1304,22 +1307,30 @@ function user_form_process_password_confirm($element) { ]; if (\Drupal::config('user.settings')->get('password_strength')) { - $password_settings['showStrengthIndicator'] = TRUE; - $password_settings += [ - 'strengthTitle' => t('Password strength:'), - 'hasWeaknesses' => t('Recommendations to make your password stronger:'), - 'tooShort' => t('Make it at least 12 characters'), - 'addLowerCase' => t('Add lowercase letters'), - 'addUpperCase' => t('Add uppercase letters'), - 'addNumbers' => t('Add numbers'), - 'addPunctuation' => t('Add punctuation'), - 'sameAsUsername' => t('Make it different from your username'), - 'weak' => t('Weak'), - 'fair' => t('Fair'), - 'good' => t('Good'), - 'strong' => t('Strong'), - 'username' => \Drupal::currentUser()->getAccountName(), - ]; + $password_settings = _user_password_strength_settings($password_settings); + } + + $element['#attached']['library'][] = 'user/drupal.user'; + $element['#attached']['drupalSettings']['password'] = $password_settings; + + return $element; +} + +/** + * Form element process handler for client-side password validation. + * + * This #process handler is automatically invoked for 'password_unmask' form + * elements to add the JavaScript to display and hide the password and string + * translations for dynamic password validation. + */ +function user_form_process_password_unmask($element) { + $password_settings = [ + 'confirmTitle' => '', + 'showStrengthIndicator' => FALSE, + ]; + + if (\Drupal::config('user.settings')->get('password_strength')) { + $password_settings = _user_password_strength_settings($password_settings); } $element['#attached']['library'][] = 'user/drupal.user'; @@ -1328,6 +1339,36 @@ function user_form_process_password_confirm($element) { return $element; } +/** + * Helper to enable password strength indicator settings. + * + * @param array $password_settings + * Current #attached password settings (optional). + * + * @return array + * Password settings with password strength indicator enabled. + */ +function _user_password_strength_settings(array $password_settings = []) { + $password_settings['showStrengthIndicator'] = TRUE; + $password_settings += [ + 'strengthTitle' => t('Password strength:'), + 'hasWeaknesses' => t('Recommendations to make your password stronger:'), + 'tooShort' => t('Make it at least 12 characters'), + 'addLowerCase' => t('Add lowercase letters'), + 'addUpperCase' => t('Add uppercase letters'), + 'addNumbers' => t('Add numbers'), + 'addPunctuation' => t('Add punctuation'), + 'sameAsUsername' => t('Make it different from your username'), + 'weak' => t('Weak'), + 'fair' => t('Fair'), + 'good' => t('Good'), + 'strong' => t('Strong'), + 'username' => \Drupal::currentUser()->getAccountName(), + ]; + + return $password_settings; +} + /** * Implements hook_modules_uninstalled(). */ diff -u b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/PasswordRevealTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/PasswordRevealTest.php --- b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/PasswordRevealTest.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/PasswordRevealTest.php @@ -17,7 +17,7 @@ public static $modules = ['form_test']; /** - * Test the password_reveal form element functionality. + * Test the password_unmask form element functionality. */ public function testPasswordRevealFormElement() { $this->drupalGet('/form-test/password-reveal'); diff -u b/core/tests/Drupal/Tests/Core/Render/Element/PasswordRevealTest.php b/core/tests/Drupal/Tests/Core/Render/Element/PasswordRevealTest.php --- b/core/tests/Drupal/Tests/Core/Render/Element/PasswordRevealTest.php +++ b/core/tests/Drupal/Tests/Core/Render/Element/PasswordRevealTest.php @@ -3,11 +3,11 @@ namespace Drupal\Tests\Core\Render\Element; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Element\PasswordReveal; +use Drupal\Core\Render\Element\PasswordUnmask; use Drupal\Tests\UnitTestCase; /** - * @coversDefaultClass \Drupal\Core\Render\Element\PasswordReveal + * @coversDefaultClass \Drupal\Core\Render\Element\PasswordUnmask * @group Render */ class PasswordRevealTest extends UnitTestCase { @@ -20,7 +20,7 @@ public function testValueCallback($expected, $input) { $element = []; $form_state = $this->prophesize(FormStateInterface::class)->reveal(); - $this->assertSame($expected, PasswordReveal::valueCallback($element, $input, $form_state)); + $this->assertSame($expected, PasswordUnmask::valueCallback($element, $input, $form_state)); } /** only in patch2: unchanged: --- /dev/null +++ b/core/lib/Drupal/Core/Render/Element/PasswordUnmask.php @@ -0,0 +1,64 @@ + 'password_unmask', + * '#title' => t('Password'), + * '#size' => 25, + * ); + * @endcode + * + * @see \Drupal\Core\Render\Element\Password + * + * @FormElement("password_unmask") + */ +class PasswordUnmask extends Password { + + /** + * {@inheritdoc} + */ + public function getInfo() { + $info = parent::getInfo(); + $info['#process'][] = [static::class, 'processUnmask']; + + return $info; + } + + /** + * Process callback for #type password_unmask elements. + * + * Adds the attributes needed for the password strength bar and the password + * unmask button. + * + * @param array $element + * An associative array containing the properties and children of the + * generic input element. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param array $complete_form + * The complete form structure. + * + * @return array + * The processed element. + */ + public static function processUnmask(array &$element, FormStateInterface $form_state, array &$complete_form) { + $element['#attached']['library'][] = 'core/drupal.password-unmask'; + $element['#attributes']['class'][] = 'password-field'; + $element['#attributes']['class'][] = 'js-password-field'; + $element['#attributes']['data-drupal-password-unmask'] = TRUE; + $element['#attributes']['data-drupal-password-strength'] = TRUE; + + return $element; + } + +} only in patch2: unchanged: --- /dev/null +++ b/core/misc/password-unmask.es6.js @@ -0,0 +1,59 @@ +/** + * @file + * Creates a reveal password link on password inputs. + */ + +(function($, Drupal) { + const hidePass = Drupal.t('Hide password'); + const showPass = Drupal.t('Show password'); + + /** + * Handle password unmask button click to toggle unmasking password. + * + * @param {jQuery.Event} event + * The jQuery event. + */ + const unmaskClickHandler = event => { + const $pass = $(event.data.password); + const $button = $(event.target); + + if ($pass.attr('type') === 'password') { + $pass.attr('type', 'text'); + $button.text(hidePass); + } else { + $pass.attr('type', 'password'); + $button.text(showPass); + } + }; + + /** + * Create password unmask button and bind event. + * + * @param {number} index + * The index in the loop, as provided by `jQuery.each` + * @param {HTMLElement} element + * The password input form element. + */ + const unmaskButton = (index, element) => { + const $trigger = $( + ``, + ); + + $trigger.on('click', { password: element }, unmaskClickHandler); + $trigger.insertAfter(element); + }; + + /** + * Initialize password unmask button. + * + * @type {Drupal~behavior} + */ + Drupal.behaviors.passwordUnmask = { + attach(context) { + $(context) + .find('input[type=password][data-drupal-password-unmask]') + .once('password-unmask') + .each(unmaskButton); + }, + }; +})(jQuery, Drupal, drupalSettings); only in patch2: unchanged: --- /dev/null +++ b/core/misc/password-unmask.js @@ -0,0 +1,61 @@ +/** + * @file + * Creates a reveal password link on password inputs. + */ + +(function ($, Drupal) { + + 'use strict'; + + var showPass = Drupal.t('Show password'); + var hidePass = Drupal.t('Hide password'); + + /** + * Handle password unmask button click to toggle unmasking password. + * + * @param {jQuery.Event} event + * The jQuery event. + */ + function unmaskClickHandler(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); + } + } + + /** + * Create password unmask button and bind event. + * + * @param {number} index + * The index in the loop, as provided by `jQuery.each` + * @param {HTMLElement} element + * The password input form element. + */ + function unmaskButton(index, element) { + var $trigger = $(''); + + $trigger.on('click', {password: element}, unmaskClickHandler); + $trigger.insertAfter(element); + } + + /** + * Initialize reveal links. + * + * @type {Drupal~behavior} + */ + Drupal.behaviors.passwordUnmask = { + attach: function (context) { + $(context).find('input[type=password][data-drupal-password-unmask]') + .once('password-unmask') + .each(unmaskButton); + } + }; + +})(jQuery, Drupal); only in patch2: unchanged: --- /dev/null +++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestPasswordUnmask.php @@ -0,0 +1,43 @@ + $this->t('Password One'), + '#type' => 'password_unmask', + '#size' => 25, + ]; + $form['password_two'] = [ + '#title' => $this->t('Password Two'), + '#type' => 'password_unmask', + '#size' => 25, + ]; + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + } + +}