Merge lp:~mandel/ubuntu-sso-client/windows_ui_3 into lp:ubuntu-sso-client

Proposed by Manuel de la Peña
Status: Superseded
Proposed branch: lp:~mandel/ubuntu-sso-client/windows_ui_3
Merge into: lp:ubuntu-sso-client
Diff against target: 2714 lines (+2503/-45) (has conflicts)
15 files modified
.bzrignore (+6/-0)
data/qt/choose_sign_in.ui (+121/-0)
data/qt/current_user_sign_in.ui (+154/-0)
data/qt/email_verification.ui (+105/-0)
data/qt/setup_account.ui (+413/-0)
data/qt/terms_and_conditions.ui (+128/-0)
setup.py (+255/-6)
ubuntu_sso/gtk/gui.py (+2/-39)
ubuntu_sso/qt/__init__.py (+1/-0)
ubuntu_sso/qt/controllers.py (+260/-0)
ubuntu_sso/qt/gui.py (+461/-0)
ubuntu_sso/qt/tests/__init__.py (+1/-0)
ubuntu_sso/qt/tests/test_controllers.py (+318/-0)
ubuntu_sso/utils/tests/test_ui.py (+110/-0)
ubuntu_sso/utils/ui.py (+168/-0)
Text conflict in setup.py
To merge this branch: bzr merge lp:~mandel/ubuntu-sso-client/windows_ui_3
Reviewer Review Type Date Requested Status
Ubuntu One hackers Pending
Review via email: mp+55560@code.launchpad.net

This proposal supersedes a proposal from 2011-03-30.

This proposal has been superseded by a proposal from 2011-03-30.

Description of the change

Provides the UI with the required controllers that will ensure that the navigation in the Wizard is the correct one. This branch does not yet provide the sso functionality.

To post a comment you must log in.
742. By Manuel de la Peña

Merged with windows_ui_3

743. By Manuel de la Peña

Removed the last old school QObject connect call.

744. By Manuel de la Peña

Removed unused import.

745. By Manuel de la Peña

Fixed as per reviewers request. Mainly fixed the foloowing:
* Typos.
* Made colors a contant.

746. By Manuel de la Peña

