~mvo/ubuntu-sso-client/strawman-lp711413

« back to all changes in this revision

Viewing changes to ubuntu_sso/qt/gui.py

  • Committer: Natalia B. Bidart
  • Date: 2011-12-20 16:29:34 UTC
  • Revision ID: natalia.bidart@canonical.com-20111220162934-2s5xou06v3usxyr6
Tags: ubuntu-sso-client-2_99_0
- Release v2.99.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# -*- coding: utf-8 -*-
 
2
# Author: Manuel de la Pena <manuel@canonical.com>
2
3
#
3
 
# Copyright 2011-2012 Canonical Ltd.
 
4
# Copyright 2011 Canonical Ltd.
4
5
#
5
6
# This program is free software: you can redistribute it and/or modify it
6
7
# under the terms of the GNU General Public License version 3, as published
13
14
#
14
15
# You should have received a copy of the GNU General Public License along
15
16
# with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 
#
17
 
# In addition, as a special exception, the copyright holders give
18
 
# permission to link the code of portions of this program with the
19
 
# OpenSSL library under certain conditions as described in each
20
 
# individual source file, and distribute linked combinations
21
 
# including the two.
22
 
# You must obey the GNU General Public License in all respects
23
 
# for all of the code used other than OpenSSL.  If you modify
24
 
# file(s) with this exception, you may extend this exception to your
25
 
# version of the file(s), but you are not obligated to do so.  If you
26
 
# do not wish to do so, delete this exception statement from your
27
 
# version.  If you delete this exception statement from all source
28
 
# files in the program, then also delete it here.
29
17
"""Qt implementation of the UI."""
30
18
 
31
 
from functools import wraps
 
19
import gettext
32
20
 
33
 
# pylint: disable=F0401,E0611
34
 
from PyQt4.QtCore import Qt, pyqtSignal
 
21
from PyQt4.QtCore import pyqtSignal, Qt, SIGNAL
35
22
from PyQt4.QtGui import (
36
23
    QApplication,
 
24
    QWidget,
37
25
    QCursor,
38
 
    QFrame,
39
26
    QHBoxLayout,
40
 
    QLabel,
 
27
    QVBoxLayout,
 
28
    QPixmap,
41
29
    QStyle,
42
 
    QVBoxLayout,
 
30
    QWizard,
43
31
    QWizardPage,
44
 
)
45
 
from twisted.internet import defer
46
 
 
47
 
from ubuntu_sso import main
48
 
from ubuntu_sso.logger import setup_gui_logging, log_call
49
 
from ubuntu_sso.qt import (
50
 
    ERROR_STYLE,
51
 
    maybe_elide_text,
52
 
    PREFERED_UI_SIZE,
53
 
    TITLE_STYLE,
54
 
)
55
 
from ubuntu_sso.utils.ui import GENERIC_BACKEND_ERROR
56
 
 
57
 
 
58
 
logger = setup_gui_logging('ubuntu_sso.sso_wizard_page')
59
 
 
60
 
 
61
 
class WizardHeader(QFrame):
62
 
    """WizardHeader Class for Title and Subtitle in all wizard pages."""
63
 
 
64
 
    def __init__(self, max_width, parent=None):
 
32
    QLabel)
 
33
 
 
34
from ubuntu_sso.logger import setup_logging
 
35
from ubuntu_sso.utils.ui import (
 
36
    PASSWORD1_ENTRY,
 
37
    PASSWORD2_ENTRY,
 
38
    RESET_CODE_ENTRY)
 
39
from ubuntu_sso.qt import common
 
40
# pylint: disable=F0401,E0611
 
41
from ubuntu_sso.qt.choose_sign_in_ui import Ui_ChooseSignInPage
 
42
from ubuntu_sso.qt.current_user_sign_in_ui import Ui_CurrentUserSignInPage
 
43
from ubuntu_sso.qt.email_verification_ui import Ui_EmailVerificationPage
 
44
from ubuntu_sso.qt.error_message_ui import Ui_ErrorPage
 
45
from ubuntu_sso.qt.setup_account_ui import Ui_SetUpAccountPage
 
