Merge lp:~mandel/ubuntu-sso-client/windows_ui_3 into lp:ubuntu-sso-client
- windows_ui_3
- Merge into trunk
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 |
Related bugs: |
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.
Commit message
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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> |
730 | +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> |
731 | +p, li { white-space: pre-wrap; } |
732 | +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> |
733 | +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If you can't read this then </span><a href="example.com"><span style=" text-decoration: underline; color:#0000ff;">refresh</span></a><span style=" font-size:8pt;"> this page</span></p></body></html></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 |