Skip to content

Commit

Permalink
aldonas pasvortkampajn baskulojn por vidigi kaj kaŝi la tajpitan pasv…
Browse files Browse the repository at this point in the history
…orton
  • Loading branch information
interDist committed Jul 18, 2024
1 parent 93b9ddb commit 8511cbf
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 40 deletions.
33 changes: 25 additions & 8 deletions core/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from .auth import auth_log
from .mixins import PasswordFormMixin, SystemEmailFormMixin, UsernameFormMixin
from .models import FEEDBACK_TYPES, SiteConfiguration
from .widgets import PasswordWithToggleInput

User = get_user_model()

Expand Down Expand Up @@ -57,8 +58,12 @@ def __init__(self, *args, **kwargs):
"Capital and small letters are treated as different. Do not use spaces."
)
self.fields['username'].widget.attrs['autocomplete'] = 'username'
self.fields['password1'].widget.attrs['autocomplete'] = 'new-password'
self.fields['password2'].widget.attrs['autocomplete'] = 'new-password'
self.fields['password1'].widget = PasswordWithToggleInput(
attrs=self.fields['password1'].widget.attrs | {'autocomplete': 'new-password'},
)
self.fields['password2'].widget = PasswordWithToggleInput(
attrs=self.fields['password2'].widget.attrs | {'autocomplete': 'new-password'},
)

def clean_realm(self):
"""
Expand Down Expand Up @@ -113,7 +118,9 @@ def __init__(self, request=None, *args, **kwargs):
# The form errors should be rendered in small font.
self.helper.form_error_class = 'small'
self.fields['username'].widget.attrs['autocomplete'] = 'username'
self.fields['password'].widget.attrs['autocomplete'] = 'current-password'
self.fields['password'].widget = PasswordWithToggleInput(
attrs=self.fields['password'].widget.attrs | {'autocomplete': 'current-password'},
)

def confirm_login_allowed(self, user):
"""
Expand Down Expand Up @@ -288,8 +295,12 @@ class SystemPasswordResetForm(PasswordFormMixin, SetPasswordForm):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['new_password1'].widget.attrs['autocomplete'] = 'new-password'
self.fields['new_password2'].widget.attrs['autocomplete'] = 'new-password'
self.fields['new_password1'].widget = PasswordWithToggleInput(
attrs=self.fields['new_password1'].widget.attrs | {'autocomplete': 'new-password'},
)
self.fields['new_password2'].widget = PasswordWithToggleInput(
attrs=self.fields['new_password2'].widget.attrs | {'autocomplete': 'new-password'},
)

def save(self, commit=True):
super().save(commit)
Expand All @@ -304,9 +315,15 @@ class SystemPasswordChangeForm(PasswordFormMixin, PasswordChangeForm):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['old_password'].widget.attrs['autocomplete'] = 'current-password'
self.fields['new_password1'].widget.attrs['autocomplete'] = 'new-password'
self.fields['new_password2'].widget.attrs['autocomplete'] = 'new-password'
self.fields['old_password'].widget = PasswordWithToggleInput(
attrs=self.fields['old_password'].widget.attrs | {'autocomplete': 'current-password'},
)
self.fields['new_password1'].widget = PasswordWithToggleInput(
attrs=self.fields['new_password1'].widget.attrs | {'autocomplete': 'new-password'},
)
self.fields['new_password2'].widget = PasswordWithToggleInput(
attrs=self.fields['new_password2'].widget.attrs | {'autocomplete': 'new-password'},
)