46
from ubuntu_sso.qt.success_message_ui import Ui_SuccessPage
 
47
from ubuntu_sso.qt.forgotten_password_ui import Ui_ForgottenPasswordPage
 
48
from ubuntu_sso.qt.reset_password_ui import Ui_ResetPasswordPage
 
49
# pylint: enable=F0401,E0611
 
50
from ubuntu_sso.qt.controllers import (
 
51
    ChooseSignInController,
 
52
    CurrentUserController,
 
53
    EmailVerificationController,
 
54
    ErrorController,
 
55
    ForgottenPasswordController,
 
56
    ResetPasswordController,
 
57
    SetUpAccountController,
 
58
    SuccessController,
 
59
    UbuntuSSOWizardController)
 
60
 
 
61
 
 
62
_ = gettext.gettext
 
63
logger = setup_logging('ubuntu_sso.gui')
 
64
RESET_TITLE = _("Reset password")
 
65
RESET_SUBTITLE = _("A password reset code has been sent to your e-mail."
 
66
                   "Please enter the code below along with your new password.")
 
67
 
 
68
 
 
69
class Header(QWidget):
 
70
    """Header Class for Title and Subtitle in all wizard pages."""
 
71
 
 
72
    def __init__(self):
65
73
        """Create a new instance."""
66
 
        super(WizardHeader, self).__init__(parent=parent)
67
 
        self.max_width = max_width
68
 
        self.max_title_width = self.max_width * 0.95
69
 
        self.max_subtitle_width = self.max_width * 1.8
70
 
 
 
74
        QWidget.__init__(self)
71
75
        vbox = QVBoxLayout(self)
72
 
        vbox.setContentsMargins(0, 0, 0, 0)
 
76
        vbox.setContentsMargins(0, 0, 0, 10)
73
77
        self.title_label = QLabel()
74
78
        self.title_label.setWordWrap(True)
75
79
        self.title_label.setObjectName('title_label')
76
80
        self.subtitle_label = QLabel()
77
81
        self.subtitle_label.setWordWrap(True)
78
 
        self.subtitle_label.setFixedHeight(32)
79
82
        vbox.addWidget(self.title_label)
80
83
        vbox.addWidget(self.subtitle_label)
81
84
        self.title_label.setVisible(False)
84
87
    def set_title(self, title):
85
88
        """Set the Title of the page or hide it otherwise"""
86
89
        if title:
87
 
            maybe_elide_text(self.title_label, title, self.max_title_width,
88
 
                             markup=TITLE_STYLE)
 
90
            self.title_label.setText(title)
89
91
            self.title_label.setVisible(True)
90
92
        else:
91
93
            self.title_label.setVisible(False)
93
95
    def set_subtitle(self, subtitle):
94
96
        """Set the Subtitle of the page or hide it otherwise"""
95
97
        if subtitle:
96
 
            maybe_elide_text(self.subtitle_label, subtitle,
97
 
                             self.max_subtitle_width)
 
98
            self.subtitle_label.setText(subtitle)
98
99
            self.subtitle_label.setVisible(True)
99
100
        else:
100
101
            self.subtitle_label.setVisible(False)
101
102
 
102
103
 
103
 
class BaseWizardPage(QWizardPage):
104
 
    """Base class for all wizard pages."""
105
 
 
106
 
    ui_class = None
107
 
    max_width = 0
108
 
    processingStarted = pyqtSignal()
109
 
    processingFinished = pyqtSignal()
110
 
 
111
 
    def __init__(self, parent=None):
112
 
        super(BaseWizardPage, self).__init__(parent=parent)
113
 
 
114
 
        self.ui = None
115
 
        if self.ui_class is not None:
116
 
            # self.ui_class is not callable, pylint: disable=E1102
117
 
            self.ui = self.ui_class()
118
 
            self.ui.setupUi(self)
119
 
 
120
 
        if self.layout() is None:
121
 
            self.setLayout(QVBoxLayout(self))
122
 
 
123
 
        # Set the error area
124
 
        self.form_errors_label = QLabel()
