205
205
"""Ubuntu single sign on GUI."""
207
207
CAPTCHA_SOLUTION_ENTRY = _('Type the characters above')
208
CAPTCHA_LOAD_ERROR = _('There was a problem getting the captcha, '
208
210
CONNECT_HELP_LABEL = _('To connect this computer to %(app_name)s ' \
209
211
'enter your details below.')
210
212
EMAIL1_ENTRY = _('Email address')
213
215
'and try entering them again.')
214
216
EMAIL_INVALID = _('The email must be a valid email address.')
215
217
EMAIL_TOKEN_ENTRY = _('Enter code verification here')
218
ERROR = _('The process did not finish successfully.')
216
219
FIELD_REQUIRED = _('This field is required.')
217
220
FORGOTTEN_PASSWORD_BUTTON = _('I\'ve forgotten my password')
218
221
JOIN_HEADER_LABEL = _('Create %(app_name)s account')
256
259
YES_TO_UPDATES = _('Yes! Email me %(app_name)s tips and updates.')
257
260
CAPTCHA_RELOAD_TOOLTIP = _('Reload')
259
def __init__(self, app_name, tc_url, help_text,
262
def __init__(self, app_name, tc_url='', help_text='',
260
263
window_id=0, login_only=False, close_callback=None):
261
264
"""Create the GUI and initialize widgets."""
262
265
gtk.link_button_set_uri_hook(NO_OP)
264
267
self._captcha_filename = tempfile.mktemp()
265
268
self._captcha_id = None
266
269
self._signals_receivers = {}
270
self._done = False # whether the whole process was completed or not
268
272
self.app_name = app_name
269
273
self.app_label = '<b>%s</b>' % self.app_name
270
274
self.tc_url = tc_url
271
275
self.help_text = help_text
272
276
self.close_callback = close_callback
277
self.user_email = None
278
self.user_password = None
274
280
ui_filename = get_data_file('ui.glade')
275
281
builder = gtk.Builder()
325
331
logger.debug('UbuntuSSOClientGUI: backend created: %r', self.backend)
327
333
self.pages = (self.enter_details_vbox, self.processing_vbox,
328
self.verify_email_vbox, self.success_vbox,
334
self.verify_email_vbox, self.finish_vbox,
329
335
self.tc_browser_vbox, self.login_vbox,
330
336
self.request_password_token_vbox,
331
337
self.set_new_password_vbox)
333
339
self._append_page(self._build_processing_page())
334
self._append_page(self._build_success_page())
340
self._append_page(self._build_finish_page())
335
341
self._append_page(self._build_login_page())
336
342
self._append_page(self._build_request_password_token_page())
337
343
self._append_page(self._build_set_new_password_page())
344
self._append_page(self._build_verify_email_page())
339
346
window_size = None
340
347
if not login_only:
341
348
window_size = (550, 500)
342
349
self._append_page(self._build_enter_details_page())
343
350
self._append_page(self._build_tc_page())
344
self._append_page(self._build_verify_email_page())
345
351
self.login_button.grab_focus()
346
352
self._set_current_page(self.enter_details_vbox)
373
379
self._filter_by_app_name(self.on_logged_in),
375
381
self._filter_by_app_name(self.on_login_error),
383
self._filter_by_app_name(self.on_user_not_validated),
376
384
'PasswordResetTokenSent':
377
385
self._filter_by_app_name(self.on_password_reset_token_sent),
378
386
'PasswordResetError':
406
413
self.window.show()
416
def success_vbox(self):
417
"""The success page."""
418
self.finish_vbox.label.set_markup('<span size="x-large">%s</span>' %
420
return self.finish_vbox
423
def error_vbox(self):
424
"""The error page."""
425
self.finish_vbox.label.set_markup('<span size="x-large">%s</span>' %
427
return self.finish_vbox
408
431
def _filter_by_app_name(self, f):
409
432
"""Excecute the decorated function only for 'self.app_name'."""
460
483
alignment.add(hbox)
461
484
alignment.show_all()
486
# remove children to avoid:
487
# GtkWarning: Attempting to add a widget with type GtkAlignment to a
488
# GtkEventBox, but as a GtkBin subclass a GtkEventBox can only contain
489
# one widget at a time
490
for child in container.get_children():
491
container.remove(child)
463
492
container.add(alignment)
467
494
def _set_warning_message(self, widget, message):
468
495
"""Set 'message' as text for 'widget'."""
469
496
widget.set_text(message)
600
627
self.verify_email_vbox.default_widget = self.verify_token_button
601
628
self.verify_email_vbox.default_widget.set_flags(gtk.CAN_DEFAULT)
603
self.verify_email_vbox.pack_start(self.email_token_entry, expand=False)
604
self.verify_email_vbox.reorder_child(self.email_token_entry, 0)
630
self.verify_email_details_vbox.pack_start(self.email_token_entry,
606
633
return self.verify_email_vbox
608
def _build_success_page(self):
635
def _build_finish_page(self):
609
636
"""Build the success page."""
610
self.success_vbox.default_widget = self.success_close_button
611
self.success_vbox.default_widget.set_flags(gtk.CAN_DEFAULT)
613
self.success_label.set_markup('<span size="x-large">%s</span>' %
615
return self.success_vbox
637
self.finish_vbox.default_widget = self.finish_close_button
638
self.finish_vbox.default_widget.set_flags(gtk.CAN_DEFAULT)
639
self.finish_vbox.label = self.finish_label
640
return self.finish_vbox
617
642
def _build_login_page(self):
618
643
"""Build the login page."""
716
741
signal_name, handler, args, kwargs)
717
742
self.window.connect(signal_name, handler, *args, **kwargs)
744
def emit(self, *args, **kwargs):
745
"""Emit a signal proxing the main window."""
746
logger.debug('emit: args %r, kwargs, %r', args, kwargs)
747
self.window.emit(*args, **kwargs)
749
def finish_success(self):
750
"""The whole process was completed succesfully. Show success page."""
752
self._set_current_page(self.success_vbox)
754
def finish_error(self, error):
755
"""The whole process was not completed succesfully. Show error page."""
757
self._set_current_page(self.error_vbox)
719
759
def on_close_clicked(self, *args, **kwargs):
720
760
"""Call self.close_callback if defined."""
721
761
if os.path.exists(self._captcha_filename):
737
777
while gtk.events_pending():
738
778
gtk.main_iteration()
740
if len(args) > 0 and args[0] in self.cancels:
741
self.window.emit(SIG_USER_CANCELATION, self.app_name)
742
elif len(self._gtk_signal_log) > 0:
743
signal = self._gtk_signal_log[-1][0]
744
args = self._gtk_signal_log[-1][1:]
745
logger.info('emitting %r with args %r.', signal, args)
746
self.window.emit(signal, *args)
748
self.window.emit(SIG_USER_CANCELATION, self.app_name)
781
self.emit(SIG_USER_CANCELATION, self.app_name)
750
783
# call user defined callback
751
784
if self.close_callback is not None:
807
840
self._set_current_page(self.processing_vbox)
841
self.user_email = email1
842
self.user_password = password1
809
844
logger.info('Calling register_user with email %r, password <hidden>,' \
810
845
' captcha_id %r and captcha_solution %r.', email1,
825
860
self.email_token_entry.set_warning(self.FIELD_REQUIRED)
828
email = self.email1_entry.get_text()
829
password = self.password1_entry.get_text()
863
email = self.user_email
864
password = self.user_password
830
865
f = self.backend.validate_email
831
866
logger.info('Calling validate_email with email %r, password <hidden>' \
832
867
', app_name %r and email_token %r.', email, self.app_name,
864
899
reply_handler=NO_OP, error_handler=NO_OP)
866
901
self._set_current_page(self.processing_vbox)
902
self.user_email = email
903
self.user_password = password
868
905
def on_login_back_button_clicked(self, *args, **kwargs):
869
906
"""User wants to go to the previous page."""
1025
1062
result = msg1 if msg1 is not None else msg2
1028
def _append_error_signal(self, signal, error):
1029
"""Append 'signal' to the log with a formatted version of 'error'.
1031
Format DBus dict 'error' into a nice string.
1034
msg = '\n'.join('%s: %s' % i for i in error.iteritems())
1035
self._gtk_signal_log.append((signal, self.app_name, msg))
1038
1066
def on_captcha_generated(self, app_name, captcha_id, *args, **kwargs):
1039
1067
"""Captcha image has been generated and is available to be shown."""
1047
1075
def on_captcha_generation_error(self, app_name, error, *args, **kwargs):
1048
1076
"""Captcha image generation failed."""
1077
self._set_warning_message(self.warning_label, self.CAPTCHA_LOAD_ERROR)
1078
self._generate_captcha()
1051
1081
def on_user_registered(self, app_name, email, *args, **kwargs):
1069
1099
self.password2_entry.set_warning(msg)
1071
1101
msg = self._build_general_error_message(error)
1102
self._generate_captcha()
1072
1103
self._set_current_page(self.enter_details_vbox, warning_text=msg)
1073
self._append_error_signal(SIG_REGISTRATION_FAILED, error)
1076
1106
def on_email_validated(self, app_name, email, *args, **kwargs):
1077
1107
"""User email was successfully verified."""
1078
self._set_current_page(self.success_vbox)
1079
self._gtk_signal_log.append((SIG_REGISTRATION_SUCCEEDED, self.app_name,
1109
self.emit(SIG_REGISTRATION_SUCCEEDED, self.app_name, email)
1083
1112
def on_email_validation_error(self, app_name, error, *args, **kwargs):
1089
1118
msg = self._build_general_error_message(error)
1090
1119
self._set_current_page(self.verify_email_vbox, warning_text=msg)
1091
self._append_error_signal(SIG_REGISTRATION_FAILED, error)
1094
1122
def on_logged_in(self, app_name, email, *args, **kwargs):
1095
1123
"""User was successfully logged in."""
1096
self._set_current_page(self.success_vbox)
1097
self._gtk_signal_log.append((SIG_LOGIN_SUCCEEDED, self.app_name,
1125
self.emit(SIG_LOGIN_SUCCEEDED, self.app_name, email)
1101
1128
def on_login_error(self, app_name, error, *args, **kwargs):
1102
1129
"""User was not successfully logged in."""
1103
1130
msg = self._build_general_error_message(error)
1104
1131
self._set_current_page(self.login_vbox, warning_text=msg)
1105
self._append_error_signal(SIG_LOGIN_FAILED, error)
1134
def on_user_not_validated(self, app_name, email, *args, **kwargs):
1135
"""User was not validated."""
1136
self.on_user_registered(app_name, email)
1108
1139
def on_password_reset_token_sent(self, app_name, email, *args, **kwargs):