summaryrefslogtreecommitdiff
path: root/src/leap
diff options
context:
space:
mode:
authorkali <kali@leap.se>2012-11-06 01:26:05 +0900
committerkali <kali@leap.se>2012-11-06 01:26:05 +0900
commitc387a52f841e8933ed7282d198ed1ece863979fc (patch)
treec3465991425dc6de6ae9296b931e8a805491fa05 /src/leap
parent0a8a34879a701a2d045f628403c6a0f8be21dc82 (diff)
new validation pages in a reusable MVC style
using progress indicators inside QTableWidget
Diffstat (limited to 'src/leap')
-rw-r--r--src/leap/base/tests/__init__.py0
-rw-r--r--src/leap/baseapp/mainwindow.py7
-rwxr-xr-xsrc/leap/gui/firstrunwizard.py166
-rw-r--r--src/leap/gui/mainwindow_rc.py97
-rw-r--r--src/leap/gui/progress.py261
-rw-r--r--src/leap/gui/tests/integration/fake_user_signup.py6
6 files changed, 442 insertions, 95 deletions
diff --git a/src/leap/base/tests/__init__.py b/src/leap/base/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/leap/base/tests/__init__.py
diff --git a/src/leap/baseapp/mainwindow.py b/src/leap/baseapp/mainwindow.py
index 8f359dbf..8e12b5f6 100644
--- a/src/leap/baseapp/mainwindow.py
+++ b/src/leap/baseapp/mainwindow.py
@@ -147,16 +147,15 @@ class LeapWindow(QtGui.QMainWindow,
self.initchecks.begin()
-class InitChecksThread(QtCore.QThread):
- # XXX rename as a generic QThread class,
- # has nothing specific to initchecks
+class FunThread(QtCore.QThread):
def __init__(self, fun, parent=None):
QtCore.QThread.__init__(self, parent)
self.fun = fun
def run(self):
- self.fun()
+ if self.fun:
+ self.fun()
def begin(self):
self.start()
diff --git a/src/leap/gui/firstrunwizard.py b/src/leap/gui/firstrunwizard.py
index 6b9921d9..7876c3c8 100755
--- a/src/leap/gui/firstrunwizard.py
+++ b/src/leap/gui/firstrunwizard.py
@@ -19,9 +19,11 @@ from leap.crypto import certs
from leap.crypto import leapkeyring
from leap.eip import checks as eipchecks
from leap.eip import exceptions as eipexceptions
-from leap.gui import mainwindow_rc
+from leap.gui.progress import ValidationPage
from leap.util.coroutines import coroutine
+from leap.gui import mainwindow_rc
+
try:
from collections import OrderedDict
except ImportError:
@@ -101,7 +103,6 @@ class FirstRunWizard(QtGui.QWizard):
# XXX ??? ^v
self.is_previously_registered = bool(self.eip_username)
self.from_login = False
- #self.allow_revisit = None
pages_dict = OrderedDict((
# (name, WizardPage)
@@ -110,13 +111,15 @@ class FirstRunWizard(QtGui.QWizard):
SelectProviderPage),
('login', LogInPage),
('providerinfo', ProviderInfoPage),
- ('providersetup', ProviderSetupPage),
+ ('providersetupvalidation', ProviderSetupValidationPage),
('signup', RegisterUserPage),
('connecting', ConnectingPage),
('lastpage', LastPage)
))
self.add_pages_from_dict(pages_dict)
+ self.validation_errors = {}
+
self.setPixmap(
QtGui.QWizard.BannerPixmap,
QtGui.QPixmap(':/images/banner.png'))
@@ -151,25 +154,11 @@ class FirstRunWizard(QtGui.QWizard):
"""
return self.pages_dict.keys().index(page_name)
- # XXX was trying to allow temporary
- # a revisit. this does not work cause visitedPages
- # is not called internally.
-
- #def allow_page_revisit(self, page_name):
- #self.allow_revisit = self.get_page_index(page_name)
-#
- #def visitedPages(self):
- #"""
- #reimplementation of visitedPages
- #that temporary allows to revisit a page
- #if allow_revisit is set
- #"""
- #visited = super(FirstRunWizard, self).visitedPages()
- #allow = self.allow_revisit
- #if allow:
- #visited.remove(allow)
- #self.allow_revisit = None
- #return visited
+ def set_validation_error(self, pagename, error):
+ self.validation_errors[pagename] = error
+
+ def get_validation_error(self, pagename):
+ return self.validation_errors.get(pagename, None)
def set_providerconfig(self, providerconfig):
self.providerconfig = providerconfig
@@ -447,6 +436,20 @@ class SelectProviderPage(QtGui.QWizardPage):
self.certinfoGroup.hide()
def validatePage(self):
+ ##################################
+ # XXX FIXME!
+ ##################################
+ ##################################
+ ##################################
+ ##################################
+ ##### validation skipped !!! #####
+ ##################################
+ ##################################
+ return True
+ ##################################
+ ##################################
+ ##################################
+
wizard = self.wizard()
netchecker = wizard.netchecker()
providercertchecker = wizard.providercertchecker()
@@ -559,39 +562,25 @@ class ProviderInfoPage(QtGui.QWizardPage):
def nextId(self):
wizard = self.wizard()
- if not wizard:
- return
- return wizard.get_page_index('providersetup')
+ next_ = "providersetupvalidation"
+ return wizard.get_page_index(next_)
-class ProviderSetupPage(QtGui.QWizardPage):
+class ProviderSetupValidationPage(ValidationPage):
def __init__(self, parent=None):
- super(ProviderSetupPage, self).__init__(parent)
-
- self.setTitle("Provider Setup")
- self.setSubTitle("Setting up provider.")
+ super(ProviderSetupValidationPage, self).__init__(parent)
+ self.setTitle("Setting up provider")
+ #self.setSubTitle(
+ #"auto configuring provider...")
self.setPixmap(
QtGui.QWizard.LogoPixmap,
QtGui.QPixmap(APP_LOGO))
- self.status = QtGui.QLabel("")
- self.progress = QtGui.QProgressBar()
- self.progress.setMaximum(100)
- self.progress.hide()
-
- layout = QtGui.QGridLayout()
- layout.addWidget(self.status, 0, 1)
- layout.addWidget(self.progress, 5, 1)
-
- self.setLayout(layout)
-
- def set_status(self, status):
- self.status.setText(status)
- self.status.setWordWrap(True)
-
- def fetch_and_validate(self):
- # Fake... till you make it...
+ def _do_checks(self, signal=None):
+ """
+ executes actual checks in a separate thread
+ """
import time
domain = self.field('provider_domain')
wizard = self.wizard()
@@ -600,7 +589,7 @@ class ProviderSetupPage(QtGui.QWizardPage):
pCertChecker = wizard.providercertchecker
certchecker = pCertChecker(domain=domain)
- self.set_status('Fetching CA certificate')
+ signal.emit('Fetching CA certificate')
self.progress.setValue(30)
if pconfig:
@@ -615,68 +604,66 @@ class ProviderSetupPage(QtGui.QWizardPage):
# (Check with the trusted fingerprints dict
# or something smart)
- certchecker.download_ca_cert(
- uri=ca_cert_uri,
- verify=False)
+ #certchecker.download_ca_cert(
+ #uri=ca_cert_uri,
+ #verify=False)
+
+ time.sleep(2)
- self.set_status('Checking CA fingerprint')
+ signal.emit('Checking CA fingerprint')
self.progress.setValue(66)
- ca_cert_fingerprint = pconfig.get('ca_cert_fingerprint', None)
+ #ca_cert_fingerprint = pconfig.get('ca_cert_fingerprint', None)
# XXX get fingerprint dict (types)
- sha256_fpr = ca_cert_fingerprint.split('=')[1]
+ #sha256_fpr = ca_cert_fingerprint.split('=')[1]
- validate_fpr = certchecker.check_ca_cert_fingerprint(
- fingerprint=sha256_fpr)
+ #validate_fpr = certchecker.check_ca_cert_fingerprint(
+ #fingerprint=sha256_fpr)
time.sleep(0.5)
- if not validate_fpr:
+ #if not validate_fpr:
# XXX update validationMsg
# should catch exception
- return False
+ #return False
- self.set_status('Validating api certificate')
+ signal.emit('Validating api certificate')
self.progress.setValue(90)
- api_uri = pconfig.get('api_uri', None)
- try:
- api_cert_verified = certchecker.verify_api_https(api_uri)
- except requests.exceptions.SSLError as exc:
- logger.error('BUG #638. %s' % exc.message)
+ #api_uri = pconfig.get('api_uri', None)
+ #try:
+ #api_cert_verified = certchecker.verify_api_https(api_uri)
+ #except requests.exceptions.SSLError as exc:
+ #logger.error('BUG #638. %s' % exc.message)
# XXX RAISE! See #638
# bypassing until the hostname is fixed.
# We probably should raise yet-another-warning
# here saying user that the hostname "XX.XX.XX.XX' does not
# match 'foo.bar.baz'
- api_cert_verified = True
+ #api_cert_verified = True
- if not api_cert_verified:
+ #if not api_cert_verified:
# XXX update validationMsg
# should catch exception
- return False
+ #return False
time.sleep(0.5)
#ca_cert_path = checker.ca_cert_path
self.progress.setValue(100)
+ signal.emit('end_sentinel')
time.sleep(1)
- # pagewizard methods
-
- def initializePage(self):
- self.set_status(
- 'We are going to contact the provider to get '
- 'the certificates that will be used to stablish '
- 'a secure connection.<br><br>Click <i>next</i> to continue.')
- self.progress.setValue(0)
- self.progress.hide()
-
- # XXX use a call to "next" instead?
- #self.wizard().next()
-
- def validatePage(self):
- self.progress.show()
- self.fetch_and_validate()
-
- return True
+ def _do_validation(self):
+ """
+ called after _do_checks has finished
+ (connected to checker thread finished signal)
+ """
+ if self.errors:
+ print 'going back with errors'
+ wizard.set_validation_error(
+ 'signup', 'that name is taken')
+ self.go_back()
+ else:
+ print 'going next'
+ self.go_next()
def nextId(self):
wizard = self.wizard()
@@ -952,12 +939,15 @@ class RegisterUserPage(QtGui.QWizardPage, UserFormMixIn):
signup = auth.LeapSRPRegister(
schema="http",
- provider=domain,
+ #provider=domain,
+ ###########################
+ # FIXME! REMOVE DEBUG!
+ #
# debug -----
- #provider="localhost",
+ provider="localhost",
#register_path="timeout",
- #port=8000
+ port=8000
)
try:
ok, req = signup.register_user(username, password)
diff --git a/src/leap/gui/mainwindow_rc.py b/src/leap/gui/mainwindow_rc.py
index be575159..63e9f6be 100644
--- a/src/leap/gui/mainwindow_rc.py
+++ b/src/leap/gui/mainwindow_rc.py
@@ -2,7 +2,7 @@
# Resource object code
#
-# Created: Thu Sep 13 16:12:58 2012
+# Created: Tue Nov 6 01:22:11 2012
# by: The Resource Compiler for PyQt (Qt v4.8.2)
#
# WARNING! All changes made in this file will be lost!
@@ -1491,6 +1491,94 @@ qt_resource_data = "\
\xc3\x25\x0d\x25\x35\x01\xd7\x0f\x5b\xb5\x7e\x8e\x93\x83\xff\x0f\
\x92\x04\x28\x92\xfd\x58\xc9\xac\x00\x00\x00\x00\x49\x45\x4e\x44\
\xae\x42\x60\x82\
+\x00\x00\x05\x5f\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x00\x8e\x00\x00\x00\x8e\
+\x01\x6b\xdf\xd6\xc9\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x04\xdc\x49\x44\
+\x41\x54\x48\x89\x8d\x95\x79\x6c\x54\x55\x14\xc6\x7f\xf7\xce\x4c\
+\x3b\xa5\xed\x14\x6b\x3b\x25\x42\x17\x18\x28\x65\xba\x90\x6e\x18\
+\xa3\xc5\x05\xa4\x86\x20\x21\xa0\x0d\x88\x24\x26\x98\x10\x28\x68\
+\x42\x40\x8c\x20\x1a\xa3\x16\x43\x48\x8c\x9a\x50\x08\xc6\x14\xad\
+\x9a\x22\x8d\xa2\xa6\x01\x42\x13\x20\x44\x49\x3b\x2c\x5d\x87\xd2\
+\x05\x64\xb1\xad\x85\x76\xa6\xed\x74\x9b\xf7\xae\x7f\xcc\x62\x9b\
+\x69\x2b\xe7\x9f\x97\x73\x5e\xce\xf7\x9d\x7b\xce\x77\xcf\x15\x4a\
+\x29\xa6\xb3\x95\x55\x0b\x2c\xe1\x26\x73\x8e\x80\x3c\x14\xb9\x20\
+\x24\x4a\xd4\x02\x0e\xa3\x34\x39\x2a\x96\xd7\xba\xa6\xcb\x17\x53\
+\x11\xac\x3b\x97\x91\xaf\x94\x2c\x45\x91\x0d\x88\x29\xf2\x15\xd0\
+\xa0\xeb\xa2\xf8\xe7\xc2\xeb\x17\x1f\x89\xa0\xe8\x44\x46\x98\x77\
+\xa6\xf8\x00\xe4\x1e\x83\x30\x18\x72\xe3\x0a\x48\x8d\x49\xc7\x66\
+\x49\xe7\x71\x73\x02\x12\xc9\x83\x91\x2e\x5a\x5d\x8d\xdc\x74\x37\
+\x52\xdb\x73\x01\xaf\x3e\xa6\x03\x9f\xc7\x98\xfa\xf7\x7e\xf3\x5c\
+\xc7\xf0\x94\x04\xaf\x9c\xc9\x4a\xd3\x05\x15\x40\x66\x72\x54\x2a\
+\xc5\xf6\xfd\xb4\xba\xae\xd2\xe6\xba\xc6\xbd\x81\x16\x06\x46\x7b\
+\x30\x00\xb1\x66\x2b\x89\x51\x69\x24\x5b\x32\x48\xb6\x64\x73\xd8\
+\xf9\x09\x6d\xee\x66\x80\x66\x29\xd5\xfa\x9f\x96\xd5\xd7\x85\x10\
+\xf8\x2a\x97\xb5\x52\xc8\xcc\x75\x29\x6f\x92\x1f\xbf\x94\x32\xe7\
+\xc7\x74\x79\xda\x31\x08\x81\x44\x60\x10\x02\x03\x4c\xf0\xe3\xcc\
+\x4f\xb0\x7a\xfe\x1e\xea\xfa\x1c\xfc\xd8\x76\x04\x4d\xd7\xda\xbc\
+\xa3\x61\x59\xa7\x56\xd5\x7a\x00\x64\x80\xc9\xd7\x16\x32\xd7\xa6\
+\x6c\x26\xde\x6c\xa5\xe4\xca\x66\xee\x0f\xb6\x4d\xd1\xfa\xff\xac\
+\x77\xf8\x6f\xca\x1b\x77\x32\x2b\x2c\x9a\x8d\xf3\x8b\x41\x60\x33\
+\x86\x8f\x95\x04\xfe\x4b\xf0\x0d\x14\xe4\x9e\xe4\xa8\x54\xf2\xe3\
+\x9f\xa5\xbc\xe5\x20\x9a\xd2\xfe\x17\x1c\x40\x20\x88\x09\x4f\xe0\
+\xcf\xfb\x15\x64\xc7\x2e\x21\x35\x26\x13\x50\x3b\xd6\x9c\x5e\x5c\
+\x00\x60\x04\x50\x4a\x96\x1a\x84\xc1\xb0\xd5\xbe\x9f\x63\xcd\x1f\
+\xa1\x29\x0d\x39\x95\x6e\xc6\xd9\x9c\xa8\x34\x5e\xb6\xed\x24\xc9\
+\x92\xc1\xe0\xd8\x43\xca\xea\xb7\xb3\xdd\xfe\x21\xbb\x2e\x6f\x14\
+\xa3\x8c\x94\x02\xe9\x72\x65\xd5\x02\x0b\x8a\xec\xdc\xb8\x02\x9c\
+\xbd\x0e\xee\x3e\x42\x5b\x00\x56\xcd\xdb\xc1\xb6\xec\x63\x24\x59\
+\x32\x00\xb8\xe3\x6e\xe0\xe1\xd0\x1d\x3a\x7a\x2f\xf1\x94\xf5\x05\
+\x00\x7b\x51\x55\x4e\xbc\x0c\x37\x99\x73\x00\x31\xdf\x92\x4e\x73\
+\xdf\x95\x10\xa0\xe4\xe8\x45\x21\xb1\xe5\x49\x6f\xb0\x74\xce\x6b\
+\x08\xff\x08\x3b\x07\x5b\x39\xd5\x7a\xc0\x4f\x74\x8d\x45\x33\xb3\
+\x00\xf0\x86\x69\xb9\x52\x40\x1e\xc0\x3c\x8b\x9d\x5b\xfd\xce\x09\
+\x40\x85\x49\x9b\x78\x2f\xef\x38\x6b\x6d\x6f\x05\x63\xb9\xd6\x97\
+\x28\x4c\xd9\x12\xf4\xbb\x06\xdb\x29\x6b\x78\x9b\x21\xaf\xdb\x47\
+\x36\xd0\xc2\xbc\x68\x3b\x00\x4a\x53\x79\x46\xdf\xf5\x87\x38\x73\
+\x02\xbd\x23\xff\xf8\x24\x28\x24\x9b\x52\xdf\x61\xd9\x9c\x22\x00\
+\x5e\x4c\xdc\x84\xa3\xeb\x0c\x46\x69\x60\x43\xda\x3e\x84\xff\x62\
+\x0f\x8e\xf5\xf1\x6d\xe3\x6e\x3c\x63\xae\xe0\xcc\x46\xb4\x41\x66\
+\x18\x23\x91\x42\xa2\x2b\x3d\xcf\x08\x42\x02\x08\x11\x54\x2c\x11\
+\xc6\x68\x22\x4d\xd1\x13\x4e\xf3\x42\xe2\x06\x52\x2c\x19\x18\x84\
+\x09\x00\x5d\x69\x7c\xdf\xbc\x8f\xbe\x91\x4e\x0c\x62\xa2\x22\x02\
+\x58\x42\x28\x19\x58\x5c\xf4\x0c\x77\x12\x1b\x6e\xf5\x57\xe6\xa2\
+\xb4\x71\x2f\xe5\x2d\x9f\xa1\xfb\xe5\xba\x24\x61\x25\xd6\x88\xa4\
+\x20\x48\x55\xc7\x61\xda\x5d\xa1\x33\x33\x1b\xa3\xf0\x78\x07\xd0\
+\x95\x0e\x88\x1a\x09\x38\x00\xda\xdd\x4d\xcc\xb5\x4c\x1c\x68\xf5\
+\xdd\x0a\xbe\xbb\x51\x12\x02\x72\x7f\xa0\x85\x8b\xf7\x7e\x08\x89\
+\x03\xcc\x8a\x5c\x48\x9b\xbb\xc9\x77\x02\x70\x48\xa3\x34\x39\x00\
+\xd5\xea\x6e\x62\x81\x25\x33\x24\xe1\x52\xe7\x29\x3a\x3d\x1d\x41\
+\x5f\xa1\xa8\x6c\x3d\xe8\xaf\x30\xd4\x66\x47\x67\xd0\xec\xaa\x07\
+\x40\x57\xa2\x56\xfa\xf7\x79\x83\xa3\xe7\x02\xf6\xd8\x7c\xe2\x23\
+\x66\x4f\x48\x50\x4a\xe7\xcc\x5f\xc7\x83\x7e\x4d\xe7\xef\xdc\x72\
+\xd7\x4f\x0a\x6e\x09\x8b\x67\xee\x63\x4f\x73\xb9\xbb\x1a\x50\x37\
+\x2b\x57\x5c\xef\x96\x00\xba\x2e\x8a\xbd\xfa\x98\x7e\xd4\xf9\x29\
+\x9b\xd3\xde\x0f\xaa\x24\x60\x8d\x0f\xff\xe0\xfc\xbd\x0a\xaa\xef\
+\x94\xf3\x6b\xfb\x57\x93\x82\x03\x14\xda\x76\x73\xc4\x59\xc2\xb0\
+\x36\x04\x8a\x62\x18\xb7\x4d\xd7\x9e\xcd\x3a\x04\xec\x5c\x6f\xdb\
+\x46\xa4\x21\x82\x93\xed\x5f\x22\x94\xf2\x6d\xd0\x69\xb6\xa9\xc1\
+\xff\x2d\x48\x7c\x9d\x11\x22\xf8\xba\xe5\x10\x42\x70\xf4\xe4\xf2\
+\xba\x2d\x30\x6e\x9b\xc6\x98\xfa\xf7\x02\xcd\x27\xda\x8f\x30\xac\
+\x8f\xf2\x6e\xf6\x31\x12\x66\x24\x4f\x59\x6d\xb0\x2d\xe1\x56\x36\
+\xd8\x0f\x32\x4c\x04\x65\xad\x5f\x00\xe2\xb6\xd7\x33\xba\x2b\xf0\
+\x7f\xe2\x83\x73\x2e\x33\x4b\xd7\x44\x25\x02\x5b\x6a\x4c\x26\x5b\
+\x17\xed\xa7\xee\xc1\x79\x5a\xfb\xae\x72\x77\xc0\xc9\x98\xe6\xc1\
+\x00\x44\x99\x2c\x24\x46\xa5\x31\x37\x26\x8b\x85\xb1\xcf\x70\xd4\
+\x79\x80\xa6\xbe\xab\x80\xb8\x2d\xa4\xf6\xea\xc9\x65\x0d\x35\x93\
+\x12\x00\xac\xfe\x2d\x6f\x86\x6f\x9f\xab\x1d\x61\x32\x5c\x3c\x69\
+\x7d\x9e\x85\x31\x59\xd8\x2c\x76\x22\x8c\x91\x48\x04\x43\x5e\x0f\
+\x6d\xfd\x8d\xdc\x70\x35\x70\xb9\xbb\x9a\x61\x6d\x08\x21\x38\xea\
+\xf5\x8c\xee\xfa\x65\xb5\xb3\x7f\x3c\xde\x94\x8f\xfe\x9a\xd3\x8b\
+\x0b\xa4\x54\xa5\x80\x3d\x10\x93\xfe\x1b\x3a\x51\xa2\xea\x26\x8a\
+\xe2\xca\x15\xf5\x67\x27\xc3\x99\x92\x20\x60\x45\x55\x39\xf1\xde\
+\x30\x2d\x57\x69\x2a\x4f\x40\x9e\x10\x4a\x82\xa8\x11\xe0\xd0\x95\
+\xa8\xad\x5c\x71\xbd\x7b\xba\xfc\x7f\x01\xe3\xf6\xed\xcb\x2c\x97\
+\xd8\xbf\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
"
qt_resource_name = "\
@@ -1521,12 +1609,17 @@ qt_resource_name = "\
\x00\x6c\
\x00\x65\x00\x61\x00\x70\x00\x2d\x00\x63\x00\x6f\x00\x6c\x00\x6f\x00\x72\x00\x2d\x00\x73\x00\x6d\x00\x61\x00\x6c\x00\x6c\x00\x2e\
\x00\x70\x00\x6e\x00\x67\
+\x00\x0b\
+\x01\x64\x80\x07\
+\x00\x63\
+\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
"
qt_resource_struct = "\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x02\
\x00\x00\x00\xa8\x00\x00\x00\x00\x00\x01\x00\x00\x2d\x4e\
+\x00\x00\x00\xd6\x00\x00\x00\x00\x00\x01\x00\x00\x5b\xd7\
\x00\x00\x00\x34\x00\x00\x00\x00\x00\x01\x00\x00\x0d\xf7\
\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x00\x5e\x00\x00\x00\x00\x00\x01\x00\x00\x19\xd2\
diff --git a/src/leap/gui/progress.py b/src/leap/gui/progress.py
new file mode 100644
index 00000000..d04e0f1f
--- /dev/null
+++ b/src/leap/gui/progress.py
@@ -0,0 +1,261 @@
+"""
+classes used in progress pages
+from first run wizard
+"""
+try:
+ from collections import OrderedDict
+except ImportError:
+ # We must be in 2.6
+ from leap.util.dicts import OrderedDict
+import time
+
+from PyQt4 import QtCore
+from PyQt4 import QtGui
+
+from leap.baseapp.mainwindow import FunThread
+
+from leap.gui import mainwindow_rc
+
+
+class ImgWidget(QtGui.QWidget):
+
+ # XXX move to widgets
+
+ def __init__(self, parent=None, img=None):
+ super(ImgWidget, self).__init__(parent)
+ self.pic = QtGui.QPixmap(img)
+
+ def paintEvent(self, event):
+ painter = QtGui.QPainter(self)
+ painter.drawPixmap(0, 0, self.pic)
+
+
+class ProgressStep(object):
+ """
+ Data model for sequential steps
+ to be used in a progress page in
+ connection wizard
+ """
+ NAME = 0
+ DONE = 1
+
+ def __init__(self, stepname, done, index=None):
+ """
+ @param step: the name of the step
+ @type step: str
+ @param done: whether is completed or not
+ @type done: bool
+ """
+ self.index = int(index) if index else 0
+ self.name = unicode(stepname)
+ self.done = bool(done)
+
+ @classmethod
+ def columns(self):
+ return ('name', 'done')
+
+
+class ProgressStepContainer(object):
+ """
+ a container for ProgressSteps objects
+ access data in the internal dict
+ """
+
+ def __init__(self):
+ self.dirty = False
+ self.steps = {}
+
+ def step(self, identity):
+ return self.step.get(identity)
+
+ def addStep(self, step):
+ self.steps[step.index] = step
+
+ def removeStep(self, step):
+ del self.steps[step.index]
+ del step
+ self.dirty = True
+
+ def removeAllSteps(self):
+ for item in iter(self):
+ self.removeStep(item)
+
+ @property
+ def columns(self):
+ return ProgressStep.columns()
+
+ def __len__(self):
+ return len(self.steps)
+
+ def __iter__(self):
+ for step in self.steps.values():
+ yield step
+
+
+class StepsTableWidget(QtGui.QTableWidget):
+ """
+ initializes a TableWidget
+ suitable for our display purposes, like removing
+ header info and grid display
+ """
+
+ def __init__(self, parent=None):
+ super(StepsTableWidget, self).__init__(parent)
+
+ # remove headers and all edit/select behavior
+ self.horizontalHeader().hide()
+ self.verticalHeader().hide()
+ self.setEditTriggers(
+ QtGui.QAbstractItemView.NoEditTriggers)
+ self.setSelectionMode(
+ QtGui.QAbstractItemView.NoSelection)
+ width = self.width()
+ # WTF? Here init width is 100...
+ # but on populating is 456... :(
+
+ # XXX do we need this initial?
+ print 'init table. width=%s' % width
+ self.horizontalHeader().resizeSection(0, width * 0.7)
+
+ # this disables the table grid.
+ # we should add alignment to the ImgWidget (it's top-left now)
+ self.setShowGrid(False)
+
+ # XXX change image for done to rc
+
+ # Note about the "done" status painting:
+ #
+ # XXX currently we are setting the CellWidget
+ # for the whole table on a per-row basis
+ # (on add_status_line method on ValidationPage).
+ # However, a more generic solution might be
+ # to implement a custom Delegate that overwrites
+ # the paint method (so it paints a checked tickmark if
+ # done is True and some other thing if checking or false).
+ # What we have now is quick and works because
+ # I'm supposing that on first fail we will
+ # go back to previous wizard page to signal the failure.
+ # A more generic solution could be used for
+ # some failing tests if they are not critical.
+
+
+class ValidationPage(QtGui.QWizardPage):
+ """
+ class to be used as an intermediate
+ between two pages in a wizard.
+ shows feedback to the user and goes back if errors,
+ goes forward if ok.
+ initializePage triggers a one shot timer
+ that calls do_checks.
+ Derived classes should implement
+ _do_checks and
+ _do_validation
+ """
+
+ # signals
+
+ stepChanged = QtCore.pyqtSignal([str])
+
+ def __init__(self, parent=None):
+ super(ValidationPage, self).__init__(parent)
+
+ self.steps = ProgressStepContainer()
+ self.progress = QtGui.QProgressBar()
+
+ # steps table widget
+ self.stepsTableWidget = StepsTableWidget(self)
+
+ layout = QtGui.QVBoxLayout()
+ layout.addWidget(self.progress)
+ layout.addWidget(self.stepsTableWidget)
+
+ self.setLayout(layout)
+ self.layout = layout
+
+ self.timer = QtCore.QTimer()
+
+ # connect the new step status
+ # signal to status handler
+ self.stepChanged.connect(
+ self.onStepStatusChanged)
+
+ self.errors = OrderedDict()
+
+ def populateStepsTable(self):
+ # from examples,
+ # but I guess it's not needed to re-populate
+ # the whole table.
+ table = self.stepsTableWidget
+ table.setRowCount(len(self.steps))
+ columns = self.steps.columns
+ table.setColumnCount(len(columns))
+
+ for row, step in enumerate(self.steps):
+ item = QtGui.QTableWidgetItem(step.name)
+ item.setData(QtCore.Qt.UserRole,
+ long(id(step)))
+ table.setItem(row, columns.index('name'), item)
+ table.setItem(row, columns.index('done'),
+ QtGui.QTableWidgetItem(step.done))
+ self.resizeTable()
+ self.update()
+
+ def clearTable(self):
+ # ??? -- not sure what's the difference
+ #self.stepsTableWidget.clear()
+ self.stepsTableWidget.clearContents()
+
+ def resizeTable(self):
+ # resize first column to ~80%
+ table = self.stepsTableWidget
+ FIRST_COLUMN_PERCENT = 0.75
+ width = table.width()
+ print 'populate table. width=%s' % width
+ table.horizontalHeader().resizeSection(0, width * FIRST_COLUMN_PERCENT)
+
+ def onStepStatusChanged(self, status):
+ if status != "end_sentinel":
+ self.add_status_line(status)
+
+ def add_status_line(self, message):
+ print 'adding status line...'
+ index = len(self.steps)
+ step = ProgressStep(message, False, index=index)
+ self.steps.addStep(step)
+ self.populateStepsTable()
+ table = self.stepsTableWidget
+
+ # setting cell widget.
+ # see note on StepsTableWidget about plans to
+ # change this for a better solution.
+
+ table.setCellWidget(
+ index - 1,
+ ProgressStep.DONE,
+ # XXX pass image in rc
+ ImgWidget(img=":/images/checked.png"))
+
+ def go_back(self):
+ self.wizard().back()
+
+ def go_next(self):
+ self.wizard().next()
+
+ def initializePage(self):
+ self.steps.removeAllSteps()
+ self.clearTable()
+ self.resizeTable()
+ self.timer.singleShot(0, self.do_checks)
+
+ def do_checks(self):
+ """
+ launches a thread to do the checks
+ """
+ signal = self.stepChanged
+ self.checks = FunThread(
+ self._do_checks(signal=signal))
+ self.checks.finished.connect(self._do_validation)
+ self.checks.begin()
+ print 'check thread started!'
+ print 'waiting for it to terminate...'
+ self.checks.wait()
diff --git a/src/leap/gui/tests/integration/fake_user_signup.py b/src/leap/gui/tests/integration/fake_user_signup.py
index 12f18966..78873749 100644
--- a/src/leap/gui/tests/integration/fake_user_signup.py
+++ b/src/leap/gui/tests/integration/fake_user_signup.py
@@ -12,6 +12,7 @@ curl -d login=python_test_user -d password_salt=54321\
from BaseHTTPServer import HTTPServer
from BaseHTTPServer import BaseHTTPRequestHandler
import cgi
+import json
import urlparse
HOST = "localhost"
@@ -19,12 +20,15 @@ PORT = 8000
LOGIN_ERROR = """{"errors":{"login":["has already been taken"]}}"""
+from leap.base.tests.test_providers import EXPECTED_DEFAULT_CONFIG
+
class request_handler(BaseHTTPRequestHandler):
responses = {
'/': ['ok\n'],
'/users.json': ['ok\n'],
- '/timeout': ['ok\n']
+ '/timeout': ['ok\n'],
+ '/provider.json': ['%s\n' % json.dumps(EXPECTED_DEFAULT_CONFIG)]
}
def do_GET(self):