125
 
        self.form_errors_label.setObjectName('form_errors')
126
 
        self.form_errors_label.setAlignment(Qt.AlignBottom)
127
 
        self.layout().insertWidget(0, self.form_errors_label)
128
 
 
129
 
        # Set the header
130
 
        self.header = WizardHeader(max_width=self.max_width)
131
 
        self.header.set_title(title='')
132
 
        self.header.set_subtitle(subtitle='')
 
104
class SSOWizardPage(QWizardPage):
 
105
    """Root class for all wizard pages."""
 
106
 
 
107
    def __init__(self, ui, controller, parent=None):
 
108
        """Create a new instance."""
 
109
        QWizardPage.__init__(self, parent)
 
110
        self.ui = ui
 
111
        self.ui.setupUi(self)
 
112
        self.header = Header()
133
113
        self.layout().insertWidget(0, self.header)
134
 
 
135
 
        self.layout().setAlignment(Qt.AlignLeft)
136
 
 
137
 
        self._is_processing = False
138
 
 
139
 
    def _get_is_processing(self):
140
 
        """Is this widget processing any request?"""
141
 
        return self._is_processing
142
 
 
143
 
    def _set_is_processing(self, new_value):
144
 
        """Set this widget to be processing a request."""
145
 
        self._is_processing = new_value
146
 
        self.setEnabled(not new_value)
147
 
        if not self._is_processing:
148
 
            self.processingFinished.emit()
149
 
        else:
150
 
            self.processingStarted.emit()
151
 
 
152
 
    is_processing = property(fget=_get_is_processing, fset=_set_is_processing)
153
 
 
154
 
    # pylint: disable=C0103
155
 
 
156
 
    def cleanupPage(self):
157
 
        """Hide the errors."""
158
 
        self.hide_error()
159
 
 
 
114
        self.controller = controller
 
115
        if self.controller:
 
116
            self.controller.setupUi(self)
 
117
        self.next = -1
 
118
 
 
119
    # pylint: disable=C0103
 
120
    def nextId(self):
 
121
        """Provide the next id."""
 
122
        return self.next
 
123
    # pylint: enable=C0103
 
124
 
 
125
    # pylint: disable=C0103
 
126
    def initializePage(self):
 
127
        """Called to prepare the page just before it is shown."""
 
128
        if self.controller:
 
129
            self.controller.pageInitialized()
 
130
    # pylint: enable=C0103
 
131
 
 
132
    # pylint: disable=C0103
160
133
    def setTitle(self, title=''):
161
134
        """Set the Wizard Page Title."""
162
135
        self.header.set_title(title)
 
136
    # pylint: enable=C0103
163
137
 
 
138
    # pylint: disable=C0103
164
139
    def setSubTitle(self, subtitle=''):
165
140
        """Set the Wizard Page Subtitle."""
166
141
        self.header.set_subtitle(subtitle)
167
 
 
168
 
    def title(self):
169
 
        """Return the header's title."""
170
 
        return self.header.title_label.text()
171
 
 
172
 
    def subTitle(self):
173
 
        """Return the header's subtitle."""
174
 
        return self.header.subtitle_label.text()
175
 
 
176
142
    # pylint: enable=C0103
177
143
 
178
 
    @log_call(logger.error)
179
 
    def show_error(self, message):
180
 
        """Show an error message inside the page."""
181
 
        self.is_processing = False
182
 
        maybe_elide_text(self.form_errors_label, message,
183
 
                         self.max_width * 0.95, markup=ERROR_STYLE)
184
 
 
185
 
    def hide_error(self):
186
 
        """Hide the label errors in the current page."""
187
 
        # We actually want the label with one empty char, because if it is an
188
 
        # empty string, the height of the label is 0
189
 
        self.form_errors_label.setText(' ')
190
 
 
191
 
 
192
 
class SSOWizardPage(BaseWizardPage):
193
 
    """Root class for all SSO specific wizard pages."""
194
 
 
195
 
    _signals = {}  # override in children
196
 
    max_width = PREFERED_UI_SIZE['width']
