From 4153bee65d2541b99d4e41aaaf2fd6b2b71b2cc3 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 19 Nov 2012 23:15:36 +0900 Subject: disable "next" button during validation and wait for user to click it. Closes #973 --- src/leap/gui/firstrun/register.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src/leap/gui/firstrun/register.py') diff --git a/src/leap/gui/firstrun/register.py b/src/leap/gui/firstrun/register.py index b46dd4cd..e1a8149c 100644 --- a/src/leap/gui/firstrun/register.py +++ b/src/leap/gui/firstrun/register.py @@ -59,6 +59,7 @@ class RegisterUserPage(QtGui.QWizardPage, UserFormMixIn): self.registerField('userName*', self.userNameLineEdit) self.registerField('userPassword*', self.userPasswordLineEdit) + self.registerField('userPassword2*', self.userPassword2LineEdit) # XXX missing password confirmation # XXX validator! -- cgit v1.2.3 From 7ab2ea1adb82d8c1c6bbae4dc58a157326f579a2 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 19 Nov 2012 23:23:50 +0900 Subject: set commit page (no back button after validation) --- src/leap/gui/firstrun/register.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/leap/gui/firstrun/register.py') diff --git a/src/leap/gui/firstrun/register.py b/src/leap/gui/firstrun/register.py index e1a8149c..f872a127 100644 --- a/src/leap/gui/firstrun/register.py +++ b/src/leap/gui/firstrun/register.py @@ -29,6 +29,13 @@ class RegisterUserPage(QtGui.QWizardPage, UserFormMixIn): self.current_page = "signup" + # commit page means there's no way back after this... + # XXX should change the text on the "commit" button... + self.setCommitPage(True) + + self.initUI() + + def initUI(self): userNameLabel = QtGui.QLabel("User &name:") userNameLineEdit = QtGui.QLineEdit() userNameLineEdit.cursorPositionChanged.connect( -- cgit v1.2.3 From 7a263b8ee74cc92ba39796cd9ad48395adfa7450 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 23 Nov 2012 05:13:36 +0900 Subject: refactor validation mixin; progress until register page --- src/leap/gui/firstrun/register.py | 158 ++++++++++++++++++++++++++++++-------- 1 file changed, 127 insertions(+), 31 deletions(-) (limited to 'src/leap/gui/firstrun/register.py') diff --git a/src/leap/gui/firstrun/register.py b/src/leap/gui/firstrun/register.py index f872a127..ddfcd1c5 100644 --- a/src/leap/gui/firstrun/register.py +++ b/src/leap/gui/firstrun/register.py @@ -2,7 +2,9 @@ Register User Page, used in First Run Wizard """ import logging +import socket +import requests from PyQt4 import QtCore from PyQt4 import QtGui @@ -11,31 +13,34 @@ from leap.gui.firstrun.mixins import UserFormMixIn logger = logging.getLogger(__name__) +from leap.base import auth from leap.gui.constants import APP_LOGO, BARE_USERNAME_REGEX +from leap.gui.progress import InlineValidationPage from leap.gui.styles import ErrorLabelStyleSheet -class RegisterUserPage(QtGui.QWizardPage, UserFormMixIn): +class RegisterUserPage(InlineValidationPage, UserFormMixIn): def __init__(self, parent=None): super(RegisterUserPage, self).__init__(parent) + self.current_page = "signup" - self.setTitle("Sign Up") + self.setTitle(self.tr("Sign Up")) + # subtitle is set in the initializePage self.setPixmap( QtGui.QWizard.LogoPixmap, QtGui.QPixmap(APP_LOGO)) - self.current_page = "signup" - # commit page means there's no way back after this... # XXX should change the text on the "commit" button... self.setCommitPage(True) - self.initUI() + self.setupSteps() + self.setupUI() - def initUI(self): + def setupUI(self): userNameLabel = QtGui.QLabel("User &name:") userNameLineEdit = QtGui.QLineEdit() userNameLineEdit.cursorPositionChanged.connect( @@ -89,8 +94,18 @@ class RegisterUserPage(QtGui.QWizardPage, UserFormMixIn): layout.addWidget(self.userPasswordLineEdit, 2, 3) layout.addWidget(self.userPassword2LineEdit, 3, 3) layout.addWidget(rememberPasswordCheckBox, 4, 3, 4, 4) + + # add validation frame + self.setupValidationFrame() + layout.addWidget(self.valFrame, 5, 2, 5, 2) + self.valFrame.hide() + self.setLayout(layout) + # change "commit" button text + self.setButtonText( + QtGui.QWizard.CommitButton, "Sign up!") + # pagewizard methods def populateErrors(self): @@ -138,9 +153,6 @@ class RegisterUserPage(QtGui.QWizardPage, UserFormMixIn): super(RegisterUserPage, self).paintEvent(event) self.populateErrors() - def set_prevalidation_error(self, error): - self.prevalidation_error = error - def validatePage(self): """ we only pre-validate here password weakness @@ -150,33 +162,117 @@ class RegisterUserPage(QtGui.QWizardPage, UserFormMixIn): and if any errors are thrown there we come back and re-display the validation label. """ + # calls checks, which after successful + # execution will call on_checks_validation_ready + self.do_checks() + return self.is_done() + + def _do_checks(self): + """ + generator that yields actual checks + that are executed in a separate thread + """ + wizard = self.wizard() + curpage = self.current_page + senderr = lambda err: wizard.set_validation_error(curpage, err) - #username = self.userNameLineEdit.text() + provider = self.field('provider_domain') + username = self.userNameLineEdit.text() password = self.userPasswordLineEdit.text() password2 = self.userPassword2LineEdit.text() - # we better have here - # some call to a password checker... - # to assess strenght and avoid silly stuff. - - if password != password2: - self.set_prevalidation_error('Password does not match.') + def fail(): + self.set_undone() return False - if len(password) < 6: - self.set_prevalidation_error('Password too short.') - return False - - if password == "123456": - # joking, but not too much. - self.set_prevalidation_error('Password too obvious.') - return False - - # some cleanup before we leave the page - self.cleanup_errormsg() - - # go - return True + def checkpass(): + # we better have here + # some call to a password checker... + # to assess strenght and avoid silly stuff. + + if password != password2: + msg = self.tr('Password does not match..') + senderr(msg) + return fail() + + if len(password) < 6: + #self.set_prevalidation_error('Password too short.') + msg = self.tr('Password too short.') + senderr(msg) + return fail() + + if password == "123456": + # joking, but not too much. + #self.set_prevalidation_error('Password too obvious.') + msg = self.tr('Password too obvious.') + senderr(msg) + return fail() + + # go + return True + + yield(("head_sentinel", 0), checkpass) + + # XXX should emit signal for .show the frame! + # XXX HERE! + + ################################################## + # 1) register user + ################################################## + + def register(): + # XXX FIXME! + verify = False + + signup = auth.LeapSRPRegister( + schema="https", + provider=provider, + verify=verify) + try: + ok, req = signup.register_user( + username, password) + + except socket.timeout: + msg = self.tr("Error connecting to provider (timeout)") + senderr(msg) + return fail() + + except requests.exceptions.ConnectionError as exc: + logger.error(exc.message) + msg = self.tr('Error Connecting to provider (connerr).') + senderr(msg) + return fail() + + # XXX check for != OK instead??? + + if req.status_code in (404, 500): + msg = self.tr( + "Error during registration (%s)") % req.status_code + return fail() + + validation_msgs = json.loads(req.content) + errors = validation_msgs.get('errors', None) + logger.debug('validation errors: %s' % validation_msgs) + + if errors and errors.get('login', None): + # XXX this sometimes catch the blank username + # but we're not allowing that (soon) + msg = self.tr('Username not available.') + senderr(msg) + return fail() + + logger.debug('registering user') + yield(("registering with provider", 40), register) + + # set_done?? + self.set_done() + yield(("end_sentinel", 0), lambda: None) + + def on_checks_validation_ready(self): + + if self.is_done(): + self.cleanup_errormsg() + self.go_next() def initializePage(self): """ @@ -184,7 +280,7 @@ class RegisterUserPage(QtGui.QWizardPage, UserFormMixIn): """ provider = self.field('provider_domain') self.setSubTitle( - "Register a new user with provider %s." % + self.tr("Register a new user with provider %s.") % provider) self.validationMsg.setText('') self.userPassword2LineEdit.setText('') -- cgit v1.2.3 From d5136a5f3b2aa8b16e8341f2eb99d05993028acf Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 27 Nov 2012 00:12:22 +0900 Subject: inline validation at register page. inline widget and focus and red marks and whistles. --- src/leap/gui/firstrun/register.py | 179 +++++++++++++++++++++++++++----------- 1 file changed, 129 insertions(+), 50 deletions(-) (limited to 'src/leap/gui/firstrun/register.py') diff --git a/src/leap/gui/firstrun/register.py b/src/leap/gui/firstrun/register.py index ddfcd1c5..7ce74892 100644 --- a/src/leap/gui/firstrun/register.py +++ b/src/leap/gui/firstrun/register.py @@ -1,6 +1,7 @@ """ Register User Page, used in First Run Wizard """ +import json import logging import socket @@ -14,6 +15,7 @@ from leap.gui.firstrun.mixins import UserFormMixIn logger = logging.getLogger(__name__) from leap.base import auth +from leap.gui import styles from leap.gui.constants import APP_LOGO, BARE_USERNAME_REGEX from leap.gui.progress import InlineValidationPage from leap.gui.styles import ErrorLabelStyleSheet @@ -39,6 +41,8 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): self.setupSteps() self.setupUI() + self.do_confirm_next = False + self.focused_field = False def setupUI(self): userNameLabel = QtGui.QLabel("User &name:") @@ -101,23 +105,64 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): self.valFrame.hide() self.setLayout(layout) + self.commitText("Sign up!") + # commit button + + def commitText(self, text): # change "commit" button text self.setButtonText( - QtGui.QWizard.CommitButton, "Sign up!") + QtGui.QWizard.CommitButton, text) - # pagewizard methods + @property + def commitButton(self): + return self.wizard().button(QtGui.QWizard.CommitButton) + + def commitFocus(self): + self.commitButton.setFocus() + + def disableCommitButton(self): + self.commitButton.setDisabled(True) + + def disableFields(self): + for field in (self.userNameLineEdit, + self.userPasswordLineEdit, + self.userPassword2LineEdit): + field.setDisabled(True) + + # error painting + + def markRedAndGetFocus(self, field): + field.setStyleSheet(styles.ErrorLineEdit) + if not self.focused_field: + self.focused_field = True + field.setFocus(QtCore.Qt.OtherFocusReason) + + def markRegular(self, field): + field.setStyleSheet(styles.RegularLineEdit) def populateErrors(self): - # XXX could move this to ValidationMixin - # used in providerselect too + def showerr(text): + self.validationMsg.setText(text) + err_lower = text.lower() + if "username" in err_lower: + self.markRedAndGetFocus( + self.userNameLineEdit) + if "password" in err_lower: + self.markRedAndGetFocus( + self.userPasswordLineEdit) + + def unmarkred(): + for field in (self.userNameLineEdit, + self.userPasswordLineEdit, + self.userPassword2LineEdit): + self.markRegular(field) errors = self.wizard().get_validation_error( self.current_page) if errors: bad_str = getattr(self, 'bad_string', None) cur_str = self.userNameLineEdit.text() - showerr = self.validationMsg.setText prev_er = getattr(self, 'prevalidation_error', None) if bad_str is None: @@ -133,7 +178,13 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): if cur_str == bad_str: showerr(errors) else: + self.focused_field = False showerr('') + unmarkred() + else: + # no errors + self.focused_field = False + unmarkred() def cleanup_errormsg(self): """ @@ -153,60 +204,32 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): super(RegisterUserPage, self).paintEvent(event) self.populateErrors() - def validatePage(self): - """ - we only pre-validate here password weakness - stuff, or any other client side validation - that we think of. - real server validation is made on next page, - and if any errors are thrown there we come back - and re-display the validation label. - """ - # calls checks, which after successful - # execution will call on_checks_validation_ready - self.do_checks() - return self.is_done() - def _do_checks(self): """ generator that yields actual checks that are executed in a separate thread """ - wizard = self.wizard() - curpage = self.current_page - senderr = lambda err: wizard.set_validation_error(curpage, err) - provider = self.field('provider_domain') username = self.userNameLineEdit.text() password = self.userPasswordLineEdit.text() password2 = self.userPassword2LineEdit.text() - def fail(): - self.set_undone() - return False - def checkpass(): # we better have here # some call to a password checker... # to assess strenght and avoid silly stuff. if password != password2: - msg = self.tr('Password does not match..') - senderr(msg) - return fail() + return self.fail(self.tr('Password does not match..')) if len(password) < 6: #self.set_prevalidation_error('Password too short.') - msg = self.tr('Password too short.') - senderr(msg) - return fail() + return self.fail(self.tr('Password too short.')) if password == "123456": # joking, but not too much. #self.set_prevalidation_error('Password too obvious.') - msg = self.tr('Password too obvious.') - senderr(msg) - return fail() + return self.fail(self.tr('Password too obvious.')) # go return True @@ -220,6 +243,10 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): # 1) register user ################################################## + # show the frame before going on... + QtCore.QMetaObject.invokeMethod( + self, "showStepsFrame") + def register(): # XXX FIXME! verify = False @@ -233,22 +260,22 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): username, password) except socket.timeout: - msg = self.tr("Error connecting to provider (timeout)") - senderr(msg) - return fail() + return self.fail( + self.tr("Error connecting to provider (timeout)")) except requests.exceptions.ConnectionError as exc: logger.error(exc.message) - msg = self.tr('Error Connecting to provider (connerr).') - senderr(msg) - return fail() + return self.fail( + self.tr('Error Connecting to provider (connerr).')) + except Exception as exc: + return self.fail(exc.message) # XXX check for != OK instead??? if req.status_code in (404, 500): - msg = self.tr( - "Error during registration (%s)") % req.status_code - return fail() + return self.fail( + self.tr( + "Error during registration (%s)") % req.status_code) validation_msgs = json.loads(req.content) errors = validation_msgs.get('errors', None) @@ -257,9 +284,8 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): if errors and errors.get('login', None): # XXX this sometimes catch the blank username # but we're not allowing that (soon) - msg = self.tr('Username not available.') - senderr(msg) - return fail() + return self.fail( + self.tr('Username not available.')) logger.debug('registering user') yield(("registering with provider", 40), register) @@ -269,10 +295,61 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): yield(("end_sentinel", 0), lambda: None) def on_checks_validation_ready(self): - + """ + after checks + """ if self.is_done(): + # XXX should disable + # all entry forms + self.disableFields() self.cleanup_errormsg() - self.go_next() + self.clean_wizard_errors(self.current_page) + # make the user confirm the transition + # to next page. + self.commitText('Connect!') + self.commitFocus() + self.green_validation_status() + self.do_confirm_next = True + + def green_validation_status(self): + val = self.validationMsg + val.setText(self.tr('Registration succeeded!')) + val.setStyleSheet(styles.GreenLineEdit) + + def reset_validation_status(self): + """ + empty the validation msg + and clean the inline validation widget. + """ + self.validationMsg.setText('') + self.steps.removeAllSteps() + self.clearTable() + + # pagewizard methods + + def validatePage(self): + """ + if not register done, do checks. + if done, wait for click. + """ + self.disableCommitButton() + self.cleanup_errormsg() + self.clean_wizard_errors(self.current_page) + + # After a successful validation + # (ie, success register with server) + # we change the commit button text + # and set this flag to True. + if self.do_confirm_next: + return True + + if not self.is_done(): + # calls checks, which after successful + # execution will call on_checks_validation_ready + self.reset_validation_status() + self.do_checks() + + return self.is_done() def initializePage(self): """ @@ -284,9 +361,11 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): provider) self.validationMsg.setText('') self.userPassword2LineEdit.setText('') + self.valFrame.hide() def nextId(self): wizard = self.wizard() if not wizard: return + # XXX this should be called connect return wizard.get_page_index('signupvalidation') -- cgit v1.2.3 From 1bb7e85425f2f427401cd02726c55922874a59a0 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 27 Nov 2012 03:34:08 +0900 Subject: login validation inline --- src/leap/gui/firstrun/register.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src/leap/gui/firstrun/register.py') diff --git a/src/leap/gui/firstrun/register.py b/src/leap/gui/firstrun/register.py index 7ce74892..e85723cb 100644 --- a/src/leap/gui/firstrun/register.py +++ b/src/leap/gui/firstrun/register.py @@ -163,7 +163,7 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): if errors: bad_str = getattr(self, 'bad_string', None) cur_str = self.userNameLineEdit.text() - prev_er = getattr(self, 'prevalidation_error', None) + #prev_er = getattr(self, 'prevalidation_error', None) if bad_str is None: # first time we fall here. @@ -171,9 +171,9 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): self.bad_string = cur_str showerr(errors) else: - if prev_er: - showerr(prev_er) - return + #if prev_er: + #showerr(prev_er) + #return # not the first time if cur_str == bad_str: showerr(errors) @@ -290,7 +290,6 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): logger.debug('registering user') yield(("registering with provider", 40), register) - # set_done?? self.set_done() yield(("end_sentinel", 0), lambda: None) @@ -299,8 +298,6 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn): after checks """ if self.is_done(): - # XXX should disable - # all entry forms self.disableFields() self.cleanup_errormsg() self.clean_wizard_errors(self.current_page) -- cgit v1.2.3