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