197
 
 
198
 
    def __init__(self, app_name, **kwargs):
199
 
        """Create a new instance."""
200
 
        parent = kwargs.pop('parent', None)
201
 
        super(SSOWizardPage, self).__init__(parent=parent)
202
 
 
203
 
        # store common useful data provided by the app
204
 
        self.app_name = app_name
205
 
        self.ping_url = kwargs.get('ping_url', '')
206
 
        self.tc_url = kwargs.get('tc_url', '')
207
 
        self.policy_url = kwargs.get('policy_url', '')
208
 
        self.help_text = kwargs.get('help_text', '')
209
 
 
210
 
        self._signals_receivers = {}
211
 
        self.backend = None
212
 
 
213
 
        self.setup_page()
214
 
 
215
 
    def hide_overlay(self):
216
 
        """Emit the signal to notify the upper container that ends loading."""
217
 
        self.is_processing = False
218
 
 
219
 
    def show_overlay(self):
220
 
        """Emit the signal to notify the upper container that is loading."""
221
 
        self.is_processing = True
222
 
 
223
 
    @defer.inlineCallbacks
224
 
    def setup_page(self):
225
 
        """Setup the widget components."""
226
 
        logger.info('Starting setup_page for: %r', self)
227
 
        # pylint: disable=W0702,W0703
228
 
        try:
229
 
            # Get Backend
230
 
            client = yield main.get_sso_client()
231
 
            self.backend = client.sso_login
232
 
            self._set_translated_strings()
233
 
            self._connect_ui()
234
 
            # Call _setup_signals at the end, so we ensure that the UI
235
 
            # is at least styled as expected if the operations with the
236
 
            # backend fails.
237
 
            self._setup_signals()
238
 
        except:
239
 
            message = 'There was a problem trying to setup the page %r' % self
240
 
            self.show_error(message)
241
 
            logger.exception(message)
242
 
            self.setEnabled(False)
243
 
        # pylint: enable=W0702,W0703
244
 
        logger.info('%r - setup_page ends, backend is %r.', self, self.backend)
245
 
 
246
 
    def _filter_by_app_name(self, f):
247
 
        """Excecute the decorated function only for 'self.app_name'."""
248
 
 
249
 
        @wraps(f)
250
 
        def inner(app_name, *args, **kwargs):
251
 
            """Execute 'f' only if 'app_name' matches 'self.app_name'."""
252
 
            result = None
253
 
            if app_name == self.app_name:
254
 
                result = f(app_name, *args, **kwargs)
255
 
            else:
256
 
                logger.info('%s: ignoring call since received app_name '\
257
 
                            '"%s" (expected "%s")',
258
 
                            f.__name__, app_name, self.app_name)
259
 
            return result
260
 
 
261
 
        return inner
262
 
 
263
 
    def _setup_signals(self):
264
 
        """Bind signals to callbacks to be able to test the pages."""
265
 
        for signal, method in self._signals.iteritems():
266
 
            actual = self._signals_receivers.get(signal)
267
 
            if actual is not None:
268
 
                msg = 'Signal %r is already connected with %r.'
269
 
                logger.warning(msg, signal, actual)
270
 
 
271
 
            match = self.backend.connect_to_signal(signal, method)
272
 
            self._signals_receivers[signal] = match
273
 
 
274
 
    def _set_translated_strings(self):
275
 
        """Implement in each child."""
276
 
 
277
 
    def _connect_ui(self):
278
 
        """Implement in each child."""
279
 
 
280
 
    def _handle_error(self, remote_call, handler, error):
281
 
        """Handle any error when calling the remote backend."""
282
 
        logger.error('Remote call %r failed with: %r', remote_call, error)
283
 
        errordict = {'message': GENERIC_BACKEND_ERROR}
284
 
        handler(self.app_name, errordict)
285
 
 
286
144
 
