Merge lp:~adiroiban/launchpad/bug-509252-take-2 into lp:launchpad
- bug-509252-take-2
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Henning Eggers | ||||
Approved revision: | not available | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~adiroiban/launchpad/bug-509252-take-2 | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
760 lines (+184/-213) 11 files modified
lib/canonical/launchpad/security.py (+0/-15) lib/lp/registry/browser/distroseries.py (+9/-17) lib/lp/registry/browser/sourcepackage.py (+8/-4) lib/lp/registry/doc/distroseries.txt (+18/-76) lib/lp/registry/interfaces/distroseries.py (+4/-13) lib/lp/registry/model/distroseries.py (+12/-37) lib/lp/translations/browser/browser_helpers.py (+4/-6) lib/lp/translations/browser/distroseries.py (+52/-29) lib/lp/translations/browser/tests/distroseries-views.txt (+69/-11) lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt (+3/-5) lib/lp/translations/templates/distroseries-langchart.pt (+5/-0) |
||||
To merge this branch: | bzr merge lp:~adiroiban/launchpad/bug-509252-take-2 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Henning Eggers (community) | code | Approve | |
Michael Nelson (community) | code | Needs Information | |
Review via email: mp+19484@code.launchpad.net |
Commit message
Refactor checkTranslatio
Description of the change
Adi Roiban (adiroiban) wrote : | # |
Michael Nelson (michael.nelson) wrote : | # |
Hi Adi,
Thanks for all the cleanups that you've done in this branch.
After reading the recent email discussion at:
https:/
and particularly BjornT's message:
https:/
I don't think putting the security check into a browser helper is the way to go.
As outlined in the above email:
"The current solution is to have this code in model code, and have the
security adapter ask the model. No code duplication really."
this ensures that any security can be applied not only in the view, but also via other modes of access (API), even if they are not yet enabled.
As it turns out in your code, it seems most of IDistroSeries.
So I wonder if we can do something like this:
http://
That is, simply add a launchpad.View check for IDistroSeriesLa
if not check_permission(
could be replaced simply by:
if not check_permission(
that way you'd have all the security in the right place, separate from the error generation (ie. translation_
I'm not 100%, but as far as I can see, the above should do what you're after while at the same time keeping all the security code in the one place. But I'll check with Henninge to be sure (hence marking this as needs info).
IRC log of conversation starts at:
http://
Michael Nelson (michael.nelson) wrote : | # |
OK, I spoke to Adi, and he said that the security for IDistroSeriesLa
Without this, I can't see a way to keep these security checks encapsulated in security.py or on the model. I'm going to have to defer to Danilo or Henninge.
Henning Eggers (henninge) wrote : | # |
Hi Adi,
thanks for cleaning this up. Your code is almost good, I'd just ask two changes of you.
I agree with Michael's point that check_distroser
The code in check_distroser
Something that was already wrong before this branch but that should be cleaned up is this: DistroSeriesVie
So my first request is that you find a new implementation for DistroSeriesVie
My other request is that you move check_distroser
Finally I noticed DistroSeries.
Thanks. ;-)
Adi Roiban (adiroiban) wrote : | # |
DistroSeriesVie
<metal:
If we want to remove this behavior, we would need to raise those exception in Distribution URL traversal for series... and then things will get a bit complicated as those exception should be raised only for „translations” facet.
-------
There are a couple of view tests in translations/
-----
Thanks for the getDistroSeries
Henning Eggers (henninge) wrote : | # |
Hi Adi,
sorry for not replying but I had expected that you push an updated branch with whatever changes you see possible.
I did not know that metal tags are being used to raise exceptions and I am pretty sure that should not be that way. If you think it's too much work to improve that in this branch, I can understand that. Please file a bug so that it gets done at some later time.
So, a new push to the branch would be the next thing to get the review forward.
Cheers,
Henning
Adi Roiban (adiroiban) wrote : | # |
Hi,
Don't worry about the communication problem ... be happy :)
I have opened a new bug for TAL expression used for raising an exception.
I have merged the tests for translation permissions.
Here is the diff. Thanks!
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -37,10 +37,8 @@
from lp.services.
from lp.registry.
from lp.registry.
-from lp.translations
+from lp.translations
check_
-from lp.translations
- IDistroSeriesLa
from lp.services.
from lp.registry.
Structural
@@ -81,19 +79,12 @@
except IndexError:
# Unknown language code.
raise NotFoundError
- distroserieslang = self.context.
- if distroserieslang is None:
- # There is no IDistroSeriesLa
- # but we still need to list it as an available language, so we
- # generate a dummy one so users have a chance to get to it in the
- # navigation and start adding translations for it.
- distroserieslangset = getUtility(
- distroserieslang = distroserieslan
- self.context, lang)
+ distroserieslang = self.context.
# Check if user is able to view the translations for
- # this distribution series
+ # this distribution series language.
+ # If not, raise TranslationUnav
return distroserieslang
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -40,7 +40,7 @@
from lp.registry.
from lp.registry.
from lp.registry.
-from lp.translations
+from lp.translations
check_
from lp.translations
from canonical.launchpad import _
@@ -68,7 +68,8 @@
# If we are able to view the translations for distribution series
# we should also be allowed to see them for a distribution
- # source package
+ # source package.
+ # If not, raise TranslationUnav
return sourcepackage_pots
=== modified file 'lib/lp/
Henning Eggers (henninge) wrote : | # |
Thanks, this looks much better. Unfortunately the diff shows conflicts, so I guess you need to merge devel and push again for the diff to be correct.
Also, is there no unit test that could go through all the different SeriesStatus values? Doing this in a doc test looks like it's in the wrong place. But please only do this if it is fairly easy. I think this branch is already old enough ... ;-)
Adi Roiban (adiroiban) wrote : | # |
În data de Mi, 24-02-2010 la 17:42 +0000, Henning Eggers a scris:
> Thanks, this looks much better. Unfortunately the diff shows
> conflicts, so I guess you need to merge devel and push again for the
> diff to be correct.
Conflict solved.
> Also, is there no unit test that could go through all the different
> SeriesStatus values? Doing this in a doc test looks like it's in the
> wrong place. But please only do this if it is fairly easy. I think
> this branch is already old enough ... ;-)
I was thinking that view unit tests should be put in:
lib/lp/
translations/
I have just moved those tests from registry/
file.
Are you saying that we should not write unit tests using doctest format?
Cheers
--
Adi Roiban
Henning Eggers (henninge) wrote : | # |
> Conflict solved.
Cool, looks good. ;)
> Are you saying that we should not write unit tests using doctest format?
Yes, at least that's what I do. They execute slower, don't they? But as you pointed out, the test was already there, so you just extended it. That's what I do, too ... ;) So you can keep it as it is if you want to.
Thanks for the extra comments in the code, btw. And all the clean-up work!
You have a double "allowed allowed" in there somewhere, btw.
Cheers,
Henning
Adi Roiban (adiroiban) wrote : | # |
În data de Mi, 24-02-2010 la 18:27 +0000, Henning Eggers a scris:
> Review: Approve code
> > Conflict solved.
>
> Cool, looks good. ;)
>
>
> > Are you saying that we should not write unit tests using doctest format?
>
> Yes, at least that's what I do. They execute slower, don't they? But
> as you pointed out, the test was already there, so you just extended
> it. That's what I do, too ... ;) So you can keep it as it is if you
> want to.
OK. This is not the only unit test written in doctest format.
$ ls lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
Maybe we should open a bug to convert all those test from doctest to
"pure" python code.
> Thanks for the extra comments in the code, btw. And all the clean-up
> work!
> You have a double "allowed allowed" in there somewhere, btw.
Typo fixed and pushed.
When you have time, can you please send this branch to ec2 test?
Kindest regards,
--
Adi Roiban
Henning Eggers (henninge) wrote : | # |
I am having trouble landing this. First, there is the current problem with ec2 test not sending emails, so the failure was kind of obscure. Secondly, after merging the current devel, it failed because "python-
Now, this error remains when running "make build":
ZopeXMLConf
ImportError: cannot import name check_distroser
Adi Roiban (adiroiban) wrote : | # |
Hm...
I did not get the „python-
I have fixed the problem and pushed the branch.
Preview Diff
1 | === modified file 'lib/canonical/launchpad/security.py' | |||
2 | --- lib/canonical/launchpad/security.py 2010-02-24 23:02:56 +0000 | |||
3 | +++ lib/canonical/launchpad/security.py 2010-03-01 23:03:20 +0000 | |||
4 | @@ -49,8 +49,6 @@ | |||
5 | 49 | from lp.registry.interfaces.distributionsourcepackage import ( | 49 | from lp.registry.interfaces.distributionsourcepackage import ( |
6 | 50 | IDistributionSourcePackage) | 50 | IDistributionSourcePackage) |
7 | 51 | from lp.registry.interfaces.distroseries import IDistroSeries | 51 | from lp.registry.interfaces.distroseries import IDistroSeries |
8 | 52 | from lp.translations.interfaces.distroserieslanguage import ( | ||
9 | 53 | IDistroSeriesLanguage) | ||
10 | 54 | from lp.registry.interfaces.entitlement import IEntitlement | 52 | from lp.registry.interfaces.entitlement import IEntitlement |
11 | 55 | from lp.hardwaredb.interfaces.hwdb import ( | 53 | from lp.hardwaredb.interfaces.hwdb import ( |
12 | 56 | IHWDBApplication, IHWDevice, IHWDeviceClass, IHWDriver, IHWDriverName, | 54 | IHWDBApplication, IHWDevice, IHWDeviceClass, IHWDriver, IHWDriverName, |
13 | @@ -1664,19 +1662,6 @@ | |||
14 | 1664 | self.obj.distribution).checkAuthenticated(user)) | 1662 | self.obj.distribution).checkAuthenticated(user)) |
15 | 1665 | 1663 | ||
16 | 1666 | 1664 | ||
17 | 1667 | class AdminDistroSeriesLanguage(AuthorizationBase): | ||
18 | 1668 | permission = 'launchpad.TranslationsAdmin' | ||
19 | 1669 | usedfor = IDistroSeriesLanguage | ||
20 | 1670 | |||
21 | 1671 | def checkAuthenticated(self, user): | ||
22 | 1672 | """Is the user able to manage `IDistroSeriesLanguage` translations. | ||
23 | 1673 | |||
24 | 1674 | Distribution managers can also manage IDistroSeriesLanguage | ||
25 | 1675 | """ | ||
26 | 1676 | return (AdminDistroSeriesTranslations( | ||
27 | 1677 | self.obj.distroseries).checkAuthenticated(user)) | ||
28 | 1678 | |||
29 | 1679 | |||
30 | 1680 | class BranchSubscriptionEdit(AuthorizationBase): | 1665 | class BranchSubscriptionEdit(AuthorizationBase): |
31 | 1681 | permission = 'launchpad.Edit' | 1666 | permission = 'launchpad.Edit' |
32 | 1682 | usedfor = IBranchSubscription | 1667 | usedfor = IBranchSubscription |
33 | 1683 | 1668 | ||
34 | === modified file 'lib/lp/registry/browser/distroseries.py' | |||
35 | --- lib/lp/registry/browser/distroseries.py 2010-02-26 19:15:17 +0000 | |||
36 | +++ lib/lp/registry/browser/distroseries.py 2010-03-01 23:03:20 +0000 | |||
37 | @@ -37,8 +37,8 @@ | |||
38 | 37 | from lp.services.worlddata.interfaces.country import ICountry | 37 | from lp.services.worlddata.interfaces.country import ICountry |
39 | 38 | from lp.registry.interfaces.series import SeriesStatus | 38 | from lp.registry.interfaces.series import SeriesStatus |
40 | 39 | from lp.registry.interfaces.distroseries import IDistroSeries | 39 | from lp.registry.interfaces.distroseries import IDistroSeries |
43 | 40 | from lp.translations.interfaces.distroserieslanguage import ( | 40 | from lp.translations.browser.distroseries import ( |
44 | 41 | IDistroSeriesLanguageSet) | 41 | check_distroseries_translations_viewable) |
45 | 42 | from lp.services.worlddata.interfaces.language import ILanguageSet | 42 | from lp.services.worlddata.interfaces.language import ILanguageSet |
46 | 43 | from lp.registry.browser.structuralsubscription import ( | 43 | from lp.registry.browser.structuralsubscription import ( |
47 | 44 | StructuralSubscriptionMenuMixin, | 44 | StructuralSubscriptionMenuMixin, |
48 | @@ -48,7 +48,6 @@ | |||
49 | 48 | from canonical.launchpad.webapp import ( | 48 | from canonical.launchpad.webapp import ( |
50 | 49 | StandardLaunchpadFacets, GetitemNavigation, action, custom_widget) | 49 | StandardLaunchpadFacets, GetitemNavigation, action, custom_widget) |
51 | 50 | from canonical.launchpad.webapp.batching import BatchNavigator | 50 | from canonical.launchpad.webapp.batching import BatchNavigator |
52 | 51 | from canonical.launchpad.webapp.authorization import check_permission | ||
53 | 52 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb | 51 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb |
54 | 53 | from canonical.launchpad.webapp.launchpadform import ( | 52 | from canonical.launchpad.webapp.launchpadform import ( |
55 | 54 | LaunchpadEditFormView, LaunchpadFormView) | 53 | LaunchpadEditFormView, LaunchpadFormView) |
56 | @@ -80,20 +79,13 @@ | |||
57 | 80 | except IndexError: | 79 | except IndexError: |
58 | 81 | # Unknown language code. | 80 | # Unknown language code. |
59 | 82 | raise NotFoundError | 81 | raise NotFoundError |
74 | 83 | distroserieslang = self.context.getDistroSeriesLanguage(lang) | 82 | |
75 | 84 | 83 | distroserieslang = self.context.getDistroSeriesLanguageOrDummy(lang) | |
76 | 85 | if distroserieslang is None: | 84 | |
77 | 86 | # There is no IDistroSeriesLanguage yet for this IDistroSeries, | 85 | # Check if user is able to view the translations for |
78 | 87 | # but we still need to list it as an available language, so we | 86 | # this distribution series language. |
79 | 88 | # generate a dummy one so users have a chance to get to it in the | 87 | # If not, raise TranslationUnavailable. |
80 | 89 | # navigation and start adding translations for it. | 88 | check_distroseries_translations_viewable(self.context) |
67 | 90 | distroserieslangset = getUtility(IDistroSeriesLanguageSet) | ||
68 | 91 | distroserieslang = distroserieslangset.getDummy( | ||
69 | 92 | self.context, lang) | ||
70 | 93 | |||
71 | 94 | if not check_permission( | ||
72 | 95 | 'launchpad.TranslationsAdmin', distroserieslang): | ||
73 | 96 | self.context.checkTranslationsViewable() | ||
81 | 97 | 89 | ||
82 | 98 | return distroserieslang | 90 | return distroserieslang |
83 | 99 | 91 | ||
84 | 100 | 92 | ||
85 | === modified file 'lib/lp/registry/browser/sourcepackage.py' | |||
86 | --- lib/lp/registry/browser/sourcepackage.py 2010-02-23 19:43:58 +0000 | |||
87 | +++ lib/lp/registry/browser/sourcepackage.py 2010-03-01 23:03:20 +0000 | |||
88 | @@ -52,7 +52,6 @@ | |||
89 | 52 | action, ApplicationMenu, custom_widget, GetitemNavigation, | 52 | action, ApplicationMenu, custom_widget, GetitemNavigation, |
90 | 53 | LaunchpadFormView, Link, redirection, StandardLaunchpadFacets, stepto) | 53 | LaunchpadFormView, Link, redirection, StandardLaunchpadFacets, stepto) |
91 | 54 | from canonical.launchpad.webapp import canonical_url | 54 | from canonical.launchpad.webapp import canonical_url |
92 | 55 | from canonical.launchpad.webapp.authorization import check_permission | ||
93 | 56 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb | 55 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb |
94 | 57 | from canonical.launchpad.webapp.menu import structured | 56 | from canonical.launchpad.webapp.menu import structured |
95 | 58 | 57 | ||
96 | @@ -70,9 +69,13 @@ | |||
97 | 70 | distroseries=self.context.distroseries, | 69 | distroseries=self.context.distroseries, |
98 | 71 | sourcepackagename=self.context.sourcepackagename) | 70 | sourcepackagename=self.context.sourcepackagename) |
99 | 72 | 71 | ||
103 | 73 | if not check_permission( | 72 | # If we are able to view the translations for distribution series |
104 | 74 | 'launchpad.TranslationsAdmin', sourcepackage_pots): | 73 | # we should also be allowed to see them for a distribution |
105 | 75 | self.context.distroseries.checkTranslationsViewable() | 74 | # source package. |
106 | 75 | # If not, raise TranslationUnavailable. | ||
107 | 76 | from lp.translations.browser.distroseries import ( | ||
108 | 77 | check_distroseries_translations_viewable) | ||
109 | 78 | check_distroseries_translations_viewable(self.context.distroseries) | ||
110 | 76 | 79 | ||
111 | 77 | return sourcepackage_pots | 80 | return sourcepackage_pots |
112 | 78 | 81 | ||
113 | @@ -92,6 +95,7 @@ | |||
114 | 92 | 95 | ||
115 | 93 | class SourcePackageBreadcrumb(Breadcrumb): | 96 | class SourcePackageBreadcrumb(Breadcrumb): |
116 | 94 | """Builds a breadcrumb for an `ISourcePackage`.""" | 97 | """Builds a breadcrumb for an `ISourcePackage`.""" |
117 | 98 | |||
118 | 95 | @property | 99 | @property |
119 | 96 | def text(self): | 100 | def text(self): |
120 | 97 | return smartquote('"%s" source package') % (self.context.name) | 101 | return smartquote('"%s" source package') % (self.context.name) |
121 | 98 | 102 | ||
122 | === modified file 'lib/lp/registry/doc/distroseries.txt' | |||
123 | --- lib/lp/registry/doc/distroseries.txt 2010-02-25 16:27:34 +0000 | |||
124 | +++ lib/lp/registry/doc/distroseries.txt 2010-03-01 23:03:20 +0000 | |||
125 | @@ -1,4 +1,5 @@ | |||
127 | 1 | = DistroSeries = | 1 | DistroSeries |
128 | 2 | ============ | ||
129 | 2 | 3 | ||
130 | 3 | From the DerivationOverview spec | 4 | From the DerivationOverview spec |
131 | 4 | <https://launchpad.canonical.com/DerivationOverview>: | 5 | <https://launchpad.canonical.com/DerivationOverview>: |
132 | @@ -200,7 +201,8 @@ | |||
133 | 200 | True | 201 | True |
134 | 201 | 202 | ||
135 | 202 | 203 | ||
137 | 203 | == Package searching == | 204 | Package searching |
138 | 205 | ----------------- | ||
139 | 204 | 206 | ||
140 | 205 | You can search through binary packages publishing in a distribution | 207 | You can search through binary packages publishing in a distribution |
141 | 206 | release by using the searchPackages method, which uses magical fti: | 208 | release by using the searchPackages method, which uses magical fti: |
142 | @@ -223,7 +225,8 @@ | |||
143 | 223 | DistroSeriesBinaryPackage: at | 225 | DistroSeriesBinaryPackage: at |
144 | 224 | 226 | ||
145 | 225 | 227 | ||
147 | 226 | == DistroSeriess have components and sections == | 228 | DistroSeriess have components and sections |
148 | 229 | ------------------------------------------ | ||
149 | 227 | 230 | ||
150 | 228 | A distroseries has some number of components and/or sections which | 231 | A distroseries has some number of components and/or sections which |
151 | 229 | are valid for that distroseries. These selections are used by (among | 232 | are valid for that distroseries. These selections are used by (among |
152 | @@ -286,7 +289,8 @@ | |||
153 | 286 | partner | 289 | partner |
154 | 287 | 290 | ||
155 | 288 | 291 | ||
157 | 289 | == DistroSeries can be initialised from their parents == | 292 | DistroSeries can be initialised from their parents |
158 | 293 | -------------------------------------------------- | ||
159 | 290 | 294 | ||
160 | 291 | When a distroseries is derived from another distroseries (be it a | 295 | When a distroseries is derived from another distroseries (be it a |
161 | 292 | derivative distribution, or simply the next release in a sequence from | 296 | derivative distribution, or simply the next release in a sequence from |
162 | @@ -396,74 +400,8 @@ | |||
163 | 396 | netapplet | 400 | netapplet |
164 | 397 | 401 | ||
165 | 398 | 402 | ||
234 | 399 | Hiding translations | 403 | Translatable Packages and Packaging |
235 | 400 | ------------------- | 404 | ----------------------------------- |
168 | 401 | |||
169 | 402 | The hide_all_translations flag, if set, hides a distroseries' | ||
170 | 403 | translations in the user interface. The check for visibility happens in | ||
171 | 404 | checkTranslationsViewable. | ||
172 | 405 | |||
173 | 406 | >>> untranslatable_series = factory.makeDistroRelease() | ||
174 | 407 | >>> untranslatable_series.hide_all_translations = False | ||
175 | 408 | |||
176 | 409 | When translations are visible, checkTranslationsViewable() completes | ||
177 | 410 | normally. | ||
178 | 411 | |||
179 | 412 | >>> from canonical.launchpad.webapp.interfaces import ( | ||
180 | 413 | ... TranslationUnavailable) | ||
181 | 414 | >>> def get_visibility_notice(series): | ||
182 | 415 | ... """Print the notice about series' translations being hidden.""" | ||
183 | 416 | ... try: | ||
184 | 417 | ... series.checkTranslationsViewable() | ||
185 | 418 | ... except TranslationUnavailable, e: | ||
186 | 419 | ... return str(e) | ||
187 | 420 | ... return None | ||
188 | 421 | |||
189 | 422 | >>> print get_visibility_notice(untranslatable_series) | ||
190 | 423 | None | ||
191 | 424 | |||
192 | 425 | But when translations are hidden, it raises TranslationUnavailable. | ||
193 | 426 | |||
194 | 427 | >>> untranslatable_series.hide_all_translations = True | ||
195 | 428 | >>> print get_visibility_notice(untranslatable_series) | ||
196 | 429 | Translations for this release series... | ||
197 | 430 | |||
198 | 431 | Exactly what message is displayed depends on the series' status. | ||
199 | 432 | |||
200 | 433 | >>> untranslatable_series.status = SeriesStatus.EXPERIMENTAL | ||
201 | 434 | >>> print get_visibility_notice(untranslatable_series) | ||
202 | 435 | Translations for this release series are not available yet. | ||
203 | 436 | |||
204 | 437 | >>> untranslatable_series.status = SeriesStatus.DEVELOPMENT | ||
205 | 438 | >>> print get_visibility_notice(untranslatable_series) | ||
206 | 439 | Translations for this release series are not available yet. | ||
207 | 440 | |||
208 | 441 | >>> untranslatable_series.status = SeriesStatus.FROZEN | ||
209 | 442 | >>> print get_visibility_notice(untranslatable_series) | ||
210 | 443 | Translations for this release series are not currently available. | ||
211 | 444 | Please come back soon. | ||
212 | 445 | |||
213 | 446 | >>> untranslatable_series.status = SeriesStatus.CURRENT | ||
214 | 447 | >>> print get_visibility_notice(untranslatable_series) | ||
215 | 448 | Translations for this release series are not currently available. | ||
216 | 449 | Please come back soon. | ||
217 | 450 | |||
218 | 451 | >>> untranslatable_series.status = SeriesStatus.SUPPORTED | ||
219 | 452 | >>> print get_visibility_notice(untranslatable_series) | ||
220 | 453 | Translations for this release series are not currently available. | ||
221 | 454 | Please come back soon. | ||
222 | 455 | |||
223 | 456 | >>> untranslatable_series.status = SeriesStatus.OBSOLETE | ||
224 | 457 | >>> print get_visibility_notice(untranslatable_series) | ||
225 | 458 | This release series is obsolete. Its translations are no longer | ||
226 | 459 | available. | ||
227 | 460 | |||
228 | 461 | >>> untranslatable_series.status = SeriesStatus.FUTURE | ||
229 | 462 | >>> print get_visibility_notice(untranslatable_series) | ||
230 | 463 | Translations for this release series are not available yet. | ||
231 | 464 | |||
232 | 465 | |||
233 | 466 | == Translatable Packages and Packaging == | ||
236 | 467 | 405 | ||
237 | 468 | You can easily find out what packages are translatable in a | 406 | You can easily find out what packages are translatable in a |
238 | 469 | distribution release: | 407 | distribution release: |
239 | @@ -668,7 +606,8 @@ | |||
240 | 668 | True | 606 | True |
241 | 669 | 607 | ||
242 | 670 | 608 | ||
244 | 671 | == SourcePackagePublishingHistory == | 609 | SourcePackagePublishingHistory |
245 | 610 | ------------------------------ | ||
246 | 672 | 611 | ||
247 | 673 | IDistroSeries.getSourcePackagePublishing returns all the ISPPH | 612 | IDistroSeries.getSourcePackagePublishing returns all the ISPPH |
248 | 674 | records for a given status in a given pocket. It makes easy to | 613 | records for a given status in a given pocket. It makes easy to |
249 | @@ -1042,7 +981,8 @@ | |||
250 | 1042 | thinclient-local-devices | 981 | thinclient-local-devices |
251 | 1043 | 982 | ||
252 | 1044 | 983 | ||
254 | 1045 | = Drivers = | 984 | Drivers |
255 | 985 | ======= | ||
256 | 1046 | 986 | ||
257 | 1047 | Distributions have drivers, who are people that have permission to approve | 987 | Distributions have drivers, who are people that have permission to approve |
258 | 1048 | bugs and features for specific releases. The rules are that: | 988 | bugs and features for specific releases. The rules are that: |
259 | @@ -1124,7 +1064,8 @@ | |||
260 | 1124 | mark | 1064 | mark |
261 | 1125 | 1065 | ||
262 | 1126 | 1066 | ||
264 | 1127 | == Lastest Uploads == | 1067 | Lastest Uploads |
265 | 1068 | --------------- | ||
266 | 1128 | 1069 | ||
267 | 1129 | IDistroSeries provides the 'getLatestUpload' method which returns a | 1070 | IDistroSeries provides the 'getLatestUpload' method which returns a |
268 | 1130 | list of the last 5 (five) IDistroSeriesSourcePackageRelease (IDRSPR) | 1071 | list of the last 5 (five) IDistroSeriesSourcePackageRelease (IDRSPR) |
269 | @@ -1150,7 +1091,8 @@ | |||
270 | 1150 | 0 | 1091 | 0 |
271 | 1151 | 1092 | ||
272 | 1152 | 1093 | ||
274 | 1153 | == Getting build records for a distro series == | 1094 | Getting build records for a distro series |
275 | 1095 | ----------------------------------------- | ||
276 | 1154 | 1096 | ||
277 | 1155 | IDistroSeries inherits the IHasBuildRecords interfaces and therefore provides | 1097 | IDistroSeries inherits the IHasBuildRecords interfaces and therefore provides |
278 | 1156 | a getBuildRecords() method. | 1098 | a getBuildRecords() method. |
279 | 1157 | 1099 | ||
280 | === modified file 'lib/lp/registry/interfaces/distroseries.py' | |||
281 | --- lib/lp/registry/interfaces/distroseries.py 2010-02-27 10:19:18 +0000 | |||
282 | +++ lib/lp/registry/interfaces/distroseries.py 2010-03-01 23:03:20 +0000 | |||
283 | @@ -54,6 +54,7 @@ | |||
284 | 54 | from lp.translations.interfaces.languagepack import ILanguagePack | 54 | from lp.translations.interfaces.languagepack import ILanguagePack |
285 | 55 | 55 | ||
286 | 56 | 56 | ||
287 | 57 | |||
288 | 57 | class DistroSeriesNameField(ContentNameField): | 58 | class DistroSeriesNameField(ContentNameField): |
289 | 58 | """A class to ensure `IDistroSeries` has unique names.""" | 59 | """A class to ensure `IDistroSeries` has unique names.""" |
290 | 59 | errormessage = _("%s is already in use by another series.") | 60 | errormessage = _("%s is already in use by another series.") |
291 | @@ -132,6 +133,7 @@ | |||
292 | 132 | 133 | ||
293 | 133 | class IDistroSeriesEditRestricted(Interface): | 134 | class IDistroSeriesEditRestricted(Interface): |
294 | 134 | """IDistroSeries properties which require launchpad.Edit.""" | 135 | """IDistroSeries properties which require launchpad.Edit.""" |
295 | 136 | |||
296 | 135 | @rename_parameters_as(dateexpected='date_targeted') | 137 | @rename_parameters_as(dateexpected='date_targeted') |
297 | 136 | @export_factory_operation( | 138 | @export_factory_operation( |
298 | 137 | IMilestone, ['name', 'dateexpected', 'summary', 'code_name']) | 139 | IMilestone, ['name', 'dateexpected', 'summary', 'code_name']) |
299 | @@ -198,7 +200,7 @@ | |||
300 | 198 | Interface, # Really IDistribution, see circular import fix below. | 200 | Interface, # Really IDistribution, see circular import fix below. |
301 | 199 | title=_("Distribution"), required=True, | 201 | title=_("Distribution"), required=True, |
302 | 200 | description=_("The distribution for which this is a series."))) | 202 | description=_("The distribution for which this is a series."))) |
304 | 201 | named_version = Attribute('The combined display name and version.') | 203 | named_version = Attribute('The combined display name and version.') |
305 | 202 | parent = Attribute('The structural parent of this series - the distro') | 204 | parent = Attribute('The structural parent of this series - the distro') |
306 | 203 | components = Attribute("The series components.") | 205 | components = Attribute("The series components.") |
307 | 204 | upload_components = Attribute("The series components that can be " | 206 | upload_components = Attribute("The series components that can be " |
308 | @@ -240,7 +242,7 @@ | |||
309 | 240 | title=_("Defer translation imports"), | 242 | title=_("Defer translation imports"), |
310 | 241 | description=_("Suspends any translation imports for this series"), | 243 | description=_("Suspends any translation imports for this series"), |
311 | 242 | default=True, | 244 | default=True, |
313 | 243 | required=True | 245 | required=True, |
314 | 244 | ) | 246 | ) |
315 | 245 | binarycount = Attribute("Binary Packages Counter") | 247 | binarycount = Attribute("Binary Packages Counter") |
316 | 246 | 248 | ||
317 | @@ -497,17 +499,6 @@ | |||
318 | 497 | :return: A result set containing `IPackageUpload` | 499 | :return: A result set containing `IPackageUpload` |
319 | 498 | """ | 500 | """ |
320 | 499 | 501 | ||
321 | 500 | def checkTranslationsViewable(): | ||
322 | 501 | """Raise `TranslationUnavailable` if translations are hidden. | ||
323 | 502 | |||
324 | 503 | Checks the `hide_all_translations` flag. If it is set, these | ||
325 | 504 | translations are not to be shown to the public. In that case an | ||
326 | 505 | appropriate message is composed based on the series' `status`, | ||
327 | 506 | and a `TranslationUnavailable` exception is raised. | ||
328 | 507 | |||
329 | 508 | Simply returns if translations are not hidden. | ||
330 | 509 | """ | ||
331 | 510 | |||
332 | 511 | def getUnlinkedTranslatableSourcePackages(): | 502 | def getUnlinkedTranslatableSourcePackages(): |
333 | 512 | """Return a list of source packages that can be translated in | 503 | """Return a list of source packages that can be translated in |
334 | 513 | this distribution series but which lack Packaging links. | 504 | this distribution series but which lack Packaging links. |
335 | 514 | 505 | ||
336 | === modified file 'lib/lp/registry/model/distroseries.py' | |||
337 | --- lib/lp/registry/model/distroseries.py 2010-02-25 16:27:34 +0000 | |||
338 | +++ lib/lp/registry/model/distroseries.py 2010-03-01 23:03:20 +0000 | |||
339 | @@ -118,8 +118,7 @@ | |||
340 | 118 | from canonical.launchpad.mail import signed_message_from_string | 118 | from canonical.launchpad.mail import signed_message_from_string |
341 | 119 | from lp.registry.interfaces.person import validate_public_person | 119 | from lp.registry.interfaces.person import validate_public_person |
342 | 120 | from canonical.launchpad.webapp.interfaces import ( | 120 | from canonical.launchpad.webapp.interfaces import ( |
345 | 121 | IStoreSelector, MAIN_STORE, NotFoundError, SLAVE_FLAVOR, | 121 | IStoreSelector, MAIN_STORE, NotFoundError, SLAVE_FLAVOR) |
344 | 122 | TranslationUnavailable) | ||
346 | 123 | from lp.soyuz.interfaces.sourcepackageformat import ( | 122 | from lp.soyuz.interfaces.sourcepackageformat import ( |
347 | 124 | ISourcePackageFormatSelectionSet) | 123 | ISourcePackageFormatSelectionSet) |
348 | 125 | 124 | ||
349 | @@ -134,7 +133,7 @@ | |||
350 | 134 | SeriesStatus.DEVELOPMENT, | 133 | SeriesStatus.DEVELOPMENT, |
351 | 135 | SeriesStatus.FROZEN, | 134 | SeriesStatus.FROZEN, |
352 | 136 | SeriesStatus.CURRENT, | 135 | SeriesStatus.CURRENT, |
354 | 137 | SeriesStatus.SUPPORTED | 136 | SeriesStatus.SUPPORTED, |
355 | 138 | ] | 137 | ] |
356 | 139 | 138 | ||
357 | 140 | 139 | ||
358 | @@ -162,7 +161,7 @@ | |||
359 | 162 | dbName='releasestatus', notNull=True, schema=SeriesStatus) | 161 | dbName='releasestatus', notNull=True, schema=SeriesStatus) |
360 | 163 | date_created = UtcDateTimeCol(notNull=False, default=UTC_NOW) | 162 | date_created = UtcDateTimeCol(notNull=False, default=UTC_NOW) |
361 | 164 | datereleased = UtcDateTimeCol(notNull=False, default=None) | 163 | datereleased = UtcDateTimeCol(notNull=False, default=None) |
363 | 165 | parent_series = ForeignKey( | 164 | parent_series = ForeignKey( |
364 | 166 | dbName='parent_series', foreignKey='DistroSeries', notNull=False) | 165 | dbName='parent_series', foreignKey='DistroSeries', notNull=False) |
365 | 167 | owner = ForeignKey( | 166 | owner = ForeignKey( |
366 | 168 | dbName='owner', foreignKey='Person', | 167 | dbName='owner', foreignKey='Person', |
367 | @@ -173,7 +172,7 @@ | |||
368 | 173 | lucilleconfig = StringCol(notNull=False, default=None) | 172 | lucilleconfig = StringCol(notNull=False, default=None) |
369 | 174 | changeslist = StringCol(notNull=False, default=None) | 173 | changeslist = StringCol(notNull=False, default=None) |
370 | 175 | nominatedarchindep = ForeignKey( | 174 | nominatedarchindep = ForeignKey( |
372 | 176 | dbName='nominatedarchindep',foreignKey='DistroArchSeries', | 175 | dbName='nominatedarchindep', foreignKey='DistroArchSeries', |
373 | 177 | notNull=False, default=None) | 176 | notNull=False, default=None) |
374 | 178 | messagecount = IntCol(notNull=True, default=0) | 177 | messagecount = IntCol(notNull=True, default=0) |
375 | 179 | binarycount = IntCol(notNull=True, default=DEFAULT) | 178 | binarycount = IntCol(notNull=True, default=DEFAULT) |
376 | @@ -751,7 +750,7 @@ | |||
377 | 751 | 750 | ||
378 | 752 | # filter based on completion. see the implementation of | 751 | # filter based on completion. see the implementation of |
379 | 753 | # Specification.is_complete() for more details | 752 | # Specification.is_complete() for more details |
381 | 754 | completeness = Specification.completeness_clause | 753 | completeness = Specification.completeness_clause |
382 | 755 | 754 | ||
383 | 756 | if SpecificationFilter.COMPLETE in filter: | 755 | if SpecificationFilter.COMPLETE in filter: |
384 | 757 | query += ' AND ( %s ) ' % completeness | 756 | query += ' AND ( %s ) ' % completeness |
385 | @@ -886,29 +885,6 @@ | |||
386 | 886 | DistroSeriesSourcePackageRelease(self, release)) | 885 | DistroSeriesSourcePackageRelease(self, release)) |
387 | 887 | for release in releases) | 886 | for release in releases) |
388 | 888 | 887 | ||
389 | 889 | def checkTranslationsViewable(self): | ||
390 | 890 | """See `IDistroSeries`.""" | ||
391 | 891 | if not self.hide_all_translations: | ||
392 | 892 | # Yup, viewable. | ||
393 | 893 | return | ||
394 | 894 | |||
395 | 895 | future = [ | ||
396 | 896 | SeriesStatus.EXPERIMENTAL, | ||
397 | 897 | SeriesStatus.DEVELOPMENT, | ||
398 | 898 | SeriesStatus.FUTURE, | ||
399 | 899 | ] | ||
400 | 900 | if self.status in future: | ||
401 | 901 | raise TranslationUnavailable( | ||
402 | 902 | "Translations for this release series are not available yet.") | ||
403 | 903 | elif self.status == SeriesStatus.OBSOLETE: | ||
404 | 904 | raise TranslationUnavailable( | ||
405 | 905 | "This release series is obsolete. Its translations are no " | ||
406 | 906 | "longer available.") | ||
407 | 907 | else: | ||
408 | 908 | raise TranslationUnavailable( | ||
409 | 909 | "Translations for this release series are not currently " | ||
410 | 910 | "available. Please come back soon.") | ||
411 | 911 | |||
412 | 912 | def getTranslatableSourcePackages(self): | 888 | def getTranslatableSourcePackages(self): |
413 | 913 | """See `IDistroSeries`.""" | 889 | """See `IDistroSeries`.""" |
414 | 914 | query = """ | 890 | query = """ |
415 | @@ -1069,13 +1045,12 @@ | |||
416 | 1069 | SourcePackagePublishingHistory.archive IN %s AND | 1045 | SourcePackagePublishingHistory.archive IN %s AND |
417 | 1070 | SourcePackagePublishingHistory.status=%s AND | 1046 | SourcePackagePublishingHistory.status=%s AND |
418 | 1071 | SourcePackagePublishingHistory.pocket=%s | 1047 | SourcePackagePublishingHistory.pocket=%s |
420 | 1072 | """ % sqlvalues(self, archives, status, pocket) | 1048 | """ % sqlvalues(self, archives, status, pocket) |
421 | 1073 | 1049 | ||
422 | 1074 | if component: | 1050 | if component: |
423 | 1075 | clause += ( | 1051 | clause += ( |
424 | 1076 | " AND SourcePackagePublishingHistory.component=%s" | 1052 | " AND SourcePackagePublishingHistory.component=%s" |
427 | 1077 | % sqlvalues(component) | 1053 | % sqlvalues(component)) |
426 | 1078 | ) | ||
428 | 1079 | 1054 | ||
429 | 1080 | orderBy = ['SourcePackageName.name'] | 1055 | orderBy = ['SourcePackageName.name'] |
430 | 1081 | clauseTables = ['SourcePackageRelease', 'SourcePackageName'] | 1056 | clauseTables = ['SourcePackageRelease', 'SourcePackageName'] |
431 | @@ -1136,7 +1111,7 @@ | |||
432 | 1136 | 1111 | ||
433 | 1137 | clauseTables = ['BinaryPackagePublishingHistory', 'DistroArchSeries', | 1112 | clauseTables = ['BinaryPackagePublishingHistory', 'DistroArchSeries', |
434 | 1138 | 'BinaryPackageRelease', 'BinaryPackageName', 'Build', | 1113 | 'BinaryPackageRelease', 'BinaryPackageName', 'Build', |
436 | 1139 | 'SourcePackageRelease', 'SourcePackageName' ] | 1114 | 'SourcePackageRelease', 'SourcePackageName'] |
437 | 1140 | 1115 | ||
438 | 1141 | result = BinaryPackagePublishingHistory.select( | 1116 | result = BinaryPackagePublishingHistory.select( |
439 | 1142 | query, distinct=False, clauseTables=clauseTables, orderBy=orderBy) | 1117 | query, distinct=False, clauseTables=clauseTables, orderBy=orderBy) |
440 | @@ -1971,8 +1946,9 @@ | |||
441 | 1971 | return '%s%s' % (self.name, pocketsuffix[pocket]) | 1946 | return '%s%s' % (self.name, pocketsuffix[pocket]) |
442 | 1972 | 1947 | ||
443 | 1973 | def isSourcePackageFormatPermitted(self, format): | 1948 | def isSourcePackageFormatPermitted(self, format): |
446 | 1974 | return getUtility(ISourcePackageFormatSelectionSet | 1949 | return getUtility( |
447 | 1975 | ).getBySeriesAndFormat(self, format) is not None | 1950 | ISourcePackageFormatSelectionSet).getBySeriesAndFormat( |
448 | 1951 | self, format) is not None | ||
449 | 1976 | 1952 | ||
450 | 1977 | 1953 | ||
451 | 1978 | class DistroSeriesSet: | 1954 | class DistroSeriesSet: |
452 | @@ -1990,8 +1966,7 @@ | |||
453 | 1990 | result_set = store.using((DistroSeries, POTemplate)).find( | 1966 | result_set = store.using((DistroSeries, POTemplate)).find( |
454 | 1991 | DistroSeries, | 1967 | DistroSeries, |
455 | 1992 | DistroSeries.hide_all_translations == False, | 1968 | DistroSeries.hide_all_translations == False, |
458 | 1993 | DistroSeries.id == POTemplate.distroseriesID | 1969 | DistroSeries.id == POTemplate.distroseriesID).config(distinct=True) |
457 | 1994 | ).config(distinct=True) | ||
459 | 1995 | # XXX: henninge 2009-02-11 bug=217644: Convert to sequence right here | 1970 | # XXX: henninge 2009-02-11 bug=217644: Convert to sequence right here |
460 | 1996 | # because ResultSet reports a wrong count() when using DISTINCT. Also | 1971 | # because ResultSet reports a wrong count() when using DISTINCT. Also |
461 | 1997 | # ResultSet does not implement __len__(), which would make it more | 1972 | # ResultSet does not implement __len__(), which would make it more |
462 | 1998 | 1973 | ||
463 | === modified file 'lib/lp/translations/browser/browser_helpers.py' | |||
464 | --- lib/lp/translations/browser/browser_helpers.py 2009-09-04 11:54:50 +0000 | |||
465 | +++ lib/lp/translations/browser/browser_helpers.py 2010-03-01 23:03:20 +0000 | |||
466 | @@ -30,16 +30,16 @@ | |||
467 | 30 | """Replace Rosetta escape sequences with the real characters.""" | 30 | """Replace Rosetta escape sequences with the real characters.""" |
468 | 31 | return helpers.text_replaced(text, {'[tab]': '\t', | 31 | return helpers.text_replaced(text, {'[tab]': '\t', |
469 | 32 | r'\[tab]': '[tab]', | 32 | r'\[tab]': '[tab]', |
472 | 33 | '[nbsp]' : u'\u00a0', | 33 | '[nbsp]': u'\u00a0', |
473 | 34 | r'\[nbsp]' : '[nbsp]' }) | 34 | r'\[nbsp]': '[nbsp]'}) |
474 | 35 | 35 | ||
475 | 36 | 36 | ||
476 | 37 | def expand_rosetta_escapes(unicode_text): | 37 | def expand_rosetta_escapes(unicode_text): |
477 | 38 | """Replace characters needing a Rosetta escape sequences.""" | 38 | """Replace characters needing a Rosetta escape sequences.""" |
478 | 39 | escapes = {u'\t': TranslationConstants.TAB_CHAR, | 39 | escapes = {u'\t': TranslationConstants.TAB_CHAR, |
479 | 40 | u'[tab]': TranslationConstants.TAB_CHAR_ESCAPED, | 40 | u'[tab]': TranslationConstants.TAB_CHAR_ESCAPED, |
482 | 41 | u'\u00a0' : TranslationConstants.NO_BREAK_SPACE_CHAR, | 41 | u'\u00a0': TranslationConstants.NO_BREAK_SPACE_CHAR, |
483 | 42 | u'[nbsp]' : TranslationConstants.NO_BREAK_SPACE_CHAR_ESCAPED } | 42 | u'[nbsp]': TranslationConstants.NO_BREAK_SPACE_CHAR_ESCAPED} |
484 | 43 | return helpers.text_replaced(unicode_text, escapes) | 43 | return helpers.text_replaced(unicode_text, escapes) |
485 | 44 | 44 | ||
486 | 45 | 45 | ||
487 | @@ -187,5 +187,3 @@ | |||
488 | 187 | raise UnrecognisedCFormatString(string) | 187 | raise UnrecognisedCFormatString(string) |
489 | 188 | 188 | ||
490 | 189 | return segments | 189 | return segments |
491 | 190 | |||
492 | 191 | |||
493 | 192 | 190 | ||
494 | === modified file 'lib/lp/translations/browser/distroseries.py' | |||
495 | --- lib/lp/translations/browser/distroseries.py 2010-02-02 15:32:00 +0000 | |||
496 | +++ lib/lp/translations/browser/distroseries.py 2010-03-01 23:03:20 +0000 | |||
497 | @@ -11,15 +11,16 @@ | |||
498 | 11 | 'DistroSeriesTranslationsAdminView', | 11 | 'DistroSeriesTranslationsAdminView', |
499 | 12 | 'DistroSeriesTranslationsMenu', | 12 | 'DistroSeriesTranslationsMenu', |
500 | 13 | 'DistroSeriesView', | 13 | 'DistroSeriesView', |
501 | 14 | 'check_distroseries_translations_viewable', | ||
502 | 14 | ] | 15 | ] |
503 | 15 | 16 | ||
504 | 16 | from zope.component import getUtility | 17 | from zope.component import getUtility |
505 | 17 | 18 | ||
506 | 18 | from canonical.cachedproperty import cachedproperty | 19 | from canonical.cachedproperty import cachedproperty |
507 | 19 | from canonical.launchpad import helpers | 20 | from canonical.launchpad import helpers |
508 | 20 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities | ||
509 | 21 | from canonical.launchpad.webapp import action | 21 | from canonical.launchpad.webapp import action |
510 | 22 | from canonical.launchpad.webapp.authorization import check_permission | 22 | from canonical.launchpad.webapp.authorization import check_permission |
511 | 23 | from canonical.launchpad.webapp.interfaces import TranslationUnavailable | ||
512 | 23 | from canonical.launchpad.webapp.launchpadform import LaunchpadEditFormView | 24 | from canonical.launchpad.webapp.launchpadform import LaunchpadEditFormView |
513 | 24 | from canonical.launchpad.webapp.menu import ( | 25 | from canonical.launchpad.webapp.menu import ( |
514 | 25 | Link, NavigationMenu, enabled_with_permission) | 26 | Link, NavigationMenu, enabled_with_permission) |
515 | @@ -27,6 +28,7 @@ | |||
516 | 27 | canonical_url, LaunchpadView) | 28 | canonical_url, LaunchpadView) |
517 | 28 | 29 | ||
518 | 29 | from lp.registry.interfaces.distroseries import IDistroSeries | 30 | from lp.registry.interfaces.distroseries import IDistroSeries |
519 | 31 | from lp.registry.interfaces.series import SeriesStatus | ||
520 | 30 | from lp.translations.browser.translations import TranslationsMixin | 32 | from lp.translations.browser.translations import TranslationsMixin |
521 | 31 | from lp.translations.browser.potemplate import BaseSeriesTemplatesView | 33 | from lp.translations.browser.potemplate import BaseSeriesTemplatesView |
522 | 32 | from lp.translations.interfaces.distroserieslanguage import ( | 34 | from lp.translations.interfaces.distroserieslanguage import ( |
523 | @@ -175,34 +177,11 @@ | |||
524 | 175 | self.context.version) | 177 | self.context.version) |
525 | 176 | 178 | ||
526 | 177 | def checkTranslationsViewable(self): | 179 | def checkTranslationsViewable(self): |
555 | 178 | """Check that these translations are visible to the current user. | 180 | """ Check if user can view translations for this `IDistroSeries`""" |
556 | 179 | 181 | ||
557 | 180 | Launchpad admins, Translations admins, and users with admin | 182 | # Is user allowed to see translations for this distroseries? |
558 | 181 | rights on the `DistroSeries` are always allowed. For others | 183 | # If not, raise TranslationUnavailable. |
559 | 182 | this delegates to `IDistroSeries.checkTranslationsViewable`, | 184 | check_distroseries_translations_viewable(self.context) |
532 | 183 | which raises `TranslationUnavailable` if the translations are | ||
533 | 184 | set to be hidden. | ||
534 | 185 | |||
535 | 186 | :return: Returns normally if this series' translations are | ||
536 | 187 | viewable to the current user. | ||
537 | 188 | :raise TranslationUnavailable: if this series' translations are | ||
538 | 189 | hidden and the user is not one of the limited caste that is | ||
539 | 190 | allowed to access them. | ||
540 | 191 | """ | ||
541 | 192 | if check_permission('launchpad.TranslationsAdmin', self.context): | ||
542 | 193 | # Anyone with admin rights on this series passes. This | ||
543 | 194 | # includes Launchpad admins. | ||
544 | 195 | return | ||
545 | 196 | |||
546 | 197 | user = self.user | ||
547 | 198 | experts = getUtility(ILaunchpadCelebrities).rosetta_experts | ||
548 | 199 | if user is not None and user.inTeam(experts): | ||
549 | 200 | # Translations admins also pass. | ||
550 | 201 | return | ||
551 | 202 | |||
552 | 203 | # Everyone else passes only if translations are viewable to the | ||
553 | 204 | # public. | ||
554 | 205 | self.context.checkTranslationsViewable() | ||
560 | 206 | 185 | ||
561 | 207 | def distroserieslanguages(self): | 186 | def distroserieslanguages(self): |
562 | 208 | """Produces a list containing a DistroSeriesLanguage object for | 187 | """Produces a list containing a DistroSeriesLanguage object for |
563 | @@ -271,3 +250,47 @@ | |||
564 | 271 | 250 | ||
565 | 272 | def language_packs(self): | 251 | def language_packs(self): |
566 | 273 | return Link('+language-packs', 'Language packs') | 252 | return Link('+language-packs', 'Language packs') |
567 | 253 | |||
568 | 254 | |||
569 | 255 | def check_distroseries_translations_viewable(distroseries): | ||
570 | 256 | """Check that these distribution series translations are visible. | ||
571 | 257 | |||
572 | 258 | Launchpad admins, Translations admins, and users with admin | ||
573 | 259 | rights on the `IDistroSeries` are always allowed. | ||
574 | 260 | |||
575 | 261 | Checks the `hide_all_translations` flag. If it is set, these | ||
576 | 262 | translations are not to be shown to the public. In that case an | ||
577 | 263 | appropriate message is composed based on the series' `status`, | ||
578 | 264 | and a `TranslationUnavailable` exception is raised. | ||
579 | 265 | |||
580 | 266 | :return: Returns normally if this series' translations are | ||
581 | 267 | viewable to the current user. | ||
582 | 268 | :raise TranslationUnavailable: if this series' translations are | ||
583 | 269 | hidden and the user is not one of the limited caste that is | ||
584 | 270 | allowed to access them. | ||
585 | 271 | """ | ||
586 | 272 | |||
587 | 273 | if not distroseries.hide_all_translations: | ||
588 | 274 | # Yup, viewable. | ||
589 | 275 | return | ||
590 | 276 | |||
591 | 277 | if check_permission( | ||
592 | 278 | 'launchpad.TranslationsAdmin', distroseries): | ||
593 | 279 | return | ||
594 | 280 | |||
595 | 281 | future = [ | ||
596 | 282 | SeriesStatus.EXPERIMENTAL, | ||
597 | 283 | SeriesStatus.DEVELOPMENT, | ||
598 | 284 | SeriesStatus.FUTURE, | ||
599 | 285 | ] | ||
600 | 286 | if distroseries.status in future: | ||
601 | 287 | raise TranslationUnavailable( | ||
602 | 288 | "Translations for this release series are not available yet.") | ||
603 | 289 | elif distroseries.status == SeriesStatus.OBSOLETE: | ||
604 | 290 | raise TranslationUnavailable( | ||
605 | 291 | "This release series is obsolete. Its translations are no " | ||
606 | 292 | "longer available.") | ||
607 | 293 | else: | ||
608 | 294 | raise TranslationUnavailable( | ||
609 | 295 | "Translations for this release series are not currently " | ||
610 | 296 | "available. Please come back soon.") | ||
611 | 274 | 297 | ||
612 | === modified file 'lib/lp/translations/browser/tests/distroseries-views.txt' | |||
613 | --- lib/lp/translations/browser/tests/distroseries-views.txt 2009-12-13 11:55:40 +0000 | |||
614 | +++ lib/lp/translations/browser/tests/distroseries-views.txt 2010-03-01 23:03:20 +0000 | |||
615 | @@ -1,9 +1,9 @@ | |||
617 | 1 | = DistroSeries translations view classes = | 1 | DistroSeries translations view classes |
618 | 2 | ====================================== | ||
619 | 3 | |||
620 | 4 | Let's use ubuntu/hoary for these tests. | ||
621 | 2 | 5 | ||
622 | 3 | >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest | 6 | >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
623 | 4 | |||
624 | 5 | Let's use ubuntu/hoary for these tests. | ||
625 | 6 | |||
626 | 7 | >>> from canonical.launchpad.interfaces import IDistributionSet | 7 | >>> from canonical.launchpad.interfaces import IDistributionSet |
627 | 8 | >>> from lp.registry.interfaces.series import SeriesStatus | 8 | >>> from lp.registry.interfaces.series import SeriesStatus |
628 | 9 | >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu') | 9 | >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
629 | @@ -15,7 +15,9 @@ | |||
630 | 15 | >>> login('foo.bar@canonical.com') | 15 | >>> login('foo.bar@canonical.com') |
631 | 16 | >>> hoary.status = SeriesStatus.CURRENT | 16 | >>> hoary.status = SeriesStatus.CURRENT |
632 | 17 | 17 | ||
634 | 18 | == Hiding translations == | 18 | |
635 | 19 | Hiding translations | ||
636 | 20 | ------------------- | ||
637 | 19 | 21 | ||
638 | 20 | Each distroseries has a switch that allows administrators to either | 22 | Each distroseries has a switch that allows administrators to either |
639 | 21 | reveal its translations to the public or hide them from the public. | 23 | reveal its translations to the public or hide them from the public. |
640 | @@ -95,9 +97,65 @@ | |||
641 | 95 | release series are not currently available. Please come back soon. | 97 | release series are not currently available. Please come back soon. |
642 | 96 | 98 | ||
643 | 97 | The same goes for anonymous users. | 99 | The same goes for anonymous users. |
650 | 98 | 100 | Exactly what message is displayed depends on the series' status. | |
651 | 99 | >>> login(ANONYMOUS) | 101 | |
652 | 100 | >>> check_effect_of_hiding(hoary) | 102 | >>> login('foo.bar@canonical.com') |
653 | 101 | User can access revealed translations. | 103 | >>> hoary.status = SeriesStatus.EXPERIMENTAL |
654 | 102 | User can not access hidden translations: Translations for this | 104 | >>> login(ANONYMOUS) |
655 | 103 | release series are not currently available. Please come back soon. | 105 | >>> check_effect_of_hiding(hoary) |
656 | 106 | User can access revealed translations. | ||
657 | 107 | User can not access hidden translations: | ||
658 | 108 | Translations for this release series are not available yet. | ||
659 | 109 | |||
660 | 110 | >>> login('foo.bar@canonical.com') | ||
661 | 111 | >>> hoary.status = SeriesStatus.DEVELOPMENT | ||
662 | 112 | >>> login(ANONYMOUS) | ||
663 | 113 | >>> check_effect_of_hiding(hoary) | ||
664 | 114 | User can access revealed translations. | ||
665 | 115 | User can not access hidden translations: | ||
666 | 116 | Translations for this release series are not available yet. | ||
667 | 117 | |||
668 | 118 | >>> login('foo.bar@canonical.com') | ||
669 | 119 | >>> hoary.status = SeriesStatus.FROZEN | ||
670 | 120 | >>> login(ANONYMOUS) | ||
671 | 121 | >>> check_effect_of_hiding(hoary) | ||
672 | 122 | User can access revealed translations. | ||
673 | 123 | User can not access hidden translations: | ||
674 | 124 | Translations for this release series are not currently available. | ||
675 | 125 | Please come back soon. | ||
676 | 126 | |||
677 | 127 | >>> login('foo.bar@canonical.com') | ||
678 | 128 | >>> hoary.status = SeriesStatus.CURRENT | ||
679 | 129 | >>> login(ANONYMOUS) | ||
680 | 130 | >>> check_effect_of_hiding(hoary) | ||
681 | 131 | User can access revealed translations. | ||
682 | 132 | User can not access hidden translations: | ||
683 | 133 | Translations for this release series are not currently available. | ||
684 | 134 | Please come back soon. | ||
685 | 135 | |||
686 | 136 | >>> login('foo.bar@canonical.com') | ||
687 | 137 | >>> hoary.status = SeriesStatus.SUPPORTED | ||
688 | 138 | >>> login(ANONYMOUS) | ||
689 | 139 | >>> check_effect_of_hiding(hoary) | ||
690 | 140 | User can access revealed translations. | ||
691 | 141 | User can not access hidden translations: | ||
692 | 142 | Translations for this release series are not currently available. | ||
693 | 143 | Please come back soon. | ||
694 | 144 | |||
695 | 145 | >>> login('foo.bar@canonical.com') | ||
696 | 146 | >>> hoary.status = SeriesStatus.OBSOLETE | ||
697 | 147 | >>> login(ANONYMOUS) | ||
698 | 148 | >>> check_effect_of_hiding(hoary) | ||
699 | 149 | User can access revealed translations. | ||
700 | 150 | User can not access hidden translations: | ||
701 | 151 | This release series is obsolete. Its translations are no longer | ||
702 | 152 | available. | ||
703 | 153 | |||
704 | 154 | >>> login('foo.bar@canonical.com') | ||
705 | 155 | >>> hoary.status = SeriesStatus.FUTURE | ||
706 | 156 | >>> login(ANONYMOUS) | ||
707 | 157 | >>> check_effect_of_hiding(hoary) | ||
708 | 158 | User can access revealed translations. | ||
709 | 159 | User can not access hidden translations: | ||
710 | 160 | Translations for this release series are not available yet. | ||
711 | 161 | |||
712 | 104 | 162 | ||
713 | === modified file 'lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt' | |||
714 | --- lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt 2009-12-21 17:18:12 +0000 | |||
715 | +++ lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt 2010-03-01 23:03:20 +0000 | |||
716 | @@ -46,9 +46,7 @@ | |||
717 | 46 | ... but the link is available to administrators: | 46 | ... but the link is available to administrators: |
718 | 47 | 47 | ||
719 | 48 | >>> dtc_browser = setupDTCBrowser() | 48 | >>> dtc_browser = setupDTCBrowser() |
720 | 49 | |||
721 | 50 | >>> dtc_browser.open('http://translations.launchpad.dev/ubuntu/hoary') | 49 | >>> dtc_browser.open('http://translations.launchpad.dev/ubuntu/hoary') |
722 | 51 | |||
723 | 52 | >>> dtc_browser.getLink('Change settings').click() | 50 | >>> dtc_browser.getLink('Change settings').click() |
724 | 53 | 51 | ||
725 | 54 | Once the administrator hides all translations... | 52 | Once the administrator hides all translations... |
726 | @@ -115,8 +113,8 @@ | |||
727 | 115 | non-administrative users. | 113 | non-administrative users. |
728 | 116 | 114 | ||
729 | 117 | >>> user_browser.open( | 115 | >>> user_browser.open( |
732 | 118 | ... 'http://translations.launchpad.dev/ubuntu/hoary/+sources/evolution/' | 116 | ... 'http://translations.launchpad.dev/ubuntu/hoary/' |
733 | 119 | ... '+pots/evolution-2.2') | 117 | ... '+sources/evolution/+pots/evolution-2.2') |
734 | 120 | Traceback (most recent call last): | 118 | Traceback (most recent call last): |
735 | 121 | ... | 119 | ... |
736 | 122 | TranslationUnavailable: ... | 120 | TranslationUnavailable: ... |
737 | @@ -126,7 +124,7 @@ | |||
738 | 126 | 124 | ||
739 | 127 | >>> dtc_browser.open( | 125 | >>> dtc_browser.open( |
740 | 128 | ... 'http://translations.launchpad.dev/ubuntu/hoary/' | 126 | ... 'http://translations.launchpad.dev/ubuntu/hoary/' |
742 | 129 | ... '+sources/evolution') | 127 | ... '+sources/evolution/+pots/evolution-2.2') |
743 | 130 | 128 | ||
744 | 131 | There is also an option to set/unset whether translation imports for a | 129 | There is also an option to set/unset whether translation imports for a |
745 | 132 | distribution should be deferred. That option is set also from the same | 130 | distribution should be deferred. That option is set also from the same |
746 | 133 | 131 | ||
747 | === modified file 'lib/lp/translations/templates/distroseries-langchart.pt' | |||
748 | --- lib/lp/translations/templates/distroseries-langchart.pt 2009-12-27 20:58:33 +0000 | |||
749 | +++ lib/lp/translations/templates/distroseries-langchart.pt 2010-03-01 23:03:20 +0000 | |||
750 | @@ -8,6 +8,11 @@ | |||
751 | 8 | <strong>Translations for this series are currently hidden.</strong> | 8 | <strong>Translations for this series are currently hidden.</strong> |
752 | 9 | 9 | ||
753 | 10 | <!-- Bounce regular users to "translations unavailable" page. --> | 10 | <!-- Bounce regular users to "translations unavailable" page. --> |
754 | 11 | <tal:XXX condition="nothing"> | ||
755 | 12 | 20100224 adiroiban bug=527069: The following tal:omit-tag is only | ||
756 | 13 | used with the sole purpose of raising an exception. | ||
757 | 14 | There should be a better solution for this problem. | ||
758 | 15 | </tal:XXX> | ||
759 | 11 | <metal:check-available tal:omit-tag="view/checkTranslationsViewable" /> | 16 | <metal:check-available tal:omit-tag="view/checkTranslationsViewable" /> |
760 | 12 | </p> | 17 | </p> |
761 | 13 | 18 |
= Bug 509252 = /code.edge. launchpad. net/~adiroiban/ launchpad/ bug-340662- take-2/ +merge/ 17598) the POTemplateSubse tNavigation will render the AdminPOTemplate Subset useless.
After landing the fix for bug 340662 (https:/
We should clean the security.py.
AdminPOTemplate Subset was added to fix bug 497438
== Proposed fix ==
Remove unused classed from security.py and refactor
== Pre-implementation notes ==
Talking with Henning we decide not to include security checking code in the model. This is why the logic of showing translations of a series was spitted between the model and the view and the security checks were done in multiple places.
This restriction lead to a strange implementation where the check was done both in the view and the model and it was a source of confusion.
I merged this logic in a browser helper function and now the security check is only done once.
== Implementation details ==
These changes renders the AdminDistroSeri esLanguage class from security.py useless, so I have also removed it.
== Tests == translations -t distroseries-views
lp-test -t distroseries-
== Demo and Q/A == l10n-coordinato r). /launchpad. dev/~ubuntu- l10n-coordinato r
Make sure you are a member of Ubuntu Translation Coordinators team (ubuntu-
https:/
Go to the translation page for a distribution and hide translations for this series /translations. launchpad. dev/ubuntu/ hoary/+ admin
ie: https:/
Next go to the distribution series +template page /translations. launchpad. dev/ubuntu/ hoary/+ templates
https:/
You should see the Administer page for Evolution templates, including disabled-template .
As a member of Ubuntu Translation Coordinators team you should be able to administer them, just like for a normal series.
Login as a normal user you should see a page informing that the translations are currently closed
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files: /launchpad/ security. py registry/ browser/ distroseries. py registry/ browser/ sourcepackage. py registry/ interfaces/ distroseries. py registry/ model/distroser ies.py translations/ browser/ browser_ helpers. py translations/ browser/ distroseries. py translations/ stories/ distroseries/ xx-distroseries -translations. txt
lib/canonical
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
== Pylint notices ==
lib/lp/ registry/ browser/ sourcepackage. py interface' (No module named restful)
29: [F0401] Unable to import 'lazr.restful.
lib/lp/ registry/ interfaces/ distroseries. py fields' (No module named restful) declarations' (No module named restful) sionField. _validate] Use super on an old style class blic.getPackage Uploads] Operator not preceded by a space =_("Return items that are more recent than this " False),
23: [F0401] Unable to import 'lazr.enum' (No module named enum)
50: [F0401] Unable to import 'lazr.restful.
51: [F0401] Unable to import 'lazr.restful.
117: [E1002, DistroSeriesVer
447: [C0322, IDistroSeriesPu
description
^
"timestamp."),
required=
status=Choice(
vocabulary= DBEnumeratedTyp e, _("Package Upload Status"),
title=
des...