Code review comment for lp:~henninge/launchpad/devel-487137-custom-language-codes
- devel-487137-custom-language-codes
- Merge into devel
Revision history for this message
Henning Eggers (henninge) wrote : | # |
1 | === modified file 'lib/canonical/launchpad/security.py' |
2 | --- lib/canonical/launchpad/security.py 2010-12-21 15:13:17 +0000 |
3 | +++ lib/canonical/launchpad/security.py 2010-12-22 12:03:59 +0000 |
4 | @@ -185,7 +185,6 @@ |
5 | from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease |
6 | from lp.translations.interfaces.customlanguagecode import ( |
7 | ICustomLanguageCode, |
8 | - IHasCustomLanguageCodes, |
9 | ) |
10 | from lp.translations.interfaces.languagepack import ILanguagePack |
11 | from lp.translations.interfaces.pofile import IPOFile |
12 | @@ -744,6 +743,7 @@ |
13 | # with launchpad.View so that this adapter is used. For now, though, it's |
14 | # going to be used only on the webservice (which explicitly checks for |
15 | # launchpad.View) so that we don't leak memberships of private teams. |
16 | + |
17 | class ViewTeamMembership(AuthorizationBase): |
18 | permission = 'launchpad.View' |
19 | usedfor = ITeamMembership |
20 | @@ -1728,27 +1728,23 @@ |
21 | usedfor = ILanguage |
22 | |
23 | |
24 | -class AdminCustomLanguageCodes(OnlyRosettaExpertsAndAdmins): |
25 | - """Controls administration of custom language codes. |
26 | - |
27 | - Rosetta experts and Launchpad administrators can administer custom |
28 | - language codes. |
29 | - """ |
30 | - |
31 | - permission = 'launchpad.TranslationsAdmin' |
32 | - usedfor = IHasCustomLanguageCodes |
33 | - |
34 | - |
35 | -class AdminCustomLanguageCode(OnlyRosettaExpertsAndAdmins): |
36 | +class AdminCustomLanguageCode(AuthorizationBase): |
37 | """Controls administration for a custom language code. |
38 | |
39 | - Rosetta experts and Launchpad administrators can administer a custom |
40 | - language code. |
41 | + Whoever can admin a product's or distribution's translations can also |
42 | + admin the custom language codes for it. |
43 | """ |
44 | - |
45 | permission = 'launchpad.TranslationsAdmin' |
46 | usedfor = ICustomLanguageCode |
47 | |
48 | + def checkAuthenticated(self, user): |
49 | + if self.obj.product is not None: |
50 | + return AdminProductTranslations( |
51 | + self.obj.product).checkAuthenticated(user) |
52 | + else: |
53 | + return AdminDistributionTranslations( |
54 | + self.obj.distribution).checkAuthenticated(user) |
55 | + |
56 | |
57 | class AccessBranch(AuthorizationBase): |
58 | """Controls visibility of branches. |
59 | @@ -1839,6 +1835,12 @@ |
60 | self.obj.distribution).checkAuthenticated(user)) |
61 | |
62 | |
63 | +class AdminDistributionSourcePackageTranslations( |
64 | + AdminDistroSeriesTranslations): |
65 | + """DistributionSourcePackage objects link to a distribution, too.""" |
66 | + usedfor = IDistributionSourcePackage |
67 | + |
68 | + |
69 | class AdminProductSeriesTranslations(AuthorizationBase): |
70 | permission = 'launchpad.TranslationsAdmin' |
71 | usedfor = IProductSeries |
72 | |
73 | === modified file 'lib/lp/translations/browser/configure.zcml' |
74 | --- lib/lp/translations/browser/configure.zcml 2010-12-21 15:13:17 +0000 |
75 | +++ lib/lp/translations/browser/configure.zcml 2010-12-22 12:01:59 +0000 |
76 | @@ -941,7 +941,7 @@ |
77 | for="lp.registry.interfaces.distribution.IDistribution" |
78 | permission="zope.Public" |
79 | template="../templates/translations-portlet-not-using-launchpad-extra.pt" |
80 | - layer="lp.translations.publisher.TranslationsLayer"/> |
81 | + layer="lp.translations.publisher.TranslationsLayer"/> |
82 | <browser:page |
83 | name="+portlet-configuration" |
84 | for="lp.registry.interfaces.distribution.IDistribution" |
85 | |
86 | === modified file 'lib/lp/translations/browser/customlanguagecode.py' |
87 | --- lib/lp/translations/browser/customlanguagecode.py 2010-12-21 15:13:17 +0000 |
88 | +++ lib/lp/translations/browser/customlanguagecode.py 2010-12-22 12:03:05 +0000 |
89 | @@ -46,6 +46,7 @@ |
90 | |
91 | class CustomLanguageCodeBreadcrumb(Breadcrumb): |
92 | """Breadcrumb for a `CustomLanguageCode`.""" |
93 | + |
94 | @property |
95 | def text(self): |
96 | return smartquote( |
97 | @@ -164,6 +165,7 @@ |
98 | class HasCustomLanguageCodesTraversalMixin: |
99 | """Navigate from an `IHasCustomLanguageCodes` to a `CustomLanguageCode`. |
100 | """ |
101 | + |
102 | @stepthrough('+customcode') |
103 | def traverseCustomCode(self, name): |
104 | """Traverse +customcode URLs.""" |
105 | |
106 | === modified file 'lib/lp/translations/stories/standalone/custom-language-codes.txt' |
107 | --- lib/lp/translations/stories/standalone/custom-language-codes.txt 2010-12-21 15:13:17 +0000 |
108 | +++ lib/lp/translations/stories/standalone/custom-language-codes.txt 2010-12-22 12:04:22 +0000 |
109 | @@ -38,16 +38,18 @@ |
110 | >>> owner_browser = setupBrowser("Basic o@example.com:test") |
111 | >>> rosetta_admin_browser = setupRosettaExpertBrowser() |
112 | |
113 | -A Launchpad administrator or Rosetta expert sees the link to the custom |
114 | -language codes on a project's main translations page. |
115 | +The project's owner sees the link to the custom language codes on a project's |
116 | +main translations page. |
117 | |
118 | - >>> admin_browser.open(product_page) |
119 | - >>> tag = find_custom_language_codes_link(admin_browser) |
120 | + >>> owner_browser.open(product_page) |
121 | + >>> tag = find_custom_language_codes_link(owner_browser) |
122 | >>> print extract_text(tag.renderContents()) |
123 | If necessary, you may |
124 | define custom language codes |
125 | for this project. |
126 | |
127 | +Translation admins also have access to this link. |
128 | + |
129 | >>> rosetta_admin_browser.open(product_page) |
130 | >>> tag = find_custom_language_codes_link(rosetta_admin_browser) |
131 | >>> print extract_text(tag.renderContents()) |
132 | @@ -57,39 +59,37 @@ |
133 | |
134 | The link goes to the custom language codes management page. |
135 | |
136 | - >>> rosetta_admin_browser.getLink("define custom language codes").click() |
137 | - >>> custom_language_codes_page = rosetta_admin_browser.url |
138 | - |
139 | -Non-admins, even the project's owner, don't see this link. We do not |
140 | -advertise this feature, since the proper solution is generally to use |
141 | -the right language codes. |
142 | - |
143 | - >>> owner_browser.open(product_page) |
144 | - >>> print find_custom_language_codes_link(owner_browser) |
145 | + >>> owner_browser.getLink("define custom language codes").click() |
146 | + >>> custom_language_codes_page = owner_browser.url |
147 | + |
148 | +Other users don't see this link. |
149 | + |
150 | + >>> user_browser.open(product_page) |
151 | + >>> print find_custom_language_codes_link(user_browser) |
152 | None |
153 | |
154 | Initially the page shows no custom language codes for the project. |
155 | |
156 | - >>> tag = find_tag_by_id(rosetta_admin_browser.contents, 'empty') |
157 | + >>> tag = find_tag_by_id(owner_browser.contents, 'empty') |
158 | >>> print extract_text(tag.renderContents()) |
159 | No custom language codes have been defined. |
160 | |
161 | -The admin can add a custom language code. |
162 | - |
163 | - >>> rosetta_admin_browser.getLink("Add a custom language code").click() |
164 | - >>> add_page = rosetta_admin_browser.url |
165 | - |
166 | - >>> rosetta_admin_browser.getControl("Language code:").value = 'no' |
167 | - >>> rosetta_admin_browser.getControl("Language:").value = ['nn'] |
168 | - >>> rosetta_admin_browser.getControl("Add").click() |
169 | +There is a link to add a custom language code. |
170 | + |
171 | + >>> owner_browser.getLink("Add a custom language code").click() |
172 | + >>> add_page = owner_browser.url |
173 | + |
174 | + >>> owner_browser.getControl("Language code:").value = 'no' |
175 | + >>> owner_browser.getControl("Language:").value = ['nn'] |
176 | + >>> owner_browser.getControl("Add").click() |
177 | |
178 | This leads back to the custom language codes overview, where the new |
179 | code is now shown. |
180 | |
181 | - >>> rosetta_admin_browser.url == custom_language_codes_page |
182 | + >>> owner_browser.url == custom_language_codes_page |
183 | True |
184 | |
185 | - >>> tag = find_tag_by_id(rosetta_admin_browser.contents, 'nonempty') |
186 | + >>> tag = find_tag_by_id(owner_browser.contents, 'nonempty') |
187 | >>> print extract_text(tag.renderContents()) |
188 | Foo uses the following custom language codes: |
189 | Code... ...maps to language |
190 | @@ -98,8 +98,8 @@ |
191 | There is an overview page for the custom code, though there's not much |
192 | to see there. |
193 | |
194 | - >>> rosetta_admin_browser.getLink("no").click() |
195 | - >>> main = find_main_content(rosetta_admin_browser.contents) |
196 | + >>> owner_browser.getLink("no").click() |
197 | + >>> main = find_main_content(owner_browser.contents) |
198 | >>> print extract_text(main.renderContents()) |
199 | Foo Translations Custom language code ...no... |
200 | For Foo, uploads with the language code |
201 | @@ -111,79 +111,79 @@ |
202 | |
203 | The overview page leads back to the custom language codes overview. |
204 | |
205 | - >>> code_page = rosetta_admin_browser.url |
206 | - >>> rosetta_admin_browser.getLink( |
207 | + >>> code_page = owner_browser.url |
208 | + >>> owner_browser.getLink( |
209 | ... "custom language codes overview").click() |
210 | - >>> rosetta_admin_browser.url == custom_language_codes_page |
211 | + >>> owner_browser.url == custom_language_codes_page |
212 | True |
213 | |
214 | - >>> rosetta_admin_browser.open(code_page) |
215 | + >>> owner_browser.open(code_page) |
216 | |
217 | -There is also a link for removing codes. The admin follows the link and |
218 | +There is also a link for removing codes. The owner follows the link and |
219 | removes the "no" custom language code. |
220 | |
221 | - >>> rosetta_admin_browser.getLink("remove custom language code").click() |
222 | - >>> remove_page = rosetta_admin_browser.url |
223 | - >>> rosetta_admin_browser.getControl("Remove").click() |
224 | + >>> owner_browser.getLink("remove custom language code").click() |
225 | + >>> remove_page = owner_browser.url |
226 | + >>> owner_browser.getControl("Remove").click() |
227 | |
228 | This leads back to the overview page. |
229 | |
230 | - >>> rosetta_admin_browser.url == custom_language_codes_page |
231 | + >>> owner_browser.url == custom_language_codes_page |
232 | True |
233 | |
234 | - >>> tag = find_tag_by_id(rosetta_admin_browser.contents, 'empty') |
235 | - >>> print extract_text(tag.renderContents()) |
236 | - No custom language codes have been defined. |
237 | - |
238 | - |
239 | -Non-admin access |
240 | -================ |
241 | - |
242 | -A non-admin can see the page, actually, if they know the URL. This can |
243 | -be convenient for debugging. |
244 | - |
245 | - >>> owner_browser.open(custom_language_codes_page) |
246 | - |
247 | >>> tag = find_tag_by_id(owner_browser.contents, 'empty') |
248 | >>> print extract_text(tag.renderContents()) |
249 | No custom language codes have been defined. |
250 | |
251 | + |
252 | +Unprivileged access |
253 | +=================== |
254 | + |
255 | +A unprivileged user can see the page, actually, if they know the URL. |
256 | +This can be convenient for debugging. |
257 | + |
258 | + >>> user_browser.open(custom_language_codes_page) |
259 | + |
260 | + >>> tag = find_tag_by_id(user_browser.contents, 'empty') |
261 | + >>> print extract_text(tag.renderContents()) |
262 | + No custom language codes have been defined. |
263 | + |
264 | However all they get is a read-only version of the page. |
265 | |
266 | - >>> owner_browser.getLink("Add a custom language code").click() |
267 | + >>> user_browser.getLink("Add a custom language code").click() |
268 | Traceback (most recent call last): |
269 | ... |
270 | LinkNotFoundError |
271 | |
272 | The page for adding custom language codes is not accessible to them. |
273 | |
274 | + >>> user_browser.open(add_page) |
275 | + Traceback (most recent call last): |
276 | + ... |
277 | + Unauthorized: ... |
278 | + |
279 | +And naturally, if the owner creates a custom language code again, an |
280 | +unprivileged user can't remove it. |
281 | + |
282 | >>> owner_browser.open(add_page) |
283 | - Traceback (most recent call last): |
284 | - ... |
285 | - Unauthorized: ... |
286 | - |
287 | -And naturally, if an admin creates a custom language code again, a |
288 | -non-admin can't remove it. |
289 | - |
290 | - >>> rosetta_admin_browser.open(add_page) |
291 | - >>> rosetta_admin_browser.getControl("Language code:").value = 'no' |
292 | - >>> rosetta_admin_browser.getControl("Language:").value = ['nn'] |
293 | - >>> rosetta_admin_browser.getControl("Add").click() |
294 | - |
295 | - >>> owner_browser.open(custom_language_codes_page) |
296 | - >>> tag = find_tag_by_id(owner_browser.contents, 'nonempty') |
297 | + >>> owner_browser.getControl("Language code:").value = 'no' |
298 | + >>> owner_browser.getControl("Language:").value = ['nn'] |
299 | + >>> owner_browser.getControl("Add").click() |
300 | + |
301 | + >>> user_browser.open(custom_language_codes_page) |
302 | + >>> tag = find_tag_by_id(user_browser.contents, 'nonempty') |
303 | >>> print extract_text(tag.renderContents()) |
304 | Foo uses the following custom language codes: |
305 | Code... ...maps to language |
306 | no Norwegian Nynorsk |
307 | |
308 | - >>> owner_browser.getLink("no").click() |
309 | - >>> owner_browser.getLink("remove custom language code") |
310 | + >>> user_browser.getLink("no").click() |
311 | + >>> user_browser.getLink("remove custom language code") |
312 | Traceback (most recent call last): |
313 | ... |
314 | LinkNotFoundError |
315 | |
316 | - >>> owner_browser.open(remove_page) |
317 | + >>> user_browser.open(remove_page) |
318 | Traceback (most recent call last): |
319 | ... |
320 | Unauthorized: ... |
321 | @@ -197,7 +197,8 @@ |
322 | package--i.e. the combination of a distribution and a source package |
323 | name. However, since there is no Translations page for that type of |
324 | object (and we'd probably never go there if there were), the link is |
325 | -shown on the source package page. |
326 | +shown on the source package page. For distributions, the owner of the |
327 | +distribution's translation group is a translations administrator. |
328 | |
329 | >>> login(ANONYMOUS) |
330 | >>> from lp.registry.model.sourcepackage import SourcePackage |
331 | @@ -219,38 +220,44 @@ |
332 | ... distroseries=other_series, |
333 | ... sourcepackagename=package.sourcepackagename), |
334 | ... rootsite="translations") |
335 | + >>> translations_admin = factory.makePerson( |
336 | + ... email='ta@example.com', password='test') |
337 | + >>> translationgroup = factory.makeTranslationGroup( |
338 | + ... owner=translations_admin) |
339 | + >>> removeSecurityProxy(distro).translationgroup = translationgroup |
340 | >>> logout() |
341 | |
342 | - >>> rosetta_admin_browser.open(package_page) |
343 | + >>> translations_browser = setupBrowser("Basic ta@example.com:test") |
344 | + >>> translations_browser.open(package_page) |
345 | |
346 | Of course in this case, the notice about there being no custom language |
347 | codes talks about a package, not a project. |
348 | |
349 | - >>> tag = find_custom_language_codes_link(rosetta_admin_browser) |
350 | + >>> tag = find_custom_language_codes_link(translations_browser) |
351 | >>> print extract_text(tag.renderContents()) |
352 | If necessary, you may |
353 | define custom language codes |
354 | for this package. |
355 | |
356 | - >>> rosetta_admin_browser.getLink("define custom language codes").click() |
357 | - >>> custom_language_codes_page = rosetta_admin_browser.url |
358 | + >>> translations_browser.getLink("define custom language codes").click() |
359 | + >>> custom_language_codes_page = translations_browser.url |
360 | |
361 | - >>> tag = find_tag_by_id(rosetta_admin_browser.contents, 'empty') |
362 | + >>> tag = find_tag_by_id(translations_browser.contents, 'empty') |
363 | >>> print extract_text(tag.renderContents()) |
364 | No custom language codes have been defined. |
365 | |
366 | -Again, an admin can add a language code. |
367 | - |
368 | - >>> rosetta_admin_browser.getLink("Add a custom language code").click() |
369 | - >>> add_page = rosetta_admin_browser.url |
370 | - |
371 | - >>> rosetta_admin_browser.getControl("Language code:").value = 'pt-br' |
372 | - >>> rosetta_admin_browser.getControl("Language:").value = ['pt_BR'] |
373 | - >>> rosetta_admin_browser.getControl("Add").click() |
374 | +A translations admin can add a language code. |
375 | + |
376 | + >>> translations_browser.getLink("Add a custom language code").click() |
377 | + >>> add_page = translations_browser.url |
378 | + |
379 | + >>> translations_browser.getControl("Language code:").value = 'pt-br' |
380 | + >>> translations_browser.getControl("Language:").value = ['pt_BR'] |
381 | + >>> translations_browser.getControl("Add").click() |
382 | |
383 | The language code is displayed. |
384 | |
385 | - >>> tag = find_tag_by_id(rosetta_admin_browser.contents, 'nonempty') |
386 | + >>> tag = find_tag_by_id(translations_browser.contents, 'nonempty') |
387 | >>> print extract_text(tag.renderContents()) |
388 | bar in distro uses the following custom language codes: |
389 | Code... ...maps to language |
390 | @@ -259,15 +266,15 @@ |
391 | It's also displayed identically on the same package but in another |
392 | release series of the same distribution. |
393 | |
394 | - >>> rosetta_admin_browser.open(page_in_other_series) |
395 | - >>> tag = find_custom_language_codes_link(rosetta_admin_browser) |
396 | + >>> translations_browser.open(page_in_other_series) |
397 | + >>> tag = find_custom_language_codes_link(translations_browser) |
398 | >>> print extract_text(tag.renderContents()) |
399 | If necessary, you may |
400 | define custom language codes |
401 | for this package. |
402 | |
403 | - >>> rosetta_admin_browser.getLink("define custom language codes").click() |
404 | - >>> tag = find_tag_by_id(rosetta_admin_browser.contents, 'nonempty') |
405 | + >>> translations_browser.getLink("define custom language codes").click() |
406 | + >>> tag = find_tag_by_id(translations_browser.contents, 'nonempty') |
407 | >>> print extract_text(tag.renderContents()) |
408 | bar in distro uses the following custom language codes: |
409 | Code... ...maps to language |
410 | @@ -276,13 +283,13 @@ |
411 | |
412 | The new code has a link there... |
413 | |
414 | - >>> rosetta_admin_browser.getLink("pt-br").click() |
415 | + >>> translations_browser.getLink("pt-br").click() |
416 | |
417 | ...and can be deleted. |
418 | |
419 | - >>> rosetta_admin_browser.getLink("remove custom language code").click() |
420 | - >>> rosetta_admin_browser.getControl("Remove").click() |
421 | + >>> translations_browser.getLink("remove custom language code").click() |
422 | + >>> translations_browser.getControl("Remove").click() |
423 | |
424 | - >>> tag = find_tag_by_id(rosetta_admin_browser.contents, 'empty') |
425 | + >>> tag = find_tag_by_id(translations_browser.contents, 'empty') |
426 | >>> print extract_text(tag.renderContents()) |
427 | No custom language codes have been defined. |
Here is the incremental diff in relation to Adi's branch. This MP is only
about these changes.