287
145
class EnhancedLineEdit(object):
288
146
    """Represents and enhanced lineedit.
294
152
    def __init__(self, line_edit, valid_cb=lambda x: False,
295
153
                 warning_sign=False):
296
154
        """Create an instance."""
297
 
        super(EnhancedLineEdit, self).__init__()
298
155
        self._line_edit = line_edit
299
156
        layout = QHBoxLayout(self._line_edit)
300
157
        layout.setMargin(0)
327
184
class SSOWizardEnhancedEditPage(SSOWizardPage):
328
185
    """Page that contains enhanced line edits."""
329
186
 
330
 
    # Method '_connect_ui', '_set_translated_strings' is abstract in class
331
 
    # 'SSOWizardPage' but is not overridden
332
 
    # pylint: disable=W0223
333
 
 
334
 
    def __init__(self, *args, **kwargs):
 
187
    def __init__(self, ui, controller, parent=None):
335
188
        """Create a new instance."""
 
189
        SSOWizardPage.__init__(self, ui, controller, parent)
336
190
        self._enhanced_edits = {}
337
 
        super(SSOWizardEnhancedEditPage, self).__init__(*args, **kwargs)
338
191
 
339
192
    def set_line_edit_validation_rule(self, edit, cb):
340
193
        """Set a new enhanced edit so that we can show an icon."""
344
197
            # create a new enhanced edit
345
198
            enhanced_edit = EnhancedLineEdit(edit, cb)
346
199
            self._enhanced_edits[edit] = enhanced_edit
 
200
 
 
201
 
 
202
class ChooseSignInPage(SSOWizardPage):
 
203
    """Widget that allows the user to choose how to sign in."""
 
204
 
 
205
    def __init__(self, ui, controller, parent=None):
 
206
        """Create a new widget to be used."""
 
207
        SSOWizardPage.__init__(self, ui, controller, parent)
 
208
 
 
209
 
 
210
class CurrentUserSignInPage(SSOWizardPage):
 
211
    """Widget that allows to get the data of user to sign in."""
 
212
 
 
213
    def __init__(self, ui, controller, parent=None):
 
214
        """Create a new widget to be used."""
 
215
        SSOWizardPage.__init__(self, ui, controller, parent)
 
216
 
 
217
 
 
218
class EmailVerificationPage(SSOWizardPage):
 
219
    """Widget used to input the email verification code."""
 
220
 
 
221
    def __init__(self, ui, controller, parent=None):
 
222
        """Create a new widget to be used."""
 
223
        SSOWizardPage.__init__(self, ui, controller, parent)
 
224
 
 
225
    @property
 
226
    def verification_code(self):
 
227
        """Return the content of the verification code edit."""
 
228
        return str(self.ui.verification_code_edit.text())
 
229
 
 
230
    @property
 
231
    def next_button(self):
 
232
        """Return the button that move to the next stage."""
 
233
        return self.ui.next_button
 
234
 
 
235
 
 
236
class ErrorPage(SSOWizardPage):
 
237
    """Widget used to show the diff errors."""
 
238
 
 
239
    def __init__(self, ui, controller, parent=None):
 
240
        """Create a new widget to be used."""
 
241
        SSOWizardPage.__init__(self, ui, controller, parent)
 
242
 
 
243
 
 
244
class ForgottenPasswordPage(SSOWizardEnhancedEditPage):
 
245
    """Widget used to deal with users that forgot the password."""
 
246
 
 
247
    def __init__(self, ui, controller, parent=None):
 
248
        """Create a new instance."""
 
249
        SSOWizardEnhancedEditPage.__init__(self, ui, controller, parent)
 
250
 
 
251
    @property
 
252
    def email_widget(self):
 
253
        """Return the widget used to show the email information."""
 
254
        return self.ui.email_widget
 
255
 
 
256
    @property
 
257
    def forgotted_password_intro_label(self):
 
258
        """Return the intro label that lets the user know the issue."""
 
259
        return self.ui.forgotted_password_intro_label
 
260
 
 
261
    @property
 
262
    def error_label(self):
 
263
        """Return the label used to show error."""
 
264
        return self.ui.error_label
 
265
 
 
266
    @property
 
267
    def email_address_label(self):
 
268
        """Return the lable used to state the use of the line edit."""
 
269
        return self.ui.email_address_label
 
270
 
 
271
    @property
 
272
    def email_address(self):
 
273
        """Return the email address provided by the user."""
 
274
        return str(self.ui.email_line_edit.text())
 
275
 
 
276
    @property
 
277
    def email_address_line_edit(self):
 
278
        """Return the line edit with the content."""
 
279
        return self.ui.email_line_edit
 
280
 
 
281
    @property
 
282
    def send_button(self):
 
283
        """Return the button used to request the new password."""
 
284
        return self.ui.send_button
 
285
 
 
286
    @property
 
287
    def try_again_widget(self):
 
288
        """Return the widget used to display the try again button."""
 
289
        return self.ui.try_again_widget
 
290
 
 
291
    @property
 
292
    def try_again_button(self):
 
293
        """Return the button used to try again the reset password."""
 
294
        return self.ui.try_again_button
 
295
 
 
296
 
 
297
class ResetPasswordPage(SSOWizardEnhancedEditPage):
 
298
    """Widget used to allow the user change his password."""
 
299
 
 
300
    def __init__(self, ui, controller, parent=None):
 
301
        """Create a new instance."""
 
302
        SSOWizardEnhancedEditPage.__init__(self, ui, controller, parent)
 
303
        self.ui.password_line_edit.textEdited.connect(
 
304
            lambda: common.password_assistance(self.ui.password_line_edit,
 
305
                                                 self.ui.password_assistance,
 
306
                                                 common.NORMAL))
 
307
        self.ui.confirm_password_line_edit.textEdited.connect(
 
308
            lambda: common.password_check_match(self.ui.password_line_edit,
 
309
                                      self.ui.confirm_password_line_edit,
 
310
                                      self.ui.password_assistance))
 
311
 
 
312
    def focus_changed(self, old, now):
 
313
        """Check who has the focus to activate password popups if necessary."""
 
314
        if now == self.ui.password_line_edit:
 
315
            self.ui.password_assistance.setVisible(True)
 
316
            common.password_default_assistance(self.ui.password_assistance)
 
317
        elif now == self.ui.confirm_password_line_edit:
 
318
            common.password_check_match(self.ui.password_line_edit,
 
319
                                      self.ui.confirm_password_line_edit,
 
320
                                      self.ui.password_assistance)
 
321
 
 
322
    # Invalid name "initializePage"
 
323
    # pylint: disable=C0103
 
324
 
 
325
    def initializePage(self):
 
326
        super(ResetPasswordPage, self).initializePage()
 
327
        common.password_default_assistance(self.ui.password_assistance)
 
328
        self.ui.password_assistance.setVisible(False)
 
329
        self.setTitle(RESET_TITLE)
 
330
        self.setSubTitle(RESET_SUBTITLE)
 
331
        self.ui.password_label.setText(PASSWORD1_ENTRY)
 
332
        self.ui.confirm_password_label.setText(PASSWORD2_ENTRY)
 
333
        self.ui.reset_code.setText(RESET_CODE_ENTRY)
 
334
 
 
335
    def showEvent(self, event):
 
336
        """Connect focusChanged signal from the application."""
 
337
        super(ResetPasswordPage, self).showEvent(event)
 
338
        self.connect(QApplication.instance(),
 
339
            SIGNAL("focusChanged(QWidget*, QWidget*)"),
 
340
            self.focus_changed)
 
341
 
 
342
    def hideEvent(self, event):
 
343
        """Disconnect the focusChanged signal when the page change."""
 
344
        super(ResetPasswordPage, self).hideEvent(event)
 
345
        try:
 
346
            self.disconnect(QApplication.instance(),
 
347
                SIGNAL("focusChanged(QWidget*, QWidget*)"),
 
348
                self.focus_changed)
 
349
        except TypeError:
 
350
            pass
 
351
 
 
352
    # pylint: enable=C0103
 
353
 
 
354
 
 
355
class SetupAccountPage(SSOWizardEnhancedEditPage):
 
356
    """Widget used to create a new account."""
 
357
 
 
358
    def __init__(self, ui, controller, parent=None):
 
359
        """Create a new widget to be used."""
 
360
        SSOWizardEnhancedEditPage.__init__(self, ui, controller, parent)
 
361
        self._enhanced_edits = {}
 
362
        # palettes that will be used to set the colors of the password strengh
 
363
        self.captcha_id = None
 
364
        self.captcha_file = None
 
365
        self.ui.captcha_view.setPixmap(QPixmap())
 
366
 
 
367
    def get_captcha_image(self):
 
368
        """Return the path to the captcha image."""
 
369
        return self.ui.captcha_view.pixmap()
 
370
 
 
371
    def set_captcha_image(self, pixmap_image):
 
372
        """Set the new image of the captcha."""
 
373
        # lets set the QPixmap for the label
 
374
        self.ui.captcha_view.setPixmap(pixmap_image)
 
375
 
 
376
    captcha_image = property(get_captcha_image, set_captcha_image)
 
377
 
 
378
 
 
379
class SuccessPage(SSOWizardPage):
 
380
    """Page used to display success message."""
 
381
 
 
382
    def __init__(self, ui, controller, parent=None):
 
383
        """Create a new instance."""
 
384
        SSOWizardPage.__init__(self, ui, controller, parent)
 
385
 
 
386
 
 
387
class UbuntuSSOWizard(QWizard):
 
388
    """Wizard used to create or use sso."""
 
389
 
 
390
    # definition of the signals raised by the widget
 
391
    recoverableError = pyqtSignal('QString', 'QString')
 
392
    loginSuccess = pyqtSignal('QString', 'QString')
 
393
    registrationSuccess = pyqtSignal('QString', 'QString')
 
394
 
 
395
    def __init__(self, controller, parent=None, app_name='', tos_url='',
 
396
                 help_text=''):
 
397
        """Create a new wizard."""
 
398
        QWizard.__init__(self, parent)
 
399
        # store common useful data provided by the app
 
400
        self.app_name = app_name
 
401
        self.tos_url = tos_url
 
402
        self.help_text = help_text
 
403
 
 
404
        # set the diff pages of the QWizard
 
405
        self.sign_in_controller = ChooseSignInController(title='Sign In')
 
406
        self.sign_in_page = ChooseSignInPage(Ui_ChooseSignInPage(),
 
407
                                             self.sign_in_controller,
 
408
                                             parent=self)
 
409
        self.setup_controller = SetUpAccountController()
 
410
        self.setup_account = SetupAccountPage(Ui_SetUpAccountPage(),
 
411
                                              self.setup_controller,
 
412
                                              parent=self)
 
413
        self.email_verification = EmailVerificationPage(
 
414
                                                Ui_EmailVerificationPage(),
 
415
                                                EmailVerificationController())
 
416
        self.current_user_controller = CurrentUserController(title='Sign in')
 
417
        self.current_user = CurrentUserSignInPage(Ui_CurrentUserSignInPage(),
 
418
                                                  self.current_user_controller,
 
419
                                                  parent=self)
 
420
        self.success_controller = SuccessController()
 
421
        self.success = SuccessPage(Ui_SuccessPage(), self.success_controller,
 
422
                                   parent=self)
 
423
        self.error_controller = ErrorController()
 
424
        self.error = ErrorPage(Ui_ErrorPage(), self.error_controller)
 
425
        self.forgotte_pwd_controller = ForgottenPasswordController()
 
426
        self.forgotten = ForgottenPasswordPage(Ui_ForgottenPasswordPage(),
 
427
                                               self.forgotte_pwd_controller,
 
428
                                               parent=self)
 
429
        self.reset_password_controller = ResetPasswordController()
 
430
        self.reset_password = ResetPasswordPage(Ui_ResetPasswordPage(),
 
431
                                                self.reset_password_controller,
 
432
                                                parent=self)
 
433
        # store the ids of the pages so that it is easier to access them later
 
434
        self._pages = {}
 
435
        for page in [self.sign_in_page, self.setup_account,
 
436
                     self.email_verification, self.current_user, self.success,
 
437
                     self.error, self.forgotten, self.reset_password]:
 
438
            self._pages[page] = self.addPage(page)
 
439
 
 
440
        # set the buttons layout to only have cancel and back since the next
 
441
        # buttons are the ones used in the diff pages.
 
442
        buttons_layout = []
 
443
        buttons_layout.append(QWizard.Stretch)
 
444
        buttons_layout.append(QWizard.BackButton)
 
445
        buttons_layout.append(QWizard.CancelButton)
 
446
        self.setButtonLayout(buttons_layout)
 
447
        self.setWindowTitle(app_name)
 
448
        self.controller = controller
 
449
        self.controller.setupUi(self)
 
450
 
 
451
    @property
 
452
    def sign_in_page_id(self):
 
453
        """Return the id of the page used for choosing sign in type."""
 
454
        return self._pages[self.sign_in_page]
 
455
 
 
456
    @property
 
457
    def setup_account_page_id(self):
 
458
        """Return the id of the page used for sign in."""
 
459
        return self._pages[self.setup_account]
 
460
 
 
461
    @property
 
462
    def email_verification_page_id(self):
 
463
        """Return the id of the verification page."""
 
464
        return self._pages[self.email_verification]
 
465
 
 
466
    @property
 
467
    def current_user_page_id(self):
 
468
        """Return the id used to signin by a current user."""
 
469
        return self._pages[self.current_user]
 
470
 
 
471
    @property
 
472
    def success_page_id(self):
 
473
        """Return the id of the success page."""
 
474
        return self._pages[self.success]
 
475
 
 
476
    @property
 
477
    def forgotten_password_page_id(self):
 
478
        """Return the id of the forgotten password page."""
 
479
        return self._pages[self.forgotten]
 
480
 
 
481
    @property
 
482
    def reset_password_page_id(self):
 
483
        """Return the id of the reset password page."""
 
484
        return self._pages[self.reset_password]
 
485
 
 
486
    @property
 
487
    def error_page_id(self):
 
488
        """Return the id of the error page."""
 
489
        return self._pages[self.error]
 
490
 
 
491
 
 
492
class UbuntuSSOClientGUI(object):
 
493
    """Ubuntu single sign-on GUI."""
 
494
 
 
495
    def __init__(self, app_name, tc_url='http://one.ubuntu.com', help_text='',
 
496
                 window_id=0, login_only=False):
 
497
        """Create a new instance."""
 
498
        # create the controller and the ui, then set the cb and call the show
 
499
        # method so that we can work
 
500
        self.controller = UbuntuSSOWizardController(app_name)
 
501
        self.view = UbuntuSSOWizard(self.controller, app_name=app_name,
 
502
                                    tos_url=tc_url, help_text=help_text)
 
503
        self.view.show()
 
504
 
 
505
    def get_login_success_callback(self):
 
506
        """Return the log in success cb."""
 
507
        return self.controller.login_success_callback
 
508
 
 
509
    def set_login_success_callback(self, cb):
 
510
        """Set log in success cb."""
 
511
        self.controller.login_success_callback = cb
 
512
 
 
513
    login_success_callback = property(get_login_success_callback,
 
514
                                      set_login_success_callback)
 
515
 
 
516
    def get_registration_success_callback(self):
 
517
        """Return the registration success cb."""
 
518
        return self.controller.registration_success_callback
 
519
 
 
520
    def set_registration_success_callback(self, cb):
 
521
        """Set registration success cb."""
 
522
        self.controller.registration_success_callback = cb
 
523
 
 
524
    registration_success_callback = property(get_registration_success_callback,
 
525
                                             set_registration_success_callback)
 
526
 
 
527
    def get_user_cancellation_callback(self):
 
528
        """Return the user cancellation callback."""
 
529
        return self.controller.user_cancellation_callback
 
530
 
 
531
    def set_user_cancellation_callback(self, cb):
 
532
        """Set the user cancellation callback."""
 
533
        self.controller.user_cancellation_callback = cb
 
534
 
 
535
    user_cancellation_callback = property(get_user_cancellation_callback,
 
536
                                          set_user_cancellation_callback)