Merge lp:~canonical-isd-hackers/canonical-identity-provider/preferred-language into lp:canonical-identity-provider/release

Proposed by Łukasz Czyżykowski
Status: Superseded
Proposed branch: lp:~canonical-isd-hackers/canonical-identity-provider/preferred-language
Merge into: lp:canonical-identity-provider/release
Diff against target: 505 lines (+254/-12) (has conflicts)
18 files modified
identityprovider/branding.py (+18/-0)
identityprovider/context_processors.py (+16/-0)
identityprovider/media/ubuntu/styles.css (+37/-0)
identityprovider/middleware/useraccount.py (+4/-0)
identityprovider/models/account.py (+1/-0)
identityprovider/templates/account/confirm_new_email.html (+5/-0)
identityprovider/templates/launchpad/registration/login.html (+5/-0)
identityprovider/templates/registration/new_account.html (+1/-1)
identityprovider/templates/select_language.html (+32/-0)
identityprovider/templates/ubuntu/base.html (+8/-0)
identityprovider/templates/ubuntu/registration/login.html (+9/-0)
identityprovider/tests/test_language_selection.py (+15/-0)
identityprovider/tests/test_views_i18n.py (+25/-0)
identityprovider/tests/test_views_ui.py (+23/-7)
identityprovider/tests/utils.py (+8/-0)
identityprovider/urls.py (+4/-3)
identityprovider/views/i18n.py (+34/-0)
identityprovider/views/ui.py (+9/-1)
Text conflict in identityprovider/branding.py
Text conflict in identityprovider/templates/account/confirm_new_email.html
Text conflict in identityprovider/templates/launchpad/registration/login.html
Text conflict in identityprovider/templates/ubuntu/registration/login.html
To merge this branch: bzr merge lp:~canonical-isd-hackers/canonical-identity-provider/preferred-language
Reviewer Review Type Date Requested Status
Canonical ISD hackers Pending
Review via email: mp+25238@code.launchpad.net

This proposal has been superseded by a proposal from 2010-05-13.

Description of the change

