Merge lp:~adiroiban/launchpad/bug-497438 into lp:launchpad

Proposed by Adi Roiban
Status: Merged
Approved by: Aaron Bentley
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~adiroiban/launchpad/bug-497438
Merge into: lp:launchpad
Diff against target: 357 lines (+108/-81)
8 files modified
lib/canonical/launchpad/security.py (+21/-5)
lib/canonical/launchpad/testing/pages.py (+28/-4)
lib/lp/registry/browser/distroseries.py (+2/-1)
lib/lp/registry/browser/sourcepackage.py (+2/-1)
lib/lp/translations/browser/distroseries.py (+1/-1)
lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt (+22/-68)
lib/lp/translations/stories/productseries/xx-productseries-translations.txt (+31/-1)
lib/lp/translations/stories/standalone/xx-language.txt (+1/-0)
To merge this branch: bzr merge lp:~adiroiban/launchpad/bug-497438
Reviewer Review Type Date Requested Status
Aaron Bentley (community) Approve
Review via email: mp+16335@code.launchpad.net

Commit message

Use launchpad.TranslationsAdmin for checking access to distribution series translations.

To post a comment you must log in.
Revision history for this message
Adi Roiban (adiroiban) wrote :

= Bug 497438 =

While reviewing the import queue for Lucid, and as a member of rosetta-admins and ubuntu-translations-coordinators, I've noticed the following:

If I go to https://translations.edge.launchpad.net/ubuntu/lucid/+source/gfxboot-theme-ubuntu, I can see the list of templates for that package in my preferred languages.

However, when I try to access the template page at https://translations.edge.launchpad.net/ubuntu/lucid/+source/gfxboot-theme-ubuntu/+pots/bootloader, I get a "Translation page is not available. Translations for this release series are not available yet." error message and cannot access the page. The same happens when I try to access the +admin page.

== Implementation details ==
Since we are migrating translations permission from launchpad.Admin to launchpad.TranslationsAdmin, just replace launchpad.Admin with launchpad.TranslationsAdmin in the appropriate places.

The tests from distroseries-translations were switched from admin_browser to utc_browser, a browser configured for launchpad.TranslationsAdmin

Also there were some productseries tests and they were moved. Same for language specific tests.

After talking with Danilo, we agreed of having an utility (helper) function for setting up a browser configured for launchpad.TranslationsAdmin

== Tests ==

bin/test -vvct distroseries-translations

== Demo and Q/A ==
Login in Launchpad as RosettaExpert (carlos) or a member of distribution translation group.

Go to distroseries translation setting page and hide the translations
https://translations.launchpad.dev/ubuntu/hoary/+admin

You should still be able to browse distroseries translations:
https://translations.launchpad.dev/ubuntu/hoary
https://translations.launchpad.dev/ubuntu/hoary/+lang/es

Login as a normal user or anonymous. Visit those pages again.
You should see a message telling you the translations are not available yet.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/canonical/launchpad/testing/pages.py
  lib/lp/registry/browser/distroseries.py
  lib/lp/registry/browser/sourcepackage.py
  lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt

== Pylint notices ==

lib/canonical/launchpad/testing/pages.py
    39: [F0401] Unable to import 'lazr.restful.testing.webservice' (No module named restful)

lib/lp/registry/browser/sourcepackage.py
    23: [F0401] Unable to import 'lazr.restful.interface' (No module named restful)

I have not touched the lazr.restful part. Those lint warning were already there.

Revision history for this message
Aaron Bentley (abentley) wrote :

This looks pretty good. I asked for a different name for utc_browser because people looking at the code will assume UTC stands for Universal Coordinated Time, and it was renamed "dtc_browser", which I think is fine.

It seems that we must do extra work to ensure that Persons used in doctests have pre-determined credentials so that we can hardcode them when calling setupBrowser. It would be nice if we had an alternative to setupBrowser that accepted arbitrary Persons, but it's not required for this patch.

By assigning to ubuntu.translationgroup, setupUTCBrowser has side effects that mean repeated calls will invalidate previous values. I was willing to accept this, since repeated calls should not be necessary, but Adi decided this should be fixed before landing.

review: Needs Fixing (code)
Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (6.3 KiB)

Here is the latest diff.

Many thanks for the review!

