Merge lp:~henninge/launchpad/bug-482267 into lp:launchpad

Proposed by Henning Eggers
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
Reviewer Review Type Date Requested Status
Edwin Grubbs (community) code Approve
Review via email: mp+14947@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Henning Eggers (henninge) wrote :

This is a continuation of this proposal:
https://code.edge.launchpad.net/~henninge/launchpad/bug-422466-api/+merge/14841

This branch is properly based on devel with no other prerequisite.

Revision history for this message
Henning Eggers (henninge) wrote :

Here is the incremental diffs wrt the previous review.

=== modified file 'lib/lp/translations/model/translationimportqueue.py'
--- lib/lp/translations/model/translationimportqueue.py 2009-11-17 09:50:33 +0000
+++ lib/lp/translations/model/translationimportqueue.py 2009-11-17 10:55:09 +0000
@@ -310,7 +310,7 @@
             # that's only possible if we know where to import it
             # (import_into not None).
             return False
- if (new_status == RosettaImportStatus.BLOCKED and not can_admin):
+ if new_status == RosettaImportStatus.BLOCKED and not can_admin:
             # Only administrators are able to set an entry to BLOCKED.
             return False
         if (new_status in (RosettaImportStatus.FAILED,
@@ -319,7 +319,7 @@
             # Only scripts set these statuses and they report as a rosetta
             # expert.
             return False
- return (self.isUserUploaderOrOwner(user) or can_admin)
+ return self.isUserUploaderOrOwner(user) or can_admin

     def setStatus(self, new_status, user):
         """See `ITranslationImportQueueEntry`."""

=== modified file 'lib/lp/translations/tests/test_translationimportqueue.py'
--- lib/lp/translations/tests/test_translationimportqueue.py 2009-11-17 09:51:40 +0000
+++ lib/lp/translations/tests/test_translationimportqueue.py 2009-11-17 11:03:11 +0000
@@ -17,14 +17,14 @@
 from canonical.testing import LaunchpadZopelessLayer

-class TestTranslationImpportQueueEntryStatus(TestCaseWithFactory):
+class TestTranslationImportQueueEntryStatus(TestCaseWithFactory):
     """Test handling of the status of a queue entry."""

     layer = LaunchpadZopelessLayer

     def setUp(self):
         """Set up context to test in."""
- super(TestTranslationImpportQueueEntryStatus, self).setUp()
+ super(TestTranslationImportQueueEntryStatus, self).setUp()

         self.queue = getUtility(ITranslationImportQueue)
         self.rosetta_experts = (
@@ -100,7 +100,7 @@
             self.factory.makeTranslationGroup(self.ubuntu_group_owner))

     def test_canSetStatus_ubuntu_translation_group(self):
- # 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.
         self._setUpUbuntu()
         ubuntu_entry = self.queue.addOrUpdateEntry(

Revision history for this message
Henning Eggers (henninge) wrote :
Download full text (5.2 KiB)

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/launchpad/icing/yui'
> === target is u'../../../../lazr-js/build/yui'
2,3c4,5
< --- lib/canonical/launchpad/security.py 2009-11-15 01:05:49 +0000
< +++ lib/canonical/launchpad/security.py 2009-11-17 09:51:03 +0000
---
> --- lib/canonical/launchpad/security.py 2009-11-13 05:13:07 +0000
> +++ lib/canonical/launchpad/security.py 2009-11-13 18:41:14 +0000
46c48
< @@ -1278,10 +1275,8 @@
---
> @@ -1310,10 +1307,8 @@
59c61
< @@ -1291,11 +1286,12 @@
---
> @@ -1323,11 +1318,12 @@
77c79
< +++ lib/lp/translations/browser/hastranslationimports.py 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/browser/hastranslationimports.py 2009-11-13 18:41:14 +0000
103c105
< +++ lib/lp/translations/browser/translationimportqueue.py 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/browser/translationimportqueue.py 2009-11-13 18:41:14 +0000
116c118
< +++ lib/lp/translations/doc/poexport-language-pack.txt 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/doc/poexport-language-pack.txt 2009-11-13 18:41:14 +0000
146c148
< +++ lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt 2009-11-13 18:41:14 +0000
185c187
< +++ lib/lp/translations/doc/poimport-pofile-old-po-imported.txt 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/doc/poimport-pofile-old-po-imported.txt 2009-11-13 18:41:14 +0000
234c236
< +++ lib/lp/translations/doc/poimport-pofile-syntax-error.txt 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/doc/poimport-pofile-syntax-error.txt 2009-11-13 18:41:14 +0000
319c321
< +++ lib/lp/translations/doc/poimport-potemplate-syntax-error.txt 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/doc/poimport-potemplate-syntax-error.txt 2009-11-13 18:41:14 +0000
358c360
< +++ lib/lp/translations/doc/poimport.txt 2009-11-17 09:51:03 +0000
---
> +++ lib/lp/translations/doc/poimport.txt 2009-11-13 18:41:14 +0000
494c496
< +++ lib/lp/translations/doc/rosetta-karma.txt 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/doc/rosetta-karma.txt 2009-11-13 18:41:14 +0000
552c554
< +++ lib/lp/translations/doc/rosetta-poimport-script.txt 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/doc/rosetta-poimport-script.txt 2009-11-13 18:41:14 +0000
583c585
< +++ lib/lp/translations/doc/translationimportqueue.txt 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/doc/translationimportqueue.txt 2009-11-13 18:41:14 +0000
835c837
< +++ lib/lp/translations/interfaces/translationimportqueue.py 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/interfaces/translationimportqueue.py 2009-11-13 18:41:14 +0000
920c922
< +++ lib/lp/translations/model/pofile.py 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/model/pofile.py 2009-11-13 18:41:14 +0000
948c950
< +++ lib/lp/translations/model/potemplate.py 2009-11-17 09:49:17 +0000
---
> +++ lib/lp/translations/model/pote...

Read more...

Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

Hi Henning,

Your changes look good. I just have one suggestion below.

merge-approved

-Edwin

>>> + if (new_status == RosettaImportStatus.APPROVED and
>>> + not (self.import_into is not None and can_admin)):
>>
>>
>> I think it would be clearer without the double negatives.
>> if (new_status == RosettaImportStatus.APPROVED and
>> (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 == RosettaImportStatus.APPROVED:
        if can_admin and self.import_into is None:
            pass
        else:
            return False

review: Approve (code)
Revision history for this message
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 == RosettaImportStatus.APPROVED:
> 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

Revision history for this message
Henning Eggers (henninge) wrote :

=== modified file 'lib/lp/translations/model/translationimportqueue.py'
--- lib/lp/translations/model/translationimportqueue.py 2009-11-17 11:05:58 +0000
+++ lib/lp/translations/model/translationimportqueue.py 2009-11-18 11:41:37 +0000
@@ -304,21 +304,20 @@
             return False
         can_admin = (is_admin_or_rosetta_expert(user) or
                      self.isUbuntuAndIsUserTranslationGroupOwner(user))
- if (new_status == RosettaImportStatus.APPROVED and
- not (self.import_into is not None and can_admin)):
+ if new_status == RosettaImportStatus.APPROVED:
             # 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 == RosettaImportStatus.BLOCKED and not can_admin:
+ return can_admin and self.import_into is not None
+ if new_status == RosettaImportStatus.BLOCKED:
             # Only administrators are able to set an entry to BLOCKED.
- return False
+ return can_admin
         if (new_status in (RosettaImportStatus.FAILED,
- RosettaImportStatus.IMPORTED)
- and not is_admin_or_rosetta_expert(user)):
+ RosettaImportStatus.IMPORTED)):
             # Only scripts set these statuses and they report as a rosetta
             # expert.
- return False
+ return is_admin_or_rosetta_expert(user)
+ # All other statuses can bset set by all authorized persons.
         return self.isUserUploaderOrOwner(user) or can_admin

     def setStatus(self, new_status, user):

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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()