Implemented language chooser

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'identityprovider/branding.py'
2--- identityprovider/branding.py 2010-05-13 10:17:21 +0000
3+++ identityprovider/branding.py 2010-05-13 15:51:29 +0000
4@@ -15,6 +15,7 @@
5 server_info = _('This is the Launchpad login service, built on OpenID. '
6 'The service enables you to use your Launchpad account '
7 'to log into OpenID-enabled sites around the Internet.')
8+<<<<<<< TREE
9 unknown_rp = _('You have used your Single Sign On ID to access a site '
10 'which is <em>not</em> recognised by Launchpad Login '
11 'Service:')
12@@ -25,6 +26,14 @@
13 'correctly and try again.')
14 oops_error = _('Sorry, something just went wrong in Launchpad Login '
15 'Service.')
16+=======
17+ log_in_to = _('Log in to Launchpad Login Service')
18+ not_found_error = _('There\'s no page with this address in the Launchpad '
19+ 'Login Service. Check that you entered the address '
20+ 'correctly and try again.')
21+ oops_error = _('Sorry, something just went wrong in Launchpad Login '
22+ 'Service.')
23+>>>>>>> MERGE-SOURCE
24
25
26 class Ubuntu(object):
27@@ -37,6 +46,7 @@
28 server_info = _('This is the Ubuntu SSO service, built on OpenID. The '
29 'service enables you to use your Ubuntu SSO account to '
30 'log into various sites run by Canonical and Ubuntu.')
31+<<<<<<< TREE
32 unknown_rp = _('You have used your Single Sign On ID to access a site '
33 'which is <em>not</em> recognised by Ubuntu Single Sign '
34 'On:')
35@@ -47,5 +57,13 @@
36 'address correctly and try again.')
37 oops_error = _('Sorry, something just went wrong in Ubuntu Single Sign '
38 'On.')
39+=======
40+ log_in_to = _('Log in to Ubuntu Single Sign On')
41+ not_found_error = _('There\'s no page with this address in the Ubuntu '
42+ 'Single Sign On service. Check that you entered the '
43+ 'address correctly and try again.')
44+ oops_error = _('Sorry, something just went wrong in Ubuntu Single Sign '
45+ 'On.')
46+>>>>>>> MERGE-SOURCE
47
48 current_brand = globals()[settings.BRAND]()
49
50=== modified file 'identityprovider/context_processors.py'
51--- identityprovider/context_processors.py 2010-05-13 14:53:17 +0000
52+++ identityprovider/context_processors.py 2010-05-13 15:51:29 +0000
53@@ -1,3 +1,4 @@
54+# -*- coding: utf-8 -*-
55 # Copyright 2010 Canonical Ltd. This software is licensed under the
56 # GNU Affero General Public License version 3 (see the file LICENSE).
57
58@@ -13,6 +14,7 @@
59 from django.conf import settings
60
61 from identityprovider.branding import current_brand
62+from django.utils.translation import ugettext as _
63
64
65 def readonly(request):
66@@ -24,3 +26,17 @@
67 'template_dir': current_brand.template_dir,
68 'brand': current_brand,
69 }
70+
71+language_names = {'es': u'Español',
72+ 'es-ar': u'Español de Argentina',
73+ 'en': u'English',
74+ 'hu': u'Magyar',
75+ 'zh-cn': u'简体中文',
76+ 'pl': u'Polski',
77+ 'de': u'Deutsch',
78+ }
79+
80+supported = [(x, language_names[x]) for x in settings.SUPPORTED_LANGUAGES]
81+
82+def i18n(request):
83+ return {'supported_languages': supported}
84
85=== renamed directory 'identityprovider/locale/es_AR' => 'identityprovider/locale/es'
86=== added directory 'identityprovider/media/flags'
87=== added file 'identityprovider/media/flags/de.png'
88Binary files identityprovider/media/flags/de.png 1970-01-01 00:00:00 +0000 and identityprovider/media/flags/de.png 2010-05-13 15:51:29 +0000 differ
89=== added file 'identityprovider/media/flags/en.png'
90Binary files identityprovider/media/flags/en.png 1970-01-01 00:00:00 +0000 and identityprovider/media/flags/en.png 2010-05-13 15:51:29 +0000 differ
91=== added file 'identityprovider/media/flags/es-ar.png'
92Binary files identityprovider/media/flags/es-ar.png 1970-01-01 00:00:00 +0000 and identityprovider/media/flags/es-ar.png 2010-05-13 15:51:29 +0000 differ
93=== added file 'identityprovider/media/flags/es.png'
94Binary files identityprovider/media/flags/es.png 1970-01-01 00:00:00 +0000 and identityprovider/media/flags/es.png 2010-05-13 15:51:29 +0000 differ
95=== added file 'identityprovider/media/flags/hu.png'
96Binary files identityprovider/media/flags/hu.png 1970-01-01 00:00:00 +0000 and identityprovider/media/flags/hu.png 2010-05-13 15:51:29 +0000 differ
97=== added file 'identityprovider/media/flags/pl.png'
98Binary files identityprovider/media/flags/pl.png 1970-01-01 00:00:00 +0000 and identityprovider/media/flags/pl.png 2010-05-13 15:51:29 +0000 differ
99=== added file 'identityprovider/media/flags/zh-cn.png'
100Binary files identityprovider/media/flags/zh-cn.png 1970-01-01 00:00:00 +0000 and identityprovider/media/flags/zh-cn.png 2010-05-13 15:51:29 +0000 differ
101=== modified file 'identityprovider/media/ubuntu/styles.css'
102--- identityprovider/media/ubuntu/styles.css 2010-04-21 15:29:24 +0000
103+++ identityprovider/media/ubuntu/styles.css 2010-05-13 15:51:29 +0000
104@@ -408,3 +408,40 @@
105 .findoutmore {
106 font-size: 120%;
107 }
108+
109+/* Internationalization stuff */
110+#language_footer {
111+ float: right;
112+ padding: 10px 0 0;
113+ height: 20px;
114+ vertical-align: center;
115+}
116+
117+#language_footer img,
118+#language_footer a {
119+ float: left;
120+}
121+
122+#language_footer a {
123+ margin-left: 4px;
124+}
125+
126+#language_select div {
127+ margin: 4px;
128+}
129+
130+#language_select input {
131+ border: 0;
132+ border-bottom: 1px solid transparent;
133+ padding: 0 0 0 20px;
134+ color: #eb6f31;
135+}
136+
137+#language_select input:hover {
138+ cursor: pointer;
139+ text-decoration: underline;
140+ border-bottom: 1px solid #eb6f31;
141+}
142+
143+ text-decoration: none;
144+}
145
146=== modified file 'identityprovider/middleware/useraccount.py'
147--- identityprovider/middleware/useraccount.py 2010-04-21 15:29:24 +0000
148+++ identityprovider/middleware/useraccount.py 2010-05-13 15:51:29 +0000
149@@ -35,3 +35,7 @@
150 request.user = user
151 except User.DoesNotExist:
152 logout(request)
153+ if isinstance(request.user, Account):
154+ lang = request.user.preferredlanguage
155+ if lang is not None:
156+ request.session['django_language'] = lang
157
158=== modified file 'identityprovider/models/account.py'
159--- identityprovider/models/account.py 2010-05-12 16:37:14 +0000
160+++ identityprovider/models/account.py 2010-05-13 15:51:29 +0000
161@@ -70,6 +70,7 @@
162 displayname = models.TextField()
163 openid_identifier = models.TextField(default=generate_openid_identifier)
164 status_comment = models.TextField(blank=True, null=True)
165+ preferredlanguage = models.TextField(blank=True, null=True)
166 old_openid_identifier = models.TextField(blank=True, null=True)
167
168 objects = AccountManager()
169
170=== modified file 'identityprovider/templates/account/confirm_new_email.html'
171--- identityprovider/templates/account/confirm_new_email.html 2010-05-13 09:46:28 +0000
172+++ identityprovider/templates/account/confirm_new_email.html 2010-05-13 15:51:29 +0000
173@@ -11,7 +11,12 @@
174 <h2 class="main">{% blocktrans %}Validate {{ email }}?{% endblocktrans %}</h2>
175
176 <div class="info">
177+<<<<<<< TREE
178 <p>{% blocktrans %}Are you sure you want to confirm and validate this email address?{% endblocktrans %}</p>
179+=======
180+ <p>{% blocktrans %}Are you sure you want to confirm and validate this
181+ email address? {% endblocktrans %}</p>
182+>>>>>>> MERGE-SOURCE
183 </div>
184
185 <div class="actions">
186
187=== modified file 'identityprovider/templates/launchpad/registration/login.html'
188--- identityprovider/templates/launchpad/registration/login.html 2010-05-13 14:45:39 +0000
189+++ identityprovider/templates/launchpad/registration/login.html 2010-05-13 15:51:29 +0000
190@@ -21,7 +21,12 @@
191 <div id="auth-text">
192 {% if token and rpconfig %}
193 <p>
194+<<<<<<< TREE
195 {% blocktrans with rpconfig.displayname as rpconfigname %}You are here because {{ rpconfigname }} uses the Launchpad Login Service.{% endblocktrans %}
196+=======
197+ {% blocktrans with rpconfig.displayname as rpconfigname %}You are here because {{ rpconfigname }} uses
198+the Launchpad Login Service.{% endblocktrans %}
199+>>>>>>> MERGE-SOURCE
200 </p>
201 {% endif %}
202 </div>
203
204=== modified file 'identityprovider/templates/registration/confirm_new_account.html'
205=== modified file 'identityprovider/templates/registration/new_account.html'
206--- identityprovider/templates/registration/new_account.html 2010-05-13 09:55:17 +0000
207+++ identityprovider/templates/registration/new_account.html 2010-05-13 15:51:29 +0000
208@@ -9,7 +9,7 @@
209 {% block "content" %}
210 <div id="auth">
211 <h2 class="main">
212- {{ brand.create_account }}
213+ {{ brand.create_account }}
214 </h2>
215
216 <p>{% blocktrans %}Enter your email address, and we will send you instructions on how to create your account.{% endblocktrans %}</p>
217
218=== modified file 'identityprovider/templates/registration/reset_password.html'
219=== added file 'identityprovider/templates/select_language.html'
220--- identityprovider/templates/select_language.html 1970-01-01 00:00:00 +0000
221+++ identityprovider/templates/select_language.html 2010-05-13 15:51:29 +0000
222@@ -0,0 +1,32 @@
223+<!-- Copyright 2010 Canonical Ltd. This software is licensed under the
224+GNU Affero General Public License version 3 (see the file LICENSE). -->
225+
226+{% extends "base.html" %}
227+{% load i18n %}
228+
229+{% block "title" %}
230+ {% trans "Choose your language" %}
231+{% endblock %}
232+
233+{% block "content" %}
234+<div id="box">
235+ <h1 class="main">{% trans "Choose your language" %}</h1>
236+ <div id="language_select">
237+ {% for lang in supported_languages %}
238+ <form action="" method="POST">
239+ <input type="hidden" name="next" value="{{next}}">
240+ <input type="hidden" name="language" value="{{lang.0}}" />
241+ <div><input type="submit" name="submit" value="{{lang.1|capfirst}}"
242+ style="background: transparent url('/assets/identityprovider/flags/{{lang.0}}.png') center left no-repeat"/>
243+ </div>
244+ </form>
245+ {% endfor %}
246+ </div>
247+ <h2 class="main">{% trans "Can't find your language?" %}</h2>
248+ <p>{% blocktrans %}We welcome volunteers to help us translate this site to new languages. If you are able to help, please visit our <a href="https://translations.launchpad.net/canonical-identity-provider">translations site</a> to get started.{% endblocktrans %}
249+ </p>
250+</div>
251+{% endblock %}
252+
253+{% comment %} Removing language link from footer on this page {% endcomment %}
254+{% block "language_footer" %}{% endblock %}
255
256=== modified file 'identityprovider/templates/ubuntu/base.html'
257--- identityprovider/templates/ubuntu/base.html 2010-05-13 14:45:39 +0000
258+++ identityprovider/templates/ubuntu/base.html 2010-05-13 15:51:29 +0000
259@@ -45,6 +45,14 @@
260 </div>
261 </div>
262 <div id="footer">
263+ {% block "language_footer" %}
264+ <div id="language_footer">
265+ <img src="/assets/identityprovider/flags/{{ LANGUAGE_CODE }}.png">
266+ <a href="/set_language?next={{request.path_info|urlencode}}">
267+ {% trans "Choose your language" %}
268+ </a>
269+ </div>
270+ {% endblock %}
271 <p>{% blocktrans %}&copy; 2009-2010 Canonical Ltd.<br />
272 Ubuntu and Canonical are registered trademarks of Canonical Group Ltd.<br />
273 Please review our <a href="http://ubuntu.com/legal">Terms of Service</a> as well as our <a href="http://ubuntu.com/legal#privacy">Privacy Policy</a>.{% endblocktrans %}</p>
274
275=== modified file 'identityprovider/templates/ubuntu/registration/login.html'
276--- identityprovider/templates/ubuntu/registration/login.html 2010-05-13 14:45:39 +0000
277+++ identityprovider/templates/ubuntu/registration/login.html 2010-05-13 15:51:29 +0000
278@@ -15,13 +15,22 @@
279 {% if rpconfig %}
280 {% include "widgets/trusted_rp.html" %}
281 {% else %}
282+<<<<<<< TREE
283 <h2 class="main">{% blocktrans %}Log in to Ubuntu Single Sign On{% endblocktrans %}</h2>
284+=======
285+ <h2 class="main">{{ brand.log_in_to }}</h2>
286+>>>>>>> MERGE-SOURCE
287 {% endif %}
288
289 <div id="auth-text">
290 {% if token and rpconfig %}
291 <p>
292+<<<<<<< TREE
293 {% blocktrans with rpconfig.displayname as rpconfigname %}You are here because {{ rpconfigname }} uses the Ubuntu Single Sign On service.{% endblocktrans %}
294+=======
295+ {% blocktrans with rpconfig.displayname as rpconfigname %}You are here because {{ rpconfigname }} uses
296+the Ubuntu Single Sign On service.{% endblocktrans %}
297+>>>>>>> MERGE-SOURCE
298 </p>
299 {% endif %}
300 <p>
301
302=== added file 'identityprovider/tests/test_language_selection.py'
303--- identityprovider/tests/test_language_selection.py 1970-01-01 00:00:00 +0000
304+++ identityprovider/tests/test_language_selection.py 2010-05-13 15:51:29 +0000
305@@ -0,0 +1,15 @@
306+from identityprovider.tests.utils import BasicAccountTestCase
307+
308+
309+class SelectLanguagePageTestCase(BasicAccountTestCase):
310+
311+ def setUp(self):
312+ self.client.login(username='mark@example.com', password='test')
313+
314+ def test_link_for_language_choosing_is_displayed_on_main_page(self):
315+ r = self.client.get('/')
316+ self.assertContains(r, 'id="language_footer"')
317+
318+ def test_link_for_language_choosing_is_not_diplayed_on_language_page(self):
319+ r = self.client.get('/set_language')
320+ self.assertNotContains(r, 'id="language_footer"')
321
322=== added file 'identityprovider/tests/test_views_i18n.py'
323--- identityprovider/tests/test_views_i18n.py 1970-01-01 00:00:00 +0000
324+++ identityprovider/tests/test_views_i18n.py 2010-05-13 15:51:29 +0000
325@@ -0,0 +1,25 @@
326+from identityprovider.tests.utils import BasicAccountTestCase
327+
328+
329+class SetLanguageTestCase(BasicAccountTestCase):
330+
331+ def setUp(self):
332+ self.disable_csrf()
333+
334+ def tearDown(self):
335+ self.reset_csrf()
336+
337+ def test_when_supplying_non_local_next_url_redirects_to_main(self):
338+ r = self.client.post('/set_language',
339+ {'language': 'es', 'next': 'http://example.com'})
340+ self.assertRedirects(r, '/')
341+
342+ def test_next_must_be_resolved_to_existing_view(self):
343+ r = self.client.post('/set_language',
344+ {'language': 'es', 'next': '/not-existing'})
345+ self.assertRedirects(r, '/')
346+
347+ def test_next_works_for_to_be_resolved_for_existing_view(self):
348+ r = self.client.post('/set_language',
349+ {'language': 'es', 'next': '/+login'})
350+ self.assertRedirects(r, '/+login')
351
352=== modified file 'identityprovider/tests/test_views_ui.py'
353--- identityprovider/tests/test_views_ui.py 2010-05-12 16:24:02 +0000
354+++ identityprovider/tests/test_views_ui.py 2010-05-13 15:51:29 +0000
355@@ -38,13 +38,6 @@
356 def tearDown(self):
357 self.reset_csrf()
358
359- def reset_csrf(self):
360- settings.MIDDLEWARE_CLASSES = self._MIDDLEWARE_CLASSES
361-
362- def disable_csrf(self):
363- self._MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
364- settings.MIDDLEWARE_CLASSES = (x for x in self._MIDDLEWARE_CLASSES
365- if not 'csrf' in x.lower())
366
367 def authenticate(self):
368 self.client.login(username='mark@example.com', password='test')
369@@ -592,3 +585,26 @@
370 self.assertTemplateUsed(r, 'registration/new_account.html')
371 msg = 'It appears that our captcha service was unable to load'
372 self.assertContains(r, msg)
373+
374+
375+class LanguagesTestCase(BasicAccountTestCase):
376+
377+ def test_preffered_lang_is_preserved_after_logout(self):
378+ account = Account.objects.get_by_email("mark@example.com")
379+ account.preferredlanguage = 'es'
380+ account.save()
381+
382+ self.client.login(username='mark@example.com', password='test')
383+ r = self.client.get('/')
384+ self.assertContains(r, 'es.png')
385+
386+ self.client.get('/+logout')
387+ r = self.client.get('/')
388+ self.assertContains(r, 'es.png')
389+
390+ def test_language_selected_before_login_is_preserved(self):
391+ self.assertNotContains(self.client.get('/'), 'es.png')
392+ self.client.post('/set_language', {'language': 'es'})
393+ self.assertContains(self.client.get('/'), 'es.png')
394+ self.client.login(username='mark@example.com', password='test')
395+ self.assertContains(self.client.get('/'), 'es.png')
396
397=== modified file 'identityprovider/tests/utils.py'
398--- identityprovider/tests/utils.py 2010-05-07 21:21:33 +0000
399+++ identityprovider/tests/utils.py 2010-05-13 15:51:29 +0000
400@@ -56,6 +56,14 @@
401 fixtures = ['lp_account', 'lp_accountpassword', 'lp_emailaddress_noperson']
402 pgsql_functions = ['generate_openid_identifier']
403
404+ def reset_csrf(self):
405+ settings.MIDDLEWARE_CLASSES = self._MIDDLEWARE_CLASSES
406+
407+ def disable_csrf(self):
408+ self._MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
409+ settings.MIDDLEWARE_CLASSES = (x for x in self._MIDDLEWARE_CLASSES
410+ if not 'csrf' in x.lower())
411+
412
413 class LPAccountTestCase(BasicAccountTestCase):
414 def setUp(self):
415
416=== modified file 'identityprovider/urls.py'
417--- identityprovider/urls.py 2010-05-11 00:38:22 +0000
418+++ identityprovider/urls.py 2010-05-13 15:51:29 +0000
419@@ -51,6 +51,10 @@
420 (r'^%(optional_token)s\+remove-email$' % repls, 'delete_email'),
421 )
422
423+urlpatterns += patterns('identityprovider.views.i18n',
424+ (r'^set_language$', 'set_language'),
425+)
426+
427 urlpatterns += patterns('identityprovider.views.readonly',
428 (r'^readonly$', 'readonly_admin'),
429 (r'^readonly/((?P<appserver>[A-Za-z0-9\-_.:]+)/)?(?P<action>enable|disable|set|clear)(/(?P<conn>[A-Za-z0-9\-_.]+))?',
430@@ -77,9 +81,6 @@
431 {'document_root': settings.SSO_MEDIA_ROOT +
432 current_brand.template_dir}),
433 (r'^i18n/', include('django.conf.urls.i18n')),
434- (r'^set_language/$',
435- 'django.views.generic.simple.direct_to_template',
436- {'template': 'set_language.html'}),
437 )
438
439 if getattr(settings, 'TESTING', False):
440
441=== added file 'identityprovider/views/i18n.py'
442--- identityprovider/views/i18n.py 1970-01-01 00:00:00 +0000
443+++ identityprovider/views/i18n.py 2010-05-13 15:51:29 +0000
444@@ -0,0 +1,34 @@
445+from urllib import quote
446+
447+from django.conf import settings
448+from django.http import Http404
449+from django.shortcuts import render_to_response
450+from django.template import RequestContext
451+from django.views.i18n import set_language as django_set_language
452+from django.core.urlresolvers import resolve, Resolver404
453+
454+
455+def set_language(request):
456+ if request.method == 'GET':
457+ next = request.GET.get('next', '/')
458+ context = RequestContext(request, {'next': quote(next)})
459+ return render_to_response('select_language.html', context)
460+ elif request.method != 'POST':
461+ raise Http404()
462+ lang = request.POST.get('language')
463+ if lang not in settings.SUPPORTED_LANGUAGES:
464+ raise Http404('Unsupported language')
465+ if request.user.is_authenticated():
466+ account = request.user
467+ account.preferredlanguage = lang
468+ account.save()
469+ response = django_set_language(request)
470+ redirection = response['Location']
471+ if not redirection.startswith('/'):
472+ response['Location'] = '/'
473+ else:
474+ try:
475+ resolve(redirection)
476+ except Resolver404:
477+ response['Location'] = '/'
478+ return response
479
480=== modified file 'identityprovider/views/ui.py'
481--- identityprovider/views/ui.py 2010-05-13 14:53:17 +0000
482+++ identityprovider/views/ui.py 2010-05-13 15:51:29 +0000
483@@ -91,13 +91,21 @@
484
485
486 def logout(request, token=None):
487+ try:
488+ lang = request.user.preferredlanguage
489+ except AttributeError:
490+ lang = None
491+
492 # We don't want to lose session[token] when we log the user out
493 raw_orequest = request.session.get(token, None)
494 auth.logout(request)
495 if token is not None and raw_orequest is not None:
496 request.session[token] = raw_orequest
497 request.session['message'] = _("You have successfully logged out")
498- return HttpResponseRedirect('+login')
499+ response = HttpResponseRedirect('+login')
500+ if lang:
501+ response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang)
502+ return response
503
504
505 def claim_token(request, authtoken):