=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2009-12-18 13:25:19 +0000
+++ lib/canonical/launchpad/security.py 2009-12-21 15:17:16 +0000
@@ -1678,7 +1678,7 @@
     def checkAuthenticated(self, user):
         """Is the user able to manage `IDistroSeries` translations.

- Disribution managers can also manage IDistroSeries
+ Distribution managers can also manage IDistroSeries
         """
         return (AdminDistributionTranslations(
             self.obj.distribution).checkAuthenticated(user))
@@ -1691,7 +1691,7 @@
     def checkAuthenticated(self, user):
         """Is the user able to manage `IDistroSeriesLanguage` translations.

- Disribution managers can also manage IDistroSeriesLanguage
+ Distribution managers can also manage IDistroSeriesLanguage
         """
         return (AdminDistroSeriesTranslations(
             self.obj.distroseries).checkAuthenticated(user))

=== modified file 'lib/canonical/launchpad/testing/pages.py'
--- lib/canonical/launchpad/testing/pages.py 2009-12-18 13:25:19 +0000
+++ lib/canonical/launchpad/testing/pages.py 2009-12-21 17:17:46 +0000
@@ -39,6 +39,7 @@
 from lazr.restful.testing.webservice import WebServiceCaller
 from lp.testing import ANONYMOUS, login, login_person, logout
 from lp.testing.factory import LaunchpadObjectFactory
+from lp.registry.interfaces.person import NameAlreadyTaken

 class UnstickyCookieHTTPCaller(HTTPCaller):
@@ -636,17 +637,24 @@
     return LaunchpadWebServiceCaller(consumer_key, access_token.key)

-def setupUTCBrowser():
- """Testbrowser configured for Ubuntu Translations Coordinators."""
+def setupDTCBrowser():
+ """Testbrowser configured for Distribution Translations Coordinators.

+ Ubuntu is the configured distribution.
+ """
     login('<email address hidden>')