def save(self, **kwargs):
return super().save(**kwargs)
Expand Down
79 changes: 74 additions & 5 deletions core/static/js/forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ $(function() {
if (!$().localConstraint.hasOwnProperty(document.documentElement.lang)) {
$.fn.localConstraint[document.documentElement.lang] = [];
}
var formControlSelectors = [
const formControlSelectors = [
'.form-control', 'input[type="radio"]', 'input[type="checkbox"]', 'input[type="file"]',
].join();
var formSubmitSelectors = [
const formSubmitSelectors = [
'#id_form_submit', '#id_form_submit_alt', '#id_form_submit_ext',
].join();

Expand Down Expand Up @@ -234,6 +234,69 @@ $(function() {
$('#id_password2').data('coupling', '#id_password1');
$('#id_new_password2').data('coupling', '#id_new_password1');

/* password field value toggling */
const passwordToggleElements = document.querySelectorAll(
'input[type="password"] + .password-toggle > button'
);
Array.prototype.forEach.call(passwordToggleElements, function(toggleButton) {
var passwordField = toggleButton.parentElement.previousElementSibling,
$toggleButton = $(toggleButton),
toggleIconNode = toggleButton.querySelector('.fa'),
$toggleTextNode = $(toggleButton.querySelector('.password-toggle-text'));
var inactivityTimeoutID;

function scheduleStateReset() {
if (inactivityTimeoutID) {
clearTimeout(inactivityTimeoutID);
}
inactivityTimeoutID = setTimeout(function() {
$toggleButton.trigger('click', [false]);
$toggleButton.prop('disabled', true);
}, 1000 * 60 * 5);
}

$toggleButton.on('click', function(event, toggleState) {
event.preventDefault();
if (toggleState == undefined) {
scheduleStateReset();
}
if (toggleState == $toggleButton.hasClass('active')) {
// when we want to reveal the value but it is already shown,
// or when we want to obscure the value but it is already hidden,
// do nothing.
return;
}
if ($toggleButton.prop('disabled')) {
return;
}
$toggleButton.button("toggle");

var currentFieldType = passwordField.getAttribute('type');
passwordField.setAttribute('type', currentFieldType === "password" ? "text" : "password");

var currentLabel = $toggleButton.attr('aria-label');
$toggleButton.attr('aria-label', $toggleButton.data('aria-label-inactive'))
.data('aria-label-inactive', currentLabel);
toggleIconNode.classList.toggle(toggleIconNode.getAttribute('data-icon-on'));
toggleIconNode.classList.toggle(toggleIconNode.getAttribute('data-icon-off'));
var currentText = $toggleTextNode.text();
$toggleTextNode.text($toggleTextNode.data('label-inactive'))
.data('label-inactive', currentText);
})

$(passwordField)
.on('keydown', function(event) {
if (event.isCommandKey() && event.which == 32) {
event.preventDefault();
$toggleButton.trigger('click');
};
})
.on('input', function() {
$toggleButton.prop('disabled', false);
scheduleStateReset();
});
});

/* password strength meter for password input fields (registration, password change) */
if (typeof $().pwstrength !== "undefined") {
$.fn.pwstrength.localui = $.fn.pwstrength.localui || {};
Expand All @@ -257,6 +320,12 @@ $(function() {
},
ui: {
bootstrap3: true,
container: pwd_elements.parents('.controls').first()
.append('<div class="password-strength-meter"></div')
.end(),
viewports: {
progress: '.password-strength-meter'
},
showVerdictsInsideProgressBar: true,
progressBarMinWidth: 55,
progressBarMinPercentage: 17,
Expand Down Expand Up @@ -519,7 +588,7 @@ $(function() {
$('#id_form_cancel').each(function() {
this.setAttribute('data-default-href', this.getAttribute('href'));
this.setAttribute('href', '#!');
}).click(function(event) {
}).on('click', function(event) {
event.preventDefault();
history.go(-1);
});
Expand Down Expand Up @@ -553,12 +622,12 @@ $(function() {
actionButtonShortcuts.length++;
});
if (actionButtonShortcuts.length) {
$(window).bind('keydown', function(event) {
$(window).on('keydown', function(event) {
if (event.isCommandKey()) {
var pressedKey = String.fromCharCode(event.which).toLowerCase();
if (actionButtonShortcuts[pressedKey] !== undefined) {
event.preventDefault();
actionButtonShortcuts[pressedKey].click();
actionButtonShortcuts[pressedKey].trigger('click');
}
};
});
Expand Down
3 changes: 3 additions & 0 deletions core/static/js/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ document.getElementsByTagName('html')[0].className += ' js-enabled ';

$(document).ready(function() {

// Fallback elements for when JavaScript is not available or not enabled
$('.no-scripting-fallback').remove();

// Antispam and fallback for unhandled mail links
function openMailtoPopover($mailLink, htmlHasAddress, emailAddress) {
var assistHtml = window.mailto_fallback && window.mailto_fallback[htmlHasAddress];
Expand Down
35 changes: 23 additions & 12 deletions core/static/sass/_all.scss
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ html:not(.js-enabled) {
display: block;
}
}
html.js-enabled {
.no-scripting-fallback {
display: none;
}
}

code, kbd, pre, samp {
font-family: "Fira Mono", Menlo, Monaco, Consolas, "Courier New", monospace;
Expand Down Expand Up @@ -1367,25 +1372,28 @@ p.tip {
}
}

html.js-enabled *[type="password"]::-ms-reveal {
display: none;
}
.password-toggle > button {
width: 6em;
}
*[type="password"] + .progress,
*[type="password"] + .popover + .progress {
margin-top: 2px;
*[type="password"] + .popover + .progress,
.password-strength-meter {
margin-top: 3px;
.progress-bar {
min-width: 0;
transition: min-width 0.6s ease, width 0.6s ease;
}
}
*[type="password"] + .progress .password-verdict,
*[type="password"] + .popover + .progress .password-verdict {
float: right;
margin-right: 1ex;
}
.help-block,
*[type="password"] + .progress .password-verdict,
*[type="password"] + .popover + .progress .password-verdict {
font-family: $font-stack-hint;
.password-verdict {
font-family: $font-stack-hint;
float: right;
margin-right: 1ex;
}
}
.help-block {
font-family: $font-stack-hint;
font-size: 0.9em;
& > kbd {
background-color: rgba(darken($color-default-bg, 75%), 0.75);
Expand Down Expand Up @@ -1499,6 +1507,9 @@ input[type="checkbox"], input[type="radio"] {
}
}

.password-toggle > button {
width: 3em;
}
#id_form_submit_ext, #id_form_submit_ext + #id_form_submit,
#id_form_submit_alt, #id_form_submit_alt + #id_form_submit {
width: 100%;
Expand Down
21 changes: 21 additions & 0 deletions core/templates/ui/widget-password+toggle.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% load i18n %}

<div class="input-group">
{% include 'django/forms/widgets/input.html' %}
<div class="input-group-btn password-toggle requires-scripting">
<button id="{{ widget.attrs.id }}_toggle" class="btn btn-default" type="button" aria-pressed="false" autocomplete="off"
aria-label="{% trans "Show the password. Note that your password will become visible on the screen." %}"
data-aria-label-inactive="{% trans "Hide (mask) the password." %}">
<span class="fa fa-regular fa-eye small" data-icon-on="fa-eye" data-icon-off="fa-eye-slash" aria-hidden="true"></span>
<span class="password-toggle-text small hidden-xxs" data-label-inactive="{% trans "Hide" %}">{% trans "Show" %}</span>
</button>
</div>
{% comment %}
A no-script fallback is needed because an input-group expectes at least 2 visible elements.
However, we cannot wrap the fallback in the semantically more correct <noscript> because it
breaks Bootstrap's :last-child selectors logic.
{% endcomment %}
<div class="input-group-addon no-scripting-fallback">
<span class="fa fa-key" aria-hidden="true"></span>
</div>
</div>
5 changes: 5 additions & 0 deletions core/widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.forms import widgets as form_widgets


class PasswordWithToggleInput(form_widgets.PasswordInput):
template_name = 'ui/widget-password+toggle.html'
24 changes: 18 additions & 6 deletions locale/eo/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,7 @@ msgstr "Bonege!"

#: core/templates/core/message.html
#: core/templates/ui/fragment-datalist_fallback.html
#: core/templates/ui/widget-password+toggle.html
#: hosting/templates/hosting/place_detail.html
#: hosting/templates/hosting/snippets/travel_advice.html
#: hosting/templates/ui/widget-form_divider.html
Expand Down Expand Up @@ -1515,6 +1516,23 @@ msgid "Copy the email address below and send an email via your mailbox:"
msgstr ""
"Kopiu la suban retpoŝtan adreson kaj sendu retmesaĝon pere de via kesto:"

#: core/templates/ui/widget-password+toggle.html
msgid ""
"Show the password. Note that your password will become visible on the screen."
msgstr ""
"Montri la pasvorton. Atentu ke via pasvorto iĝos legebla sur la ekrano."

#: core/templates/ui/widget-password+toggle.html
msgid "Hide (mask) the password."
msgstr "Kaŝi (maski) la pasvorton."

#: core/templates/ui/widget-password+toggle.html
#: hosting/templates/hosting/place_detail.html
#: hosting/templates/ui/widget-form_divider.html
#: pages/templates/pages/privacy.html
msgid "Show"
msgstr "Montri"

#: core/urls.py
msgctxt "URL"
msgid "register/"
Expand Down Expand Up @@ -3129,12 +3147,6 @@ msgstr "Uzi plene kapablan mapon"
msgid "Print map"
msgstr "Printi mapon"

#: hosting/templates/hosting/place_detail.html
#: hosting/templates/ui/widget-form_divider.html
#: pages/templates/pages/privacy.html
msgid "Show"
msgstr "Montri"

#: hosting/templates/hosting/place_detail.html
#: hosting/templates/hosting/profile_detail.html
#: pasportaservo/templates/postman/base_write.html
Expand Down
2 changes: 1 addition & 1 deletion requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ factory-boy==3.2.0
flake8==6.1.0
isort==5.12.0
pre-commit==3.3.3
pyquery==1.4.3
pyquery==2.0.0
sqlparse>=0.2.4
Loading

0 comments on commit 8511cbf

Please sign in to comment.