From ae46639e35adbfb348c4ce7766c5eabb55ad7475 Mon Sep 17 00:00:00 2001
From: Ryan Cramer
Date: Fri, 12 Feb 2021 15:09:08 -0500
Subject: [PATCH] Add feature request processwire/processwire-requests#203
support for password unmask in InputfieldPassword
---
.../InputfieldPassword/InputfieldPassword.js | 19 ++++++++++
.../InputfieldPassword.min.js | 2 +-
.../InputfieldPassword.module | 38 ++++++++++++++++---
3 files changed, 52 insertions(+), 7 deletions(-)
diff --git a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.js b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.js
index 0939b884..91c7575e 100644
--- a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.js
+++ b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.js
@@ -168,6 +168,25 @@ jQuery(document).ready(function($) {
}
if($on) $on.addClass('on').siblings('.on').removeClass('on');
});
+
+ var $passMask = $inputfield.find('.pass-mask');
+ if($passMask.length) {
+ var $passMaskShow = $passMask.find('.pass-mask-show');
+ var $passMaskHide = $passMask.find('.pass-mask-hide');
+ $passMaskHide.hide();
+ $passMaskShow.on('click', function(e) {
+ $(this).hide();
+ $passMaskHide.show();
+ $inputfield.find('input[type=password]').prop('type', 'text').addClass('pass-unmask');
+ return false;
+ });
+ $passMaskHide.hide().on('click', function(e) {
+ $(this).hide();
+ $passMaskShow.show();
+ $inputfield.find('input.pass-unmask').prop('type', 'password').removeClass('pass-unmask');
+ return false;
+ });
+ }
});
// accommodate issue where Firefox auto-populates remembered password when it shouldn't
diff --git a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.min.js b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.min.js
index 99d34185..52c0e2b8 100644
--- a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.min.js
+++ b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.min.js
@@ -1 +1 @@
-jQuery(document).ready(function($){var $inputs=$("input.InputfieldPasswordComplexify");$inputs.each(function(){var $input=$(this);var $inputfield=$input.closest(".Inputfield");var $confirm=$inputfield.find(".InputfieldPasswordConfirm");var $confirms=$confirm.next(".pass-confirm");var $inputOld=$inputfield.find("input.InputfieldPasswordOld");var $wrapScores=$input.siblings(".pass-scores");var $percent=$input.siblings(".pass-percent");var $scores=$wrapScores.children();var requirements=$wrapScores.attr("data-requirements").split(" ");var minlength=parseInt($input.attr("data-minlength"));var inputOldEvent;var options={banMode:$input.attr("data-banMode"),strengthScaleFactor:parseFloat($input.attr("data-factor")),minimumChars:minlength};if($inputOld.length){$input.attr("disabled","disabled");inputOldEvent=function(){if($(this).val().length>0){$inputOld.off("keyup input change blur",inputOldEvent);$input.removeAttr("disabled")}};$inputOld.on("keyup input change blur",inputOldEvent)}if($confirm.length)$confirm.attr("disabled","disabled");$input.complexify(options,function(valid,complexity){var $on=null;var val=$input.val();var len=val.length;var numGood=0;if(len>0){for(var n=0;n("+Math.floor(complexity)+"%)")}if($confirm.val().length){$confirm.change()}});$input.on("change",function(){var val=$(this).val();if(val.length>0){$input.attr("required","required");$confirm.attr("required","required")}else if(!$(this).closest(".InputfieldStateRequired").length){$input.removeAttr("required");$confirm.removeAttr("required")}});$confirm.on("keyup change",function(){var val1=$input.val();var val2=$(this).val();var $on=null;var $p=$input.closest("p").removeClass("pass-matches");if(val2.length==0){$on=$confirms.children(".confirm-pending")}else if(val1==val2){$on=$confirms.children(".confirm-yes");$p.addClass("pass-matches")}else if(val1.indexOf(val2)===0){$on=$confirms.children(".confirm-qty");$on.children("span").html(val2.length+"/"+val1.length)}else{$on=$confirms.children(".confirm-no")}if($on)$on.addClass("on").siblings(".on").removeClass("on")})});var $ffinputs=$(".InputfieldPassword").find("input[autocomplete='new-password']");if($ffinputs.length){setTimeout(function(){$ffinputs.each(function(){if($(this).val().length<1||$(this).attr("value").length>0)return;$(this).val("").trigger("keyup").change().closest(".Inputfield").removeClass("InputfieldStateChanged")})},1e3)}});
\ No newline at end of file
+jQuery(document).ready(function($){var $inputs=$("input.InputfieldPasswordComplexify");$inputs.each(function(){var $input=$(this);var $inputfield=$input.closest(".Inputfield");var $confirm=$inputfield.find(".InputfieldPasswordConfirm");var $confirms=$confirm.next(".pass-confirm");var $inputOld=$inputfield.find("input.InputfieldPasswordOld");var $wrapScores=$input.siblings(".pass-scores");var $percent=$input.siblings(".pass-percent");var $scores=$wrapScores.children();var requirements=$wrapScores.attr("data-requirements").split(" ");var minlength=parseInt($input.attr("data-minlength"));var inputOldEvent;var options={banMode:$input.attr("data-banMode"),strengthScaleFactor:parseFloat($input.attr("data-factor")),minimumChars:minlength};if($inputOld.length){$input.attr("disabled","disabled");inputOldEvent=function(){if($(this).val().length>0){$inputOld.off("keyup input change blur",inputOldEvent);$input.removeAttr("disabled")}};$inputOld.on("keyup input change blur",inputOldEvent)}if($confirm.length)$confirm.attr("disabled","disabled");$input.complexify(options,function(valid,complexity){var $on=null;var val=$input.val();var len=val.length;var numGood=0;if(len>0){for(var n=0;n("+Math.floor(complexity)+"%)")}if($confirm.val().length){$confirm.change()}});$input.on("change",function(){var val=$(this).val();if(val.length>0){$input.attr("required","required");$confirm.attr("required","required")}else if(!$(this).closest(".InputfieldStateRequired").length){$input.removeAttr("required");$confirm.removeAttr("required")}});$confirm.on("keyup change",function(){var val1=$input.val();var val2=$(this).val();var $on=null;var $p=$input.closest("p").removeClass("pass-matches");if(val2.length==0){$on=$confirms.children(".confirm-pending")}else if(val1==val2){$on=$confirms.children(".confirm-yes");$p.addClass("pass-matches")}else if(val1.indexOf(val2)===0){$on=$confirms.children(".confirm-qty");$on.children("span").html(val2.length+"/"+val1.length)}else{$on=$confirms.children(".confirm-no")}if($on)$on.addClass("on").siblings(".on").removeClass("on")});var $passMask=$inputfield.find(".pass-mask");if($passMask.length){var $passMaskShow=$passMask.find(".pass-mask-show");var $passMaskHide=$passMask.find(".pass-mask-hide");$passMaskHide.hide();$passMaskShow.on("click",function(e){$(this).hide();$passMaskHide.show();$inputfield.find("input[type=password]").prop("type","text").addClass("pass-unmask");return false});$passMaskHide.hide().on("click",function(e){$(this).hide();$passMaskShow.show();$inputfield.find("input.pass-unmask").prop("type","password").removeClass("pass-unmask");return false})}});var $ffinputs=$(".InputfieldPassword").find("input[autocomplete='new-password']");if($ffinputs.length){setTimeout(function(){$ffinputs.each(function(){if($(this).val().length<1||$(this).attr("value").length>0)return;$(this).val("").trigger("keyup").change().closest(".Inputfield").removeClass("InputfieldStateChanged")})},1e3)}});
\ No newline at end of file
diff --git a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.module b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.module
index 4a7d9753..c8fc80a9 100644
--- a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.module
+++ b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.module
@@ -3,7 +3,7 @@
/**
* An Inputfield for handling a password
*
- * ProcessWire 3.x, Copyright 2020 by Ryan Cramer
+ * ProcessWire 3.x, Copyright 2021 by Ryan Cramer
* https://processwire.com
*
* @property array $requirements Array of requirements (See require* constants)
@@ -12,6 +12,7 @@
* @property float $complexifyFactor Complexify factor, lower numbers enable simpler passwords (default=0.7)
* @property int $requireOld Require previous password? 0=Auto, 1=Yes, -1=No, Default=0 (Auto)
* @property bool $showPass Allow password to be rendered in renderValue and/or re-populated in form?
+ * @property bool $unmask Allow passwords to be unmasked? (3.0.173+)
* @property string $defaultLabel Default label for field (default='Set Password'). Used when no 'label' has been set.
* @property string $oldPassLabel Label for old/current password placeholder.
* @property string $newPassLabel Label for new password placeholder.
@@ -114,6 +115,7 @@ class InputfieldPassword extends InputfieldText {
self::requireNone => $this->_('none (disable all above)'),
));
$this->set('showPass', false); // allow password to be rendered in renderValue and/or re-populated in form?
+ $this->set('unmask', false);
$this->set('oldPassLabel', $this->_('Current password'));
$this->set('newPassLabel', $this->_('New password'));
$this->set('confirmLabel', $this->_('Confirm'));
@@ -262,6 +264,14 @@ class InputfieldPassword extends InputfieldText {
"$okIcon" .
"" .
"
";
+
+ if($this->unmask) {
+ $out .=
+ "" .
+ "" . $this->_('Show Password') . "" .
+ "" . $this->_('Hide Password') . "" .
+ "
";
+ }
$this->attr('value', $value);
if($trackChanges) $this->setTrackChanges(true);
@@ -449,13 +459,14 @@ class InputfieldPassword extends InputfieldText {
'maxlength',
'showCount',
'size',
- );
+ );
foreach($skips as $name) {
$f = $inputfields->get($name);
if($f) $inputfields->remove($f);
}
-
+
+ /** @var InputfieldCheckboxes $f */
$f = $this->wire('modules')->get('InputfieldCheckboxes');
$f->attr('name', 'requirements');
$f->label = $this->_('Password requirements');
@@ -467,7 +478,8 @@ class InputfieldPassword extends InputfieldText {
$f->attr('value', $value);
$f->columnWidth = 50;
$inputfields->add($f);
-
+
+ /** @var InputfieldRadios $f */
$f = $this->wire('modules')->get('InputfieldRadios');
$f->attr('name', 'complexifyBanMode');
$f->label = $this->_('Word ban mode');
@@ -477,7 +489,8 @@ class InputfieldPassword extends InputfieldText {
$f->attr('value', $this->complexifyBanMode);
$f->columnWidth = 50;
$inputfields->add($f);
-
+
+ /** @var InputfieldFloat $f */
$f = $this->wire('modules')->get('InputfieldFloat');
$f->attr('name', 'complexifyFactor');
$f->label = $this->_('Complexify factor');
@@ -488,6 +501,7 @@ class InputfieldPassword extends InputfieldText {
$f->columnWidth = 50;
$inputfields->add($f);
+ /** @var InputfieldInteger $f */
$f = $this->wire('modules')->get('InputfieldInteger');
$f->attr('name', 'minlength');
$f->label = $this->_('Minimum password length');
@@ -497,13 +511,15 @@ class InputfieldPassword extends InputfieldText {
$inputfields->add($f);
if(!$this->getSetting('hasFieldtype')) {
+ /** @var InputfieldCheckbox $f */
$f = $this->wire('modules')->get('InputfieldCheckbox');
$f->attr('name', 'showPass');
$f->label = $this->_('Allow existing passwords to be shown and/or rendered in form?');
if($this->getSetting("showPass")) $f->attr('checked', 'checked');
$inputfields->add($f);
}
-
+
+ /** @var InputfieldRadios $f */
$f = $this->wire('modules')->get('InputfieldRadios');
$f->attr('name', 'requireOld');
$f->label = $this->_('Require old password before allowing changes?');
@@ -515,6 +531,16 @@ class InputfieldPassword extends InputfieldText {
$f->attr('value', (int) $this->requireOld);
$inputfields->add($f);
+ $f = $this->wire()->modules->get('InputfieldRadios');
+ $f->attr('name', 'unmask');
+ $f->label = $this->_('Allow user to show/unmask password during changes?');
+ $f->description = $this->_('Provides a show/hide password control so users can see what they type when in an appropriate environment.');
+ $f->addOption(1, $this->_('Yes'));
+ $f->addOption(0, $this->_('No'));
+ $f->optionColumns = 1;
+ $f->attr('value', (int) $this->unmask);
+ $inputfields->add($f);
+
return $inputfields;
}
}