mirror of https://github.com/borgbase/vorta
173 lines
6.3 KiB
Python
173 lines
6.3 KiB
Python
from typing import Optional
|
|
|
|
from PyQt6.QtCore import QObject
|
|
from PyQt6.QtGui import QAction
|
|
from PyQt6.QtWidgets import QFormLayout, QLabel, QLineEdit, QWidget
|
|
|
|
from vorta.i18n import translate
|
|
from vorta.views.utils import get_colored_icon
|
|
|
|
|
|
class PasswordLineEdit(QLineEdit):
|
|
def __init__(self, *, parent: Optional[QWidget] = None, show_visibility_button: bool = True) -> None:
|
|
super().__init__(parent)
|
|
|
|
self._show_visibility_button = show_visibility_button
|
|
self._error_state = False
|
|
self._visible = False
|
|
|
|
self.setEchoMode(QLineEdit.EchoMode.Password)
|
|
|
|
if self._show_visibility_button:
|
|
self.showHideAction = QAction(self.tr("Show password"), self)
|
|
self.showHideAction.setCheckable(True)
|
|
self.showHideAction.toggled.connect(self.toggle_visibility)
|
|
self.showHideAction.setIcon(get_colored_icon("eye"))
|
|
self.addAction(self.showHideAction, QLineEdit.ActionPosition.TrailingPosition)
|
|
|
|
def get_password(self) -> str:
|
|
"""Return password text"""
|
|
return self.text()
|
|
|
|
@property
|
|
def visible(self) -> bool:
|
|
"""Return password visibility"""
|
|
return self._visible
|
|
|
|
@visible.setter
|
|
def visible(self, value: bool) -> None:
|
|
"""Set password visibility"""
|
|
if not isinstance(value, bool):
|
|
raise TypeError("visible must be a boolean value")
|
|
self._visible = value
|
|
self.setEchoMode(QLineEdit.EchoMode.Normal if self._visible else QLineEdit.EchoMode.Password)
|
|
|
|
if self._show_visibility_button:
|
|
if self._visible:
|
|
self.showHideAction.setIcon(get_colored_icon("eye-slash"))
|
|
self.showHideAction.setText(self.tr("Hide password"))
|
|
|
|
else:
|
|
self.showHideAction.setIcon(get_colored_icon("eye"))
|
|
self.showHideAction.setText(self.tr("Show password"))
|
|
|
|
def toggle_visibility(self) -> None:
|
|
"""Toggle password visibility"""
|
|
self.visible = not self._visible
|
|
|
|
@property
|
|
def error_state(self) -> bool:
|
|
"""Return error state"""
|
|
return self._error_state
|
|
|
|
@error_state.setter
|
|
def error_state(self, error: bool) -> None:
|
|
"""Set error state and update style"""
|
|
self._error_state = error
|
|
if error:
|
|
self.setStyleSheet("QLineEdit { border: 2px solid red; }")
|
|
else:
|
|
self.setStyleSheet('')
|
|
|
|
|
|
class PasswordInput(QObject):
|
|
def __init__(self, *, parent=None, minimum_length: int = 9, show_error: bool = True, label: list = None) -> None:
|
|
super().__init__(parent)
|
|
self._minimum_length = minimum_length
|
|
self._show_error = show_error
|
|
|
|
if label:
|
|
self._label_password = QLabel(label[0])
|
|
self._label_confirm = QLabel(label[1])
|
|
else:
|
|
self._label_password = QLabel(self.tr("Enter passphrase:"))
|
|
self._label_confirm = QLabel(self.tr("Confirm passphrase:"))
|
|
|
|
# Create password line edits
|
|
self.passwordLineEdit = PasswordLineEdit()
|
|
self.confirmLineEdit = PasswordLineEdit()
|
|
self.validation_label = QLabel("")
|
|
|
|
self.passwordLineEdit.editingFinished.connect(self.on_editing_finished)
|
|
self.confirmLineEdit.textChanged.connect(self.validate)
|
|
|
|
def on_editing_finished(self) -> None:
|
|
self.passwordLineEdit.editingFinished.disconnect(self.on_editing_finished)
|
|
self.passwordLineEdit.textChanged.connect(self.validate)
|
|
self.validate()
|
|
|
|
def set_labels(self, label_1: str, label_2: str) -> None:
|
|
self._label_password.setText(label_1)
|
|
self._label_confirm.setText(label_2)
|
|
|
|
def set_error_label(self, text: str) -> None:
|
|
self.validation_label.setText(text)
|
|
|
|
def set_validation_enabled(self, enable: bool) -> None:
|
|
self._show_error = enable
|
|
self.passwordLineEdit.error_state = False
|
|
self.confirmLineEdit.error_state = False
|
|
if not enable:
|
|
self.set_error_label("")
|
|
|
|
def clear(self) -> None:
|
|
self.passwordLineEdit.clear()
|
|
self.confirmLineEdit.clear()
|
|
self.passwordLineEdit.error_state = False
|
|
self.confirmLineEdit.error_state = False
|
|
self.set_error_label("")
|
|
|
|
def get_password(self) -> str:
|
|
return self.passwordLineEdit.text()
|
|
|
|
def validate(self) -> bool:
|
|
if not self._show_error:
|
|
return True
|
|
|
|
first_pass = self.passwordLineEdit.text()
|
|
second_pass = self.confirmLineEdit.text()
|
|
|
|
pass_equal = first_pass == second_pass
|
|
pass_long = len(first_pass) >= self._minimum_length
|
|
|
|
self.passwordLineEdit.error_state = False
|
|
self.confirmLineEdit.error_state = False
|
|
self.set_error_label("")
|
|
|
|
if not pass_long and not pass_equal:
|
|
self.passwordLineEdit.error_state = True
|
|
self.confirmLineEdit.error_state = True
|
|
self.set_error_label(
|
|
translate('PasswordInput', "Passwords must be identical and atleast {0} characters long.").format(
|
|
self._minimum_length
|
|
)
|
|
)
|
|
elif not pass_equal:
|
|
self.confirmLineEdit.error_state = True
|
|
self.set_error_label(translate('PasswordInput', "Passwords must be identical."))
|
|
elif not pass_long:
|
|
self.passwordLineEdit.error_state = True
|
|
self.set_error_label(
|
|
translate('PasswordInput', "Passwords must be atleast {0} characters long.").format(
|
|
self._minimum_length
|
|
)
|
|
)
|
|
|
|
return not bool(self.validation_label.text())
|
|
|
|
def add_form_to_layout(self, form_layout: QFormLayout) -> None:
|
|
"""Adds form to layout"""
|
|
form_layout.addRow(self._label_password, self.passwordLineEdit)
|
|
form_layout.addRow(self._label_confirm, self.confirmLineEdit)
|
|
form_layout.addRow(self.validation_label)
|
|
|
|
def create_form_widget(self, parent: Optional[QWidget] = None) -> QWidget:
|
|
""" "Creates and Returns a new QWidget with form layout"""
|
|
widget = QWidget(parent=parent)
|
|
form_layout = QFormLayout(widget)
|
|
form_layout.setContentsMargins(0, 0, 0, 0)
|
|
form_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
|
|
self.add_form_to_layout(form_layout)
|
|
widget.setLayout(form_layout)
|
|
return widget
|