Merge lp:~henninge/launchpad/bug-482267 into lp:launchpad
- bug-482267
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Edwin Grubbs |
Approved revision: | not available |
Merged at revision: | not available |
Proposed branch: | lp:~henninge/launchpad/bug-482267 |
Merge into: | lp:launchpad |
Diff against target: |
1439 lines (+411/-122) 26 files modified
lib/canonical/launchpad/security.py (+10/-14) lib/lp/translations/browser/hastranslationimports.py (+4/-4) lib/lp/translations/browser/translationimportqueue.py (+1/-1) lib/lp/translations/doc/poexport-language-pack.txt (+6/-2) lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt (+5/-3) lib/lp/translations/doc/poimport-pofile-old-po-imported.txt (+6/-4) lib/lp/translations/doc/poimport-pofile-syntax-error.txt (+10/-8) lib/lp/translations/doc/poimport-potemplate-syntax-error.txt (+5/-3) lib/lp/translations/doc/poimport.txt (+14/-14) lib/lp/translations/doc/rosetta-karma.txt (+6/-6) lib/lp/translations/doc/rosetta-poimport-script.txt (+4/-2) lib/lp/translations/doc/translationimportqueue.txt (+43/-32) lib/lp/translations/interfaces/translationimportqueue.py (+37/-8) lib/lp/translations/model/pofile.py (+5/-2) lib/lp/translations/model/potemplate.py (+5/-2) lib/lp/translations/model/translationbranchapprover.py (+3/-1) lib/lp/translations/model/translationimportqueue.py (+63/-8) lib/lp/translations/scripts/po_import.py (+2/-1) lib/lp/translations/stories/webservice/xx-translationimportqueue.txt (+17/-2) lib/lp/translations/tests/test_autoapproval.py (+7/-1) lib/lp/translations/tests/test_translationbranchapprover.py (+3/-1) lib/lp/translations/tests/test_translationimportqueue.py (+125/-0) lib/lp/translations/utilities/permission_helpers.py (+21/-0) lib/lp/translations/utilities/tests/helpers.py (+3/-1) lib/lp/translations/utilities/tests/test_xpi_import.py (+3/-1) lib/lp/translations/utilities/tests/test_xpi_po_exporter.py (+3/-1) |
To merge this branch: | bzr merge lp:~henninge/launchpad/bug-482267 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Edwin Grubbs (community) | code | Approve | |
Review via email: mp+14947@code.launchpad.net |
Commit message
Description of the change
Henning Eggers (henninge) wrote : | # |
Henning Eggers (henninge) wrote : | # |
Here is the incremental diffs wrt the previous review.
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -310,7 +310,7 @@
# that's only possible if we know where to import it
# (import_into not None).
return False
- if (new_status == RosettaImportSt
+ if new_status == RosettaImportSt
# Only administrators are able to set an entry to BLOCKED.
return False
if (new_status in (RosettaImportS
@@ -319,7 +319,7 @@
# Only scripts set these statuses and they report as a rosetta
# expert.
return False
- return (self.isUserUpl
+ return self.isUserUplo
def setStatus(self, new_status, user):
"""See `ITranslationIm
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -17,14 +17,14 @@
from canonical.testing import LaunchpadZopele
-class TestTranslation
+class TestTranslation
"""Test handling of the status of a queue entry."""
layer = LaunchpadZopele
def setUp(self):
"""Set up context to test in."""
- super(TestTrans
+ super(TestTrans
self.queue = getUtility(
@@ -100,7 +100,7 @@
def test_canSetStat
- # Owners of the Ubuntu translation Groups can set entries
+ # Owners of the Ubuntu translation Groups can set entries to approved
# that are targeted to Ubuntu.
Henning Eggers (henninge) wrote : | # |
This is the diff-diff, comparing the branch from the first proposal and this branch before the above changes were made. It shows that the changes are identical and that the additional symlink does not appear here.
0a1,2
> === added symlink 'lib/canonical/
> === target is u'../..
2,3c4,5
< --- lib/canonical/
< +++ lib/canonical/
---
> --- lib/canonical/
> +++ lib/canonical/
46c48
< @@ -1278,10 +1275,8 @@
---
> @@ -1310,10 +1307,8 @@
59c61
< @@ -1291,11 +1286,12 @@
---
> @@ -1323,11 +1318,12 @@
77c79
< +++ lib/lp/
---
> +++ lib/lp/
103c105
< +++ lib/lp/
---
> +++ lib/lp/
116c118
< +++ lib/lp/
---
> +++ lib/lp/
146c148
< +++ lib/lp/
---
> +++ lib/lp/
185c187
< +++ lib/lp/
---
> +++ lib/lp/
234c236
< +++ lib/lp/
---
> +++ lib/lp/
319c321
< +++ lib/lp/
---
> +++ lib/lp/
358c360
< +++ lib/lp/
---
> +++ lib/lp/
494c496
< +++ lib/lp/
---
> +++ lib/lp/
552c554
< +++ lib/lp/
---
> +++ lib/lp/
583c585
< +++ lib/lp/
---
> +++ lib/lp/
835c837
< +++ lib/lp/
---
> +++ lib/lp/
920c922
< +++ lib/lp/
---
> +++ lib/lp/
948c950
< +++ lib/lp/
---
> +++ lib/lp/
Edwin Grubbs (edwin-grubbs) wrote : | # |
Hi Henning,
Your changes look good. I just have one suggestion below.
merge-approved
-Edwin
>>> + if (new_status == RosettaImportSt
>>> + not (self.import_into is not None and can_admin)):
>>
>>
>> I think it would be clearer without the double negatives.
>> if (new_status == RosettaImportSt
>> (self.import_into is None or not can_admin)):
>
>Actually, that is what I changed this line from (previously in the view
>code) because I think "and" is easier to understand here. It derives
>more directly from the definition "an entry can be approved by an admin
>but only if we have an import target".
>
>So, I like this better but if you insist, will change it back ... ;)
Here is another thought of how you can keep the "and" but get rid of the
double negative. You can take it or leave it.
if new_status == RosettaImportSt
if can_admin and self.import_into is None:
pass
else:
return False
Henning Eggers (henninge) wrote : | # |
Am 17.11.2009 17:14, Edwin Grubbs schrieb:
> Review: Approve code
> Hi Henning,
>
> Your changes look good. I just have one suggestion below.
>
> merge-approved
>
Thank you very much! ;)
> Here is another thought of how you can keep the "and" but get rid of the
> double negative. You can take it or leave it.
>
> if new_status == RosettaImportSt
> if can_admin and self.import_into is None:
> pass
> else:
> return False
>
Ah, good idea. I was not really happy with the flow in setStatus anyway,
all these "return False" were confusing. I changed similar to your
suggestions that is much clearer (and has no double negative ;).
Incremental diff will be pasted.
Henning
Henning Eggers (henninge) wrote : | # |
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -304,21 +304,20 @@
return False
can_admin = (is_admin_
- if (new_status == RosettaImportSt
- not (self.import_into is not None and can_admin)):
+ if new_status == RosettaImportSt
# Only administrators are able to set the APPROVED status, and
# that's only possible if we know where to import it
# (import_into not None).
- return False
- if new_status == RosettaImportSt
+ return can_admin and self.import_into is not None
+ if new_status == RosettaImportSt
# Only administrators are able to set an entry to BLOCKED.
- return False
+ return can_admin
if (new_status in (RosettaImportS
- RosettaImportSt
- and not is_admin_
+ RosettaImportSt
# Only scripts set these statuses and they report as a rosetta
# expert.
- return False
+ return is_admin_
+ # All other statuses can bset set by all authorized persons.
return self.isUserUplo
def setStatus(self, new_status, user):
Preview Diff
1 | === modified file 'lib/canonical/launchpad/security.py' |
2 | --- lib/canonical/launchpad/security.py 2009-11-15 01:05:49 +0000 |
3 | +++ lib/canonical/launchpad/security.py 2009-11-18 11:51:13 +0000 |
4 | @@ -10,6 +10,7 @@ |
5 | from zope.component import getUtility |
6 | |
7 | from canonical.launchpad.interfaces.account import IAccount |
8 | +from canonical.launchpad.interfaces.emailaddress import IEmailAddress |
9 | from lp.archiveuploader.permission import verify_upload |
10 | from lp.registry.interfaces.announcement import IAnnouncement |
11 | from lp.soyuz.interfaces.archive import IArchive |
12 | @@ -48,7 +49,8 @@ |
13 | from lp.registry.interfaces.distroseries import IDistroSeries |
14 | from lp.translations.interfaces.distroserieslanguage import ( |
15 | IDistroSeriesLanguage) |
16 | -from canonical.launchpad.interfaces.emailaddress import IEmailAddress |
17 | +from lp.translations.utilities.permission_helpers import ( |
18 | + is_admin_or_rosetta_expert) |
19 | from lp.registry.interfaces.entitlement import IEntitlement |
20 | from canonical.launchpad.interfaces.hwdb import ( |
21 | IHWDBApplication, IHWDevice, IHWDeviceClass, IHWDriver, IHWDriverName, |
22 | @@ -475,10 +477,7 @@ |
23 | |
24 | def checkAuthenticated(self, user): |
25 | """Allow Launchpad's admins and Rosetta experts edit all fields.""" |
26 | - celebrities = getUtility(ILaunchpadCelebrities) |
27 | - return (user.inTeam(celebrities.admin) or |
28 | - user.inTeam(celebrities.rosetta_experts)) |
29 | - |
30 | + return is_admin_or_rosetta_expert(user) |
31 | |
32 | class AdminProductTranslations(AuthorizationBase): |
33 | permission = 'launchpad.TranslationsAdmin' |
34 | @@ -490,10 +489,8 @@ |
35 | Any Launchpad/Launchpad Translations administrator or owners are |
36 | able to change translation settings for a product. |
37 | """ |
38 | - celebrities = getUtility(ILaunchpadCelebrities) |
39 | return (user.inTeam(self.obj.owner) or |
40 | - user.inTeam(celebrities.admin) or |
41 | - user.inTeam(celebrities.rosetta_experts)) |
42 | + is_admin_or_rosetta_expert(user)) |
43 | |
44 | |
45 | class AdminSeriesByVCSImports(AuthorizationBase): |
46 | @@ -1278,10 +1275,8 @@ |
47 | |
48 | # As a special case, the Ubuntu translation group owners can |
49 | # manage Ubuntu uploads. |
50 | - if self.obj.is_targeted_to_ubuntu: |
51 | - group = self.obj.distroseries.distribution.translationgroup |
52 | - if group is not None and user.inTeam(group.owner): |
53 | - return True |
54 | + if self.obj.isUbuntuAndIsUserTranslationGroupOwner(user): |
55 | + return True |
56 | |
57 | return False |
58 | |
59 | @@ -1291,11 +1286,12 @@ |
60 | usedfor = ITranslationImportQueueEntry |
61 | |
62 | def checkAuthenticated(self, user): |
63 | - """Anyone who can admin an entry, plus its owner, can edit it. |
64 | + """Anyone who can admin an entry, plus its owner or the owner of the |
65 | + product or distribution, can edit it. |
66 | """ |
67 | if AdminTranslationImportQueueEntry.checkAuthenticated(self, user): |
68 | return True |
69 | - if user.inTeam(self.obj.importer): |
70 | + if self.obj.isUserUploaderOrOwner(user): |
71 | return True |
72 | |
73 | return False |
74 | |
75 | === modified file 'lib/lp/translations/browser/hastranslationimports.py' |
76 | --- lib/lp/translations/browser/hastranslationimports.py 2009-09-17 20:11:48 +0000 |
77 | +++ lib/lp/translations/browser/hastranslationimports.py 2009-11-18 11:51:13 +0000 |
78 | @@ -223,17 +223,17 @@ |
79 | # special permissions to change status. |
80 | if (new_status_name == RosettaImportStatus.DELETED.name and |
81 | check_permission('launchpad.Edit', entry)): |
82 | - entry.setStatus(RosettaImportStatus.DELETED) |
83 | + entry.setStatus(RosettaImportStatus.DELETED, self.user) |
84 | elif (new_status_name == RosettaImportStatus.BLOCKED.name and |
85 | check_permission('launchpad.Admin', entry)): |
86 | - entry.setStatus(RosettaImportStatus.BLOCKED) |
87 | + entry.setStatus(RosettaImportStatus.BLOCKED, self.user) |
88 | elif (new_status_name == RosettaImportStatus.APPROVED.name and |
89 | check_permission('launchpad.Admin', entry) and |
90 | entry.import_into is not None): |
91 | - entry.setStatus(RosettaImportStatus.APPROVED) |
92 | + entry.setStatus(RosettaImportStatus.APPROVED, self.user) |
93 | elif (new_status_name == RosettaImportStatus.NEEDS_REVIEW.name and |
94 | check_permission('launchpad.Admin', entry)): |
95 | - entry.setStatus(RosettaImportStatus.NEEDS_REVIEW) |
96 | + entry.setStatus(RosettaImportStatus.NEEDS_REVIEW, self.user) |
97 | else: |
98 | # The user was not the importer or we are trying to set a |
99 | # status that must not be set from this form. That means that |
100 | |
101 | === modified file 'lib/lp/translations/browser/translationimportqueue.py' |
102 | --- lib/lp/translations/browser/translationimportqueue.py 2009-09-17 20:11:48 +0000 |
103 | +++ lib/lp/translations/browser/translationimportqueue.py 2009-11-18 11:51:13 +0000 |
104 | @@ -414,7 +414,7 @@ |
105 | # Store the associated IPOTemplate. |
106 | self.context.potemplate = potemplate |
107 | |
108 | - self.context.setStatus(RosettaImportStatus.APPROVED) |
109 | + self.context.setStatus(RosettaImportStatus.APPROVED, self.user) |
110 | self.context.date_status_changed = UTC_NOW |
111 | |
112 | |
113 | |
114 | === modified file 'lib/lp/translations/doc/poexport-language-pack.txt' |
115 | --- lib/lp/translations/doc/poexport-language-pack.txt 2009-08-13 19:03:36 +0000 |
116 | +++ lib/lp/translations/doc/poexport-language-pack.txt 2009-11-18 11:51:13 +0000 |
117 | @@ -12,10 +12,12 @@ |
118 | >>> import transaction |
119 | >>> from canonical.launchpad.ftests import login |
120 | >>> from canonical.launchpad.helpers import string_to_tarfile |
121 | + >>> from canonical.launchpad.interfaces import ILaunchpadCelebrities |
122 | >>> from lp.translations.scripts.language_pack import \ |
123 | ... export_language_pack |
124 | >>> from canonical.librarian.interfaces import ILibrarianClient |
125 | >>> from canonical.database.sqlbase import flush_database_caches |
126 | + >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
127 | >>> login("daf@canonical.com") |
128 | |
129 | # This is a dummy logger class to capture the export's log messages. |
130 | @@ -183,8 +185,10 @@ |
131 | Before we are ready to import the attached files, we need to approve |
132 | them first. |
133 | |
134 | - >>> template_entry.setStatus(RosettaImportStatus.APPROVED) |
135 | - >>> translation_entry.setStatus(RosettaImportStatus.APPROVED) |
136 | + >>> template_entry.setStatus( |
137 | + ... RosettaImportStatus.APPROVED, rosetta_experts) |
138 | + >>> translation_entry.setStatus( |
139 | + ... RosettaImportStatus.APPROVED, rosetta_experts) |
140 | |
141 | Given that the files are attached to Librarian, we need to commit the |
142 | transaction to make sure it's stored properly and available. |
143 | |
144 | === modified file 'lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt' |
145 | --- lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt 2009-08-13 15:12:16 +0000 |
146 | +++ lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt 2009-11-18 11:51:13 +0000 |
147 | @@ -19,13 +19,15 @@ |
148 | |
149 | Here are some imports we need to get this test running. |
150 | |
151 | - >>> from canonical.launchpad.interfaces import IPersonSet |
152 | + >>> from canonical.launchpad.interfaces import ( |
153 | + ... ILaunchpadCelebrities, IPersonSet) |
154 | >>> from lp.translations.interfaces.translationimportqueue import ( |
155 | ... ITranslationImportQueue, RosettaImportStatus) |
156 | >>> from lp.translations.model.potemplate import POTemplateSubset |
157 | >>> import pytz |
158 | >>> UTC = pytz.timezone('UTC') |
159 | >>> translation_import_queue = getUtility(ITranslationImportQueue) |
160 | + >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
161 | |
162 | We need this for the Librarian to work properly. |
163 | |
164 | @@ -80,7 +82,7 @@ |
165 | |
166 | We must approve the entry to be able to import it. |
167 | |
168 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
169 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
170 | |
171 | We do the import. |
172 | |
173 | @@ -150,7 +152,7 @@ |
174 | |
175 | We must approve the entry to be able to import it. |
176 | |
177 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
178 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
179 | |
180 | We do the import. |
181 | |
182 | |
183 | === modified file 'lib/lp/translations/doc/poimport-pofile-old-po-imported.txt' |
184 | --- lib/lp/translations/doc/poimport-pofile-old-po-imported.txt 2009-08-13 15:12:16 +0000 |
185 | +++ lib/lp/translations/doc/poimport-pofile-old-po-imported.txt 2009-11-18 11:51:13 +0000 |
186 | @@ -16,7 +16,8 @@ |
187 | |
188 | Here are some imports we need to get this test running. |
189 | |
190 | - >>> from canonical.launchpad.interfaces import IPersonSet |
191 | + >>> from canonical.launchpad.interfaces import ( |
192 | + ... ILaunchpadCelebrities, IPersonSet) |
193 | >>> from lp.translations.interfaces.translationimportqueue import ( |
194 | ... ITranslationImportQueue) |
195 | >>> from lp.translations.model.potemplate import POTemplateSubset |
196 | @@ -24,6 +25,7 @@ |
197 | >>> import pytz |
198 | >>> UTC = pytz.timezone('UTC') |
199 | >>> translation_import_queue = getUtility(ITranslationImportQueue) |
200 | + >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
201 | |
202 | And also, the DBSchema to change the imports status |
203 | |
204 | @@ -80,7 +82,7 @@ |
205 | |
206 | We must approve the entry to be able to import it. |
207 | |
208 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
209 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
210 | |
211 | We do the import. This succeeds without errors. |
212 | |
213 | @@ -128,7 +130,7 @@ |
214 | |
215 | We must approve the entry to be able to import it. |
216 | |
217 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
218 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
219 | |
220 | We do the import. |
221 | |
222 | @@ -191,7 +193,7 @@ |
223 | |
224 | We approve the entry and import it. |
225 | |
226 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
227 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
228 | >>> (subject, message) = pofile.importFromQueue(entry) |
229 | |
230 | This succeeds although the file's timestamp is older than that of the |
231 | |
232 | === modified file 'lib/lp/translations/doc/poimport-pofile-syntax-error.txt' |
233 | --- lib/lp/translations/doc/poimport-pofile-syntax-error.txt 2009-08-13 15:12:16 +0000 |
234 | +++ lib/lp/translations/doc/poimport-pofile-syntax-error.txt 2009-11-18 11:51:13 +0000 |
235 | @@ -7,7 +7,8 @@ |
236 | |
237 | Here are some imports we need to get this test running. |
238 | |
239 | - >>> from canonical.launchpad.interfaces import IPersonSet |
240 | + >>> from canonical.launchpad.interfaces import ( |
241 | + ... ILaunchpadCelebrities, IPersonSet) |
242 | >>> from lp.translations.interfaces.translationimportqueue import ( |
243 | ... ITranslationImportQueue) |
244 | >>> from lp.translations.model.potemplate import POTemplateSubset |
245 | @@ -15,6 +16,7 @@ |
246 | >>> import pytz |
247 | >>> UTC = pytz.timezone('UTC') |
248 | >>> translation_import_queue = getUtility(ITranslationImportQueue) |
249 | + >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
250 | |
251 | We need this for the Librarian to work properly. |
252 | |
253 | @@ -76,7 +78,7 @@ |
254 | |
255 | We must approve the entry to be able to import it. |
256 | |
257 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
258 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
259 | |
260 | The import fails. |
261 | |
262 | @@ -135,7 +137,7 @@ |
263 | >>> entry = translation_import_queue.addOrUpdateEntry( |
264 | ... pofile.path, pofile_contents, published, person, |
265 | ... productseries=series, potemplate=potemplate, pofile=pofile) |
266 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
267 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
268 | >>> transaction.commit() |
269 | >>> (subject, message) = pofile.importFromQueue(entry) |
270 | >>> print entry.status.name |
271 | @@ -202,7 +204,7 @@ |
272 | ... pofile.path, pofile_contents, False, person, |
273 | ... productseries=series, potemplate=potemplate, pofile=pofile) |
274 | >>> transaction.commit() |
275 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
276 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
277 | >>> (subject, message) = pofile.importFromQueue(entry) |
278 | |
279 | |
280 | @@ -245,7 +247,7 @@ |
281 | ... pofile.path, pofile_contents, False, person, |
282 | ... productseries=series, potemplate=potemplate, pofile=pofile) |
283 | >>> transaction.commit() |
284 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
285 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
286 | >>> (subject, message) = pofile.importFromQueue(entry) |
287 | |
288 | >>> print entry.status.name |
289 | @@ -283,7 +285,7 @@ |
290 | ... pofile.path, pofile_contents, False, person, |
291 | ... productseries=series, potemplate=potemplate, pofile=pofile) |
292 | >>> transaction.commit() |
293 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
294 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
295 | >>> (subject, message) = pofile.importFromQueue(entry) |
296 | |
297 | >>> print entry.status.name |
298 | @@ -336,7 +338,7 @@ |
299 | ... pofile.path, pofile_contents, False, person, |
300 | ... productseries=series, potemplate=potemplate, pofile=pofile) |
301 | >>> transaction.commit() |
302 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
303 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
304 | >>> (subject, message) = pofile.importFromQueue(entry) |
305 | |
306 | >>> print entry.status.name |
307 | @@ -398,7 +400,7 @@ |
308 | ... pofile.path, pofile_contents, False, person, |
309 | ... productseries=series, potemplate=potemplate, pofile=pofile) |
310 | >>> transaction.commit() |
311 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
312 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
313 | >>> (subject, message) = pofile.importFromQueue(entry) |
314 | |
315 | >>> print entry.status.name |
316 | |
317 | === modified file 'lib/lp/translations/doc/poimport-potemplate-syntax-error.txt' |
318 | --- lib/lp/translations/doc/poimport-potemplate-syntax-error.txt 2009-08-13 15:12:16 +0000 |
319 | +++ lib/lp/translations/doc/poimport-potemplate-syntax-error.txt 2009-11-18 11:51:13 +0000 |
320 | @@ -7,13 +7,15 @@ |
321 | |
322 | Here are some imports we need to get this test running. |
323 | |
324 | - >>> from canonical.launchpad.interfaces import IPersonSet |
325 | + >>> from canonical.launchpad.interfaces import ( |
326 | + ... ILaunchpadCelebrities, IPersonSet) |
327 | >>> from lp.translations.interfaces.translationimportqueue import ( |
328 | ... ITranslationImportQueue) |
329 | >>> from lp.translations.model.potemplate import POTemplateSubset |
330 | >>> import pytz |
331 | >>> UTC = pytz.timezone('UTC') |
332 | >>> translation_import_queue = getUtility(ITranslationImportQueue) |
333 | + >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
334 | |
335 | We need this for the Librarian to work properly. |
336 | |
337 | @@ -59,7 +61,7 @@ |
338 | ... potemplate.path, potemplate_contents, published, person, |
339 | ... productseries=series, potemplate=potemplate) |
340 | >>> transaction.commit() |
341 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
342 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
343 | >>> (subject, message) = potemplate.importFromQueue(entry) |
344 | |
345 | The import failed. |
346 | @@ -120,7 +122,7 @@ |
347 | ... potemplate.path, potemplate_contents, published, person, |
348 | ... productseries=series, potemplate=potemplate) |
349 | >>> transaction.commit() |
350 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
351 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
352 | >>> (subject, message) = potemplate.importFromQueue(entry) |
353 | |
354 | The import failed. |
355 | |
356 | === modified file 'lib/lp/translations/doc/poimport.txt' |
357 | --- lib/lp/translations/doc/poimport.txt 2009-10-29 17:46:00 +0000 |
358 | +++ lib/lp/translations/doc/poimport.txt 2009-11-18 11:51:13 +0000 |
359 | @@ -21,6 +21,7 @@ |
360 | >>> import datetime |
361 | >>> import pytz |
362 | >>> UTC = pytz.timezone('UTC') |
363 | + >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
364 | |
365 | We need this for the Librarian to work properly. |
366 | |
367 | @@ -125,7 +126,7 @@ |
368 | |
369 | The entry gets approved, so it can be imported. |
370 | |
371 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
372 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
373 | |
374 | >>> import datetime |
375 | >>> import pytz |
376 | @@ -201,7 +202,7 @@ |
377 | The entry indicates what file it is to be imported to; importing it to |
378 | any other file would be an error. |
379 | |
380 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
381 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
382 | >>> from lp.translations.interfaces.potemplate import IPOTemplateSet |
383 | >>> other_product = getUtility(IProductSet).getByName('netapplet') |
384 | >>> other_productseries = other_product.getSeries('trunk') |
385 | @@ -306,7 +307,7 @@ |
386 | |
387 | We must approve the entry to be able to import it. |
388 | |
389 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
390 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
391 | |
392 | And we do the import. |
393 | |
394 | @@ -448,7 +449,7 @@ |
395 | ... productseries=series, potemplate=potemplate, |
396 | ... pofile=sumerian_pofile) |
397 | >>> transaction.commit() |
398 | - >>> warning_entry.setStatus(RosettaImportStatus.APPROVED) |
399 | + >>> warning_entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
400 | >>> (subject, message) = sumerian_pofile.importFromQueue(warning_entry) |
401 | |
402 | The warning is noted in the confirmation email. Note that this |
403 | @@ -477,14 +478,13 @@ |
404 | <BLANKLINE> |
405 | Line 12: We got a second header. |
406 | |
407 | - >>> warning_entry.setStatus(RosettaImportStatus.DELETED) |
408 | + >>> warning_entry.setStatus(RosettaImportStatus.DELETED, rosetta_experts) |
409 | |
410 | |
411 | === Import Without Errors === |
412 | |
413 | Now, let's import one without errors. |
414 | |
415 | - >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
416 | >>> pofile_without_errors = r''' |
417 | ... msgid "" |
418 | ... msgstr "" |
419 | @@ -522,7 +522,7 @@ |
420 | |
421 | We must approve the entry to be able to import it. |
422 | |
423 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
424 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
425 | |
426 | At this point, the statistics note that we have 2 translations coming |
427 | from imported files (currentCount), and none updated in Launchpad |
428 | @@ -608,7 +608,7 @@ |
429 | The entry indicates what file it is to be imported to; importing it to |
430 | any other file would be an error. |
431 | |
432 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
433 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
434 | >>> other_pofile = potemplate.newPOFile('de') |
435 | >>> other_pofile.importFromQueue(entry) |
436 | Traceback (most recent call last): |
437 | @@ -638,7 +638,7 @@ |
438 | ... if (entry.status == RosettaImportStatus.IMPORTED or |
439 | ... entry.status == RosettaImportStatus.FAILED) and ( |
440 | ... entry.productseries == series): |
441 | - ... entry.setStatus(RosettaImportStatus.APPROVED) |
442 | + ... entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
443 | ... syncUpdate(entry) |
444 | >>> transaction.commit() |
445 | |
446 | @@ -859,7 +859,7 @@ |
447 | ... potemplate.path, potemplate_contents, True, potemplate.owner, |
448 | ... sourcepackagename=firefox_name, distroseries=warty, |
449 | ... potemplate=potemplate) |
450 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
451 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
452 | >>> syncUpdate(entry) |
453 | >>> transaction.commit() |
454 | |
455 | @@ -941,7 +941,7 @@ |
456 | >>> # Allow Librarian to see the change. |
457 | >>> transaction.commit() |
458 | >>> entry.pofile = firefox_dv |
459 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
460 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
461 | >>> (subject, body) = firefox_dv.importFromQueue(entry, FakeLogger()) |
462 | >>> flush_database_updates() |
463 | >>> print entry.status.name |
464 | @@ -989,7 +989,7 @@ |
465 | ... productseries=pofile.potemplate.productseries, |
466 | ... potemplate=pofile.potemplate, pofile=pofile) |
467 | >>> transaction.commit() |
468 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
469 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
470 | >>> (subject, message) = pofile.importFromQueue(entry) |
471 | |
472 | Import succeeds but no email is sent out. |
473 | @@ -1008,7 +1008,7 @@ |
474 | ... productseries=pofile.potemplate.productseries, |
475 | ... potemplate=pofile.potemplate, pofile=pofile) |
476 | >>> transaction.commit() |
477 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
478 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
479 | >>> (subject, message) = pofile.importFromQueue(entry) |
480 | |
481 | Import fails and email is sent out even though it's a published upload. |
482 | @@ -1042,7 +1042,7 @@ |
483 | ... 'lo.po', 'Invalid content', True, hermit, |
484 | ... pofile=pofile, potemplate=pofile.potemplate, |
485 | ... productseries=pofile.potemplate.productseries) |
486 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
487 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
488 | >>> transaction.commit() |
489 | |
490 | The import fails. The importer would like to send Kermit an email about |
491 | |
492 | === modified file 'lib/lp/translations/doc/rosetta-karma.txt' |
493 | --- lib/lp/translations/doc/rosetta-karma.txt 2009-07-23 17:49:31 +0000 |
494 | +++ lib/lp/translations/doc/rosetta-karma.txt 2009-11-18 11:51:13 +0000 |
495 | @@ -58,7 +58,7 @@ |
496 | |
497 | # Login as a rosetta expert to be able to change the import's status. |
498 | >>> login('carlos@canonical.com') |
499 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
500 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
501 | >>> entry_id = entry.id |
502 | |
503 | The file data is stored in the Librarian, so we have to commit the transaction |
504 | @@ -90,7 +90,7 @@ |
505 | ... potemplate.path, potemplate_contents, comes_from_upstream, |
506 | ... foo_bar, productseries=potemplate.productseries, |
507 | ... potemplate=potemplate) |
508 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
509 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
510 | >>> entry_id = entry.id |
511 | |
512 | The file data is stored in the Librarian, so we have to commit the transaction |
513 | @@ -146,7 +146,7 @@ |
514 | ... pofile.path, pofile_contents, comes_from_upstream, |
515 | ... rosetta_experts, productseries=potemplate.productseries, |
516 | ... potemplate=potemplate, pofile=pofile) |
517 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
518 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
519 | >>> entry_id = entry.id |
520 | |
521 | The file data is stored in the Librarian, so we have to commit the transaction |
522 | @@ -173,7 +173,7 @@ |
523 | ... pofile.path, pofile_contents, comes_from_upstream, foo_bar, |
524 | ... productseries=potemplate.productseries, potemplate=potemplate, |
525 | ... pofile=pofile) |
526 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
527 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
528 | >>> entry_id = entry.id |
529 | |
530 | The file data is stored in the Librarian, so we have to commit the transaction |
531 | @@ -210,7 +210,7 @@ |
532 | ... pofile.path, pofile_contents, not comes_from_upstream, foo_bar, |
533 | ... productseries=potemplate.productseries, potemplate=potemplate, |
534 | ... pofile=pofile) |
535 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
536 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
537 | >>> entry_id = entry.id |
538 | |
539 | The file data is stored in the Librarian, so we have to commit the transaction |
540 | @@ -242,7 +242,7 @@ |
541 | ... pofile.path, pofile_contents, not comes_from_upstream, foo_bar, |
542 | ... productseries=potemplate.productseries, potemplate=potemplate, |
543 | ... pofile=pofile) |
544 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
545 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
546 | >>> entry_id = entry.id |
547 | |
548 | The file data is stored in the Librarian, so we have to commit the transaction |
549 | |
550 | === modified file 'lib/lp/translations/doc/rosetta-poimport-script.txt' |
551 | --- lib/lp/translations/doc/rosetta-poimport-script.txt 2009-07-02 17:16:50 +0000 |
552 | +++ lib/lp/translations/doc/rosetta-poimport-script.txt 2009-11-18 11:51:13 +0000 |
553 | @@ -1,7 +1,8 @@ |
554 | = PO import script = |
555 | |
556 | >>> from lp.translations.model.potemplate import POTemplate |
557 | - >>> from canonical.launchpad.interfaces import IPersonSet |
558 | + >>> from canonical.launchpad.interfaces import ( |
559 | + ... ILaunchpadCelebrities, IPersonSet) |
560 | >>> from lp.translations.interfaces.translationimportqueue import ( |
561 | ... ITranslationImportQueue, |
562 | ... RosettaImportStatus) |
563 | @@ -9,6 +10,7 @@ |
564 | >>> import datetime |
565 | >>> import pytz |
566 | >>> UTC = pytz.timezone('UTC') |
567 | + >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
568 | |
569 | Login as an admin to be able to do changes to the import queue. |
570 | |
571 | @@ -66,7 +68,7 @@ |
572 | ... distroseries=pofile.potemplate.distroseries, |
573 | ... productseries=pofile.potemplate.productseries) |
574 | >>> entry.pofile = pofile |
575 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
576 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
577 | >>> transaction.commit() |
578 | >>> translation_import_queue.countEntries() |
579 | 1 |
580 | |
581 | === modified file 'lib/lp/translations/doc/translationimportqueue.txt' |
582 | --- lib/lp/translations/doc/translationimportqueue.txt 2009-10-01 07:04:18 +0000 |
583 | +++ lib/lp/translations/doc/translationimportqueue.txt 2009-11-18 11:51:13 +0000 |
584 | @@ -136,7 +136,7 @@ |
585 | we need to be logged in as an admin. |
586 | |
587 | >>> login('carlos@canonical.com') |
588 | - >>> entry.setStatus(RosettaImportStatus.IMPORTED) |
589 | + >>> entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
590 | |
591 | The status change updates date_status_changed as well. |
592 | |
593 | @@ -202,7 +202,7 @@ |
594 | |
595 | But if that entry is imported, the guessing algorithm works. |
596 | |
597 | - >>> pot_entry.setStatus(RosettaImportStatus.IMPORTED) |
598 | + >>> pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
599 | >>> guessed_pofile = po_sr_entry.getGuessedPOFile() |
600 | >>> guessed_pofile is None |
601 | False |
602 | @@ -276,7 +276,7 @@ |
603 | |
604 | And set this entry as already imported. |
605 | |
606 | - >>> kde_pot_entry.setStatus(RosettaImportStatus.IMPORTED) |
607 | + >>> kde_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
608 | >>> flush_database_updates() |
609 | |
610 | Let's attach a .po file from kde-i18n-es |
611 | @@ -342,7 +342,7 @@ |
612 | |
613 | And set this entry as already imported. |
614 | |
615 | - >>> kde_pot_entry.setStatus(RosettaImportStatus.IMPORTED) |
616 | + >>> kde_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
617 | >>> flush_database_updates() |
618 | |
619 | Let's attach a .po file from kde-i18n-es |
620 | @@ -402,7 +402,7 @@ |
621 | |
622 | And set this entry as already imported. |
623 | |
624 | - >>> koffice_pot_entry.setStatus(RosettaImportStatus.IMPORTED) |
625 | + >>> koffice_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
626 | >>> flush_database_updates() |
627 | |
628 | Let's attach a .po file from koffice-l10n |
629 | @@ -500,7 +500,7 @@ |
630 | |
631 | And set this entry as already imported. |
632 | |
633 | - >>> adept_pot_entry.setStatus(RosettaImportStatus.IMPORTED) |
634 | + >>> adept_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
635 | >>> flush_database_updates() |
636 | |
637 | Let's attach a .po file now. |
638 | @@ -551,7 +551,7 @@ |
639 | |
640 | And set this entry as already imported. |
641 | |
642 | - >>> ktorrent_pot_entry.setStatus(RosettaImportStatus.IMPORTED) |
643 | + >>> ktorrent_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
644 | >>> flush_database_updates() |
645 | |
646 | Let's attach a .po file now. |
647 | @@ -604,7 +604,7 @@ |
648 | |
649 | And set this entry as already imported. |
650 | |
651 | - >>> zope_pot_entry.setStatus(RosettaImportStatus.IMPORTED) |
652 | + >>> zope_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
653 | >>> flush_database_updates() |
654 | |
655 | Let's attach a .po file now. |
656 | @@ -656,7 +656,7 @@ |
657 | |
658 | And set this entry as already imported. |
659 | |
660 | - >>> k3b_pot_entry.setStatus(RosettaImportStatus.IMPORTED) |
661 | + >>> k3b_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
662 | >>> flush_database_updates() |
663 | |
664 | Let's attach a .po file now. |
665 | @@ -706,7 +706,7 @@ |
666 | |
667 | And set this entry as already imported. |
668 | |
669 | - >>> k3b_pot_entry.setStatus(RosettaImportStatus.IMPORTED) |
670 | + >>> k3b_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
671 | >>> flush_database_updates() |
672 | |
673 | Let's attach a .po file now. |
674 | @@ -782,7 +782,7 @@ |
675 | |
676 | We need it blocked for this test. |
677 | |
678 | - >>> entry5.setStatus(RosettaImportStatus.BLOCKED) |
679 | + >>> entry5.setStatus(RosettaImportStatus.BLOCKED, rosetta_experts) |
680 | |
681 | Let's see how many entries are blocked. |
682 | |
683 | @@ -873,7 +873,7 @@ |
684 | For the third entry, we have one .pot file on that directory, which is already |
685 | in sample data. |
686 | |
687 | - >>> entry3.setStatus(RosettaImportStatus.NEEDS_REVIEW) |
688 | + >>> entry3.setStatus(RosettaImportStatus.NEEDS_REVIEW, rosetta_experts) |
689 | >>> entries = entry3.getTemplatesOnSameDirectory() |
690 | >>> entries.count() |
691 | 1 |
692 | @@ -884,7 +884,7 @@ |
693 | |
694 | For the fourth entry, we have one. |
695 | |
696 | - >>> entry4.setStatus(RosettaImportStatus.NEEDS_REVIEW) |
697 | + >>> entry4.setStatus(RosettaImportStatus.NEEDS_REVIEW, rosetta_experts) |
698 | >>> entries = entry4.getTemplatesOnSameDirectory() |
699 | >>> entries.count() |
700 | 1 |
701 | @@ -898,7 +898,7 @@ |
702 | obviously, we are not returning it as being at the same directory as it makes |
703 | no sense at all. |
704 | |
705 | - >>> entry5.setStatus(RosettaImportStatus.NEEDS_REVIEW) |
706 | + >>> entry5.setStatus(RosettaImportStatus.NEEDS_REVIEW, rosetta_experts) |
707 | >>> entries = entry5.getTemplatesOnSameDirectory() |
708 | >>> entries.count() |
709 | 0 |
710 | @@ -967,10 +967,17 @@ |
711 | NEEDS_REVIEW |
712 | |
713 | Now we approve these entries (we need to be Rosetta administrator to do this). |
714 | +We also need to set an import target. |
715 | |
716 | >>> login('carlos@canonical.com') |
717 | - >>> entry1.setStatus(RosettaImportStatus.APPROVED) |
718 | - >>> entry2.setStatus(RosettaImportStatus.APPROVED) |
719 | + >>> entry1.potemplate = factory.makePOTemplate( |
720 | + ... distroseries=hoary_distroseries, |
721 | + ... sourcepackagename=evolution_sourcepackagename) |
722 | + >>> entry1.pofile = factory.makePOFile('sr', potemplate=entry1.potemplate) |
723 | + >>> entry1.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
724 | + >>> entry2.potemplate = factory.makePOTemplate( |
725 | + ... productseries=productseries) |
726 | + >>> entry2.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
727 | |
728 | >>> flush_database_updates() |
729 | >>> translationimportqueue = getUtility(ITranslationImportQueue) |
730 | @@ -1100,8 +1107,8 @@ |
731 | |
732 | >>> queue = getUtility(ITranslationImportQueue) |
733 | >>> print_queue_entries(queue) |
734 | - hoary evolution | None | po/sr.po |
735 | - firefox | None | foo/bar.pot |
736 | + hoary evolution | generic-string1 | po/sr.po |
737 | + firefox | generic-string3 | foo/bar.pot |
738 | evolution | evolution-2.2-test | po/evolution-2.2-test.pot |
739 | evolution | evolution-2.2-test | po/pt_BR.po |
740 | firefox | None | foo/bar.po |
741 | @@ -1121,8 +1128,8 @@ |
742 | And those new entries in the queue appear in the list. |
743 | |
744 | >>> print_queue_entries(queue) |
745 | - hoary evolution | None | po/sr.po |
746 | - firefox | None | foo/bar.pot |
747 | + hoary evolution | generic-string1 | po/sr.po |
748 | + firefox | generic-string3 | foo/bar.pot |
749 | evolution | evolution-2.2-test | po/evolution-2.2-test.pot |
750 | evolution | evolution-2.2-test | po/pt_BR.po |
751 | firefox | None | foo/bar.po |
752 | @@ -1320,7 +1327,8 @@ |
753 | >>> entry.import_into is None |
754 | True |
755 | |
756 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
757 | +Set the entry to approved, which is only possible if we don't use setStatus. |
758 | + >>> removeSecurityProxy(entry).status = RosettaImportStatus.APPROVED |
759 | |
760 | >>> import logging |
761 | >>> from canonical.launchpad.scripts import FakeLogger |
762 | @@ -1355,7 +1363,8 @@ |
763 | >>> entry.import_into is None |
764 | True |
765 | |
766 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
767 | +Set the entry to approved, which is only possible if we don't use setStatus. |
768 | + >>> removeSecurityProxy(entry).status = RosettaImportStatus.APPROVED |
769 | |
770 | >>> script = TranslationsImport('poimport', test_args=[]) |
771 | >>> script.logger.setLevel(logging.FATAL) |
772 | @@ -1382,11 +1391,13 @@ |
773 | ... distro = distroset[distro_name] |
774 | ... series = distro.getSeries(series_name) |
775 | ... package = packageset['pmount'] |
776 | + ... template = factory.makePOTemplate(distroseries=series, |
777 | + ... sourcepackagename=package) |
778 | ... # In a completely arbitrary move, we make all import requests for |
779 | ... # distro series imported. |
780 | ... return translationimportqueue.addOrUpdateEntry('messages.pot', |
781 | ... 'dummy file', True, rosetta_experts, distroseries=series, |
782 | - ... sourcepackagename=package) |
783 | + ... sourcepackagename=package, potemplate=template) |
784 | |
785 | >>> def create_product_request(product_name, template_name): |
786 | ... """Enqueue an import request for given product and template.""" |
787 | @@ -1403,19 +1414,19 @@ |
788 | |
789 | >>> # Populate import queue with wild mix of requests. |
790 | >>> entry = create_product_request('evolution', 'evolution-2.2') |
791 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
792 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
793 | >>> entry = create_distro_request('debian', 'sarge') |
794 | - >>> entry.setStatus(RosettaImportStatus.NEEDS_REVIEW) |
795 | + >>> entry.setStatus(RosettaImportStatus.NEEDS_REVIEW, rosetta_experts) |
796 | >>> entry = create_product_request('alsa-utils', 'alsa-utils') |
797 | - >>> entry.setStatus(RosettaImportStatus.FAILED) |
798 | + >>> entry.setStatus(RosettaImportStatus.FAILED, rosetta_experts) |
799 | >>> entry = create_distro_request('ubuntu', 'grumpy') |
800 | - >>> entry.setStatus(RosettaImportStatus.BLOCKED) |
801 | + >>> entry.setStatus(RosettaImportStatus.BLOCKED, rosetta_experts) |
802 | >>> entry = create_distro_request('kubuntu', 'krunch') |
803 | - >>> entry.setStatus(RosettaImportStatus.APPROVED) |
804 | + >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts) |
805 | >>> entry = create_product_request('evolution', 'evolution-2.2-test') |
806 | - >>> entry.setStatus(RosettaImportStatus.IMPORTED) |
807 | + >>> entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
808 | >>> entry = create_distro_request('debian', 'woody') |
809 | - >>> entry.setStatus(RosettaImportStatus.NEEDS_REVIEW) |
810 | + >>> entry.setStatus(RosettaImportStatus.NEEDS_REVIEW, rosetta_experts) |
811 | >>> flush_database_updates() |
812 | |
813 | TranslationImportQueue.getRequestTargets first lists distro series |
814 | @@ -1479,7 +1490,7 @@ |
815 | >>> entry = translationimportqueue.addOrUpdateEntry( |
816 | ... u'po/nl.po', 'hoi', True, rosetta_experts, |
817 | ... productseries=evolution_productseries) |
818 | - >>> entry.setStatus(RosettaImportStatus.DELETED) |
819 | + >>> entry.setStatus(RosettaImportStatus.DELETED, rosetta_experts) |
820 | |
821 | The entry stays on the queue long enough to make it a candidate for |
822 | purging. |
823 | @@ -1507,7 +1518,7 @@ |
824 | >>> entry = translationimportqueue.addOrUpdateEntry( |
825 | ... u'po/nl.po', 'hoi', True, rosetta_experts, |
826 | ... productseries=evolution_productseries) |
827 | - >>> entry.setStatus(RosettaImportStatus.IMPORTED) |
828 | + >>> entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts) |
829 | >>> print_queue_entries(translationimportqueue) |
830 | evolution | None | po/nl.po |
831 | |
832 | |
833 | === modified file 'lib/lp/translations/interfaces/translationimportqueue.py' |
834 | --- lib/lp/translations/interfaces/translationimportqueue.py 2009-10-09 13:20:38 +0000 |
835 | +++ lib/lp/translations/interfaces/translationimportqueue.py 2009-11-18 11:51:13 +0000 |
836 | @@ -6,6 +6,7 @@ |
837 | from zope.interface import Interface, Attribute |
838 | from zope.schema import ( |
839 | Bool, Choice, Datetime, Field, Int, Object, Text, TextLine) |
840 | +from zope.security.interfaces import Unauthorized |
841 | from lazr.enum import DBEnumeratedType, DBItem, EnumeratedType, Item |
842 | |
843 | from canonical.launchpad import _ |
844 | @@ -18,11 +19,12 @@ |
845 | |
846 | from lazr.restful.interface import copy_field |
847 | from lazr.restful.fields import Reference |
848 | -from lazr.restful.declarations import ( |
849 | +from lazr.restful.declarations import (call_with, |
850 | collection_default_content, exported, export_as_webservice_collection, |
851 | - export_as_webservice_entry, export_read_operation, operation_parameters, |
852 | - operation_returns_entry, operation_returns_collection_of) |
853 | - |
854 | + export_as_webservice_entry, export_read_operation, |
855 | + export_write_operation, operation_parameters, |
856 | + operation_returns_entry, operation_returns_collection_of, |
857 | + REQUEST_USER, webservice_error) |
858 | from lp.translations.interfaces.translationcommonformat import ( |
859 | TranslationImportExportBaseException) |
860 | |
861 | @@ -37,6 +39,7 @@ |
862 | 'RosettaImportStatus', |
863 | 'SpecialTranslationImportTargetFilter', |
864 | 'TranslationFileType', |
865 | + 'UserCannotSetTranslationImportStatus', |
866 | ] |
867 | |
868 | |
869 | @@ -46,6 +49,15 @@ |
870 | conflicts with existing entries.""" |
871 | |
872 | |
873 | +class UserCannotSetTranslationImportStatus(Unauthorized): |
874 | + """User not permitted to change status. |
875 | + |
876 | + Raised when a user tries to transition to a new status who doesn't |
877 | + have the necessary permissions. |
878 | + """ |
879 | + webservice_error(401) # HTTP Error: 'Unauthorized' |
880 | + |
881 | + |
882 | class RosettaImportStatus(DBEnumeratedType): |
883 | """Rosetta Import Status |
884 | |
885 | @@ -254,10 +266,27 @@ |
886 | required=False, |
887 | readonly=True)) |
888 | |
889 | - def setStatus(status): |
890 | - """Set status. |
891 | - |
892 | - :param status: new status to set. |
893 | + def isUbuntuAndIsUserTranslationGroupOwner(self, user): |
894 | + """Check for special Ubuntu Translation Group. |
895 | + |
896 | + Return true if the entry is targeted to Ubuntu and the user is in |
897 | + the team owning the Ubuntu translation group. |
898 | + """ |
899 | + |
900 | + def isUserUploaderOrOwner(user): |
901 | + """Check for entry uploader or series owner.""" |
902 | + |
903 | + def canSetStatus(new_status, user): |
904 | + """Check if the user can set this new status.""" |
905 | + |
906 | + @call_with(user=REQUEST_USER) |
907 | + @operation_parameters(new_status=copy_field(status)) |
908 | + @export_write_operation() |
909 | + def setStatus(new_status, user): |
910 | + """Transition to a new status if possible. |
911 | + |
912 | + :param new_status: Status to transition to. |
913 | + :param user: The user that is doing the transition. |
914 | """ |
915 | |
916 | def setErrorOutput(output): |
917 | |
918 | === modified file 'lib/lp/translations/model/pofile.py' |
919 | --- lib/lp/translations/model/pofile.py 2009-10-29 12:36:32 +0000 |
920 | +++ lib/lp/translations/model/pofile.py 2009-11-18 11:51:13 +0000 |
921 | @@ -1140,10 +1140,12 @@ |
922 | subject = 'Translation import - %s - %s' % ( |
923 | self.language.displayname, self.potemplate.displayname) |
924 | |
925 | + rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
926 | if import_rejected: |
927 | # There were no imports at all and the user needs to review that |
928 | # file, we tag it as FAILED. |
929 | - entry_to_import.setStatus(RosettaImportStatus.FAILED) |
930 | + entry_to_import.setStatus(RosettaImportStatus.FAILED, |
931 | + rosetta_experts) |
932 | else: |
933 | if (entry_to_import.is_published and |
934 | not needs_notification_for_imported): |
935 | @@ -1152,7 +1154,8 @@ |
936 | # are needed. |
937 | subject = None |
938 | |
939 | - entry_to_import.setStatus(RosettaImportStatus.IMPORTED) |
940 | + entry_to_import.setStatus(RosettaImportStatus.IMPORTED, |
941 | + rosetta_experts) |
942 | # Assign karma to the importer if this is not an automatic import |
943 | # (all automatic imports come from the rosetta expert user) and |
944 | # comes from upstream. |
945 | |
946 | === modified file 'lib/lp/translations/model/potemplate.py' |
947 | --- lib/lp/translations/model/potemplate.py 2009-11-06 21:06:38 +0000 |
948 | +++ lib/lp/translations/model/potemplate.py 2009-11-18 11:51:13 +0000 |
949 | @@ -891,6 +891,7 @@ |
950 | |
951 | translation_importer = getUtility(ITranslationImporter) |
952 | |
953 | + rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts |
954 | subject = 'Translation template import - %s' % self.displayname |
955 | template_mail = 'poimport-template-confirmation.txt' |
956 | errors, warnings = None, None |
957 | @@ -908,7 +909,8 @@ |
958 | template_mail = 'poimport-bad-encoding.txt' |
959 | else: |
960 | template_mail = 'poimport-syntax-error.txt' |
961 | - entry_to_import.setStatus(RosettaImportStatus.FAILED) |
962 | + entry_to_import.setStatus(RosettaImportStatus.FAILED, |
963 | + rosetta_experts) |
964 | error_text = str(exception) |
965 | entry_to_import.setErrorOutput(error_text) |
966 | else: |
967 | @@ -926,7 +928,8 @@ |
968 | entry_to_import.addWarningOutput(replacements['warnings']) |
969 | |
970 | if entry_to_import.status != RosettaImportStatus.FAILED: |
971 | - entry_to_import.setStatus(RosettaImportStatus.IMPORTED) |
972 | + entry_to_import.setStatus(RosettaImportStatus.IMPORTED, |
973 | + rosetta_experts) |
974 | |
975 | # Assign karma to the importer if this is not an automatic import |
976 | # (all automatic imports come from the rosetta expert team). |
977 | |
978 | === modified file 'lib/lp/translations/model/translationbranchapprover.py' |
979 | --- lib/lp/translations/model/translationbranchapprover.py 2009-10-10 19:37:03 +0000 |
980 | +++ lib/lp/translations/model/translationbranchapprover.py 2009-11-18 11:51:13 +0000 |
981 | @@ -11,6 +11,7 @@ |
982 | |
983 | from zope.component import getUtility |
984 | |
985 | +from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
986 | from canonical.launchpad.validators.name import sanitize_name |
987 | from lp.translations.interfaces.potemplate import IPOTemplateSet |
988 | from lp.translations.interfaces.translationimportqueue import ( |
989 | @@ -165,6 +166,7 @@ |
990 | # Approve the entry |
991 | entry.potemplate = potemplate |
992 | if entry.status == RosettaImportStatus.NEEDS_REVIEW: |
993 | - entry.setStatus(RosettaImportStatus.APPROVED) |
994 | + entry.setStatus(RosettaImportStatus.APPROVED, |
995 | + getUtility(ILaunchpadCelebrities).rosetta_experts) |
996 | return entry |
997 | |
998 | |
999 | === modified file 'lib/lp/translations/model/translationimportqueue.py' |
1000 | --- lib/lp/translations/model/translationimportqueue.py 2009-10-22 07:32:02 +0000 |
1001 | +++ lib/lp/translations/model/translationimportqueue.py 2009-11-18 11:51:13 +0000 |
1002 | @@ -56,11 +56,14 @@ |
1003 | ITranslationImportQueueEntry, |
1004 | RosettaImportStatus, |
1005 | SpecialTranslationImportTargetFilter, |
1006 | - TranslationImportQueueConflictError) |
1007 | + TranslationImportQueueConflictError, |
1008 | + UserCannotSetTranslationImportStatus) |
1009 | from lp.translations.interfaces.potemplate import IPOTemplate |
1010 | from lp.translations.interfaces.translations import TranslationConstants |
1011 | from lp.translations.utilities.gettext_po_importer import ( |
1012 | GettextPOImporter) |
1013 | +from lp.translations.utilities.permission_helpers import ( |
1014 | + is_admin_or_rosetta_expert) |
1015 | from canonical.librarian.interfaces import ILibrarianClient |
1016 | |
1017 | |
1018 | @@ -271,9 +274,57 @@ |
1019 | distroseries=self.distroseries, |
1020 | sourcepackagename=self.sourcepackagename) |
1021 | |
1022 | - def setStatus(self, status): |
1023 | - """See `ITranslationImportQueueEntry`.""" |
1024 | - self.status = status |
1025 | + def isUbuntuAndIsUserTranslationGroupOwner(self, user): |
1026 | + """See `ITranslationImportQueueEntry`.""" |
1027 | + # As a special case, the Ubuntu translation group owners can |
1028 | + # manage Ubuntu uploads. |
1029 | + if self.is_targeted_to_ubuntu: |
1030 | + group = self.distroseries.distribution.translationgroup |
1031 | + if group is not None and user.inTeam(group.owner): |
1032 | + return True |
1033 | + return False |
1034 | + |
1035 | + def isUserUploaderOrOwner(self, user): |
1036 | + """See `ITranslationImportQueueEntry`.""" |
1037 | + if user.inTeam(self.importer): |
1038 | + return True |
1039 | + if self.productseries is not None: |
1040 | + return user.inTeam(self.productseries.product.owner) |
1041 | + if self.distroseries is not None: |
1042 | + return user.inTeam(self.distroseries.distribution.owner) |
1043 | + return False |
1044 | + |
1045 | + def canSetStatus(self, new_status, user): |
1046 | + """See `ITranslationImportQueueEntry`.""" |
1047 | + if new_status == self.status: |
1048 | + # Leaving status as it is is always allowed. |
1049 | + return True |
1050 | + if user is None: |
1051 | + # Anonymous user cannot do anything. |
1052 | + return False |
1053 | + can_admin = (is_admin_or_rosetta_expert(user) or |
1054 | + self.isUbuntuAndIsUserTranslationGroupOwner(user)) |
1055 | + if new_status == RosettaImportStatus.APPROVED: |
1056 | + # Only administrators are able to set the APPROVED status, and |
1057 | + # that's only possible if we know where to import it |
1058 | + # (import_into not None). |
1059 | + return can_admin and self.import_into is not None |
1060 | + if new_status == RosettaImportStatus.BLOCKED: |
1061 | + # Only administrators are able to set an entry to BLOCKED. |
1062 | + return can_admin |
1063 | + if (new_status in (RosettaImportStatus.FAILED, |
1064 | + RosettaImportStatus.IMPORTED)): |
1065 | + # Only scripts set these statuses and they report as a rosetta |
1066 | + # expert. |
1067 | + return is_admin_or_rosetta_expert(user) |
1068 | + # All other statuses can bset set by all authorized persons. |
1069 | + return self.isUserUploaderOrOwner(user) or can_admin |
1070 | + |
1071 | + def setStatus(self, new_status, user): |
1072 | + """See `ITranslationImportQueueEntry`.""" |
1073 | + if not self.canSetStatus(new_status, user): |
1074 | + raise UserCannotSetTranslationImportStatus() |
1075 | + self.status = new_status |
1076 | self.date_status_changed = UTC_NOW |
1077 | |
1078 | def setErrorOutput(self, output): |
1079 | @@ -473,7 +524,8 @@ |
1080 | if guessed_language is None: |
1081 | # Custom language code says to ignore imports with this language |
1082 | # code. |
1083 | - self.setStatus(RosettaImportStatus.DELETED) |
1084 | + self.setStatus(RosettaImportStatus.DELETED, |
1085 | + getUtility(ILaunchpadCelebrities).rosetta_experts) |
1086 | return None |
1087 | elif guessed_language == '': |
1088 | # We don't recognize this as a translation file with a name |
1089 | @@ -877,7 +929,7 @@ |
1090 | # We got an update for this entry. If the previous import is |
1091 | # deleted or failed or was already imported we should retry |
1092 | # the import now, just in case it can be imported now. |
1093 | - entry.setStatus(RosettaImportStatus.NEEDS_REVIEW) |
1094 | + entry.setStatus(RosettaImportStatus.NEEDS_REVIEW, importer) |
1095 | |
1096 | entry.date_status_changed = UTC_NOW |
1097 | entry.format = format |
1098 | @@ -1130,7 +1182,8 @@ |
1099 | |
1100 | # Already know where it should be imported. The entry is approved |
1101 | # automatically. |
1102 | - entry.setStatus(RosettaImportStatus.APPROVED) |
1103 | + entry.setStatus(RosettaImportStatus.APPROVED, |
1104 | + getUtility(ILaunchpadCelebrities).rosetta_experts) |
1105 | |
1106 | if txn is not None: |
1107 | txn.commit() |
1108 | @@ -1165,7 +1218,9 @@ |
1109 | if has_templates and not has_templates_unblocked: |
1110 | # All templates on the same directory as this entry are |
1111 | # blocked, so we can block it too. |
1112 | - entry.setStatus(RosettaImportStatus.BLOCKED) |
1113 | + entry.setStatus( |
1114 | + RosettaImportStatus.BLOCKED, |
1115 | + getUtility(ILaunchpadCelebrities).rosetta_experts) |
1116 | num_blocked += 1 |
1117 | if txn is not None: |
1118 | txn.commit() |
1119 | |
1120 | === modified file 'lib/lp/translations/scripts/po_import.py' |
1121 | --- lib/lp/translations/scripts/po_import.py 2009-10-20 05:17:01 +0000 |
1122 | +++ lib/lp/translations/scripts/po_import.py 2009-11-18 11:51:13 +0000 |
1123 | @@ -73,7 +73,8 @@ |
1124 | def _registerFailure(self, entry, reason, traceback=False, abort=False): |
1125 | """Note that a queue entry is unusable in some way.""" |
1126 | reason_text = unicode(reason) |
1127 | - entry.setStatus(RosettaImportStatus.FAILED) |
1128 | + entry.setStatus(RosettaImportStatus.FAILED, |
1129 | + getUtility(ILaunchpadCelebrities).rosetta_experts) |
1130 | entry.setErrorOutput(reason_text) |
1131 | |
1132 | if abort: |
1133 | |
1134 | === modified file 'lib/lp/translations/stories/webservice/xx-translationimportqueue.txt' |
1135 | --- lib/lp/translations/stories/webservice/xx-translationimportqueue.txt 2009-07-01 20:45:39 +0000 |
1136 | +++ lib/lp/translations/stories/webservice/xx-translationimportqueue.txt 2009-11-18 11:51:13 +0000 |
1137 | @@ -114,8 +114,23 @@ |
1138 | ... |
1139 | status: You tried to modify a read-only attribute. |
1140 | |
1141 | -An attempt to do so will leave the entry's status unchanged. |
1142 | +But you can set the status using the setStatus method. |
1143 | + |
1144 | + >>> print webservice.named_post( |
1145 | + ... first_entry, 'setStatus', {}, new_status='Approved') |
1146 | + HTTP/1.1 200 Ok |
1147 | + ... |
1148 | + |
1149 | +The entry's status is changed. |
1150 | |
1151 | >>> queue = webservice.get("/+imports").jsonBody() |
1152 | >>> print queue['entries'][0]['status'] |
1153 | - Imported |
1154 | + Approved |
1155 | + |
1156 | +Unprivileged users cannot change the status. |
1157 | + |
1158 | + >>> print user_webservice.named_post( |
1159 | + ... first_entry, 'setStatus', {}, new_status='Deleted') |
1160 | + HTTP/1.1 401 Unauthorized |
1161 | + ... |
1162 | + |
1163 | |
1164 | === modified file 'lib/lp/translations/tests/test_autoapproval.py' |
1165 | --- lib/lp/translations/tests/test_autoapproval.py 2009-10-29 17:46:00 +0000 |
1166 | +++ lib/lp/translations/tests/test_autoapproval.py 2009-11-18 11:51:13 +0000 |
1167 | @@ -13,6 +13,9 @@ |
1168 | import transaction |
1169 | import unittest |
1170 | |
1171 | +from zope.component import getUtility |
1172 | + |
1173 | +from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1174 | from canonical.launchpad.interfaces.lpstorm import IMasterStore |
1175 | |
1176 | from lp.registry.interfaces.distroseries import DistroSeriesStatus |
1177 | @@ -749,7 +752,8 @@ |
1178 | |
1179 | def _setStatus(self, entry, status, when=None): |
1180 | """Simulate status on queue entry having been set at a given time.""" |
1181 | - entry.setStatus(status) |
1182 | + entry.setStatus(status, |
1183 | + getUtility(ILaunchpadCelebrities).rosetta_experts) |
1184 | if when is not None: |
1185 | entry.date_status_changed = when |
1186 | entry.syncUpdate() |
1187 | @@ -760,6 +764,8 @@ |
1188 | # are. |
1189 | one_year_ago = datetime.now(UTC) - timedelta(days=366) |
1190 | entry = self._makeProductEntry() |
1191 | + entry.potemplate = ( |
1192 | + self.factory.makePOTemplate(productseries=entry.productseries)) |
1193 | entry_id = entry.id |
1194 | |
1195 | self._setStatus(entry, RosettaImportStatus.APPROVED, one_year_ago) |
1196 | |
1197 | === modified file 'lib/lp/translations/tests/test_translationbranchapprover.py' |
1198 | --- lib/lp/translations/tests/test_translationbranchapprover.py 2009-10-19 10:57:28 +0000 |
1199 | +++ lib/lp/translations/tests/test_translationbranchapprover.py 2009-11-18 11:51:13 +0000 |
1200 | @@ -8,6 +8,7 @@ |
1201 | from unittest import TestLoader |
1202 | from zope.component import getUtility |
1203 | |
1204 | +from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1205 | from canonical.launchpad.validators.name import valid_name |
1206 | from canonical.testing import LaunchpadZopelessLayer |
1207 | from lp.translations.interfaces.translationimportqueue import ( |
1208 | @@ -213,7 +214,8 @@ |
1209 | RosettaImportStatus.BLOCKED, |
1210 | ) |
1211 | for status in not_approve_status: |
1212 | - entry.setStatus(status) |
1213 | + entry.setStatus( |
1214 | + status, getUtility(ILaunchpadCelebrities).rosetta_experts) |
1215 | approver = self._create_approver(pot_path) |
1216 | approver.approve(entry) |
1217 | self.assertEqual(status, entry.status) |
1218 | |
1219 | === added file 'lib/lp/translations/tests/test_translationimportqueue.py' |
1220 | --- lib/lp/translations/tests/test_translationimportqueue.py 1970-01-01 00:00:00 +0000 |
1221 | +++ lib/lp/translations/tests/test_translationimportqueue.py 2009-11-18 11:51:13 +0000 |
1222 | @@ -0,0 +1,125 @@ |
1223 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
1224 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
1225 | + |
1226 | +# pylint: disable-msg=C0102 |
1227 | + |
1228 | +__metaclass__ = type |
1229 | + |
1230 | +import unittest |
1231 | + |
1232 | +from zope.component import getUtility |
1233 | + |
1234 | +from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1235 | +from lp.translations.interfaces.translationimportqueue import ( |
1236 | + ITranslationImportQueue, RosettaImportStatus) |
1237 | + |
1238 | +from lp.testing import TestCaseWithFactory |
1239 | +from canonical.testing import LaunchpadZopelessLayer |
1240 | + |
1241 | + |
1242 | +class TestTranslationImportQueueEntryStatus(TestCaseWithFactory): |
1243 | + """Test handling of the status of a queue entry.""" |
1244 | + |
1245 | + layer = LaunchpadZopelessLayer |
1246 | + |
1247 | + def setUp(self): |
1248 | + """Set up context to test in.""" |
1249 | + super(TestTranslationImportQueueEntryStatus, self).setUp() |
1250 | + |
1251 | + self.queue = getUtility(ITranslationImportQueue) |
1252 | + self.rosetta_experts = ( |
1253 | + getUtility(ILaunchpadCelebrities).rosetta_experts) |
1254 | + self.productseries = self.factory.makeProductSeries() |
1255 | + self.uploaderperson = self.factory.makePerson() |
1256 | + self.potemplate = self.factory.makePOTemplate( |
1257 | + productseries=self.productseries) |
1258 | + self.entry = self.queue.addOrUpdateEntry( |
1259 | + 'demo.pot', '#demo', False, self.uploaderperson, |
1260 | + productseries=self.productseries, potemplate=self.potemplate) |
1261 | + |
1262 | + def _assertCanSetStatus(self, user, entry, expected_list): |
1263 | + # Helper to check for all statuses. |
1264 | + # Could iterate RosettaImportStatus.items but listing them here |
1265 | + # explicitely is better to read. They are sorted alphabetically. |
1266 | + possible_statuses = [ |
1267 | + RosettaImportStatus.APPROVED, |
1268 | + RosettaImportStatus.BLOCKED, |
1269 | + RosettaImportStatus.DELETED, |
1270 | + RosettaImportStatus.FAILED, |
1271 | + RosettaImportStatus.IMPORTED, |
1272 | + RosettaImportStatus.NEEDS_REVIEW, |
1273 | + ] |
1274 | + # Do *not* use assertContentEqual here, as the order matters. |
1275 | + self.assertEqual(expected_list, |
1276 | + [entry.canSetStatus(status, user) |
1277 | + for status in possible_statuses]) |
1278 | + |
1279 | + def test_canSetStatus_non_admin(self): |
1280 | + # A non-privileged users cannot set any status except for retaining |
1281 | + # the current status of an entry. |
1282 | + some_user = self.factory.makePerson() |
1283 | + self._assertCanSetStatus(some_user, self.entry, |
1284 | + # A B D F I NR |
1285 | + [False, False, False, False, False, True]) |
1286 | + self.entry.setStatus( |
1287 | + RosettaImportStatus.DELETED, self.rosetta_experts) |
1288 | + self._assertCanSetStatus(some_user, self.entry, |
1289 | + # A B D F I NR |
1290 | + [False, False, True, False, False, False]) |
1291 | + |
1292 | + def test_canSetStatus_rosetta_expert(self): |
1293 | + # Rosetta experts are all-powerful, didn't you know that? |
1294 | + self._assertCanSetStatus(self.rosetta_experts, self.entry, |
1295 | + # A B D F I NR |
1296 | + [True, True, True, True, True, True]) |
1297 | + |
1298 | + def test_canSetStatus_rosetta_expert_no_target(self): |
1299 | + # If the entry has no import target set, even Rosetta experts |
1300 | + # cannot set it to approved. |
1301 | + self.entry.potemplate = None |
1302 | + self._assertCanSetStatus(self.rosetta_experts, self.entry, |
1303 | + # A B D F I NR |
1304 | + [False, True, True, True, True, True]) |
1305 | + |
1306 | + def test_canSetStatus_uploader(self): |
1307 | + # The uploader can set some statuses. |
1308 | + self._assertCanSetStatus(self.uploaderperson, self.entry, |
1309 | + # A B D F I NR |
1310 | + [False, False, True, False, False, True]) |
1311 | + |
1312 | + def test_canSetStatus_owner(self): |
1313 | + # The owner gets the same permissions. |
1314 | + self._assertCanSetStatus(self.productseries.product.owner, self.entry, |
1315 | + # A B D F I NR |
1316 | + [False, False, True, False, False, True]) |
1317 | + |
1318 | + def _setUpUbuntu(self): |
1319 | + self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
1320 | + self.ubuntu_group_owner = self.factory.makePerson() |
1321 | + self.ubuntu.translationgroup = ( |
1322 | + self.factory.makeTranslationGroup(self.ubuntu_group_owner)) |
1323 | + |
1324 | + def test_canSetStatus_ubuntu_translation_group(self): |
1325 | + # Owners of the Ubuntu translation Groups can set entries to approved |
1326 | + # that are targeted to Ubuntu. |
1327 | + self._setUpUbuntu() |
1328 | + ubuntu_entry = self.queue.addOrUpdateEntry( |
1329 | + 'demo.pot', '#demo', False, self.uploaderperson, |
1330 | + distroseries=self.factory.makeDistroRelease(self.ubuntu), |
1331 | + sourcepackagename=self.factory.makeSourcePackageName(), |
1332 | + potemplate=self.potemplate) |
1333 | + self._assertCanSetStatus(self.ubuntu_group_owner, ubuntu_entry, |
1334 | + # A B D F I NR |
1335 | + [True, True, True, False, False, True]) |
1336 | + |
1337 | + def test_canSetStatus_ubuntu_translation_group_not_ubuntu(self): |
1338 | + # Outside of Ubuntu, owners of the Ubuntu translation Groups have no |
1339 | + # powers. |
1340 | + self._setUpUbuntu() |
1341 | + self._assertCanSetStatus(self.ubuntu_group_owner, self.entry, |
1342 | + # A B D F I NR |
1343 | + [False, False, False, False, False, True]) |
1344 | + |
1345 | + |
1346 | +def test_suite(): |
1347 | + return unittest.TestLoader().loadTestsFromName(__name__) |
1348 | |
1349 | === added file 'lib/lp/translations/utilities/permission_helpers.py' |
1350 | --- lib/lp/translations/utilities/permission_helpers.py 1970-01-01 00:00:00 +0000 |
1351 | +++ lib/lp/translations/utilities/permission_helpers.py 2009-11-18 11:51:13 +0000 |
1352 | @@ -0,0 +1,21 @@ |
1353 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
1354 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
1355 | + |
1356 | +"""Helpful functions to enforce permissions.""" |
1357 | + |
1358 | +__metaclass__ = type |
1359 | + |
1360 | +__all__ = [ |
1361 | + 'is_admin_or_rosetta_expert', |
1362 | + ] |
1363 | + |
1364 | +from zope.component import getUtility |
1365 | + |
1366 | +from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1367 | + |
1368 | + |
1369 | +def is_admin_or_rosetta_expert(user): |
1370 | + """Check if the user is a Launchpad admin or a Rosettta expert.""" |
1371 | + celebrities = getUtility(ILaunchpadCelebrities) |
1372 | + return (user.inTeam(celebrities.admin) or |
1373 | + user.inTeam(celebrities.rosetta_experts)) |
1374 | |
1375 | === modified file 'lib/lp/translations/utilities/tests/helpers.py' |
1376 | --- lib/lp/translations/utilities/tests/helpers.py 2009-07-17 00:26:05 +0000 |
1377 | +++ lib/lp/translations/utilities/tests/helpers.py 2009-11-18 11:51:13 +0000 |
1378 | @@ -15,6 +15,7 @@ |
1379 | |
1380 | from zope.component import getUtility |
1381 | |
1382 | +from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1383 | from lp.translations.interfaces.translationimportqueue import ( |
1384 | ITranslationImportQueue, RosettaImportStatus) |
1385 | from canonical.launchpad.scripts import FakeLogger |
1386 | @@ -64,7 +65,8 @@ |
1387 | else: |
1388 | commit() |
1389 | |
1390 | - entry.setStatus(RosettaImportStatus.APPROVED) |
1391 | + entry.setStatus(RosettaImportStatus.APPROVED, |
1392 | + getUtility(ILaunchpadCelebrities).rosetta_experts) |
1393 | (subject, body) = target.importFromQueue(entry, FakeLogger()) |
1394 | return entry |
1395 | |
1396 | |
1397 | === modified file 'lib/lp/translations/utilities/tests/test_xpi_import.py' |
1398 | --- lib/lp/translations/utilities/tests/test_xpi_import.py 2009-08-13 15:12:16 +0000 |
1399 | +++ lib/lp/translations/utilities/tests/test_xpi_import.py 2009-11-18 11:51:13 +0000 |
1400 | @@ -9,6 +9,7 @@ |
1401 | |
1402 | from zope.component import getUtility |
1403 | |
1404 | +from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1405 | from lp.registry.interfaces.person import IPersonSet |
1406 | from lp.registry.interfaces.product import IProductSet |
1407 | from lp.translations.interfaces.potemplate import IPOTemplateSet |
1408 | @@ -204,7 +205,8 @@ |
1409 | ).count() |
1410 | |
1411 | # Force the entry to be imported again: |
1412 | - entry.setStatus(RosettaImportStatus.APPROVED) |
1413 | + entry.setStatus(RosettaImportStatus.APPROVED, |
1414 | + getUtility(ILaunchpadCelebrities).rosetta_experts) |
1415 | # Now, we tell the PO template to import from the file data it has. |
1416 | (subject, body) = self.firefox_template.importFromQueue(entry) |
1417 | |
1418 | |
1419 | === modified file 'lib/lp/translations/utilities/tests/test_xpi_po_exporter.py' |
1420 | --- lib/lp/translations/utilities/tests/test_xpi_po_exporter.py 2009-08-13 15:12:16 +0000 |
1421 | +++ lib/lp/translations/utilities/tests/test_xpi_po_exporter.py 2009-11-18 11:51:13 +0000 |
1422 | @@ -10,6 +10,7 @@ |
1423 | |
1424 | from canonical.database.sqlbase import commit |
1425 | from canonical.launchpad.ftests import sync |
1426 | +from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1427 | from lp.registry.interfaces.person import IPersonSet |
1428 | from lp.registry.interfaces.product import IProductSet |
1429 | from lp.translations.interfaces.potemplate import IPOTemplateSet |
1430 | @@ -78,7 +79,8 @@ |
1431 | potemplate=self.firefox_template) |
1432 | |
1433 | # We must approve the entry to be able to import it. |
1434 | - entry.setStatus(RosettaImportStatus.APPROVED) |
1435 | + entry.setStatus(RosettaImportStatus.APPROVED, |
1436 | + getUtility(ILaunchpadCelebrities).rosetta_experts) |
1437 | # The file data is stored in the Librarian, so we have to commit the |
1438 | # transaction to make sure it's stored properly. |
1439 | commit() |
This is a continuation of this proposal: /code.edge. launchpad. net/~henninge/ launchpad/ bug-422466- api/+merge/ 14841
https:/
This branch is properly based on devel with no other prerequisite.