- utg_member = LaunchpadObjectFactory().makePerson(
- <email address hidden>", password="test")
- utg = LaunchpadObjectFactory().makeTranslationGroup(owner=utg_member)
- ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
- ubuntu.translationgroup = utg
+ try:
+ dtg_member = LaunchpadObjectFactory().makePerson(
+ <email address hidden>", password="test")
+ except NameAlreadyTaken:
+ # We have already created the translations coordinator
+ pass
+ else:
+ dtg = LaunchpadObjectFactory().makeTranslationGroup(owner=dtg_member)
+ ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
+ ubuntu.translationgroup = dtg
     logout()
- return setupBrowser(auth='Basic <email address hidden>:test')
+ return setupBrowser(auth='Basic <email address hidden>:test')

 def stop():
@@ -669,7 +677,7 @@
     test.globs['user_webservice'] = LaunchpadWebServiceCaller(
         'launchpad-library', 'nopriv-read-nonprivate')
     test.globs['setupBrowser'] = setupBrowser
- test.globs['setupUTCBrowser'] = setupUTCBrowser
+ test.globs['setupDTCBrowser'] = setupDTCBrowser
     test.globs['browser'] = setupBrowser()
     test.globs['anon_browser'] = setupBrowser()
     test.globs['user_browser'] = setupBrowser(...

Read more...

Revision history for this message
Aaron Bentley (abentley) wrote :

Thanks, this looks fine now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2009-12-12 06:32:12 +0000
+++ lib/canonical/launchpad/security.py 2009-12-21 17:23:18 +0000
@@ -1671,14 +1671,30 @@
1671 return OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user)1671 return OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user)
16721672
16731673
1674class AdminDistroSeriesLanguage(OnlyRosettaExpertsAndAdmins):1674class AdminDistroSeriesTranslations(AuthorizationBase):
1675 permission = 'launchpad.Admin'1675 permission = 'launchpad.TranslationsAdmin'
1676 usedfor = IDistroSeries
1677
1678 def checkAuthenticated(self, user):
1679 """Is the user able to manage `IDistroSeries` translations.
1680
1681 Distribution managers can also manage IDistroSeries
1682 """
1683 return (AdminDistributionTranslations(
1684 self.obj.distribution).checkAuthenticated(user))
1685
1686
1687class AdminDistroSeriesLanguage(AuthorizationBase):
1688 permission = 'launchpad.TranslationsAdmin'
1676 usedfor = IDistroSeriesLanguage1689 usedfor = IDistroSeriesLanguage
16771690
1691 def checkAuthenticated(self, user):
1692 """Is the user able to manage `IDistroSeriesLanguage` translations.
16781693
1679class AdminDistroSeriesTranslations(OnlyRosettaExpertsAndAdmins):1694 Distribution managers can also manage IDistroSeriesLanguage
1680 permission = 'launchpad.TranslationsAdmin'1695 """
1681 usedfor = IDistroSeries1696 return (AdminDistroSeriesTranslations(
1697 self.obj.distroseries).checkAuthenticated(user))
16821698
16831699
1684class BranchSubscriptionEdit(AuthorizationBase):1700class BranchSubscriptionEdit(AuthorizationBase):
16851701
=== modified file 'lib/canonical/launchpad/testing/pages.py'
--- lib/canonical/launchpad/testing/pages.py 2009-10-16 17:14:42 +0000
+++ lib/canonical/launchpad/testing/pages.py 2009-12-21 17:23:18 +0000
@@ -28,7 +28,8 @@
28from zope.testbrowser.testing import Browser28from zope.testbrowser.testing import Browser
29from zope.testing import doctest29from zope.testing import doctest
3030
31from canonical.launchpad.interfaces import IOAuthConsumerSet, OAUTH_REALM31from canonical.launchpad.interfaces import (
32 IOAuthConsumerSet, OAUTH_REALM, ILaunchpadCelebrities)
32from canonical.launchpad.testing.systemdocs import (33from canonical.launchpad.testing.systemdocs import (
33 LayeredDocFileSuite, SpecialOutputChecker, strip_prefix)34 LayeredDocFileSuite, SpecialOutputChecker, strip_prefix)
34from canonical.launchpad.webapp import canonical_url35from canonical.launchpad.webapp import canonical_url
@@ -38,6 +39,7 @@
38from lazr.restful.testing.webservice import WebServiceCaller39from lazr.restful.testing.webservice import WebServiceCaller
39from lp.testing import ANONYMOUS, login, login_person, logout40from lp.testing import ANONYMOUS, login, login_person, logout
40from lp.testing.factory import LaunchpadObjectFactory41from lp.testing.factory import LaunchpadObjectFactory
42from lp.registry.interfaces.person import NameAlreadyTaken
4143
4244
43class UnstickyCookieHTTPCaller(HTTPCaller):45class UnstickyCookieHTTPCaller(HTTPCaller):
@@ -350,9 +352,10 @@
350 #352 #
351 # The CData class does not override slicing though, so by slicing353 # The CData class does not override slicing though, so by slicing
352 # node first, we're effectively turning it into a concrete unicode354 # node first, we're effectively turning it into a concrete unicode
353 # instance, which does not wrap the contents when its __unicode__()355 # instance, which does not wrap the contents when its
354 # is called of course. We could remove the unicode() call356 # __unicode__() is called of course. We could remove the
355 # here, but we keep it for consistency and clarity purposes.357 # unicode() call here, but we keep it for consistency and clarity
358 # purposes.
356 result.append(unicode(node[:]))359 result.append(unicode(node[:]))
357 elif isinstance(node, NavigableString):360 elif isinstance(node, NavigableString):
358 result.append(unicode(node))361 result.append(unicode(node))
@@ -634,6 +637,26 @@
634 return LaunchpadWebServiceCaller(consumer_key, access_token.key)637 return LaunchpadWebServiceCaller(consumer_key, access_token.key)
635638
636639
640def setupDTCBrowser():
641 """Testbrowser configured for Distribution Translations Coordinators.
642
643 Ubuntu is the configured distribution.
644 """
645 login('foo.bar@canonical.com')
646 try:
647 dtg_member = LaunchpadObjectFactory().makePerson(
648 email="dtg-member@ex.com", password="test")
649 except NameAlreadyTaken:
650 # We have already created the translations coordinator
651 pass
652 else:
653 dtg = LaunchpadObjectFactory().makeTranslationGroup(owner=dtg_member)
654 ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
655 ubuntu.translationgroup = dtg
656 logout()
657 return setupBrowser(auth='Basic dtg-member@ex.com:test')
658
659
637def stop():660def stop():
638 # Temporarily restore the real stdout.661 # Temporarily restore the real stdout.
639 old_stdout = sys.stdout662 old_stdout = sys.stdout
@@ -654,6 +677,7 @@
654 test.globs['user_webservice'] = LaunchpadWebServiceCaller(677 test.globs['user_webservice'] = LaunchpadWebServiceCaller(
655 'launchpad-library', 'nopriv-read-nonprivate')678 'launchpad-library', 'nopriv-read-nonprivate')
656 test.globs['setupBrowser'] = setupBrowser679 test.globs['setupBrowser'] = setupBrowser
680 test.globs['setupDTCBrowser'] = setupDTCBrowser
657 test.globs['browser'] = setupBrowser()681 test.globs['browser'] = setupBrowser()
658 test.globs['anon_browser'] = setupBrowser()682 test.globs['anon_browser'] = setupBrowser()
659 test.globs['user_browser'] = setupBrowser(683 test.globs['user_browser'] = setupBrowser(
660684
=== modified file 'lib/lp/registry/browser/distroseries.py'
--- lib/lp/registry/browser/distroseries.py 2009-12-09 19:47:23 +0000
+++ lib/lp/registry/browser/distroseries.py 2009-12-21 17:23:18 +0000
@@ -91,7 +91,8 @@
91 distroserieslang = distroserieslangset.getDummy(91 distroserieslang = distroserieslangset.getDummy(
92 self.context, lang)92 self.context, lang)
9393
94 if not check_permission('launchpad.Admin', distroserieslang):94 if not check_permission(
95 'launchpad.TranslationsAdmin', distroserieslang):
95 self.context.checkTranslationsViewable()96 self.context.checkTranslationsViewable()
9697
97 return distroserieslang98 return distroserieslang
9899
=== modified file 'lib/lp/registry/browser/sourcepackage.py'
--- lib/lp/registry/browser/sourcepackage.py 2009-12-03 17:14:05 +0000
+++ lib/lp/registry/browser/sourcepackage.py 2009-12-21 17:23:18 +0000
@@ -56,7 +56,8 @@
56 distroseries=self.context.distroseries,56 distroseries=self.context.distroseries,
57 sourcepackagename=self.context.sourcepackagename)57 sourcepackagename=self.context.sourcepackagename)
5858
59 if not check_permission('launchpad.Admin', sourcepackage_pots):59 if not check_permission(
60 'launchpad.TranslationsAdmin', sourcepackage_pots):
60 self.context.distroseries.checkTranslationsViewable()61 self.context.distroseries.checkTranslationsViewable()
6162
62 return sourcepackage_pots63 return sourcepackage_pots
6364
=== modified file 'lib/lp/translations/browser/distroseries.py'
--- lib/lp/translations/browser/distroseries.py 2009-10-30 10:09:17 +0000
+++ lib/lp/translations/browser/distroseries.py 2009-12-21 17:23:18 +0000
@@ -195,7 +195,7 @@
195 hidden and the user is not one of the limited caste that is195 hidden and the user is not one of the limited caste that is
196 allowed to access them.196 allowed to access them.
197 """197 """
198 if check_permission('launchpad.Admin', self.context):198 if check_permission('launchpad.TranslationsAdmin', self.context):
199 # Anyone with admin rights on this series passes. This199 # Anyone with admin rights on this series passes. This
200 # includes Launchpad admins.200 # includes Launchpad admins.
201 return201 return
202202
=== modified file 'lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt'
--- lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt 2009-10-23 16:17:40 +0000
+++ lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt 2009-12-21 17:23:18 +0000
@@ -1,29 +1,9 @@
1= Distribution series translations =1Distribution series translations
2================================
23
3This page shows a list of PO templates contained within all source4This page shows a list of PO templates contained within all source
4packages in a particular distibution series.5packages in a particular distibution series.
56
6We are going to work with visible (Spanish) and non visible
7(Spanish (Spain)) languages.
8
9Here we can see that Spanish (Spain) is not visible.
10
11 >>> admin_browser.open(
12 ... 'http://translations.launchpad.dev/+languages/es_ES/+admin')
13 >>> print admin_browser.getControl('The English name').value
14 Spanish (Spain)
15 >>> admin_browser.getControl('Visible').selected
16 False
17
18But Spanish is visible.
19
20 >>> admin_browser.open(
21 ... 'http://translations.launchpad.dev/+languages/es/+admin')
22 >>> print admin_browser.getControl('The English name').value
23 Spanish
24 >>> admin_browser.getControl('Visible').selected
25 True
26
27In this case, we're asking for the translation overview for Hoary.7In this case, we're asking for the translation overview for Hoary.
288
29 >>> anon_browser.open('http://translations.launchpad.dev/ubuntu/hoary')9 >>> anon_browser.open('http://translations.launchpad.dev/ubuntu/hoary')
@@ -65,22 +45,24 @@
6545
66... but the link is available to administrators:46... but the link is available to administrators:
6747
68 >>> admin_browser.open('http://translations.launchpad.dev/ubuntu/hoary')48 >>> dtc_browser = setupDTCBrowser()
6949
70 >>> admin_browser.getLink('Change settings').click()50 >>> dtc_browser.open('http://translations.launchpad.dev/ubuntu/hoary')
51
52 >>> dtc_browser.getLink('Change settings').click()
7153
72Once the administrator hides all translations...54Once the administrator hides all translations...
7355
74 >>> admin_browser.getControl(56 >>> dtc_browser.getControl(
75 ... 'Hide translations for this release').selected = True57 ... 'Hide translations for this release').selected = True
76 >>> admin_browser.getControl('Change').click()58 >>> dtc_browser.getControl('Change').click()
77 >>> print admin_browser.url59 >>> print dtc_browser.url
78 http://translations.launchpad.dev/ubuntu/hoary60 http://translations.launchpad.dev/ubuntu/hoary
7961
80...a notice about the fact shows up on the overview page.62...a notice about the fact shows up on the overview page.
8163
82 >>> notices = find_tags_by_class(64 >>> notices = find_tags_by_class(
83 ... admin_browser.contents, 'visibility-notice')65 ... dtc_browser.contents, 'visibility-notice')
84 >>> for notice in notices:66 >>> for notice in notices:
85 ... print extract_text(notice)67 ... print extract_text(notice)
86 Translations for this series are currently hidden.68 Translations for this series are currently hidden.
@@ -108,9 +90,9 @@
10890
109 >>> user_browser.handleErrors = False91 >>> user_browser.handleErrors = False
11092
111Although, an administrator can see the same page.93Translations administrator have access series with hidden translations.
11294
113 >>> admin_browser.open(95 >>> dtc_browser.open(
114 ... 'http://translations.launchpad.dev/ubuntu/hoary/+lang/es')96 ... 'http://translations.launchpad.dev/ubuntu/hoary/+lang/es')
11597
116Non existing languages are not viewable. English is a special case98Non existing languages are not viewable. English is a special case
@@ -142,7 +124,7 @@
142However, source package translations are still available to the124However, source package translations are still available to the
143administrators.125administrators.
144126
145 >>> admin_browser.open(127 >>> dtc_browser.open(
146 ... 'http://translations.launchpad.dev/ubuntu/hoary/'128 ... 'http://translations.launchpad.dev/ubuntu/hoary/'
147 ... '+sources/evolution')129 ... '+sources/evolution')
148130
@@ -150,52 +132,24 @@
150distribution should be deferred. That option is set also from the same132distribution should be deferred. That option is set also from the same
151form where we hide all translations and an admin is able to change it:133form where we hide all translations and an admin is able to change it:
152134
153 >>> admin_browser.open('http://translations.launchpad.dev/ubuntu/hoary')135 >>> dtc_browser.open('http://translations.launchpad.dev/ubuntu/hoary')
154 >>> admin_browser.getLink('Change settings').click()136 >>> dtc_browser.getLink('Change settings').click()
155 >>> admin_browser.getControl(137 >>> dtc_browser.getControl(
156 ... 'Defer translation imports').selected138 ... 'Defer translation imports').selected
157 False139 False
158 >>> admin_browser.getControl(140 >>> dtc_browser.getControl(
159 ... 'Defer translation imports').selected = True141 ... 'Defer translation imports').selected = True
160 >>> admin_browser.getControl('Change').click()142 >>> dtc_browser.getControl('Change').click()
161 >>> print admin_browser.url143 >>> print dtc_browser.url
162 http://translations.launchpad.dev/ubuntu/hoary144 http://translations.launchpad.dev/ubuntu/hoary
163145
164Once the system accepts the submission, we can see such change applied.146Once the system accepts the submission, we can see such change applied.
165147
166 >>> admin_browser.getLink('Change settings').click()148 >>> dtc_browser.getLink('Change settings').click()
167 >>> admin_browser.getControl(149 >>> dtc_browser.getControl(
168 ... 'Defer translation imports').selected150 ... 'Defer translation imports').selected
169 True151 True
170152
171There are no visible user interface changes once this flag is changed. It153There are no visible user interface changes once this flag is changed. It
172just prevents that the translation import script, which is executed by cron,154just prevents that the translation import script, which is executed by cron,
173handle translation imports for this distro series.155handle translation imports for this distro series.
174
175== Translation focus ==
176
177If translation focus is not set, there is no recommendation of what
178release series should be translated.
179
180 >>> login('admin@canonical.com')
181 >>> distribution = factory.makeDistribution(name='earthian')
182 >>> distroseries = factory.makeDistroRelease(
183 ... name='1.4', distribution=distribution)
184 >>> logout()
185 >>> print distribution.translation_focus
186 None
187 >>> admin_browser.open('http://translations.launchpad.dev/earthian/1.4')
188 >>> print find_tag_by_id(admin_browser.contents, 'translation-focus')
189 None
190
191If focus is set, nice explanatory text is displayed.
192
193 >>> login('admin@canonical.com')
194 >>> focus_series = factory.makeDistroRelease(
195 ... name='1.6', distribution=distribution)
196 >>> distribution.translation_focus = focus_series
197 >>> logout()
198 >>> admin_browser.open('http://translations.launchpad.dev/earthian/1.4')
199 >>> print extract_text(
200 ... find_tag_by_id(admin_browser.contents, 'translation-focus'))
201 Launchpad currently recommends translating 1.6.
202156
=== renamed file 'lib/lp/translations/stories/standalone/xx-productseries-translations.txt' => 'lib/lp/translations/stories/productseries/xx-productseries-translations.txt'
--- lib/lp/translations/stories/standalone/xx-productseries-translations.txt 2009-12-10 10:46:05 +0000
+++ lib/lp/translations/stories/productseries/xx-productseries-translations.txt 2009-12-21 17:23:18 +0000
@@ -114,7 +114,7 @@
114 >>> serbian_link['href']114 >>> serbian_link['href']
115 u'/frobnicator/trunk/+lang/sr'115 u'/frobnicator/trunk/+lang/sr'
116116
117Upload page and Translations use117Upload page and translations use
118--------------------------------118--------------------------------
119119
120If the product a series belongs to is not configured to use Launchpad120If the product a series belongs to is not configured to use Launchpad
@@ -238,3 +238,33 @@
238 Translations...238 Translations...
239 Translations are exported daily to branch239 Translations are exported daily to branch
240 ...240 ...
241
242
243Translation focus
244-----------------
245
246If translation focus is not set, there is no recommendation of what
247release series should be translated.
248
249 >>> login('admin@canonical.com')
250 >>> distribution = factory.makeDistribution(name='earthian')
251 >>> distroseries = factory.makeDistroRelease(
252 ... name='1.4', distribution=distribution)
253 >>> logout()
254 >>> print distribution.translation_focus
255 None
256 >>> admin_browser.open('http://translations.launchpad.dev/earthian/1.4')
257 >>> print find_tag_by_id(admin_browser.contents, 'translation-focus')
258 None
259
260If focus is set, nice explanatory text is displayed.
261
262 >>> login('admin@canonical.com')
263 >>> focus_series = factory.makeDistroRelease(
264 ... name='1.6', distribution=distribution)
265 >>> distribution.translation_focus = focus_series
266 >>> logout()
267 >>> admin_browser.open('http://translations.launchpad.dev/earthian/1.4')
268 >>> print extract_text(
269 ... find_tag_by_id(admin_browser.contents, 'translation-focus'))
270 Launchpad currently recommends translating 1.6.
241271
=== modified file 'lib/lp/translations/stories/standalone/xx-language.txt'
--- lib/lp/translations/stories/standalone/xx-language.txt 2009-12-08 08:16:35 +0000
+++ lib/lp/translations/stories/standalone/xx-language.txt 2009-12-21 17:23:18 +0000
@@ -308,3 +308,4 @@
308 ...308 ...
309 NotFound:...309 NotFound:...
310310
311