diff options
| author | kali <kali@leap.se> | 2012-11-27 00:12:22 +0900 | 
|---|---|---|
| committer | kali <kali@leap.se> | 2012-11-27 00:43:01 +0900 | 
| commit | d5136a5f3b2aa8b16e8341f2eb99d05993028acf (patch) | |
| tree | 7ef847e02cb9d60fb38761d0001f41968ea5a23f /src | |
| parent | 7a263b8ee74cc92ba39796cd9ad48395adfa7450 (diff) | |
inline validation at register page.
inline widget and
focus and red marks and whistles.
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/gui/firstrun/providerselect.py | 58 | ||||
| -rw-r--r-- | src/leap/gui/firstrun/providersetup.py | 23 | ||||
| -rw-r--r-- | src/leap/gui/firstrun/register.py | 179 | ||||
| -rw-r--r-- | src/leap/gui/firstrun/regvalidation.py | 80 | ||||
| -rwxr-xr-x | src/leap/gui/firstrun/wizard.py | 2 | ||||
| -rw-r--r-- | src/leap/gui/progress.py | 14 | ||||
| -rw-r--r-- | src/leap/gui/styles.py | 13 | 
7 files changed, 212 insertions, 157 deletions
| diff --git a/src/leap/gui/firstrun/providerselect.py b/src/leap/gui/firstrun/providerselect.py index dffde040..e59a23a9 100644 --- a/src/leap/gui/firstrun/providerselect.py +++ b/src/leap/gui/firstrun/providerselect.py @@ -169,7 +169,7 @@ class SelectProviderPage(InlineValidationPage):      @QtCore.pyqtSlot()      def onDisableCheckButton(self): -        print 'CHECK BUTTON DISABLED!!!' +        #print 'CHECK BUTTON DISABLED!!!'          self.providerCheckButton.setDisabled(True)      @QtCore.pyqtSlot() @@ -183,6 +183,8 @@ class SelectProviderPage(InlineValidationPage):          QtCore.QMetaObject.invokeMethod(              self, "showStepsFrame") +        # is this still needed? +        # XXX can I doo delay(self, "do_checks") ?          delay(self, "launch_checks")      def _do_checks(self): @@ -192,8 +194,6 @@ class SelectProviderPage(InlineValidationPage):          """          wizard = self.wizard() -        curpage = "providerselection" -          full_domain = self.providerNameEdit.text()          # we check if we have a port in the domain string. @@ -205,10 +205,6 @@ class SelectProviderPage(InlineValidationPage):          providercertchecker = wizard.providercertchecker()          eipconfigchecker = wizard.eipconfigchecker(domain=_domain) -        def fail(): -            self.is_done = False -            return False -          yield(("head_sentinel", 0), lambda: None)          ######################## @@ -227,20 +223,16 @@ class SelectProviderPage(InlineValidationPage):              except baseexceptions.LeapException as exc:                  logger.error(exc.message) -                wizard.set_validation_error( -                    curpage, exc.usermessage) -                return fail() +                return self.fail(exc.usermessage)              except Exception as exc: -                wizard.set_validation_error( -                    curpage, exc.message) -                return fail() +                return self.fail(exc.message)              else:                  return True          logger.debug('checking name resolution') -        yield(("check name", 20), namecheck) +        yield((self.tr("checking domain name"), 20), namecheck)          #########################          # 2) try https connection @@ -260,6 +252,7 @@ class SelectProviderPage(InlineValidationPage):              except eipexceptions.HttpsBadCertError as exc:                  logger.debug('exception') +                return self.fail(exc.usermessage)                  # XXX skipping for now...                  ##############################################                  # We had this validation logic @@ -268,8 +261,6 @@ class SelectProviderPage(InlineValidationPage):                  #if self.trustProviderCertCheckBox.isChecked():                      #pass                  #else: -                wizard.set_validation_error( -                    curpage, exc.usermessage)                  #fingerprint = certs.get_cert_fingerprint(                      #domain=domain, sep=" ") @@ -284,23 +275,18 @@ class SelectProviderPage(InlineValidationPage):                      #self.did_cert_check = True                      #self.completeChanged.emit()                      #return False -                return fail()              except baseexceptions.LeapException as exc: -                wizard.set_validation_error( -                    curpage, exc.usermessage) -                return fail() +                return self.fail(exc.usermessage)              except Exception as exc: -                wizard.set_validation_error( -                    curpage, exc.message) -                return fail() +                return self.fail(exc.message)              else:                  return True          logger.debug('checking https connection') -        yield(("https check", 40), httpscheck) +        yield((self.tr("checking https connection"), 40), httpscheck)          ##################################          # 3) try download provider info... @@ -316,28 +302,20 @@ class SelectProviderPage(InlineValidationPage):              except requests.exceptions.SSLError:                  # XXX we should have catched this before.                  # but cert checking is broken. -                wizard.set_validation_error( -                    curpage, -                    self.tr( -                        "Could not get info from provider.")) -                return fail() +                return self.fail(self.tr( +                    "Could not get info from provider."))              except requests.exceptions.ConnectionError: -                wizard.set_validation_error( -                    curpage, -                    self.tr( -                        "Could not download provider info " -                        "(refused conn.).")) -                return fail() +                return self.fail(self.tr( +                    "Could not download provider info " +                    "(refused conn.)."))              except Exception as exc: -                wizard.set_validation_error( -                    curpage, exc.message) -                return fail() - +                return self.fail( +                    self.tr(exc.message))              else:                  return True -        yield(("fetch info", 80), fetchinfo) +        yield((self.tr("fetching provider info"), 80), fetchinfo)          # done! diff --git a/src/leap/gui/firstrun/providersetup.py b/src/leap/gui/firstrun/providersetup.py index 7904538d..1a362794 100644 --- a/src/leap/gui/firstrun/providersetup.py +++ b/src/leap/gui/firstrun/providersetup.py @@ -17,6 +17,9 @@ logger = logging.getLogger(__name__)  class ProviderSetupValidationPage(ValidationPage):      def __init__(self, parent=None):          super(ProviderSetupValidationPage, self).__init__(parent) +        self.current_page = "providersetupvalidation" + +        # XXX needed anymore?          is_signup = self.field("is_signup")          self.is_signup = is_signup @@ -33,7 +36,6 @@ class ProviderSetupValidationPage(ValidationPage):          generator that yields actual checks          that are executed in a separate thread          """ -        curpage = "providersetupvalidation"          full_domain = self.field('provider_domain')          wizard = self.wizard() @@ -44,10 +46,6 @@ class ProviderSetupValidationPage(ValidationPage):          pCertChecker = wizard.providercertchecker(              domain=full_domain) -        def fail(): -            self.is_done = False -            return False -          yield(("head_sentinel", 0), lambda: None)          ######################## @@ -73,19 +71,17 @@ class ProviderSetupValidationPage(ValidationPage):              except baseexceptions.LeapException as exc:                  logger.error(exc.message) -                wizard.set_validation_error( -                    curpage, exc.usermessage) -                return fail() +                # XXX this should be _ method +                return self.fail(self.tr(exc.usermessage))              except Exception as exc: -                wizard.set_validation_error( -                    curpage, exc.message) -                return fail() +                return self.fail(exc.message)              else:                  return True -        yield(('Fetching CA certificate', 30), fetchcacert) +        yield((self.tr('Fetching CA certificate'), 30), +              fetchcacert)          #########################          # 2) check CA fingerprint @@ -106,7 +102,8 @@ class ProviderSetupValidationPage(ValidationPage):              # should catch exception              #return False -        yield((self.tr("Checking CA fingerprint"), 60), checkcafingerprint) +        yield((self.tr("Checking CA fingerprint"), 60), +              checkcafingerprint)          #########################          # 2) check CA fingerprint 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') diff --git a/src/leap/gui/firstrun/regvalidation.py b/src/leap/gui/firstrun/regvalidation.py index 79971944..0e67834b 100644 --- a/src/leap/gui/firstrun/regvalidation.py +++ b/src/leap/gui/firstrun/regvalidation.py @@ -9,12 +9,12 @@ used in First Run Wizard  # the login branch of the wizard.  import logging -import json -import socket +#import json +#import socket  from PyQt4 import QtGui -import requests +#import requests  from leap.gui.progress import ValidationPage  from leap.util.web import get_https_domain_and_port @@ -77,67 +77,59 @@ class RegisterUserValidationPage(ValidationPage):          pCertChecker = wizard.providercertchecker(              domain=full_domain) -        update_signal.emit("head_sentinel", 0) +        yield(("head_sentinel", 0), lambda: None)          ################################################## -        # 2) fetching eip service config +        # 1) fetching eip service config          ################################################## +        def fetcheipconf(): +            try: +                eipconfigchecker.fetch_eip_service_config( +                    domain=full_domain) -        step = "fetch_eipconf" -        fetching_eipconf_msg = "Fetching eip service configuration" -        update_signal.emit(fetching_eipconf_msg, 60) -        try: -            eipconfigchecker.fetch_eip_service_config( -                domain=full_domain) - -        # XXX get specific exception -        except: -            self.set_error( -                step, -                'Could not download eip config.') -            #pause_for_user() -            return False -        #pause_for_user() +            # XXX get specific exception +            except Exception as exc: +                return self.fail(exc.message) + +        yield((self.tr("Fetching provider config..."), 40), +              fetcheipconf)          ################################################## -        # 3) getting client certificate +        # 2) getting client certificate          ################################################## -        # XXX maybe only do this if we come from signup - -        step = "fetch_eipcert" -        fetching_clientcert_msg = "Fetching eip certificate" -        update_signal.emit(fetching_clientcert_msg, 80) -        try: -            pCertChecker.download_new_client_cert( -                credentials=credentials, -                verify=verify) +        def fetcheipcert(): +            try: +                pCertChecker.download_new_client_cert( +                    credentials=credentials, +                    verify=verify) -        except auth.SRPAuthenticationError as exc: -            self.set_error( -                step, -                "Authentication error: %s" % exc.message) -            return False +            except auth.SRPAuthenticationError as exc: +                return self.fail(self.tr( +                    "Authentication error: %s" % exc.message)) +            else: +                return True -        #pause_for_user() +        yield((self.tr("Fetching eip certificate"), 80), +              fetcheipcert)          ################          # end !          ################ - -        update_signal.emit("end_sentinel", 100) -        #pause_for_user() - -        # here we go! :) -        # this should be called CONNECT PAGE AGAIN. -        self.run_eip_checks_for_provider_and_connect(_domain) +        self.set_done() +        yield(("end_sentinel", 100), lambda: None)      def on_checks_validation_ready(self):          """          called after _do_checks has finished          (connected to checker thread finished signal)          """ -        pass +        # this should be called CONNECT PAGE AGAIN. +        # here we go! :) +        full_domain = self.field('provider_domain') +        domain, port = get_https_domain_and_port(full_domain) +        _domain = u"%s:%s" % (domain, port) if port != 443 else unicode(domain) +        self.run_eip_checks_for_provider_and_connect(_domain)      def run_eip_checks_for_provider_and_connect(self, domain):          wizard = self.wizard() diff --git a/src/leap/gui/firstrun/wizard.py b/src/leap/gui/firstrun/wizard.py index 2ee1947a..9b77b877 100755 --- a/src/leap/gui/firstrun/wizard.py +++ b/src/leap/gui/firstrun/wizard.py @@ -39,7 +39,7 @@ TODO-ish:  [ ] Document signals used / expected.  [ ] Separate style from widgets.  [ ] Fix TOFU Widget for provider cert. -[ ] Refactor widgets out. +[X] Refactor widgets out.  [ ] Follow more MVC style.  [ ] Maybe separate "first run wizard" into different wizards      that share some of the pages? diff --git a/src/leap/gui/progress.py b/src/leap/gui/progress.py index 4f3a7d81..6f13a1ac 100644 --- a/src/leap/gui/progress.py +++ b/src/leap/gui/progress.py @@ -202,6 +202,20 @@ class WithStepsMixIn(object):          t.begin()          self.threads.append(t) +    def fail(self, err=None): +        """ +        return failed state +        and send error notification as +        a nice side effect +        """ +        wizard = self.wizard() +        senderr = lambda err: wizard.set_validation_error( +            self.current_page, err) +        self.set_undone() +        if err: +            senderr(err) +        return False +      @QtCore.pyqtSlot()      def launch_checks(self):          self.do_checks() diff --git a/src/leap/gui/styles.py b/src/leap/gui/styles.py index 18c0ac97..b482922e 100644 --- a/src/leap/gui/styles.py +++ b/src/leap/gui/styles.py @@ -1,19 +1,14 @@ -ErrorLabelStyleSheet = """ -QLabel { color: red; -         font-weight: bold} -""" +GreenLineEdit = "QLabel {color: green; font-weight: bold}" +ErrorLabelStyleSheet = """QLabel { color: red; font-weight: bold }""" +ErrorLineEdit = """QLineEdit { border: 1px solid red; }""" -ErrorLineEdit = """ -QLineEdit { -    border: 1px solid red; -} -"""  # XXX this is bad.  # and you should feel bad for it.  # The original style has a sort of box color  # white/beige left-top/right-bottom or something like  # that. +  RegularLineEdit = """  QLineEdit {      border: 1px solid black; | 