Fixed mock test.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2010-11-22 15:06:52 +0000
3+++ .bzrignore 2011-03-30 14:55:45 +0000
4@@ -4,3 +4,9 @@
5 dist/
6 MANIFEST
7 po/ubuntu-sso-client.pot
8+ubuntu_sso/qt/captcha_ui.py
9+ubuntu_sso/qt/choose_sign_in_ui.py
10+ubuntu_sso/qt/current_user_sign_in_ui.py
11+ubuntu_sso/qt/email_verification_ui.py
12+ubuntu_sso/qt/setup_account_ui.py
13+ubuntu_sso/qt/terms_and_conditions_ui.py
14
15=== added directory 'data/gtk'
16=== renamed file 'data/ui.glade' => 'data/gtk/ui.glade'
17=== added directory 'data/qt'
18=== added file 'data/qt/choose_sign_in.ui'
19--- data/qt/choose_sign_in.ui 1970-01-01 00:00:00 +0000
20+++ data/qt/choose_sign_in.ui 2011-03-30 14:55:45 +0000
21@@ -0,0 +1,121 @@
22+<?xml version="1.0" encoding="UTF-8"?>
23+<ui version="4.0">
24+ <class>ChooseSingInPage</class>
25+ <widget class="QWizardPage" name="ChooseSingInPage">
26+ <property name="geometry">
27+ <rect>
28+ <x>0</x>
29+ <y>0</y>
30+ <width>400</width>
31+ <height>300</height>
32+ </rect>
33+ </property>
34+ <property name="windowTitle">
35+ <string>WizardPage</string>
36+ </property>
37+ <layout class="QHBoxLayout" name="horizontalLayout">
38+ <item>
39+ <layout class="QHBoxLayout" name="horizontalLayout_3">
40+ <item>
41+ <spacer name="horizontalSpacer_2">
42+ <property name="orientation">
43+ <enum>Qt::Horizontal</enum>
44+ </property>
45+ <property name="sizeType">
46+ <enum>QSizePolicy::Expanding</enum>
47+ </property>
48+ <property name="sizeHint" stdset="0">
49+ <size>
50+ <width>40</width>
51+ <height>20</height>
52+ </size>
53+ </property>
54+ </spacer>
55+ </item>
56+ <item>
57+ <layout class="QVBoxLayout" name="verticalLayout">
58+ <item>
59+ <spacer name="verticalSpacer_3">
60+ <property name="orientation">
61+ <enum>Qt::Vertical</enum>
62+ </property>
63+ <property name="sizeHint" stdset="0">
64+ <size>
65+ <width>20</width>
66+ <height>40</height>
67+ </size>
68+ </property>
69+ </spacer>
70+ </item>
71+ <item>
72+ <layout class="QHBoxLayout" name="horizontalLayout_2">
73+ <item>
74+ <widget class="QPushButton" name="existing_account_button">
75+ <property name="text">
76+ <string/>
77+ </property>
78+ </widget>
79+ </item>
80+ </layout>
81+ </item>
82+ <item>
83+ <spacer name="verticalSpacer">
84+ <property name="orientation">
85+ <enum>Qt::Vertical</enum>
86+ </property>
87+ <property name="sizeType">
88+ <enum>QSizePolicy::Fixed</enum>
89+ </property>
90+ <property name="sizeHint" stdset="0">
91+ <size>
92+ <width>20</width>
93+ <height>10</height>
94+ </size>
95+ </property>
96+ </spacer>
97+ </item>
98+ <item>
99+ <widget class="QPushButton" name="setup_account_button">
100+ <property name="text">
101+ <string/>
102+ </property>
103+ </widget>
104+ </item>
105+ <item>
106+ <spacer name="verticalSpacer_2">
107+ <property name="orientation">
108+ <enum>Qt::Vertical</enum>
109+ </property>
110+ <property name="sizeHint" stdset="0">
111+ <size>
112+ <width>20</width>
113+ <height>40</height>
114+ </size>
115+ </property>
116+ </spacer>
117+ </item>
118+ </layout>
119+ </item>
120+ <item>
121+ <spacer name="horizontalSpacer">
122+ <property name="orientation">
123+ <enum>Qt::Horizontal</enum>
124+ </property>
125+ <property name="sizeType">
126+ <enum>QSizePolicy::Expanding</enum>
127+ </property>
128+ <property name="sizeHint" stdset="0">
129+ <size>
130+ <width>40</width>
131+ <height>20</height>
132+ </size>
133+ </property>
134+ </spacer>
135+ </item>
136+ </layout>
137+ </item>
138+ </layout>
139+ </widget>
140+ <resources/>
141+ <connections/>
142+</ui>
143
144=== added file 'data/qt/current_user_sign_in.ui'
145--- data/qt/current_user_sign_in.ui 1970-01-01 00:00:00 +0000
146+++ data/qt/current_user_sign_in.ui 2011-03-30 14:55:45 +0000
147@@ -0,0 +1,154 @@
148+<?xml version="1.0" encoding="UTF-8"?>
149+<ui version="4.0">
150+ <class>CurrentUserSignInPage</class>
151+ <widget class="QWizardPage" name="CurrentUserSignInPage">
152+ <property name="geometry">
153+ <rect>
154+ <x>0</x>
155+ <y>0</y>
156+ <width>400</width>
157+ <height>300</height>
158+ </rect>
159+ </property>
160+ <property name="windowTitle">
161+ <string>WizardPage</string>
162+ </property>
163+ <layout class="QHBoxLayout" name="horizontalLayout">
164+ <item>
165+ <layout class="QHBoxLayout" name="horizontalLayout_3">
166+ <item>
167+ <spacer name="horizontalSpacer_2">
168+ <property name="orientation">
169+ <enum>Qt::Horizontal</enum>
170+ </property>
171+ <property name="sizeType">
172+ <enum>QSizePolicy::Fixed</enum>
173+ </property>
174+ <property name="sizeHint" stdset="0">
175+ <size>
176+ <width>40</width>
177+ <height>20</height>
178+ </size>
179+ </property>
180+ </spacer>
181+ </item>
182+ <item>
183+ <layout class="QVBoxLayout" name="verticalLayout">
184+ <item>
185+ <spacer name="verticalSpacer_3">
186+ <property name="orientation">
187+ <enum>Qt::Vertical</enum>
188+ </property>
189+ <property name="sizeHint" stdset="0">
190+ <size>
191+ <width>20</width>
192+ <height>40</height>
193+ </size>
194+ </property>
195+ </spacer>
196+ </item>
197+ <item>
198+ <widget class="QFrame" name="_signInFrame">
199+ <property name="frameShape">
200+ <enum>QFrame::NoFrame</enum>
201+ </property>
202+ <layout class="QVBoxLayout" name="verticalLayout_3">
203+ <item>
204+ <layout class="QVBoxLayout" name="verticalLayout_2">
205+ <item>
206+ <widget class="QLineEdit" name="email_edit">
207+ <property name="placeholderText">
208+ <string/>
209+ </property>
210+ </widget>
211+ </item>
212+ <item>
213+ <widget class="QLineEdit" name="password_edit">
214+ <property name="echoMode">
215+ <enum>QLineEdit::Password</enum>
216+ </property>
217+ <property name="placeholderText">
218+ <string/>
219+ </property>
220+ </widget>
221+ </item>
222+ <item>
223+ <widget class="QLabel" name="forgot_label">
224+ <property name="sizePolicy">
225+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
226+ <horstretch>0</horstretch>
227+ <verstretch>0</verstretch>
228+ </sizepolicy>
229+ </property>
230+ <property name="text">
231+ <string/>
232+ </property>
233+ </widget>
234+ </item>
235+ <item>
236+ <layout class="QHBoxLayout" name="horizontalLayout_4">
237+ <item>
238+ <spacer name="horizontalSpacer_3">
239+ <property name="orientation">
240+ <enum>Qt::Horizontal</enum>
241+ </property>
242+ <property name="sizeHint" stdset="0">
243+ <size>
244+ <width>40</width>
245+ <height>20</height>
246+ </size>
247+ </property>
248+ </spacer>
249+ </item>
250+ <item>
251+ <widget class="QPushButton" name="sign_in_button">
252+ <property name="text">
253+ <string/>
254+ </property>
255+ </widget>
256+ </item>
257+ </layout>
258+ </item>
259+ </layout>
260+ </item>
261+ </layout>
262+ </widget>
263+ </item>
264+ <item>
265+ <spacer name="verticalSpacer_2">
266+ <property name="orientation">
267+ <enum>Qt::Vertical</enum>
268+ </property>
269+ <property name="sizeHint" stdset="0">
270+ <size>
271+ <width>20</width>
272+ <height>40</height>
273+ </size>
274+ </property>
275+ </spacer>
276+ </item>
277+ </layout>
278+ </item>
279+ <item>
280+ <spacer name="horizontalSpacer">
281+ <property name="orientation">
282+ <enum>Qt::Horizontal</enum>
283+ </property>
284+ <property name="sizeType">
285+ <enum>QSizePolicy::Fixed</enum>
286+ </property>
287+ <property name="sizeHint" stdset="0">
288+ <size>
289+ <width>40</width>
290+ <height>20</height>
291+ </size>
292+ </property>
293+ </spacer>
294+ </item>
295+ </layout>
296+ </item>
297+ </layout>
298+ </widget>
299+ <resources/>
300+ <connections/>
301+</ui>
302
303=== added file 'data/qt/email_verification.ui'
304--- data/qt/email_verification.ui 1970-01-01 00:00:00 +0000
305+++ data/qt/email_verification.ui 2011-03-30 14:55:45 +0000
306@@ -0,0 +1,105 @@
307+<?xml version="1.0" encoding="UTF-8"?>
308+<ui version="4.0">
309+ <class>EmailVerificationPage</class>
310+ <widget class="QWizardPage" name="EmailVerificationPage">
311+ <property name="geometry">
312+ <rect>
313+ <x>0</x>
314+ <y>0</y>
315+ <width>400</width>
316+ <height>300</height>
317+ </rect>
318+ </property>
319+ <property name="windowTitle">
320+ <string>WizardPage</string>
321+ </property>
322+ <layout class="QHBoxLayout" name="horizontalLayout">
323+ <item>
324+ <layout class="QHBoxLayout" name="horizontalLayout_3">
325+ <item>
326+ <spacer name="horizontalSpacer_2">
327+ <property name="orientation">
328+ <enum>Qt::Horizontal</enum>
329+ </property>
330+ <property name="sizeType">
331+ <enum>QSizePolicy::Fixed</enum>
332+ </property>
333+ <property name="sizeHint" stdset="0">
334+ <size>
335+ <width>40</width>
336+ <height>20</height>
337+ </size>
338+ </property>
339+ </spacer>
340+ </item>
341+ <item>
342+ <layout class="QVBoxLayout" name="verticalLayout">
343+ <item>
344+ <widget class="QLineEdit" name="verification_code_edit">
345+ <property name="placeholderText">
346+ <string/>
347+ </property>
348+ </widget>
349+ </item>
350+ <item>
351+ <layout class="QHBoxLayout" name="horizontalLayout_2">
352+ <item>
353+ <spacer name="horizontalSpacer_3">
354+ <property name="orientation">
355+ <enum>Qt::Horizontal</enum>
356+ </property>
357+ <property name="sizeHint" stdset="0">
358+ <size>
359+ <width>40</width>
360+ <height>20</height>
361+ </size>
362+ </property>
363+ </spacer>
364+ </item>
365+ <item>
366+ <widget class="QPushButton" name="next_button">
367+ <property name="text">
368+ <string>Next</string>
369+ </property>
370+ </widget>
371+ </item>
372+ </layout>
373+ </item>
374+ <item>
375+ <spacer name="verticalSpacer_2">
376+ <property name="orientation">
377+ <enum>Qt::Vertical</enum>
378+ </property>
379+ <property name="sizeHint" stdset="0">
380+ <size>
381+ <width>20</width>
382+ <height>40</height>
383+ </size>
384+ </property>
385+ </spacer>
386+ </item>
387+ </layout>
388+ </item>
389+ <item>
390+ <spacer name="horizontalSpacer">
391+ <property name="orientation">
392+ <enum>Qt::Horizontal</enum>
393+ </property>
394+ <property name="sizeType">
395+ <enum>QSizePolicy::Fixed</enum>
396+ </property>
397+ <property name="sizeHint" stdset="0">
398+ <size>
399+ <width>40</width>
400+ <height>20</height>
401+ </size>
402+ </property>
403+ </spacer>
404+ </item>
405+ </layout>
406+ </item>
407+ </layout>
408+ </widget>
409+ <resources/>
410+ <connections/>
411+</ui>
412
413=== added file 'data/qt/setup_account.ui'
414--- data/qt/setup_account.ui 1970-01-01 00:00:00 +0000
415+++ data/qt/setup_account.ui 2011-03-30 14:55:45 +0000
416@@ -0,0 +1,413 @@
417+<?xml version="1.0" encoding="UTF-8"?>
418+<ui version="4.0">
419+ <class>SetUpAccountPage</class>
420+ <widget class="QWizardPage" name="SetUpAccountPage">
421+ <property name="geometry">
422+ <rect>
423+ <x>0</x>
424+ <y>0</y>
425+ <width>415</width>
426+ <height>359</height>
427+ </rect>
428+ </property>
429+ <property name="windowTitle">
430+ <string>WizardPage</string>
431+ </property>
432+ <layout class="QHBoxLayout" name="horizontalLayout">
433+ <item>
434+ <layout class="QVBoxLayout" name="verticalLayout">
435+ <item>
436+ <spacer name="verticalSpacer_3">
437+ <property name="orientation">
438+ <enum>Qt::Vertical</enum>
439+ </property>
440+ <property name="sizeHint" stdset="0">
441+ <size>
442+ <width>20</width>
443+ <height>40</height>
444+ </size>
445+ </property>
446+ </spacer>
447+ </item>
448+ <item>
449+ <widget class="QFrame" name="_signInFrame">
450+ <property name="frameShape">
451+ <enum>QFrame::NoFrame</enum>
452+ </property>
453+ <layout class="QVBoxLayout" name="verticalLayout_3">
454+ <item>
455+ <layout class="QVBoxLayout" name="verticalLayout_2">
456+ <item>
457+ <layout class="QVBoxLayout" name="verticalLayout_7">
458+ <item>
459+ <layout class="QHBoxLayout" name="horizontalLayout_10">
460+ <item>
461+ <widget class="QLineEdit" name="first_name_edit">
462+ <property name="placeholderText">
463+ <string/>
464+ </property>
465+ </widget>
466+ </item>
467+ <item>
468+ <widget class="QLineEdit" name="last_name_edit">
469+ <property name="placeholderText">
470+ <string/>
471+ </property>
472+ </widget>
473+ </item>
474+ </layout>
475+ </item>
476+ <item>
477+ <layout class="QHBoxLayout" name="horizontalLayout_11">
478+ <item>
479+ <widget class="QLineEdit" name="email_edit">
480+ <property name="placeholderText">
481+ <string/>
482+ </property>
483+ </widget>
484+ </item>
485+ <item>
486+ <widget class="QLineEdit" name="confirm_email_edit">
487+ <property name="placeholderText">
488+ <string/>
489+ </property>
490+ </widget>
491+ </item>
492+ </layout>
493+ </item>
494+ <item>
495+ <layout class="QHBoxLayout" name="horizontalLayout_12">
496+ <item>
497+ <layout class="QVBoxLayout" name="verticalLayout_32">
498+ <item>
499+ <widget class="QLineEdit" name="password_edit">
500+ <property name="toolTip">
501+ <string>Your password must be at least 8 characters long and at least contain one number and one upper later.</string>
502+ </property>
503+ <property name="statusTip">
504+ <string/>
505+ </property>
506+ <property name="echoMode">
507+ <enum>QLineEdit::Password</enum>
508+ </property>
509+ <property name="placeholderText">
510+ <string/>
511+ </property>
512+ </widget>
513+ </item>
514+ <item>
515+ <widget class="QFrame" name="strenght_frame">
516+ <property name="sizePolicy">
517+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
518+ <horstretch>0</horstretch>
519+ <verstretch>0</verstretch>
520+ </sizepolicy>
521+ </property>
522+ <property name="minimumSize">
523+ <size>
524+ <width>0</width>
525+ <height>20</height>
526+ </size>
527+ </property>
528+ <property name="maximumSize">
529+ <size>
530+ <width>16777215</width>
531+ <height>30</height>
532+ </size>
533+ </property>
534+ <property name="locale">
535+ <locale language="English" country="UnitedStates"/>
536+ </property>
537+ <property name="frameShape">
538+ <enum>QFrame::StyledPanel</enum>
539+ </property>
540+ <property name="frameShadow">
541+ <enum>QFrame::Raised</enum>
542+ </property>
543+ <layout class="QHBoxLayout" name="horizontalLayout_15">
544+ <property name="spacing">
545+ <number>3</number>
546+ </property>
547+ <property name="leftMargin">
548+ <number>2</number>
549+ </property>
550+ <property name="topMargin">
551+ <number>0</number>
552+ </property>
553+ <property name="rightMargin">
554+ <number>2</number>
555+ </property>
556+ <property name="bottomMargin">
557+ <number>2</number>
558+ </property>
559+ <item>
560+ <widget class="QFrame" name="weak_frame">
561+ <property name="maximumSize">
562+ <size>
563+ <width>16777215</width>
564+ <height>10</height>
565+ </size>
566+ </property>
567+ <property name="frameShape">
568+ <enum>QFrame::StyledPanel</enum>
569+ </property>
570+ <property name="frameShadow">
571+ <enum>QFrame::Plain</enum>
572+ </property>
573+ <layout class="QVBoxLayout" name="verticalLayout_33">
574+ <property name="margin">
575+ <number>0</number>
576+ </property>
577+ </layout>
578+ </widget>
579+ </item>
580+ <item>
581+ <widget class="QFrame" name="medium_frame">
582+ <property name="maximumSize">
583+ <size>
584+ <width>16777215</width>
585+ <height>10</height>
586+ </size>
587+ </property>
588+ <property name="frameShape">
589+ <enum>QFrame::StyledPanel</enum>
590+ </property>
591+ <property name="frameShadow">
592+ <enum>QFrame::Plain</enum>
593+ </property>
594+ <layout class="QVBoxLayout" name="verticalLayout_34">
595+ <property name="margin">
596+ <number>0</number>
597+ </property>
598+ </layout>
599+ </widget>
600+ </item>
601+ <item>
602+ <widget class="QFrame" name="strong_frame">
603+ <property name="maximumSize">
604+ <size>
605+ <width>16777215</width>
606+ <height>10</height>
607+ </size>
608+ </property>
609+ <property name="frameShape">
610+ <enum>QFrame::StyledPanel</enum>
611+ </property>
612+ <property name="frameShadow">
613+ <enum>QFrame::Plain</enum>
614+ </property>
615+ <layout class="QVBoxLayout" name="verticalLayout_35">
616+ <property name="margin">
617+ <number>0</number>
618+ </property>
619+ </layout>
620+ </widget>
621+ </item>
622+ </layout>
623+ </widget>
624+ </item>
625+ <item>
626+ <spacer name="_strenghSpacer">
627+ <property name="orientation">
628+ <enum>Qt::Vertical</enum>
629+ </property>
630+ <property name="sizeHint" stdset="0">
631+ <size>
632+ <width>20</width>
633+ <height>5</height>
634+ </size>
635+ </property>
636+ </spacer>
637+ </item>
638+ </layout>
639+ </item>
640+ <item>
641+ <layout class="QVBoxLayout" name="verticalLayout_8">
642+ <item>
643+ <widget class="QLineEdit" name="confirm_password_edit">
644+ <property name="echoMode">
645+ <enum>QLineEdit::Password</enum>
646+ </property>
647+ <property name="placeholderText">
648+ <string/>
649+ </property>
650+ </widget>
651+ </item>
652+ <item>
653+ <spacer name="verticalSpacer_6">
654+ <property name="orientation">
655+ <enum>Qt::Vertical</enum>
656+ </property>
657+ <property name="sizeHint" stdset="0">
658+ <size>
659+ <width>20</width>
660+ <height>10</height>
661+ </size>
662+ </property>
663+ </spacer>
664+ </item>
665+ </layout>
666+ </item>
667+ </layout>
668+ </item>
669+ </layout>
670+ </item>
671+ <item>
672+ <widget class="QLabel" name="password_info_label">
673+ <property name="sizePolicy">
674+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
675+ <horstretch>0</horstretch>
676+ <verstretch>0</verstretch>
677+ </sizepolicy>
678+ </property>
679+ <property name="text">
680+ <string/>
681+ </property>
682+ <property name="wordWrap">
683+ <bool>true</bool>
684+ </property>
685+ </widget>
686+ </item>
687+ <item>
688+ <layout class="QVBoxLayout" name="verticalLayout_9">
689+ <item>
690+ <widget class="QFrame" name="frame_2">
691+ <property name="sizePolicy">
692+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
693+ <horstretch>0</horstretch>
694+ <verstretch>0</verstretch>
695+ </sizepolicy>
696+ </property>
697+ <property name="frameShape">
698+ <enum>QFrame::StyledPanel</enum>
699+ </property>
700+ <property name="frameShadow">
701+ <enum>QFrame::Raised</enum>
702+ </property>
703+ <layout class="QHBoxLayout" name="horizontalLayout_16">
704+ <property name="leftMargin">
705+ <number>0</number>
706+ </property>
707+ <item>
708+ <widget class="QLabel" name="captcha_view">
709+ <property name="frameShape">
710+ <enum>QFrame::Box</enum>
711+ </property>
712+ <property name="text">
713+ <string/>
714+ </property>
715+ </widget>
716+ </item>
717+ <item>
718+ <widget class="QLabel" name="refresh_label">
719+ <property name="sizePolicy">
720+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
721+ <horstretch>0</horstretch>
722+ <verstretch>0</verstretch>
723+ </sizepolicy>
724+ </property>
725+ <property name="locale">
726+ <locale language="English" country="UnitedStates"/>
727+ </property>
728+ <property name="text">
729+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
730+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
731+p, li { white-space: pre-wrap; }
732+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
733+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;If you can't read this then &lt;/span&gt;&lt;a href=&quot;example.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;refresh&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; this page&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
734+ </property>
735+ <property name="wordWrap">
736+ <bool>true</bool>
737+ </property>
738+ </widget>
739+ </item>
740+ </layout>
741+ </widget>
742+ </item>
743+ <item>
744+ <widget class="QLineEdit" name="captcha_solution_edit">
745+ <property name="locale">
746+ <locale language="English" country="UnitedStates"/>
747+ </property>
748+ <property name="inputMask">
749+ <string/>
750+ </property>
751+ <property name="text">
752+ <string/>
753+ </property>
754+ <property name="placeholderText">
755+ <string/>
756+ </property>
757+ </widget>
758+ </item>
759+ </layout>
760+ </item>
761+ <item>
762+ <widget class="QCheckBox" name="terms_checkbox">
763+ <property name="text">
764+ <string/>
765+ </property>
766+ </widget>
767+ </item>
768+ <item>
769+ <layout class="QHBoxLayout" name="horizontalLayout_4">
770+ <property name="spacing">
771+ <number>0</number>
772+ </property>
773+ <item>
774+ <widget class="QPushButton" name="terms_button">
775+ <property name="text">
776+ <string/>
777+ </property>
778+ </widget>
779+ </item>
780+ <item>
781+ <spacer name="horizontalSpacer_3">
782+ <property name="orientation">
783+ <enum>Qt::Horizontal</enum>
784+ </property>
785+ <property name="sizeHint" stdset="0">
786+ <size>
787+ <width>40</width>
788+ <height>20</height>
789+ </size>
790+ </property>
791+ </spacer>
792+ </item>
793+ <item>
794+ <widget class="QPushButton" name="set_up_button">
795+ <property name="enabled">
796+ <bool>false</bool>
797+ </property>
798+ <property name="text">
799+ <string/>
800+ </property>
801+ </widget>
802+ </item>
803+ </layout>
804+ </item>
805+ </layout>
806+ </item>
807+ </layout>
808+ </widget>
809+ </item>
810+ <item>
811+ <spacer name="verticalSpacer_2">
812+ <property name="orientation">
813+ <enum>Qt::Vertical</enum>
814+ </property>
815+ <property name="sizeHint" stdset="0">
816+ <size>
817+ <width>20</width>
818+ <height>40</height>
819+ </size>
820+ </property>
821+ </spacer>
822+ </item>
823+ </layout>
824+ </item>
825+ </layout>
826+ </widget>
827+ <resources/>
828+ <connections/>
829+</ui>
830
831=== added file 'data/qt/terms_and_conditions.ui'
832--- data/qt/terms_and_conditions.ui 1970-01-01 00:00:00 +0000
833+++ data/qt/terms_and_conditions.ui 2011-03-30 14:55:45 +0000
834@@ -0,0 +1,128 @@
835+<?xml version="1.0" encoding="UTF-8"?>
836+<ui version="4.0">
837+ <class>TosPage</class>
838+ <widget class="QWizardPage" name="TosPage">
839+ <property name="geometry">
840+ <rect>
841+ <x>0</x>
842+ <y>0</y>
843+ <width>400</width>
844+ <height>300</height>
845+ </rect>
846+ </property>
847+ <property name="windowTitle">
848+ <string>WizardPage</string>
849+ </property>
850+ <layout class="QHBoxLayout" name="horizontalLayout">
851+ <item>
852+ <layout class="QHBoxLayout" name="horizontalLayout_3">
853+ <item>
854+ <spacer name="horizontalSpacer_2">
855+ <property name="orientation">
856+ <enum>Qt::Horizontal</enum>
857+ </property>
858+ <property name="sizeType">
859+ <enum>QSizePolicy::Fixed</enum>
860+ </property>
861+ <property name="sizeHint" stdset="0">
862+ <size>
863+ <width>40</width>
864+ <height>20</height>
865+ </size>
866+ </property>
867+ </spacer>
868+ </item>
869+ <item>
870+ <layout class="QVBoxLayout" name="verticalLayout">
871+ <item>
872+ <spacer name="verticalSpacer_3">
873+ <property name="orientation">
874+ <enum>Qt::Vertical</enum>
875+ </property>
876+ <property name="sizeType">
877+ <enum>QSizePolicy::Fixed</enum>
878+ </property>
879+ <property name="sizeHint" stdset="0">
880+ <size>
881+ <width>20</width>
882+ <height>40</height>
883+ </size>
884+ </property>
885+ </spacer>
886+ </item>
887+ <item>
888+ <widget class="QScrollArea" name="scrollArea">
889+ <property name="widgetResizable">
890+ <bool>true</bool>
891+ </property>
892+ <widget class="QWidget" name="scrollAreaWidgetContents">
893+ <property name="geometry">
894+ <rect>
895+ <x>0</x>
896+ <y>0</y>
897+ <width>284</width>
898+ <height>184</height>
899+ </rect>
900+ </property>
901+ <layout class="QHBoxLayout" name="horizontalLayout_2">
902+ <item>
903+ <widget class="QWebView" name="terms_webkit">
904+ <property name="url">
905+ <url>
906+ <string>about:blank</string>
907+ </url>
908+ </property>
909+ </widget>
910+ </item>
911+ </layout>
912+ </widget>
913+ </widget>
914+ </item>
915+ <item>
916+ <spacer name="verticalSpacer_2">
917+ <property name="orientation">
918+ <enum>Qt::Vertical</enum>
919+ </property>
920+ <property name="sizeType">
921+ <enum>QSizePolicy::Fixed</enum>
922+ </property>
923+ <property name="sizeHint" stdset="0">
924+ <size>
925+ <width>20</width>
926+ <height>40</height>
927+ </size>
928+ </property>
929+ </spacer>
930+ </item>
931+ </layout>
932+ </item>
933+ <item>
934+ <spacer name="horizontalSpacer">
935+ <property name="orientation">
936+ <enum>Qt::Horizontal</enum>
937+ </property>
938+ <property name="sizeType">
939+ <enum>QSizePolicy::Fixed</enum>
940+ </property>
941+ <property name="sizeHint" stdset="0">
942+ <size>
943+ <width>40</width>
944+ <height>20</height>
945+ </size>
946+ </property>
947+ </spacer>
948+ </item>
949+ </layout>
950+ </item>
951+ </layout>
952+ </widget>
953+ <customwidgets>
954+ <customwidget>
955+ <class>QWebView</class>
956+ <extends>QWidget</extends>
957+ <header>QtWebKit/QWebView</header>
958+ </customwidget>
959+ </customwidgets>
960+ <resources/>
961+ <connections/>
962+</ui>
963
964=== modified file 'setup.py'
965--- setup.py 2011-03-22 22:32:28 +0000
966+++ setup.py 2011-03-30 14:55:45 +0000
967@@ -2,6 +2,7 @@
968 # setup.py - Build system for Ubuntu SSO Client package
969 #
970 # Author: Natalia B. Bidart <natalia.bidart@canonical.com>
971+# Author: Manuel de la Pena <manuel@canonical.com>
972 #
973 # Copyright 2010 Canonical Ltd.
974 #
975@@ -18,6 +19,7 @@
976 # with this program. If not, see <http://www.gnu.org/licenses/>.
977 """setup.py"""
978
979+import cgi
980 import os
981 import sys
982
983@@ -31,17 +33,19 @@
984 assert DistUtilsExtra.auto.__version__ >= '2.18', \
985 'needs DistUtilsExtra.auto >= 2.18'
986
987+from distutils import log
988+from distutils.command import clean
989 from distutils.spawn import find_executable
990-from distutils.command import clean
991
992 # Defining variables for various rules here, similar to a Makefile.am
993-CLEANFILES = ['data/com.ubuntu.sso.service', 'po/ubuntu-sso-client.pot',
994+LINUX_CLEANFILES = ['data/com.ubuntu.sso.service', 'po/ubuntu-sso-client.pot',
995 'MANIFEST']
996
997
998+# pylint: disable=W0511
999 # This needs some serious cleanup
1000-class SSOBuild(build_extra.build_extra):
1001- """Class to build the extra files."""
1002+class SSOLinuxBuild(build_extra.build_extra):
1003+ """Build the extra files required on Linux.."""
1004
1005 description = 'build extra files needed by ubuntu-sso-client'
1006
1007@@ -69,14 +73,14 @@
1008 build_extra.build_extra.run(self)
1009
1010
1011-class SSOClean(clean.clean):
1012+class SSOLinuxClean(clean.clean):
1013 """Class to clean up after the build."""
1014
1015 description = 'Clean up built files.'
1016
1017 def run(self):
1018 """Clean up the built files."""
1019- for built_file in CLEANFILES:
1020+ for built_file in LINUX_CLEANFILES:
1021 if os.path.exists(built_file):
1022 os.unlink(built_file)
1023
1024@@ -84,6 +88,7 @@
1025 clean.clean.run(self)
1026
1027
1028+<<<<<<< TREE
1029 DistUtilsExtra.auto.setup(
1030 name='ubuntu-sso-client',
1031 version='1.1.12',
1032@@ -104,3 +109,247 @@
1033 cmdclass={
1034 'build' : SSOBuild,
1035 'clean' : SSOClean})
1036+=======
1037+class SSOWindowsBuild(build_extra.build_extra):
1038+ """Build PyQt (.ui) files and resources."""
1039+
1040+ description = "build PyQt GUIs (.ui) and resources (.qrc)"
1041+
1042+ def compile_ui(self, ui_file, py_file=None):
1043+ """Compile the .ui files to python modules."""
1044+ # Search for pyuic4 in python bin dir, then in the $Path.
1045+ if py_file is None:
1046+ # go from the ui_file in the data folder to the
1047+ # python file in the qt moodule
1048+ py_file = os.path.split(ui_file)[1]
1049+ py_file = os.path.splitext(py_file)[0] + '_ui.py'
1050+ py_file = os.path.join('ubuntu_sso', 'qt', py_file)
1051+ # we indeed want to catch Exception, is ugle but w need it
1052+ # pylint: disable=W0703
1053+ try:
1054+ # import the uic compiler from pyqt and generate the .py files
1055+ # something similar could be done with pyside but that is left
1056+ # as an exercise for the reader.
1057+ from PyQt4 import uic
1058+ fp = open(py_file, 'w')
1059+ uic.compileUi(ui_file, fp)
1060+ fp.close()
1061+ log.info('Compiled %s into %s', ui_file, py_file)
1062+ except Exception, e:
1063+ self.warn('Unable to compile user interface %s: %s', py_file, e)
1064+ if not os.path.exists(py_file) or not file(py_file).read():
1065+ raise SystemExit(1)
1066+ return
1067+ # pylint: enable=W0703
1068+
1069+ def compile_rc(self, qrc_file, py_file=None):
1070+ """Compile the resources that will be included with the project."""
1071+ import PyQt4
1072+ # Search for pyuic4 in python bin dir, then in the $Path.
1073+ if py_file is None:
1074+ py_file = os.path.split(qrc_file)[1]
1075+ py_file = os.path.splitext(py_file)[0] + '_rc.py'
1076+ py_file = os.path.join('ubuntu_sso', 'qt', py_file)
1077+ path = os.getenv('PATH')
1078+ os.putenv('PATH', path + ';' + os.path.join(
1079+ os.path.dirname(PyQt4.__file__),'bin'))
1080+ if os.system('pyrcc4 "%s" -o "%s"' % (qrc_file, py_file)) > 0:
1081+ self.warn('Unable to generate python module %s '
1082+ + 'for resource file %s', py_file, qrc_file)
1083+ if not os.path.exists(py_file) or not file(py_file).read():
1084+ raise SystemExit(1)
1085+ else:
1086+ log.info('compiled %s into %s' % (qrc_file, py_file))
1087+ os.putenv('PATH', path)
1088+
1089+ def _generate_qrc(self, qrc_file, srcfiles, prefix):
1090+ """Generate the qrc file for the given src files."""
1091+ basedir = os.path.dirname(qrc_file)
1092+ f = open(qrc_file, 'w')
1093+ try:
1094+ f.write('<!DOCTYPE RCC><RCC version="1.0">\n')
1095+ f.write(' <qresource prefix="%s">\n' % cgi.escape(prefix))
1096+ for e in srcfiles:
1097+ relpath = e[len(basedir) + 1:]
1098+ f.write(' <file>%s</file>\n'
1099+ % cgi.escape(relpath.replace(os.path.sep, '/')))
1100+ f.write(' </qresource>\n')
1101+ f.write('</RCC>\n')
1102+ finally:
1103+ f.close()
1104+
1105+ def build_rc(self, py_file, basedir, prefix='/'):
1106+ """Generate compiled resource including any files under basedir"""
1107+ # For details, see http://doc.qt.nokia.com/latest/resources.html
1108+ qrc_file = os.path.join(basedir, '%s.qrc' % os.path.basename(basedir))
1109+ srcfiles = [os.path.join(root, e)
1110+ for root, _dirs, files in os.walk(basedir) for e in files]
1111+ # NOTE: Here we cannot detect deleted files. In such case, we need
1112+ # to remove .qrc manually.
1113+ try:
1114+ self._generate_qrc(qrc_file, srcfiles, prefix)
1115+ self.compile_rc(qrc_file, py_file)
1116+ finally:
1117+ os.unlink(qrc_file)
1118+
1119+ def run(self):
1120+ """Execute the command."""
1121+ self._wrapuic()
1122+ basepath = os.path.join('data', 'qt')
1123+ # TODO: build the resource files so that we can include them
1124+ #self.build_rc(os.path.join(basepath, 'icons_rc.py'),
1125+ # os.path.join(os.path.dirname(__file__), 'icons'),
1126+ # '/icons')
1127+ for dirpath, _, filenames in os.walk(basepath):
1128+ for filename in filenames:
1129+ if filename.endswith('.ui'):
1130+ self.compile_ui(os.path.join(dirpath, filename))
1131+ elif filename.endswith('.qrc'):
1132+ self.compile_rc(os.path.join(dirpath, filename))
1133+
1134+ # pylint: disable=E1002
1135+ _wrappeduic = False
1136+ @classmethod
1137+ def _wrapuic(cls):
1138+ """Wrap uic to use gettext's _() in place of tr()"""
1139+ if cls._wrappeduic:
1140+ return
1141+
1142+ from PyQt4.uic.Compiler import compiler, qtproxies, indenter
1143+
1144+ # pylint: disable=C0103
1145+ class _UICompiler(compiler.UICompiler):
1146+ """Speciallized compiler for qt .ui files."""
1147+ def createToplevelWidget(self, classname, widgetname):
1148+ o = indenter.getIndenter()
1149+ o.level = 0
1150+ o.write('from ubuntu_sso.utils.ui import _')
1151+ return super(_UICompiler, self).createToplevelWidget(classname,
1152+ widgetname)
1153+ compiler.UICompiler = _UICompiler
1154+
1155+ class _i18n_string(qtproxies.i18n_string):
1156+ """Provide a trnalated text."""
1157+
1158+ def __str__(self):
1159+ return "_('%s')" % self.string.encode('string-escape')
1160+
1161+ qtproxies.i18n_string = _i18n_string
1162+
1163+ cls._wrappeduic = True
1164+ # pylint: enable=C0103
1165+ # pylint: enable=E1002
1166+
1167+class SSOWindowsClean(clean.clean):
1168+ """Clean the files from a Windows build."""
1169+
1170+ description = 'Clean up built files.'
1171+
1172+ def run(self):
1173+ """Clean up the built files."""
1174+ # remove the generated ui files
1175+ for dirpath, _, filenames in os.walk(os.path.join('ubuntu_sso', 'qt')):
1176+ for current_file in filenames:
1177+ if current_file.endswith('_ui.py') or\
1178+ current_file.endswith('_rc.py'):
1179+ os.unlink(os.path.join(dirpath, current_file))
1180+
1181+# pylint: disable=W0621
1182+def setup_windows():
1183+ """Provide the required info to setup the project on windows."""
1184+ scripts = []
1185+ data_files = []
1186+ packages = ['ubuntu_sso', 'ubuntu_sso.qt', 'ubuntu_sso.utils',
1187+ 'ubuntu_sso.keyring', 'ubuntu_sso.networkstate',
1188+ 'ubuntu_sso.main']
1189+ extra = {}
1190+ cmdclass = {'build' : SSOWindowsBuild,
1191+ 'clean' : SSOWindowsClean}
1192+
1193+ # for PyQt, see http://www.py2exe.org/index.cgi/Py2exeAndPyQt
1194+ includes = ['sip',]
1195+
1196+ # Qt4 plugins, see http://stackoverflow.com/questions/2206406/
1197+ def qt4_plugins(subdir, *dlls):
1198+ """Add the required plugin so that we can package them."""
1199+ import PyQt4
1200+ pluginsdir = os.path.join(os.path.dirname(PyQt4.__file__), 'plugins')
1201+ return (subdir, [os.path.join(pluginsdir, subdir, e) for e in dlls])
1202+
1203+ data_files.append(qt4_plugins('imageformats', 'qico4.dll',
1204+ 'qsvg4.dll'))
1205+ # ModuleFinder can't handle runtime changes to __path__,
1206+ # but win32com uses them
1207+ try:
1208+ # py2exe 0.6.4 introduced a replacement modulefinder.
1209+ # This means we have to add package paths there,
1210+ # not to the built-in one. If this new modulefinder gets
1211+ # integrated into Python, then we might be able to revert
1212+ # this some day. If this doesn't work, try import modulefinder
1213+ # pylint: disable=F0401
1214+ try:
1215+ import py2exe.mf as modulefinder
1216+ except ImportError:
1217+ import modulefinder
1218+
1219+ import win32com
1220+ # pylint: enable=F0401
1221+ for package_path in win32com.__path__[1:]:
1222+ modulefinder.AddPackagePath("win32com", package_path)
1223+ for extra_mod in ["win32com.server" ,"win32com.client"]:
1224+ __import__(extra_mod)
1225+ module = sys.modules[extra_mod]
1226+ for module_path in module.__path__[1:]:
1227+ modulefinder.AddPackagePath(extra_mod, module_path)
1228+ except ImportError:
1229+ # no build path setup, no worries.
1230+ pass
1231+
1232+ extra['options'] = {
1233+ 'py2exe' : {
1234+ 'skip_archive' : 0,
1235+ 'includes' : includes,
1236+ 'optimize' : 1
1237+ }
1238+ }
1239+ return scripts, data_files, packages, extra, cmdclass
1240+
1241+def setup_linux():
1242+ """Provide the required infor to setup the project on linux."""
1243+ scripts = []
1244+ data_files = [('share/dbus-1/services', ['data/com.ubuntu.sso.service']),
1245+ ('lib/ubuntu-sso-client', ['bin/ubuntu-sso-login']),
1246+ ('share/ubuntu-sso-client/data', ['data/gtk/ui.glade'])]
1247+ packages = ['ubuntu_sso', 'ubuntu_sso.gtk', 'ubuntu_sso.utils',
1248+ 'ubuntu_sso.keyring', 'ubuntu_sso.networkstate',
1249+ 'ubuntu_sso.main']
1250+ extra = {}
1251+ cmdclass = {'build' : SSOLinuxBuild,
1252+ 'clean' : SSOLinuxClean}
1253+ return scripts, data_files, packages, extra, cmdclass
1254+
1255+if __name__ == "__main__":
1256+
1257+ # pylint: disable=C0103
1258+ scripts = data_files = packages = extra = cmdclass = None
1259+ if sys.platform == 'win32':
1260+ scripts, data_files, packages, extra, cmdclass = setup_windows()
1261+ else:
1262+ scripts, data_files, packages, extra, cmdclass = setup_linux()
1263+
1264+ DistUtilsExtra.auto.setup(
1265+ name='ubuntu-sso-client',
1266+ version='1.1.11',
1267+ license='GPL v3',
1268+ author='Natalia Bidart',
1269+ author_email='natalia.bidart@canonical.com',
1270+ description='Ubuntu Single Sign-On client',
1271+ long_description='Desktop service to allow applications to sign in' \
1272+ 'to Ubuntu services via SSO',
1273+ url='https://launchpad.net/ubuntu-sso-client',
1274+ scripts=scripts,
1275+ data_files = data_files,
1276+ packages=packages,
1277+ cmdclass=cmdclass,
1278+ **extra)
1279+>>>>>>> MERGE-SOURCE
1280
1281=== modified file 'ubuntu_sso/gtk/gui.py'
1282--- ubuntu_sso/gtk/gui.py 2011-03-16 01:36:07 +0000
1283+++ ubuntu_sso/gtk/gui.py 2011-03-30 14:55:45 +0000
1284@@ -29,9 +29,7 @@
1285 from functools import wraps
1286
1287 import dbus
1288-import gettext
1289 import gtk
1290-import xdg
1291
1292 from dbus.mainloop.glib import DBusGMainLoop
1293 from twisted.internet.defer import inlineCallbacks
1294@@ -39,15 +37,12 @@
1295 from ubuntu_sso import (DBUS_ACCOUNT_PATH, DBUS_BUS_NAME, DBUS_IFACE_USER_NAME,
1296 NO_OP)
1297 from ubuntu_sso.logger import setup_logging
1298-
1299+from ubuntu_sso.utils.ui import get_data_file, _
1300
1301 # Instance of 'UbuntuSSOClientGUI' has no 'yyy' member
1302 # pylint: disable=E1101
1303
1304
1305-_ = gettext.gettext
1306-gettext.textdomain('ubuntu-sso-client')
1307-
1308 DBusGMainLoop(set_as_default=True)
1309 logger = setup_logging('ubuntu_sso.gui')
1310
1311@@ -73,38 +68,6 @@
1312 WARNING_TEXT_COLOR = gtk.gdk.Color("red")
1313
1314
1315-def get_data_dir():
1316- """Build absolute path to the 'data' directory."""
1317- module = os.path.dirname(__file__)
1318- result = os.path.abspath(os.path.join(module, os.pardir,
1319- os.pardir, 'data'))
1320- logger.debug('get_data_file: trying to load from %r (exists? %s)',
1321- result, os.path.exists(result))
1322- if os.path.exists(result):
1323- logger.info('get_data_file: returning data dir located at %r.', result)
1324- return result
1325-
1326- # no local data dir, looking within system data dirs
1327- data_dirs = xdg.BaseDirectory.xdg_data_dirs
1328- for path in data_dirs:
1329- result = os.path.join(path, 'ubuntu-sso-client', 'data')
1330- result = os.path.abspath(result)
1331- logger.debug('get_data_file: trying to load from %r (exists? %s)',
1332- result, os.path.exists(result))
1333- if os.path.exists(result):
1334- logger.info('get_data_file: data dir located at %r.', result)
1335- return result
1336- else:
1337- msg = 'get_data_file: can not build a valid data path. Giving up.' \
1338- '__file__ is %r, data_dirs are %r'
1339- logger.error(msg, __file__, data_dirs)
1340-
1341-
1342-def get_data_file(filename):
1343- """Build absolute path to 'filename' within the 'data' directory."""
1344- return os.path.join(get_data_dir(), filename)
1345-
1346-
1347 def log_call(f):
1348 """Decorator to log call funtions."""
1349
1350@@ -265,7 +228,7 @@
1351 self.user_email = None
1352 self.user_password = None
1353
1354- ui_filename = get_data_file('ui.glade')
1355+ ui_filename = get_data_file('gtk', 'ui.glade')
1356 builder = gtk.Builder()
1357 builder.add_from_file(ui_filename)
1358 builder.connect_signals(self)
1359
1360=== added directory 'ubuntu_sso/qt'
1361=== added file 'ubuntu_sso/qt/__init__.py'
1362--- ubuntu_sso/qt/__init__.py 1970-01-01 00:00:00 +0000
1363+++ ubuntu_sso/qt/__init__.py 2011-03-30 14:55:45 +0000
1364@@ -0,0 +1,1 @@
1365+"""Qt Ui."""
1366
1367=== added file 'ubuntu_sso/qt/controllers.py'
1368--- ubuntu_sso/qt/controllers.py 1970-01-01 00:00:00 +0000
1369+++ ubuntu_sso/qt/controllers.py 2011-03-30 14:55:45 +0000
1370@@ -0,0 +1,260 @@
1371+# -*- coding: utf-8 -*-
1372+# Author: Manuel de la Pena <manuel@canonical.com>
1373+#
1374+# Copyright 2011 Canonical Ltd.
1375+#
1376+# This program is free software: you can redistribute it and/or modify it
1377+# under the terms of the GNU General Public License version 3, as published
1378+# by the Free Software Foundation.
1379+#
1380+# This program is distributed in the hope that it will be useful, but
1381+# WITHOUT ANY WARRANTY; without even the implied warranties of
1382+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1383+# PURPOSE. See the GNU General Public License for more details.
1384+#
1385+# You should have received a copy of the GNU General Public License along
1386+# with this program. If not, see <http://www.gnu.org/licenses/>.
1387+"""Controllers with the logic of the UI."""
1388+
1389+from PyQt4.QtCore import QUrl
1390+
1391+from ubuntu_sso.logger import setup_logging
1392+from ubuntu_sso.utils.ui import (
1393+ CAPTCHA_SOLUTION_ENTRY,
1394+ EMAIL1_ENTRY,
1395+ EMAIL2_ENTRY,
1396+ EXISTING_ACCOUNT_CHOICE_BUTTON,
1397+ FORGOTTEN_PASSWORD_BUTTON,
1398+ JOIN_HEADER_LABEL,
1399+ NAME_ENTRY,
1400+ PASSWORD1_ENTRY,
1401+ PASSWORD2_ENTRY,
1402+ PASSWORD_HELP,
1403+ SET_UP_ACCOUNT_CHOICE_BUTTON,
1404+ SET_UP_ACCOUNT_BUTTON,
1405+ SIGN_IN_BUTTON,
1406+ SURNAME_ENTRY,
1407+ TC_BUTTON,
1408+ VERIFY_EMAIL_TITLE,
1409+ VERIFY_EMAIL_CONTENT,
1410+ YES_TO_TC,
1411+ get_password_strength,
1412+ is_min_required_password)
1413+
1414+
1415+logger = setup_logging('ubuntu_sso.controllers')
1416+
1417+
1418+class ChooseSignInController(object):
1419+ """Controlled to the ChooseSignIn view/widget."""
1420+
1421+ def __init__(self, title='', existing_account_id=1,
1422+ new_account_id=2):
1423+ """Create a new instance to manage the view."""
1424+ self._title = title
1425+ self._existing_account_id = existing_account_id
1426+ self._new_account_id = new_account_id
1427+
1428+ # use an ugly name just so have a simlar api as found in PyQt
1429+ #pylint: disable=C0103
1430+ def setupUi(self, view):
1431+ """Perform the required actions to set up the ui."""
1432+ view.setTitle(self._title)
1433+ self._set_up_translated_strings(view)
1434+ self._connect_buttons(view)
1435+ #pylint: enable=C0103
1436+
1437+ def _set_up_translated_strings(self, view):
1438+ """Set the correct strings for the UI."""
1439+ view.existing_account_button.setText(EXISTING_ACCOUNT_CHOICE_BUTTON)
1440+ view.new_account_button.setText(SET_UP_ACCOUNT_CHOICE_BUTTON)
1441+
1442+ def _connect_buttons(self, view):
1443+ """Connect the buttons to the actions to perform."""
1444+ view.existing_account_button.clicked.connect(
1445+ lambda: self._set_next_existing(view))
1446+ view.new_account_button.clicked.connect(
1447+ lambda: self._set_next_new(view))
1448+
1449+ def _set_next_existing(self, view):
1450+ """Set the next id and fire signal."""
1451+ view.next = self._existing_account_id
1452+ view.wizard().next()
1453+
1454+ def _set_next_new(self, view):
1455+ """Set the next id and fire signal."""
1456+ view.next = self._new_account_id
1457+ view.wizard().next()
1458+
1459+
1460+class CurrentUserController(object):
1461+ """Controller used in the view that is used to allow the signin."""
1462+
1463+ def __init__(self, title=''):
1464+ """Create a new instance."""
1465+ self._title = title
1466+
1467+ def _set_translated_strings(self, view):
1468+ """Set the translated strings."""
1469+ logger.debug('Setting tranlated strings.')
1470+ view.email_edit.setPlaceholderText(EMAIL1_ENTRY)
1471+ view.password_edit.setPlaceholderText(PASSWORD1_ENTRY)
1472+ view.forgot_password_label.setText(FORGOTTEN_PASSWORD_BUTTON)
1473+ view.sign_in_button.setText(SIGN_IN_BUTTON)
1474+
1475+ # use an ugly name just so have a simlar api as found in PyQt
1476+ #pylint: disable=C0103
1477+ def setupUi(self, view):
1478+ """Setup the view."""
1479+ view.setTitle(self._title)
1480+ self._set_translated_strings(view)
1481+ #pylint: enable=C0103
1482+
1483+
1484+class SetUpAccountController(object):
1485+ """Conroller for the setup account view."""
1486+
1487+ def __init__(self, tos_id=0, validation_id=1, app_name='',
1488+ help_message=''):
1489+ """Create a new instance."""
1490+ self._tos_id = tos_id
1491+ self._validation_id = validation_id
1492+ self._app_name = app_name
1493+ self._help_message = help_message
1494+
1495+ def _set_translated_strings(self, view):
1496+ """Set the different gettext translated strings."""
1497+ logger.debug('Setting tranlated strings.')
1498+ # set the translated string
1499+ view.name_edit.setPlaceholderText(NAME_ENTRY)
1500+ view.last_name_edit.setPlaceholderText(SURNAME_ENTRY)
1501+ view.email_edit.setPlaceholderText(EMAIL1_ENTRY)
1502+ view.confirm_email_edit.setPlaceholderText(EMAIL2_ENTRY)
1503+ view.password_edit.setPlaceholderText(PASSWORD1_ENTRY)
1504+ view.confirm_password_edit.setPlaceholderText(PASSWORD2_ENTRY)
1505+ view.password_info_label.setText(PASSWORD_HELP)
1506+ view.captcha_solution_edit.setPlaceholderText(CAPTCHA_SOLUTION_ENTRY)
1507+ view.terms_and_conditions_check_box.setText(YES_TO_TC)
1508+ view.terms_and_conditions_button.setText(TC_BUTTON)
1509+ view.set_up_button.setText(SET_UP_ACCOUNT_BUTTON)
1510+
1511+ def _set_line_edits_validations(self, view):
1512+ """Set the validations to be performed on the edits."""
1513+ view.set_line_edit_validation_rule(view.email_edit,
1514+ self.is_correct_email)
1515+ # set the validation rule for the email confirmation
1516+ view.set_line_edit_validation_rule(view.confirm_email_edit,
1517+ lambda x: self.is_correct_email_confirmation(x, view))
1518+ # connect the changed text of the password to trigger a changed text
1519+ # in the confirm so that the validation is redone
1520+ view.email_edit.textChanged.connect(
1521+ view.confirm_email_edit.textChanged.emit)
1522+ view.set_line_edit_validation_rule(view.password_edit,
1523+ is_min_required_password)
1524+ view.set_line_edit_validation_rule(view.confirm_password_edit,
1525+ lambda x: self.is_correct_password_confirmation(x, view))
1526+ # same as the above case, lets connect a signal to a signal
1527+ view.password_edit.textChanged.connect(
1528+ view.confirm_password_edit.textChanged.emit)
1529+
1530+ def _connect_ui_elements(self, view):
1531+ """Set the connection of signals."""
1532+ view.terms_and_conditions_button.clicked.connect(
1533+ lambda: self.set_next_tos(view))
1534+ view.set_up_button.clicked.connect(lambda: self.set_next_validation(
1535+ view))
1536+ view.password_edit.textChanged.connect(
1537+ lambda x: self.update_password_strength(x, view))
1538+ view.terms_and_conditions_check_box.stateChanged.connect(
1539+ view.set_up_button.setEnabled)
1540+
1541+ def _set_titles(self, view):
1542+ """Set the diff titles of the view."""
1543+ view.setTitle(JOIN_HEADER_LABEL % {'app_name': self._app_name})
1544+ view.setSubTitle(self._help_message)
1545+
1546+ def set_next_tos(self, view):
1547+ """Set the tos page as the next one."""
1548+ view.next = self._tos_id
1549+ view.wizard().next()
1550+
1551+ def set_next_validation(self, view):
1552+ """Set the validation as the enxt page."""
1553+ view.next = self._validation_id
1554+ view.wizard().next()
1555+
1556+ def update_password_strength(self, password, view):
1557+ """Callback used to update the password strenght UI code."""
1558+ # get the strengh and then according to it color the frames
1559+ strengh = get_password_strength(password)
1560+ logger.info('Password strengh is %s', strengh)
1561+ view.set_strengh_level(strengh, password)
1562+
1563+ def is_correct_email(self, email_address):
1564+ """Return if the email is correct."""
1565+ return '@' in email_address
1566+
1567+ def is_correct_email_confirmation(self, email_address, view):
1568+ """Return that the email is the same."""
1569+ return view.email_edit.text() == email_address
1570+
1571+ def is_correct_password_confirmation(self, password, view):
1572+ """Return that the passwords are correct."""
1573+ return view.password_edit.text() == password
1574+
1575+ #pylint: disable=C0103
1576+ def setupUi(self, view):
1577+ """Setup the view."""
1578+ self._set_titles(view)
1579+ self._set_translated_strings(view)
1580+ self._set_line_edits_validations(view)
1581+ self._connect_ui_elements(view)
1582+ #pylint: enable=C0103
1583+
1584+
1585+class TosController(object):
1586+ """Controller used for the tos page."""
1587+
1588+ def __init__(self, title='', subtitle='', tos_url='http://www.ubuntu.com'):
1589+ """Create a new instance."""
1590+ self._title = title
1591+ self._subtitle = subtitle
1592+ self._tos_url = tos_url
1593+
1594+ #pylint: disable=C0103
1595+ def setupUi(self, view):
1596+ """Set up the ui."""
1597+ view.setTitle(self._title)
1598+ view.setSubTitle(self._subtitle)
1599+ # load the tos page
1600+ view.webkit.load(QUrl(self._tos_url))
1601+ #pylint: enable=C0103
1602+
1603+
1604+class EmailVerificationController(object):
1605+ """Controller used for the verification page."""
1606+
1607+ def _set_translated_strings(self, view):
1608+ """Set the trnaslated strings."""
1609+ view.verification_code_edit.setPlaceholderText(VERIFY_EMAIL_TITLE)
1610+
1611+ def _connect_ui_elements(self, view):
1612+ """Set the connection of signals."""
1613+ view.next_button.clicked.connect(lambda: self.next_page(view))
1614+
1615+ def _set_titles(self, view):
1616+ """Set the different titles."""
1617+ view.setTitle(VERIFY_EMAIL_TITLE)
1618+ view.setSubTitle(VERIFY_EMAIL_CONTENT)
1619+
1620+ #pylint: disable=C0103
1621+ def setupUi(self, view):
1622+ """Setup the view."""
1623+ self._set_titles(view)
1624+ self._set_translated_strings(view)
1625+ self._connect_ui_elements(view)
1626+ #pylint: enable=C0103
1627+
1628+ def next_page(self, view):
1629+ """Call the next action."""
1630+ view.wizard().next()
1631
1632=== added file 'ubuntu_sso/qt/gui.py'
1633--- ubuntu_sso/qt/gui.py 1970-01-01 00:00:00 +0000
1634+++ ubuntu_sso/qt/gui.py 2011-03-30 14:55:45 +0000
1635@@ -0,0 +1,461 @@
1636+# -*- coding: utf-8 -*-
1637+# Author: Manuel de la Pena <manuel@canonical.com>
1638+#
1639+# Copyright 2011 Canonical Ltd.
1640+#
1641+# This program is free software: you can redistribute it and/or modify it
1642+# under the terms of the GNU General Public License version 3, as published
1643+# by the Free Software Foundation.
1644+#
1645+# This program is distributed in the hope that it will be useful, but
1646+# WITHOUT ANY WARRANTY; without even the implied warranties of
1647+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1648+# PURPOSE. See the GNU General Public License for more details.
1649+#
1650+# You should have received a copy of the GNU General Public License along
1651+# with this program. If not, see <http://www.gnu.org/licenses/>.
1652+"""Qt implementation of the UI."""
1653+
1654+from PyQt4 import Qt
1655+from PyQt4.QtCore import QObject, SIGNAL
1656+from PyQt4.QtGui import (
1657+ QApplication,
1658+ QColor,
1659+ QCursor,
1660+ QHBoxLayout,
1661+ QPalette,
1662+ QPixmap,
1663+ QPushButton,
1664+ QStyle,
1665+ QWizard,
1666+ QWizardPage,
1667+ QGraphicsScene)
1668+
1669+from ubuntu_sso.logger import setup_logging
1670+# pylint: disable=F0401,E0611
1671+from ubuntu_sso.qt.choose_sign_in_ui import Ui_ChooseSingInPage
1672+from ubuntu_sso.qt.current_user_sign_in_ui import Ui_CurrentUserSignInPage
1673+from ubuntu_sso.qt.email_verification_ui import Ui_EmailVerificationPage
1674+from ubuntu_sso.qt.setup_account_ui import Ui_SetUpAccountPage
1675+from ubuntu_sso.qt.terms_and_conditions_ui import Ui_TosPage
1676+# pylint: enable=F0401,E0611
1677+from ubuntu_sso.qt.controllers import (
1678+ ChooseSignInController,
1679+ CurrentUserController,
1680+ EmailVerificationController,
1681+ SetUpAccountController,
1682+ TosController)
1683+
1684+
1685+logger = setup_logging('ubuntu_sso.gui')
1686+
1687+
1688+class ChooseSignInPage(QWizardPage):
1689+ """Widget that allows the user to choose how to sign in."""
1690+
1691+ def __init__(self, ui, controller, parent=None):
1692+ """Create a new widget to be used."""
1693+ QWizardPage.__init__(self, parent)
1694+ self.ui = ui
1695+ self.controller = controller
1696+ ui.setupUi(self)
1697+ controller.setupUi(self)
1698+ self.next = -1
1699+
1700+ # pylint: disable=C0103
1701+ def nextId(self):
1702+ """Provide the next id."""
1703+ return self.next
1704+ # pylint: enable=C0103
1705+
1706+ # allow to access to the different useful children
1707+ @property
1708+ def existing_account_button(self):
1709+ """Return the button used to sign in using an existing account."""
1710+ return self.ui.existing_account_button
1711+
1712+ @property
1713+ def new_account_button(self):
1714+ """Return the button used to sign in with a new account."""
1715+ return self.ui.setup_account_button
1716+
1717+
1718+class CurrentUserSignInPage(QWizardPage):
1719+ """Widget that allows to get the data of user to sign in."""
1720+
1721+ def __init__(self, ui, controller, parent=None):
1722+ """Create a new widget to be used."""
1723+ QWizardPage.__init__(self, parent)
1724+ self.ui = ui
1725+ self.controller = controller
1726+ self.ui.setupUi(self)
1727+ self.controller.setupUi(self)
1728+
1729+ # allow to access to the different useful data
1730+ @property
1731+ def email(self):
1732+ """Return the email data in the edit field."""
1733+ return str(self.ui.email_edit.text())
1734+
1735+ @property
1736+ def email_edit(self):
1737+ """Return the email edit that is used in the view."""
1738+ return self.ui.email_edit
1739+
1740+ @property
1741+ def password(self):
1742+ """Return the password data in the edit field."""
1743+ return str(self.ui.password_edit.text())
1744+
1745+ @property
1746+ def password_edit(self):
1747+ """Return the password edit used in the view."""
1748+ return self.ui.password_edit
1749+
1750+ @property
1751+ def forgot_password_label(self):
1752+ """Return the forgot password label."""
1753+ return self.ui.forgot_label
1754+
1755+ @property
1756+ def sign_in_button(self):
1757+ """Return the sign in button."""
1758+ return self.ui.sign_in_button
1759+
1760+
1761+class EmailVerificationPage(QWizardPage):
1762+ """Widget used to input the email verification code."""
1763+
1764+ def __init__(self, ui, controller, parent=None):
1765+ """Create a new widget to be used."""
1766+ QWizardPage.__init__(self, parent)
1767+ self.ui = ui
1768+ self.ui.setupUi(self)
1769+ self.controller = controller
1770+ self.controller.setupUi(self)
1771+
1772+ @property
1773+ def verification_code(self):
1774+ """Return the content of the verification code edit."""
1775+ return str(self.ui.verification_code_edit)
1776+
1777+ @property
1778+ def verification_code_edit(self):
1779+ """Return the edit used for the verification code."""
1780+ return self.ui.verification_code_edit
1781+
1782+ @property
1783+ def next_button(self):
1784+ """Return the button that move to the next stage."""
1785+ return self.ui.next_button
1786+
1787+
1788+class TosPage(QWizardPage):
1789+ """Widget used to show the tos."""
1790+
1791+ def __init__(self, ui, controller, parent=None):
1792+ """Create a new instance."""
1793+ QWizardPage.__init__(self, parent)
1794+ self.ui = ui
1795+ self.ui.setupUi(self)
1796+ self.controller = controller
1797+ self.controller.setupUi(self)
1798+
1799+ # pylint: disable=C0103
1800+ def nextId(self):
1801+ """Return the next page id."""
1802+ return -1
1803+ # pylint: enable=C0103
1804+
1805+ @property
1806+ def webkit(self):
1807+ """Return the webkit widget used to load the terms."""
1808+ return self.ui.terms_webkit
1809+
1810+
1811+class EnhancedLineEdit(object):
1812+ """Represents and enhanced lineedit.
1813+
1814+ This class works on an already added lineedit to the widget so
1815+ that we are just adding extra items to it.
1816+ """
1817+
1818+ def __init__(self, line_edit, valid_cb=lambda x: False):
1819+ """Create an instance."""
1820+ self._line_edit = line_edit
1821+ layout = QHBoxLayout(self._line_edit)
1822+ layout.setMargin(0)
1823+ self._line_edit.setLayout(layout)
1824+ self.valid_cb = valid_cb
1825+ layout.addStretch()
1826+ self.clear_button = QPushButton(self._line_edit)
1827+ layout.addWidget(self.clear_button)
1828+ self.clear_button.setMinimumSize(16, 16)
1829+ self.clear_button.setVisible(False)
1830+ self.clear_button.setFlat(True)
1831+ self.clear_button.setCursor(QCursor(0))
1832+ self.clear_button.setIcon(QApplication.style().standardIcon(
1833+ QStyle.SP_MessageBoxWarning))
1834+ # connect the change of text to the cation that will check if the
1835+ # text is valid and if the icon should be shown.
1836+ QObject.connect(self._line_edit, SIGNAL('textChanged(QString)'),
1837+ self.show_button)
1838+
1839+ def show_button(self, string):
1840+ """Decide if we show the button or not."""
1841+ if not self.valid_cb(string):
1842+ self.clear_button.setVisible(True)
1843+ else:
1844+ self.clear_button.setVisible(False)
1845+
1846+
1847+class SetupAccountPage(QWizardPage):
1848+ """Widget used to create a new account."""
1849+
1850+ def __init__(self, ui, controller, parent=None):
1851+ """Create a new widget to be used."""
1852+ QWizardPage.__init__(self, parent)
1853+ self._enhanced_edits = {}
1854+ self.ui = ui
1855+ self.ui.setupUi(self)
1856+ # palettes that will be used to set the colors of the password strengh
1857+ original_palette = self.ui.strenght_frame.palette()
1858+ self._original_color = original_palette.background().color()
1859+ self._weak_color = QColor(220, 20, 60)
1860+ self._medium_color = QColor(255, 215, 0)
1861+ self._strong_color = QColor(50, 205, 50)
1862+ self.controller = controller
1863+ self.controller.setupUi(self)
1864+ self.next = -1
1865+ # create the scene to be used to show the captcha
1866+ self.image = None
1867+ self.image_file = None
1868+ self.scene = QGraphicsScene(self.ui.captcha_view)
1869+ #self.scene.setSceneRect(self.ui._captchaView.rect())
1870+ #self.ui._captchaView.setScene(self.scene)
1871+
1872+ def set_strengh_level(self, level, password):
1873+ """Set the strengh level colors."""
1874+ if password != '':
1875+ if level <= 1:
1876+ # low password
1877+ self._set_frame_color(self.ui.weak_frame, self._weak_color)
1878+ self._set_frame_color(self.ui.medium_frame,
1879+ self._original_color)
1880+ self._set_frame_color(self.ui.strong_frame,
1881+ self._original_color)
1882+ elif level <= 3:
1883+ # medium password
1884+ self._set_frame_color(self.ui.weak_frame, self._medium_color)
1885+ self._set_frame_color(self.ui.medium_frame, self._medium_color)
1886+ self._set_frame_color(self.ui.strong_frame,
1887+ self._original_color)
1888+ else:
1889+ # nice!
1890+ self._set_frame_color(self.ui.weak_frame, self._strong_color)
1891+ self._set_frame_color(self.ui.medium_frame, self._strong_color)
1892+ self._set_frame_color(self.ui.strong_frame, self._strong_color)
1893+ else:
1894+ # set it to the minimum
1895+ self._set_frame_color(self.ui.weak_frame, self._original_color)
1896+ self._set_frame_color(self.ui.medium_frame, self._original_color)
1897+ self._set_frame_color(self.ui.strong_frame, self._original_color)
1898+
1899+ def _set_frame_color(self, frame, color):
1900+ """Set the color of a frame to indicated a strenght."""
1901+ # change the color background for the given frame
1902+ palette = frame.palette()
1903+ palette.setColor(QPalette.Background, color)
1904+ frame.setPalette(palette)
1905+ frame.setAutoFillBackground(True)
1906+
1907+ # pylint: disable=C0103
1908+ def nextId(self):
1909+ """Return the next page id."""
1910+ return self.next
1911+ # pylint: enable=C0103
1912+
1913+ def set_line_edit_validation_rule(self, edit, cb):
1914+ """Set a new enhanced edit so that we can show an icon."""
1915+ if edit in self._enhanced_edits:
1916+ self._enhanced_edits[edit].valid_cb = cb
1917+ else:
1918+ # create a new enhanced edit
1919+ enhanced_edit = EnhancedLineEdit(edit, cb)
1920+ self._enhanced_edits[edit] = enhanced_edit
1921+
1922+ @property
1923+ def name(self):
1924+ """Return the name input."""
1925+ return str(self.ui.first_name_edit.text())
1926+
1927+ @property
1928+ def name_edit(self):
1929+ """Return the edit used for the name."""
1930+ return self.ui.first_name_edit
1931+
1932+ @property
1933+ def last_name(self):
1934+ """Return the last name input."""
1935+ return str(self.ui.last_name_edit.text())
1936+
1937+ @property
1938+ def last_name_edit(self):
1939+ """Return the edit used for the last name."""
1940+ return self.ui.last_name_edit
1941+
1942+ @property
1943+ def email(self):
1944+ """Return the email input."""
1945+ return str(self.ui.email_edit.text())
1946+
1947+ @property
1948+ def email_edit(self):
1949+ """Return the edit used for the email."""
1950+ return self.ui.email_edit
1951+
1952+ @property
1953+ def retyped_email(self):
1954+ """Return the retyped email."""
1955+ return str(self.ui.confirm_email_edit.text())
1956+
1957+ @property
1958+ def confirm_email_edit(self):
1959+ """Return the edit used for the retype of the email."""
1960+ return self.ui.confirm_email_edit
1961+
1962+ @property
1963+ def password_info_label(self):
1964+ """Return the password used to show the info of the label."""
1965+ return self.ui.password_info_label
1966+
1967+ @property
1968+ def password(self):
1969+ """Return the password data."""
1970+ return str(self.ui.password_edit.text())
1971+
1972+ @property
1973+ def password_edit(self):
1974+ """Return the edit used for the password."""
1975+ return self.ui.password_edit
1976+
1977+ @property
1978+ def retyped_password(self):
1979+ """Return the retyped password."""
1980+ return str(self.ui.confirm_password_edit.text())
1981+
1982+ @property
1983+ def confirm_password_edit(self):
1984+ """Return the edit used to confirm the password."""
1985+ return self.ui.confirm_password_edit
1986+
1987+ @property
1988+ def captcha_solution(self):
1989+ """Return the provided captcha solution."""
1990+ return str(self.ui.captcha_solution_edit.text())
1991+
1992+ @property
1993+ def captcha_solution_edit(self):
1994+ """Return the edit used for the captcha solution."""
1995+ return self.ui.captcha_solution_edit
1996+
1997+ def get_captcha_image(self):
1998+ """Return the path to the captcha image."""
1999+ return self.image_file
2000+
2001+ def set_captcha_image(self, file_path):
2002+ """Set the new image of the captcha."""
2003+ if self.image:
2004+ self.scene.removeItem(self.image)
2005+ self.image_file = file_path
2006+ pix = QPixmap(self.image_file)
2007+ self.image = self.scene.addPixmap(pix)
2008+
2009+ captcha_image = property(get_captcha_image, set_captcha_image)
2010+
2011+ @property
2012+ def terms_and_conditions_agreed(self):
2013+ """Return if the user agreed the terms."""
2014+ return self.ui.terms_checkbox.isChecked()
2015+
2016+ @property
2017+ def terms_and_conditions_check_box(self):
2018+ """Return the terms and codition item."""
2019+ return self.ui.terms_checkbox
2020+
2021+ @property
2022+ def terms_and_conditions_button(self):
2023+ """Return the terms and conditions button."""
2024+ return self.ui.terms_button
2025+
2026+ @property
2027+ def set_up_button(self):
2028+ """Return the set up button."""
2029+ return self.ui.set_up_button
2030+
2031+
2032+class UbuntuSSOWizard(QWizard):
2033+ """Wizard used to create or use sso."""
2034+
2035+ def __init__(self, parent=None, app_name='', tos_url='', help_text=''):
2036+ """Create a new wizard."""
2037+ QWizard.__init__(self, parent)
2038+ # set the diff pages of the QWizard
2039+ self.sign_in_controller = ChooseSignInController(title='Sign In',
2040+ existing_account_id=4,
2041+ new_account_id=1)
2042+ self.sign_in_page = ChooseSignInPage(Ui_ChooseSingInPage(),
2043+ self.sign_in_controller,
2044+ parent=self)
2045+ self.setup_controller = SetUpAccountController(tos_id=2,
2046+ validation_id=3,
2047+ app_name=app_name,
2048+ help_message='')
2049+ self.setup_account = SetupAccountPage(Ui_SetUpAccountPage(),
2050+ self.setup_controller,
2051+ parent=self)
2052+ self.tos = TosPage(Ui_TosPage(), TosController())
2053+ self.email_verification = EmailVerificationPage(
2054+ Ui_EmailVerificationPage(),
2055+ EmailVerificationController())
2056+ self.current_user_controller = CurrentUserController(title='Sign in')
2057+ self.current_user = CurrentUserSignInPage(Ui_CurrentUserSignInPage(),
2058+ self.current_user_controller,
2059+ parent=self)
2060+ for page in [self.sign_in_page, self.setup_account, self.tos,
2061+ self.email_verification, self.current_user]:
2062+ self.addPage(page)
2063+ # set the buttons layout to only have cancel and back since the next
2064+ # buttons are the ones used in the diff pages.
2065+ buttons_layout = []
2066+ buttons_layout.append(QWizard.Stretch)
2067+ buttons_layout.append(QWizard.BackButton)
2068+ buttons_layout.append(QWizard.CancelButton)
2069+ self.setButtonLayout(buttons_layout)
2070+ self.setWindowTitle(app_name)
2071+
2072+
2073+class UbuntuSSOWizardController(object):
2074+ """Performs the loginc of the sso wizard."""
2075+
2076+ def __init__(self, app_name, tc_url='', help_text='',
2077+ window_id=0, login_only=False):
2078+ """Create a new instance."""
2079+ # create a new wizard and show it
2080+ self.wizard = None
2081+
2082+
2083+class UbuntuSSOClientGUI(object):
2084+ """Ubuntu single sign-on GUI."""
2085+
2086+if __name__ == '__main__':
2087+ # pylint: disable=C0103
2088+ # show the ui
2089+ import sys
2090+
2091+ app = Qt.QApplication(sys.argv)
2092+
2093+ wizard = UbuntuSSOWizard()
2094+ wizard.show()
2095+ # Now we can start it.
2096+ app.exec_()
2097
2098=== added directory 'ubuntu_sso/qt/tests'
2099=== added file 'ubuntu_sso/qt/tests/__init__.py'
2100--- ubuntu_sso/qt/tests/__init__.py 1970-01-01 00:00:00 +0000
2101+++ ubuntu_sso/qt/tests/__init__.py 2011-03-30 14:55:45 +0000
2102@@ -0,0 +1,1 @@
2103+"""Test the Ui code."""
2104
2105=== added file 'ubuntu_sso/qt/tests/test_controllers.py'
2106--- ubuntu_sso/qt/tests/test_controllers.py 1970-01-01 00:00:00 +0000
2107+++ ubuntu_sso/qt/tests/test_controllers.py 2011-03-30 14:55:45 +0000
2108@@ -0,0 +1,318 @@
2109+# -*- coding: utf-8 -*-
2110+# Author: Manuel de la Pena <manuel@canonical.com>
2111+#
2112+# Copyright 2011 Canonical Ltd.
2113+#
2114+# This program is free software: you can redistribute it and/or modify it
2115+# under the terms of the GNU General Public License version 3, as published
2116+# by the Free Software Foundation.
2117+#
2118+# This program is distributed in the hope that it will be useful, but
2119+# WITHOUT ANY WARRANTY; without even the implied warranties of
2120+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2121+# PURPOSE. See the GNU General Public License for more details.
2122+#
2123+# You should have received a copy of the GNU General Public License along
2124+# with this program. If not, see <http://www.gnu.org/licenses/>.
2125+"""Test the ui controllers."""
2126+
2127+from mocker import ANY, MATCH, MockerTestCase
2128+
2129+from ubuntu_sso.qt.controllers import (
2130+ ChooseSignInController,
2131+ CurrentUserController,
2132+ EmailVerificationController,
2133+ SetUpAccountController,
2134+ TosController)
2135+from ubuntu_sso.utils.ui import (
2136+ CAPTCHA_SOLUTION_ENTRY,
2137+ EMAIL1_ENTRY,
2138+ EMAIL2_ENTRY,
2139+ EXISTING_ACCOUNT_CHOICE_BUTTON,
2140+ FORGOTTEN_PASSWORD_BUTTON,
2141+ JOIN_HEADER_LABEL,
2142+ NAME_ENTRY,
2143+ PASSWORD1_ENTRY,
2144+ PASSWORD2_ENTRY,
2145+ PASSWORD_HELP,
2146+ SET_UP_ACCOUNT_CHOICE_BUTTON,
2147+ SET_UP_ACCOUNT_BUTTON,
2148+ SIGN_IN_BUTTON,
2149+ SURNAME_ENTRY,
2150+ TC_BUTTON,
2151+ VERIFY_EMAIL_TITLE,
2152+ VERIFY_EMAIL_CONTENT,
2153+ YES_TO_TC,
2154+ is_min_required_password)
2155+
2156+#ignore the comon mocker issues with lint
2157+# pylint: disable=W0212,W0104
2158+
2159+
2160+class ChooseSignInControllerTestCase(MockerTestCase):
2161+ """Test the choose sign in controller."""
2162+
2163+ def setUp(self):
2164+ """Set tests."""
2165+ super(ChooseSignInControllerTestCase, self).setUp()
2166+ self.view = self.mocker.mock()
2167+ self.controller = ChooseSignInController()
2168+
2169+ def test_set_up_translated_strings(self):
2170+ """Ensure that the translations are used."""
2171+ self.view.existing_account_button.setText(
2172+ EXISTING_ACCOUNT_CHOICE_BUTTON)
2173+ self.view.new_account_button.setText(SET_UP_ACCOUNT_CHOICE_BUTTON)
2174+ self.mocker.replay()
2175+ self.controller._set_up_translated_strings(self.view)
2176+
2177+ def test_connect_buttons(self):
2178+ """Ensure that all the buttons are correcly connected."""
2179+ self.view.existing_account_button.clicked.connect(MATCH(callable))
2180+ self.view.new_account_button.clicked.connect(MATCH(callable))
2181+ self.mocker.replay()
2182+ self.controller._connect_buttons(self.view)
2183+
2184+ def test_set_next_existing(self):
2185+ """Test the execution of the callback."""
2186+ self.view.next = self.controller._existing_account_id
2187+ self.view.wizard().next()
2188+ self.mocker.replay()
2189+ self.controller._set_next_existing(self.view)
2190+
2191+ def test_set_next_new(self):
2192+ """Test the execution of the callback."""
2193+ self.view.next = self.controller._new_account_id
2194+ self.view.wizard().next()
2195+ self.mocker.replay()
2196+ self.controller._set_next_new(self.view)
2197+
2198+
2199+class CurrentUserControllerTestCase(MockerTestCase):
2200+ """Test the current user controller."""
2201+
2202+ def setUp(self):
2203+ """Setup tests."""
2204+ super(CurrentUserControllerTestCase, self).setUp()
2205+ self.view = self.mocker.mock()
2206+ self.controller = CurrentUserController(title='the title')
2207+
2208+ def test_setup_ui(self):
2209+ """test that the ui is correctly set up."""
2210+ self.view.setTitle(self.controller._title)
2211+ self.view.email_edit.setPlaceholderText(EMAIL1_ENTRY)
2212+ self.view.password_edit.setPlaceholderText(PASSWORD1_ENTRY)
2213+ self.view.forgot_password_label.setText(FORGOTTEN_PASSWORD_BUTTON)
2214+ self.view.sign_in_button.setText(SIGN_IN_BUTTON)
2215+ self.mocker.replay()
2216+ self.controller.setupUi(self.view)
2217+
2218+
2219+class SetUpAccountControllerTestCase(MockerTestCase):
2220+ """test the controller used to setup a new account."""
2221+
2222+ def setUp(self):
2223+ """Set the different tests."""
2224+ super(SetUpAccountControllerTestCase, self).setUp()
2225+ self.view = self.mocker.mock()
2226+ self.get_password_strength = self.mocker.replace(
2227+ 'ubuntu_sso.utils.ui.get_password_strength')
2228+ self.app_name = 'test'
2229+ self.help = 'help'
2230+ self.controller = SetUpAccountController(app_name=self.app_name,
2231+ help_message=self.help)
2232+
2233+ def test_set_translated_strings(self):
2234+ """Ensure all the strings are set."""
2235+ self.view.name_edit.setPlaceholderText(NAME_ENTRY)
2236+ self.view.last_name_edit.setPlaceholderText(SURNAME_ENTRY)
2237+ self.view.email_edit.setPlaceholderText(EMAIL1_ENTRY)
2238+ self.view.confirm_email_edit.setPlaceholderText(EMAIL2_ENTRY)
2239+ self.view.password_edit.setPlaceholderText(PASSWORD1_ENTRY)
2240+ self.view.confirm_password_edit.setPlaceholderText(PASSWORD2_ENTRY)
2241+ self.view.password_info_label.setText(PASSWORD_HELP)
2242+ self.view.captcha_solution_edit.setPlaceholderText(
2243+ CAPTCHA_SOLUTION_ENTRY)
2244+ self.view.terms_and_conditions_check_box.setText(YES_TO_TC)
2245+ self.view.terms_and_conditions_button.setText(TC_BUTTON)
2246+ self.view.set_up_button.setText(SET_UP_ACCOUNT_BUTTON)
2247+ self.mocker.replay()
2248+ self.controller._set_translated_strings(self.view)
2249+
2250+ def test_set_titles(self):
2251+ """Test how the different titles are set."""
2252+ self.view.setTitle(JOIN_HEADER_LABEL % {'app_name': self.app_name})
2253+ self.view.setSubTitle(self.help)
2254+ self.mocker.replay()
2255+ self.controller._set_titles(self.view)
2256+
2257+ def test_connect_ui_elements(self):
2258+ """Test that the ui elements are correctly connect."""
2259+ self.view.terms_and_conditions_button.clicked.connect(MATCH(callable))
2260+ self.view.set_up_button.clicked.connect(MATCH(callable))
2261+ self.view.password_edit.textChanged.connect(MATCH(callable))
2262+ self.view.set_up_button.setEnabled
2263+ self.mocker.result(lambda:None)
2264+ self.view.terms_and_conditions_check_box.stateChanged.connect(
2265+ MATCH(callable))
2266+ self.mocker.replay()
2267+ self.controller._connect_ui_elements(self.view)
2268+
2269+ def test_is_correct_password_confirmation_false(self):
2270+ """Test when the password is not correct."""
2271+ password = 'test'
2272+ self.view.password_edit.text()
2273+ self.mocker.result('other')
2274+ self.mocker.replay()
2275+ self.assertFalse(
2276+ self.controller.is_correct_password_confirmation(password,
2277+ self.view))
2278+
2279+ def test_is_correct_password_confirmation_true(self):
2280+ """Test when the password is correct."""
2281+ password = 'test'
2282+ self.view.password_edit.text()
2283+ self.mocker.result(password)
2284+ self.mocker.replay()
2285+ self.assertTrue(
2286+ self.controller.is_correct_password_confirmation(password,
2287+ self.view))
2288+
2289+ def test_is_correct_email_confirmation_false(self):
2290+ """Test when the emails are not the same."""
2291+ email_address = 'address'
2292+ self.view.email_edit.text()
2293+ self.mocker.result('other')
2294+ self.mocker.replay()
2295+ self.assertFalse(
2296+ self.controller.is_correct_email_confirmation(email_address,
2297+ self.view))
2298+
2299+ def test_is_correct_email_confirmation_true(self):
2300+ """Test when the emails are the same."""
2301+ email_address = 'address'
2302+ self.view.email_edit.text()
2303+ self.mocker.result(email_address)
2304+ self.mocker.replay()
2305+ self.assertTrue(
2306+ self.controller.is_correct_email_confirmation(email_address,
2307+ self.view))
2308+
2309+ def test_is_correct_email_true(self):
2310+ """Test when the email is correct."""
2311+ email = 'manuel@canonical.com'
2312+ self.mocker.replay()
2313+ self.assertTrue(self.controller.is_correct_email(email))
2314+
2315+ def test_is_correct_email_false(self):
2316+ """Test when the email is not correct."""
2317+ email = 'manuelcanonical.com'
2318+ self.mocker.replay()
2319+ self.assertFalse(self.controller.is_correct_email(email))
2320+
2321+ def test_set_next_tos(self):
2322+ """Test the callback."""
2323+ self.view.next = self.controller._tos_id
2324+ self.view.wizard().next()
2325+ self.mocker.replay()
2326+ self.controller.set_next_tos(self.view)
2327+
2328+ def test_set_next_validation(self):
2329+ """Test the callback."""
2330+ self.view.next = self.controller._validation_id
2331+ self.view.wizard().next()
2332+ self.mocker.replay()
2333+ self.controller.set_next_validation(self.view)
2334+
2335+ def test_update_password_strength(self):
2336+ """Test the callback."""
2337+ password = 'test'
2338+ strength = 4
2339+ self.get_password_strength(password)
2340+ self.mocker.result(strength)
2341+ self.view.set_strengh_level(strength, password)
2342+ self.mocker.replay()
2343+ self.controller.update_password_strength(password, self.view)
2344+
2345+ def test_set_line_edits_validations(self):
2346+ """Set the validations to be performed on the edits."""
2347+ self.view.email_edit
2348+ self.mocker.result(self.view)
2349+ self.view.set_line_edit_validation_rule(self.view,
2350+ self.controller.is_correct_email)
2351+ self.view.confirm_email_edit
2352+ self.mocker.result(self.view)
2353+ self.view.set_line_edit_validation_rule(self.view, MATCH(callable))
2354+ self.view.confirm_email_edit.textChanged.emit
2355+ self.mocker.result(lambda: None)
2356+ self.view.email_edit.textChanged.connect(MATCH(callable))
2357+ self.view.password_edit
2358+ self.mocker.result(self.view)
2359+ self.view.set_line_edit_validation_rule(self.view,
2360+ is_min_required_password)
2361+ self.view.confirm_password_edit
2362+ self.mocker.result(self.view)
2363+ self.view.set_line_edit_validation_rule(self.view, MATCH(callable))
2364+ self.view.confirm_password_edit.textChanged.emit
2365+ self.mocker.result(lambda: None)
2366+ self.view.password_edit.textChanged.connect(MATCH(callable))
2367+ self.mocker.replay()
2368+ self.controller._set_line_edits_validations(self.view)
2369+
2370+
2371+class TosControllerTestCase(MockerTestCase):
2372+ """Test the tos controller."""
2373+
2374+ def setUp(self):
2375+ """Set the tests."""
2376+ super(TosControllerTestCase, self).setUp()
2377+ self.view = self.mocker.mock()
2378+ self.title = 'title'
2379+ self.subtitle = 'sub'
2380+ self.url = 'url'
2381+ self.controller = TosController(title=self.title,
2382+ subtitle=self.subtitle,
2383+ tos_url=self.url)
2384+
2385+ def test_setup_ui(self):
2386+ """Test the set up of the ui."""
2387+ self.view.setTitle(self.title)
2388+ self.view.setSubTitle(self.subtitle)
2389+ self.view.webkit.load(ANY)
2390+ self.mocker.replay()
2391+ self.controller.setupUi(self.view)
2392+
2393+
2394+class EmailVerificationControllerTestCase(MockerTestCase):
2395+ """Test the controller."""
2396+
2397+ def setUp(self):
2398+ """Set tests."""
2399+ super(EmailVerificationControllerTestCase, self).setUp()
2400+ self.view = self.mocker.mock()
2401+ self.controller = EmailVerificationController()
2402+
2403+ def test_set_translated_strings(self):
2404+ """Test that strings are translated."""
2405+ self.view.verification_code_edit.setPlaceholderText(VERIFY_EMAIL_TITLE)
2406+ self.mocker.replay()
2407+ self.controller._set_translated_strings(self.view)
2408+
2409+ def test_connect_ui_elements(self):
2410+ """Set the ui connections."""
2411+ self.view.next_button.clicked.connect(MATCH(callable))
2412+ self.mocker.replay()
2413+ self.controller._connect_ui_elements(self.view)
2414+
2415+ def test_set_titles(self):
2416+ """Test that the titles are set."""
2417+ self.view.setTitle(VERIFY_EMAIL_TITLE)
2418+ self.view.setSubTitle(VERIFY_EMAIL_CONTENT)
2419+ self.mocker.replay()
2420+ self.controller._set_titles(self.view)
2421+
2422+ def test_next_page(self):
2423+ """Test the callback."""
2424+ self.view.wizard().next()
2425+ self.mocker.replay()
2426+ self.controller.next_page(self.view)
2427
2428=== added file 'ubuntu_sso/utils/tests/test_ui.py'
2429--- ubuntu_sso/utils/tests/test_ui.py 1970-01-01 00:00:00 +0000
2430+++ ubuntu_sso/utils/tests/test_ui.py 2011-03-30 14:55:45 +0000
2431@@ -0,0 +1,110 @@
2432+# -*- coding: utf-8 -*-
2433+# Author: Manuel de la Pena <manuel@canonical.com>
2434+#
2435+# Copyright 2011 Canonical Ltd.
2436+#
2437+# This program is free software: you can redistribute it and/or modify it
2438+# under the terms of the GNU General Public License version 3, as published
2439+# by the Free Software Foundation.
2440+#
2441+# This program is distributed in the hope that it will be useful, but
2442+# WITHOUT ANY WARRANTY; without even the implied warranties of
2443+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2444+# PURPOSE. See the GNU General Public License for more details.
2445+#
2446+# You should have received a copy of the GNU General Public License along
2447+# with this program. If not, see <http://www.gnu.org/licenses/>.
2448+"""Test the ui functions."""
2449+
2450+from unittest import TestCase
2451+
2452+from ubuntu_sso.utils.ui import (get_password_strength,
2453+ is_min_required_password)
2454+
2455+
2456+class GetPasswordStrengTestCase(TestCase):
2457+ """Test the function that returns the strengh of a password."""
2458+
2459+ def test_too_small_password(self):
2460+ """Test the points given to a very small password."""
2461+ password = 'abc'
2462+ self.assertEqual(1, get_password_strength(password))
2463+
2464+ def test_small_password(self):
2465+ """Test the points given to a small passwod 4 or more chars."""
2466+ password = 'testtwe'
2467+ self.assertEqual(0, get_password_strength(password))
2468+
2469+ def test_eight_chars_password(self):
2470+ """Test the points given to a normal 8 chars password."""
2471+ password = 'abcdabcd'
2472+ self.assertEqual(1, get_password_strength(password))
2473+
2474+ def test_eight_chars_and_num(self):
2475+ """Test the points given to a 8 chars password with a num."""
2476+ password = 'abcdabc8'
2477+ self.assertEqual(2, get_password_strength(password))
2478+
2479+ def test_eight_chars_low_and_cap(self):
2480+ """Test the points given to a 8 chars password with a capital."""
2481+ password = 'abcdabcD'
2482+ self.assertEqual(2, get_password_strength(password))
2483+
2484+ def test_eight_chars_low_canp_num(self):
2485+ """Test the points given to a 8 chars password with capitals & num."""
2486+ password = 'abcdab7D'
2487+ self.assertEqual(3, get_password_strength(password))
2488+
2489+ def test_eiqgh_chars_and_special(self):
2490+ """Test the points given to a 8 chars password with special chars."""
2491+ password = 'abcdabc*'
2492+ self.assertEqual(2, get_password_strength(password))
2493+
2494+ def test_long_password(self):
2495+ """Test the points goven to a long password."""
2496+ password = 'abcdabcdabcd'
2497+ self.assertEqual(2, get_password_strength(password))
2498+
2499+ def test_eleven_chars_and_num(self):
2500+ """Test the points of a loong password with a num."""
2501+ password = 'abcdabcdabcd99'
2502+ self.assertEqual(3, get_password_strength(password))
2503+
2504+ def test_eleven_chars_low_cap(self):
2505+ """Test the points of a long password with low and cap."""
2506+ password = 'abcdabcdabcdbABCD'
2507+ self.assertEqual(3, get_password_strength(password))
2508+
2509+ def test_eleven_num_low_cap(self):
2510+ """Test the points of a long password with num and diff cap."""
2511+ password = 'ABCDabcdacbd723'
2512+ self.assertEqual(4, get_password_strength(password))
2513+
2514+ def test_eleven_num_special(self):
2515+ """Test the point of a long password with a number and special char."""
2516+ password = 'abcdabcdabcd*9'
2517+ self.assertEqual(4, get_password_strength(password))
2518+
2519+
2520+class IsMinRequiredPasswordTestCase(TestCase):
2521+ """Test the fnction that returns if the password is the min required."""
2522+
2523+ def test_no_enough_chars(self):
2524+ """Test a password that does not have enough chars."""
2525+ password = 'Test8'
2526+ self.assertFalse(is_min_required_password(password))
2527+
2528+ def test_no_uppercase(self):
2529+ """Test a password that does not have an uppercase."""
2530+ password = 'longenoughtobeapassword8'
2531+ self.assertFalse(is_min_required_password(password))
2532+
2533+ def test_no_number(self):
2534+ """Test a password that does not have a number."""
2535+ password = 'longenoughtobeapassworD'
2536+ self.assertFalse(is_min_required_password(password))
2537+
2538+ def test_correct_password(self):
2539+ """Test a password that is correct."""
2540+ password = 'TodasLasPaswordPasan88'
2541+ self.assertTrue(is_min_required_password(password))
2542
2543=== added file 'ubuntu_sso/utils/ui.py'
2544--- ubuntu_sso/utils/ui.py 1970-01-01 00:00:00 +0000
2545+++ ubuntu_sso/utils/ui.py 2011-03-30 14:55:45 +0000
2546@@ -0,0 +1,168 @@
2547+# -*- coding: utf-8 -*-
2548+# Author: Manuel de la Pena <manuel@canonical.com>
2549+#
2550+# Copyright 2011 Canonical Ltd.
2551+#
2552+# This program is free software: you can redistribute it and/or modify it
2553+# under the terms of the GNU General Public License version 3, as published
2554+# by the Free Software Foundation.
2555+#
2556+# This program is distributed in the hope that it will be useful, but
2557+# WITHOUT ANY WARRANTY; without even the implied warranties of
2558+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2559+# PURPOSE. See the GNU General Public License for more details.
2560+#
2561+# You should have received a copy of the GNU General Public License along
2562+# with this program. If not, see <http://www.gnu.org/licenses/>.
2563+"""Utils to be used by the UI modules."""
2564+
2565+import os
2566+import re
2567+import xdg
2568+import gettext
2569+
2570+from ubuntu_sso.logger import setup_logging
2571+
2572+logger = setup_logging('ubuntu_sso.utils.ui')
2573+
2574+
2575+gettext.textdomain('ubuntu-sso-client')
2576+_ = gettext.gettext
2577+
2578+# all the text that is used in the gui
2579+CAPTCHA_SOLUTION_ENTRY = _('Type the characters above')
2580+CAPTCHA_LOAD_ERROR = _('There was a problem getting the captcha, '
2581+ 'reloading...')
2582+CONNECT_HELP_LABEL = _('To connect this computer to %(app_name)s ' \
2583+ 'enter your details below.')
2584+EMAIL1_ENTRY = _('Email address')
2585+EMAIL2_ENTRY = _('Re-type Email address')
2586+EMAIL_MISMATCH = _('The email addresses don\'t match, please double check '
2587+ 'and try entering them again.')
2588+EMAIL_INVALID = _('The email must be a valid email address.')
2589+EMAIL_TOKEN_ENTRY = _('Enter code verification here')
2590+ERROR = _('The process did not finish successfully.')
2591+EXISTING_ACCOUNT_CHOICE_BUTTON = _('Sign me in with my existing account')
2592+FIELD_REQUIRED = _('This field is required.')
2593+FORGOTTEN_PASSWORD_BUTTON = _('I\'ve forgotten my password')
2594+JOIN_HEADER_LABEL = _('Create %(app_name)s account')
2595+LOADING = _('Loading...')
2596+LOGIN_BUTTON_LABEL = _('Already have an account? Click here to sign in')
2597+LOGIN_EMAIL_ENTRY = _('Email address')
2598+LOGIN_HEADER_LABEL = _('Connect to %(app_name)s')
2599+LOGIN_PASSWORD_ENTRY = _('Password')
2600+NAME_ENTRY = _('Name')
2601+NEXT = _('Next')
2602+ONE_MOMENT_PLEASE = _('One moment please...')
2603+PASSWORD_CHANGED = _('Your password was successfully changed.')
2604+PASSWORD1_ENTRY = RESET_PASSWORD1_ENTRY = _('Password')
2605+PASSWORD2_ENTRY = RESET_PASSWORD2_ENTRY = _('Re-type Password')
2606+PASSWORD_HELP = _('The password must have a minimum of 8 characters and ' \
2607+ 'include one uppercase character and one number.')
2608+PASSWORD_MISMATCH = _('The passwords don\'t match, please double check ' \
2609+ 'and try entering them again.')
2610+PASSWORD_TOO_WEAK = _('The password is too weak.')
2611+REQUEST_PASSWORD_TOKEN_LABEL = _('To reset your %(app_name)s password,'
2612+ ' enter your email address below:')
2613+RESET_PASSWORD = _('Reset password')
2614+RESET_CODE_ENTRY = _('Reset code')
2615+RESET_EMAIL_ENTRY = _('Email address')
2616+SET_NEW_PASSWORD_LABEL = _('A password reset code has been sent to ' \
2617+ '%(email)s.\nPlease enter the code below ' \
2618+ 'along with your new password.')
2619+SET_UP_ACCOUNT_CHOICE_BUTTON = _('I don\'t have an account yet - sign me up')
2620+SET_UP_ACCOUNT_BUTTON = _('Set up Account')
2621+SIGN_IN_BUTTON = _('Sign In')
2622+SUCCESS = _('The process finished successfully. Congratulations!')
2623+SURNAME_ENTRY = _('Surname')
2624+TC_BUTTON = _('Show Terms & Conditions')
2625+TC_NOT_ACCEPTED = _('Agreeing to the Ubuntu One Terms & Conditions is ' \
2626+ 'required to subscribe.')
2627+UNKNOWN_ERROR = _('There was an error when trying to complete the ' \
2628+ 'process. Please check the information and try again.')
2629+VERIFY_EMAIL_TITLE = _('Enter verification code')
2630+VERIFY_EMAIL_CONTENT = _('Check %(email)s for an email from'
2631+ ' Ubuntu Single Sign On.'
2632+ ' This message contains a verification code.'
2633+ ' Enter the code in the field below and click OK'
2634+ ' to complete creating your %(app_name)s account')
2635+VERIFY_EMAIL_LABEL = ('<b>%s</b>\n\n' % VERIFY_EMAIL_TITLE +
2636+ VERIFY_EMAIL_CONTENT)
2637+YES_TO_TC = _('I agree with the %(app_name)s terms and conditions')
2638+YES_TO_UPDATES = _('Yes! Email me %(app_name)s tips and updates.')
2639+CAPTCHA_RELOAD_TOOLTIP = _('Reload')
2640+
2641+
2642+def get_data_dir():
2643+ """Build absolute path to the 'data' directory."""
2644+ module = os.path.dirname(__file__)
2645+ result = os.path.abspath(os.path.join(module, os.pardir,
2646+ os.pardir, 'data'))
2647+ logger.debug('get_data_file: trying to load from %r (exists? %s)',
2648+ result, os.path.exists(result))
2649+ if os.path.exists(result):
2650+ logger.info('get_data_file: returning data dir located at %r.', result)
2651+ return result
2652+
2653+ # no local data dir, looking within system data dirs
2654+ data_dirs = xdg.BaseDirectory.xdg_data_dirs
2655+ for path in data_dirs:
2656+ result = os.path.join(path, 'ubuntu-sso-client', 'data')
2657+ result = os.path.abspath(result)
2658+ logger.debug('get_data_file: trying to load from %r (exists? %s)',
2659+ result, os.path.exists(result))
2660+ if os.path.exists(result):
2661+ logger.info('get_data_file: data dir located at %r.', result)
2662+ return result
2663+ else:
2664+ msg = 'get_data_file: can not build a valid data path. Giving up.' \
2665+ '__file__ is %r, data_dirs are %r'
2666+ logger.error(msg, __file__, data_dirs)
2667+
2668+
2669+def get_data_file(*args):
2670+ """Build absolute path to the path within the 'data' directory."""
2671+ return os.path.join(get_data_dir(), *args)
2672+
2673+
2674+def get_password_strength(password):
2675+ """Return the strenght of the password.
2676+
2677+ This function returns the strenght of the password so that ui elements
2678+ can show the user how good his password is. The logic used is the
2679+ following:
2680+
2681+ * 1 extra point for 4 chars passwords
2682+ * 1 extra point for 8 chars passwords
2683+ * 1 extra point for more than 11 chars passwords.
2684+ * 1 extra point for passwords with at least one number.
2685+ * 1 extra point for passwords for lwer and capital chars.
2686+ * 1 extra point for passwords with a special char.
2687+
2688+ A passwords starts with 0 and the extra points are added accordingly.
2689+ """
2690+ score = 0
2691+ if len(password) < 1:
2692+ return 0
2693+ if len(password) < 4:
2694+ score = 1
2695+ if len(password) >= 8:
2696+ score += 1
2697+ if len(password) >= 11:
2698+ score += 1
2699+ if re.search('\d+', password):
2700+ score += 1
2701+ if re.search('[a-z]', password) and re.search('[A-Z]', password):
2702+ score += 1
2703+ if re.search('.[!,@,#,$,%,^,&,*,?,_,~,-,£,(,)]', password):
2704+ score += 1
2705+ return score
2706+
2707+
2708+def is_min_required_password(password):
2709+ """Return if the password meets the minimun requirements."""
2710+ if (len(password) < 8 or
2711+ re.search('[A-Z]', password) is None or
2712+ re.search('\d+', password) is None):
2713+ return False
2714+ return True

Subscribers

People subscribed via source and target branches