Merge lp:~henninge/launchpad/bug-422466-api into lp:launchpad

Proposed by Henning Eggers
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
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
To post a comment you must log in.
Revision history for this message
Henning Eggers (henninge) wrote :
Download full text (4.2 KiB)

= Bug 482267 =

In peparation for fixing bug 422466 which adds a lazr-js status picker to the translation import queue, the TranslationImportQueueEntry.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.
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 isUbuntuAndIsUserTranslationGroupOwner and isUserUploaderOrOwner and called from security.py.
Also, a new module permission_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.

== 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
    entry = queue.getAllEntries(import_status="Imported")[0]
    print repr(entry)
    print entry.status
    entry.setStatus(new_status='Blocked')
    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:
  lib/canonical/launchpad/security.py
  lib/lp/translations/browser/hastranslationimports.py
  lib/lp/translations/browser/translationimportqueue.py
  lib/lp/translations/doc/poexport-language-pack.txt
  lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt
  lib/lp/translations/doc/poimport-pofile-old-po-imported.txt
  lib/lp/translations/doc/poimport-pofile-syntax-error.txt
  lib/lp/translations/doc/poimport-potemplate-syntax-error.txt
  lib/lp/translations/doc/poimport.txt
  lib/lp/translations/doc/rosetta-karma.txt
  lib/lp/translations/doc/rosetta-poimport-script.txt
  lib/lp/translations/doc/translationimportqueue.txt
  lib/lp/translations/interfaces/translationimportqueue.py
  lib/lp/translations/model/pofile.py
  lib/...

Read more...

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

Correct QA code for launchpadlib:

    queue = launchpad.translation_import_queue_entries
    entry = queue.getAllEntries(import_status="Imported")[0]
    print repr(entry)
    print entry.status

    entry.setStatus(new_status='Deleted')
    entry_link = entry.self_link
    entry = launchpad.load(entry_link)
    print repr(entry)
    print entry.status

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

 canonical/launchpad/security.py | 24 -
 lp/translations/browser/hastranslationimports.py | 8
 lp/translations/browser/translationimportqueue.py | 2
 lp/translations/doc/poexport-language-pack.txt | 8
 lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt | 8
 lp/translations/doc/poimport-pofile-old-po-imported.txt | 10
 lp/translations/doc/poimport-pofile-syntax-error.txt | 18 -
 lp/translations/doc/poimport-potemplate-syntax-error.txt | 8
 lp/translations/doc/poimport.txt | 28 +-
 lp/translations/doc/rosetta-karma.txt | 12
 lp/translations/doc/rosetta-poimport-script.txt | 6
 lp/translations/doc/translationimportqueue.txt | 75 +++---
 lp/translations/interfaces/translationimportqueue.py | 45 ++-
 lp/translations/model/pofile.py | 7
 lp/translations/model/potemplate.py | 7
 lp/translations/model/translationbranchapprover.py | 4
 lp/translations/model/translationimportqueue.py | 72 +++++
 lp/translations/scripts/po_import.py | 3
 lp/translations/stories/webservice/xx-translationimportqueue.txt | 19 +
 lp/translations/tests/test_autoapproval.py | 8
 lp/translations/tests/test_translationbranchapprover.py | 4
 lp/translations/tests/test_translationimportqueue.py | 125 ++++++++++
 lp/translations/utilities/permission_helpers.py | 21 +
 lp/translations/utilities/tests/helpers.py | 4
 lp/translations/utilities/tests/test_xpi_import.py | 4
 lp/translations/utilities/tests/test_xpi_po_exporter.py | 4
 26 files changed, 412 insertions(+), 122 deletions(-)

Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :
Download full text (11.9 KiB)

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/launchpad/icing/yui'
>=== target is u'../../../../lazr-js/build/yui'

I think this symlink must have been for testing so should be removed.

>=== modified file 'lib/lp/translations/model/translationimportqueue.py'
>--- lib/lp/translations/model/translationimportqueue.py 2009-10-22 07:32:02 +0000
>+++ lib/lp/translations/model/translationimportqueue.py 2009-11-13 18:41:14 +0000
>@@ -56,11 +56,14 @@
> ITranslationImportQueueEntry,
> RosettaImportStatus,
> SpecialTranslationImportTargetFilter,
>- TranslationImportQueueConflictError)
>+ TranslationImportQueueConflictError,
>+ UserCannotSetTranslationImportStatus)
> from lp.translations.interfaces.potemplate import IPOTemplate
> from lp.translations.interfaces.translations import TranslationConstants
> from lp.translations.utilities.gettext_po_importer import (
> GettextPOImporter)
>+from lp.translations.utilities.permission_helpers import (
>+ is_admin_or_rosetta_expert)
> from canonical.librarian.interfaces import ILibrarianClient
>
>
>@@ -271,9 +274,58 @@
> distroseries=self.distroseries,
> sourcepackagename=self.sourcepackagename)
>
>- def setStatus(self, status):
>- """See `ITranslationImportQueueEntry`."""
>- self.status = status
>+ def isUbuntuAndIsUserTranslationGroupOwner(self, user):
>+ """See `ITranslationImportQueueEntry`."""
>+ # As a special case, the Ubuntu translation group owners can
>+ # manage Ubuntu uploads.
>+ if self.is_targeted_to_ubuntu:
>+ group = self.distroseries.distribution.translationgroup
>+ if group is not None and user.inTeam(group.owner):
>+ return True
>+ return False
>+
>+ def isUserUploaderOrOwner(self, user):
>+ """See `ITranslationImportQueueEntry`."""
>+ if user.inTeam(self.importer):
>+ return True
>+ if self.productseries is not None:
>+ return user.inTeam(self.productseries.product.owner)
>+ if self.distroseries is not None:
>+ return user.inTeam(self.distroseries.distribution.owner)

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 `ITranslationImportQueueEntry`."""
>+ 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_or_rosetta_expert(user) or
>+ self.isUbuntuAndIsUserTranslationGroupOwner(user))
>+ 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 ...

review: Needs Fixing (code)
Revision history for this message
Henning Eggers (henninge) wrote :
Download full text (13.9 KiB)

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/launchpad/icing/yui'
>> === target is u'../../../../lazr-js/build/yui'
>
>
> 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/translations/model/translationimportqueue.py'
>> --- lib/lp/translations/model/translationimportqueue.py 2009-10-22 07:32:02 +0000
>> +++ lib/lp/translations/model/translationimportqueue.py 2009-11-13 18:41:14 +0000
>> @@ -56,11 +56,14 @@
>> ITranslationImportQueueEntry,
>> RosettaImportStatus,
>> SpecialTranslationImportTargetFilter,
>> - TranslationImportQueueConflictError)
>> + TranslationImportQueueConflictError,
>> + UserCannotSetTranslationImportStatus)
>> from lp.translations.interfaces.potemplate import IPOTemplate
>> from lp.translations.interfaces.translations import TranslationConstants
>> from lp.translations.utilities.gettext_po_importer import (
>> GettextPOImporter)
>> +from lp.translations.utilities.permission_helpers import (
>> + is_admin_or_rosetta_expert)
>> from canonical.librarian.interfaces import ILibrarianClient
>>
>>
>> @@ -271,9 +274,58 @@
>> distroseries=self.distroseries,
>> sourcepackagename=self.sourcepackagename)
>>
>> - def setStatus(self, status):
>> - """See `ITranslationImportQueueEntry`."""
>> - self.status = status
>> + def isUbuntuAndIsUserTranslationGroupOwner(self, user):
>> + """See `ITranslationImportQueueEntry`."""
>> + # As a special case, the Ubuntu translation group owners can
>> + # manage Ubuntu uploads.
>> + if self.is_targeted_to_ubuntu:
>> + group = self.distroseries.distribution.translationgroup
>> + if group is not None and user.inTeam(group.owner):
>> + return True
>> + return False
>> +
>> + def isUserUploaderOrOwner(self, user):
>> + """See `ITranslationImportQueueEntry`."""
>> + if user.inTeam(self.importer):
>> + return True
>> + if self.productseries is not None:
>> + return user.inTeam(self.productseries.product.owner)
>> + if self.distroseries is not None:
>> + return user.inTeam(self.distroseries.distribution.owner)
>
>
> Is there n...

Revision history for this message
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://code.edge.launchpad.net/~henninge/launchpad/bug-482267/+merge/14947

review: Disapprove (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added symlink 'lib/canonical/launchpad/icing/yui'
=== target is u'../../../../lazr-js/build/yui'
=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2009-11-13 05:13:07 +0000
+++ lib/canonical/launchpad/security.py 2009-11-13 18:41:14 +0000
@@ -10,6 +10,7 @@
10from zope.component import getUtility10from zope.component import getUtility
1111
12from canonical.launchpad.interfaces.account import IAccount12from canonical.launchpad.interfaces.account import IAccount
13from canonical.launchpad.interfaces.emailaddress import IEmailAddress
13from lp.archiveuploader.permission import verify_upload14from lp.archiveuploader.permission import verify_upload
14from lp.registry.interfaces.announcement import IAnnouncement15from lp.registry.interfaces.announcement import IAnnouncement
15from lp.soyuz.interfaces.archive import IArchive16from lp.soyuz.interfaces.archive import IArchive
@@ -48,7 +49,8 @@
48from lp.registry.interfaces.distroseries import IDistroSeries49from lp.registry.interfaces.distroseries import IDistroSeries
49from lp.translations.interfaces.distroserieslanguage import (50from lp.translations.interfaces.distroserieslanguage import (
50 IDistroSeriesLanguage)51 IDistroSeriesLanguage)
51from canonical.launchpad.interfaces.emailaddress import IEmailAddress52from lp.translations.utilities.permission_helpers import (
53 is_admin_or_rosetta_expert)
52from lp.registry.interfaces.entitlement import IEntitlement54from lp.registry.interfaces.entitlement import IEntitlement
53from canonical.launchpad.interfaces.hwdb import (55from canonical.launchpad.interfaces.hwdb import (
54 IHWDBApplication, IHWDevice, IHWDeviceClass, IHWDriver, IHWDriverName,56 IHWDBApplication, IHWDevice, IHWDeviceClass, IHWDriver, IHWDriverName,
@@ -475,10 +477,7 @@
475477
476 def checkAuthenticated(self, user):478 def checkAuthenticated(self, user):
477 """Allow Launchpad's admins and Rosetta experts edit all fields."""479 """Allow Launchpad's admins and Rosetta experts edit all fields."""
478 celebrities = getUtility(ILaunchpadCelebrities)480 return is_admin_or_rosetta_expert(user)
479 return (user.inTeam(celebrities.admin) or
480 user.inTeam(celebrities.rosetta_experts))
481
482481
483class AdminProductTranslations(AuthorizationBase):482class AdminProductTranslations(AuthorizationBase):
484 permission = 'launchpad.TranslationsAdmin'483 permission = 'launchpad.TranslationsAdmin'
@@ -490,10 +489,8 @@
490 Any Launchpad/Launchpad Translations administrator or owners are489 Any Launchpad/Launchpad Translations administrator or owners are
491 able to change translation settings for a product.490 able to change translation settings for a product.
492 """491 """
493 celebrities = getUtility(ILaunchpadCelebrities)
494 return (user.inTeam(self.obj.owner) or492 return (user.inTeam(self.obj.owner) or
495 user.inTeam(celebrities.admin) or493 is_admin_or_rosetta_expert(user))
496 user.inTeam(celebrities.rosetta_experts))
497494
498495
499class AdminSeriesByVCSImports(AuthorizationBase):496class AdminSeriesByVCSImports(AuthorizationBase):
@@ -1310,10 +1307,8 @@
13101307
1311 # As a special case, the Ubuntu translation group owners can1308 # As a special case, the Ubuntu translation group owners can
1312 # manage Ubuntu uploads.1309 # manage Ubuntu uploads.
1313 if self.obj.is_targeted_to_ubuntu:1310 if self.obj.isUbuntuAndIsUserTranslationGroupOwner(user):
1314 group = self.obj.distroseries.distribution.translationgroup1311 return True
1315 if group is not None and user.inTeam(group.owner):
1316 return True
13171312
1318 return False1313 return False
13191314
@@ -1323,11 +1318,12 @@
1323 usedfor = ITranslationImportQueueEntry1318 usedfor = ITranslationImportQueueEntry
13241319
1325 def checkAuthenticated(self, user):1320 def checkAuthenticated(self, user):
1326 """Anyone who can admin an entry, plus its owner, can edit it.1321 """Anyone who can admin an entry, plus its owner or the owner of the
1322 product or distribution, can edit it.
1327 """1323 """
1328 if AdminTranslationImportQueueEntry.checkAuthenticated(self, user):1324 if AdminTranslationImportQueueEntry.checkAuthenticated(self, user):
1329 return True1325 return True
1330 if user.inTeam(self.obj.importer):1326 if self.obj.isUserUploaderOrOwner(user):
1331 return True1327 return True
13321328
1333 return False1329 return False
13341330
=== modified file 'lib/lp/translations/browser/hastranslationimports.py'
--- lib/lp/translations/browser/hastranslationimports.py 2009-09-17 20:11:48 +0000
+++ lib/lp/translations/browser/hastranslationimports.py 2009-11-13 18:41:14 +0000
@@ -223,17 +223,17 @@
223 # special permissions to change status.223 # special permissions to change status.
224 if (new_status_name == RosettaImportStatus.DELETED.name and224 if (new_status_name == RosettaImportStatus.DELETED.name and
225 check_permission('launchpad.Edit', entry)):225 check_permission('launchpad.Edit', entry)):
226 entry.setStatus(RosettaImportStatus.DELETED)226 entry.setStatus(RosettaImportStatus.DELETED, self.user)
227 elif (new_status_name == RosettaImportStatus.BLOCKED.name and227 elif (new_status_name == RosettaImportStatus.BLOCKED.name and
228 check_permission('launchpad.Admin', entry)):228 check_permission('launchpad.Admin', entry)):
229 entry.setStatus(RosettaImportStatus.BLOCKED)229 entry.setStatus(RosettaImportStatus.BLOCKED, self.user)
230 elif (new_status_name == RosettaImportStatus.APPROVED.name and230 elif (new_status_name == RosettaImportStatus.APPROVED.name and
231 check_permission('launchpad.Admin', entry) and231 check_permission('launchpad.Admin', entry) and
232 entry.import_into is not None):232 entry.import_into is not None):
233 entry.setStatus(RosettaImportStatus.APPROVED)233 entry.setStatus(RosettaImportStatus.APPROVED, self.user)
234 elif (new_status_name == RosettaImportStatus.NEEDS_REVIEW.name and234 elif (new_status_name == RosettaImportStatus.NEEDS_REVIEW.name and
235 check_permission('launchpad.Admin', entry)):235 check_permission('launchpad.Admin', entry)):
236 entry.setStatus(RosettaImportStatus.NEEDS_REVIEW)236 entry.setStatus(RosettaImportStatus.NEEDS_REVIEW, self.user)
237 else:237 else:
238 # The user was not the importer or we are trying to set a238 # The user was not the importer or we are trying to set a
239 # status that must not be set from this form. That means that239 # status that must not be set from this form. That means that
240240
=== modified file 'lib/lp/translations/browser/translationimportqueue.py'
--- lib/lp/translations/browser/translationimportqueue.py 2009-09-17 20:11:48 +0000
+++ lib/lp/translations/browser/translationimportqueue.py 2009-11-13 18:41:14 +0000
@@ -414,7 +414,7 @@
414 # Store the associated IPOTemplate.414 # Store the associated IPOTemplate.
415 self.context.potemplate = potemplate415 self.context.potemplate = potemplate
416416
417 self.context.setStatus(RosettaImportStatus.APPROVED)417 self.context.setStatus(RosettaImportStatus.APPROVED, self.user)
418 self.context.date_status_changed = UTC_NOW418 self.context.date_status_changed = UTC_NOW
419419
420420
421421
=== modified file 'lib/lp/translations/doc/poexport-language-pack.txt'
--- lib/lp/translations/doc/poexport-language-pack.txt 2009-08-13 19:03:36 +0000
+++ lib/lp/translations/doc/poexport-language-pack.txt 2009-11-13 18:41:14 +0000
@@ -12,10 +12,12 @@
12 >>> import transaction12 >>> import transaction
13 >>> from canonical.launchpad.ftests import login13 >>> from canonical.launchpad.ftests import login
14 >>> from canonical.launchpad.helpers import string_to_tarfile14 >>> from canonical.launchpad.helpers import string_to_tarfile
15 >>> from canonical.launchpad.interfaces import ILaunchpadCelebrities
15 >>> from lp.translations.scripts.language_pack import \16 >>> from lp.translations.scripts.language_pack import \
16 ... export_language_pack17 ... export_language_pack
17 >>> from canonical.librarian.interfaces import ILibrarianClient18 >>> from canonical.librarian.interfaces import ILibrarianClient
18 >>> from canonical.database.sqlbase import flush_database_caches19 >>> from canonical.database.sqlbase import flush_database_caches
20 >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
19 >>> login("daf@canonical.com")21 >>> login("daf@canonical.com")
2022
21 # This is a dummy logger class to capture the export's log messages.23 # This is a dummy logger class to capture the export's log messages.
@@ -183,8 +185,10 @@
183Before we are ready to import the attached files, we need to approve185Before we are ready to import the attached files, we need to approve
184them first.186them first.
185187
186 >>> template_entry.setStatus(RosettaImportStatus.APPROVED)188 >>> template_entry.setStatus(
187 >>> translation_entry.setStatus(RosettaImportStatus.APPROVED)189 ... RosettaImportStatus.APPROVED, rosetta_experts)
190 >>> translation_entry.setStatus(
191 ... RosettaImportStatus.APPROVED, rosetta_experts)
188192
189Given that the files are attached to Librarian, we need to commit the193Given that the files are attached to Librarian, we need to commit the
190transaction to make sure it's stored properly and available.194transaction to make sure it's stored properly and available.
191195
=== modified file 'lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt'
--- lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt 2009-08-13 15:12:16 +0000
+++ lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt 2009-11-13 18:41:14 +0000
@@ -19,13 +19,15 @@
1919
20Here are some imports we need to get this test running.20Here are some imports we need to get this test running.
2121
22 >>> from canonical.launchpad.interfaces import IPersonSet22 >>> from canonical.launchpad.interfaces import (
23 ... ILaunchpadCelebrities, IPersonSet)
23 >>> from lp.translations.interfaces.translationimportqueue import (24 >>> from lp.translations.interfaces.translationimportqueue import (
24 ... ITranslationImportQueue, RosettaImportStatus)25 ... ITranslationImportQueue, RosettaImportStatus)
25 >>> from lp.translations.model.potemplate import POTemplateSubset26 >>> from lp.translations.model.potemplate import POTemplateSubset
26 >>> import pytz27 >>> import pytz
27 >>> UTC = pytz.timezone('UTC')28 >>> UTC = pytz.timezone('UTC')
28 >>> translation_import_queue = getUtility(ITranslationImportQueue)29 >>> translation_import_queue = getUtility(ITranslationImportQueue)
30 >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
2931
30We need this for the Librarian to work properly.32We need this for the Librarian to work properly.
3133
@@ -80,7 +82,7 @@
8082
81We must approve the entry to be able to import it.83We must approve the entry to be able to import it.
8284
83 >>> entry.setStatus(RosettaImportStatus.APPROVED)85 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
8486
85We do the import.87We do the import.
8688
@@ -150,7 +152,7 @@
150152
151We must approve the entry to be able to import it.153We must approve the entry to be able to import it.
152154
153 >>> entry.setStatus(RosettaImportStatus.APPROVED)155 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
154156
155We do the import.157We do the import.
156158
157159
=== modified file 'lib/lp/translations/doc/poimport-pofile-old-po-imported.txt'
--- lib/lp/translations/doc/poimport-pofile-old-po-imported.txt 2009-08-13 15:12:16 +0000
+++ lib/lp/translations/doc/poimport-pofile-old-po-imported.txt 2009-11-13 18:41:14 +0000
@@ -16,7 +16,8 @@
1616
17Here are some imports we need to get this test running.17Here are some imports we need to get this test running.
1818
19 >>> from canonical.launchpad.interfaces import IPersonSet19 >>> from canonical.launchpad.interfaces import (
20 ... ILaunchpadCelebrities, IPersonSet)
20 >>> from lp.translations.interfaces.translationimportqueue import (21 >>> from lp.translations.interfaces.translationimportqueue import (
21 ... ITranslationImportQueue)22 ... ITranslationImportQueue)
22 >>> from lp.translations.model.potemplate import POTemplateSubset23 >>> from lp.translations.model.potemplate import POTemplateSubset
@@ -24,6 +25,7 @@
24 >>> import pytz25 >>> import pytz
25 >>> UTC = pytz.timezone('UTC')26 >>> UTC = pytz.timezone('UTC')
26 >>> translation_import_queue = getUtility(ITranslationImportQueue)27 >>> translation_import_queue = getUtility(ITranslationImportQueue)
28 >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
2729
28And also, the DBSchema to change the imports status30And also, the DBSchema to change the imports status
2931
@@ -80,7 +82,7 @@
8082
81We must approve the entry to be able to import it.83We must approve the entry to be able to import it.
8284
83 >>> entry.setStatus(RosettaImportStatus.APPROVED)85 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
8486
85We do the import. This succeeds without errors.87We do the import. This succeeds without errors.
8688
@@ -128,7 +130,7 @@
128130
129We must approve the entry to be able to import it.131We must approve the entry to be able to import it.
130132
131 >>> entry.setStatus(RosettaImportStatus.APPROVED)133 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
132134
133We do the import.135We do the import.
134136
@@ -191,7 +193,7 @@
191193
192We approve the entry and import it.194We approve the entry and import it.
193195
194 >>> entry.setStatus(RosettaImportStatus.APPROVED)196 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
195 >>> (subject, message) = pofile.importFromQueue(entry)197 >>> (subject, message) = pofile.importFromQueue(entry)
196198
197This succeeds although the file's timestamp is older than that of the199This succeeds although the file's timestamp is older than that of the
198200
=== modified file 'lib/lp/translations/doc/poimport-pofile-syntax-error.txt'
--- lib/lp/translations/doc/poimport-pofile-syntax-error.txt 2009-08-13 15:12:16 +0000
+++ lib/lp/translations/doc/poimport-pofile-syntax-error.txt 2009-11-13 18:41:14 +0000
@@ -7,7 +7,8 @@
77
8Here are some imports we need to get this test running.8Here are some imports we need to get this test running.
99
10 >>> from canonical.launchpad.interfaces import IPersonSet10 >>> from canonical.launchpad.interfaces import (
11 ... ILaunchpadCelebrities, IPersonSet)
11 >>> from lp.translations.interfaces.translationimportqueue import (12 >>> from lp.translations.interfaces.translationimportqueue import (
12 ... ITranslationImportQueue)13 ... ITranslationImportQueue)
13 >>> from lp.translations.model.potemplate import POTemplateSubset14 >>> from lp.translations.model.potemplate import POTemplateSubset
@@ -15,6 +16,7 @@
15 >>> import pytz16 >>> import pytz
16 >>> UTC = pytz.timezone('UTC')17 >>> UTC = pytz.timezone('UTC')
17 >>> translation_import_queue = getUtility(ITranslationImportQueue)18 >>> translation_import_queue = getUtility(ITranslationImportQueue)
19 >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
1820
19We need this for the Librarian to work properly.21We need this for the Librarian to work properly.
2022
@@ -76,7 +78,7 @@
7678
77We must approve the entry to be able to import it.79We must approve the entry to be able to import it.
7880
79 >>> entry.setStatus(RosettaImportStatus.APPROVED)81 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
8082
81The import fails.83The import fails.
8284
@@ -135,7 +137,7 @@
135 >>> entry = translation_import_queue.addOrUpdateEntry(137 >>> entry = translation_import_queue.addOrUpdateEntry(
136 ... pofile.path, pofile_contents, published, person,138 ... pofile.path, pofile_contents, published, person,
137 ... productseries=series, potemplate=potemplate, pofile=pofile)139 ... productseries=series, potemplate=potemplate, pofile=pofile)
138 >>> entry.setStatus(RosettaImportStatus.APPROVED)140 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
139 >>> transaction.commit()141 >>> transaction.commit()
140 >>> (subject, message) = pofile.importFromQueue(entry)142 >>> (subject, message) = pofile.importFromQueue(entry)
141 >>> print entry.status.name143 >>> print entry.status.name
@@ -202,7 +204,7 @@
202 ... pofile.path, pofile_contents, False, person,204 ... pofile.path, pofile_contents, False, person,
203 ... productseries=series, potemplate=potemplate, pofile=pofile)205 ... productseries=series, potemplate=potemplate, pofile=pofile)
204 >>> transaction.commit()206 >>> transaction.commit()
205 >>> entry.setStatus(RosettaImportStatus.APPROVED)207 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
206 >>> (subject, message) = pofile.importFromQueue(entry)208 >>> (subject, message) = pofile.importFromQueue(entry)
207209
208210
@@ -245,7 +247,7 @@
245 ... pofile.path, pofile_contents, False, person,247 ... pofile.path, pofile_contents, False, person,
246 ... productseries=series, potemplate=potemplate, pofile=pofile)248 ... productseries=series, potemplate=potemplate, pofile=pofile)
247 >>> transaction.commit()249 >>> transaction.commit()
248 >>> entry.setStatus(RosettaImportStatus.APPROVED)250 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
249 >>> (subject, message) = pofile.importFromQueue(entry)251 >>> (subject, message) = pofile.importFromQueue(entry)
250252
251 >>> print entry.status.name253 >>> print entry.status.name
@@ -283,7 +285,7 @@
283 ... pofile.path, pofile_contents, False, person,285 ... pofile.path, pofile_contents, False, person,
284 ... productseries=series, potemplate=potemplate, pofile=pofile)286 ... productseries=series, potemplate=potemplate, pofile=pofile)
285 >>> transaction.commit()287 >>> transaction.commit()
286 >>> entry.setStatus(RosettaImportStatus.APPROVED)288 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
287 >>> (subject, message) = pofile.importFromQueue(entry)289 >>> (subject, message) = pofile.importFromQueue(entry)
288290
289 >>> print entry.status.name291 >>> print entry.status.name
@@ -336,7 +338,7 @@
336 ... pofile.path, pofile_contents, False, person,338 ... pofile.path, pofile_contents, False, person,
337 ... productseries=series, potemplate=potemplate, pofile=pofile)339 ... productseries=series, potemplate=potemplate, pofile=pofile)
338 >>> transaction.commit()340 >>> transaction.commit()
339 >>> entry.setStatus(RosettaImportStatus.APPROVED)341 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
340 >>> (subject, message) = pofile.importFromQueue(entry)342 >>> (subject, message) = pofile.importFromQueue(entry)
341343
342 >>> print entry.status.name344 >>> print entry.status.name
@@ -398,7 +400,7 @@
398 ... pofile.path, pofile_contents, False, person,400 ... pofile.path, pofile_contents, False, person,
399 ... productseries=series, potemplate=potemplate, pofile=pofile)401 ... productseries=series, potemplate=potemplate, pofile=pofile)
400 >>> transaction.commit()402 >>> transaction.commit()
401 >>> entry.setStatus(RosettaImportStatus.APPROVED)403 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
402 >>> (subject, message) = pofile.importFromQueue(entry)404 >>> (subject, message) = pofile.importFromQueue(entry)
403405
404 >>> print entry.status.name406 >>> print entry.status.name
405407
=== modified file 'lib/lp/translations/doc/poimport-potemplate-syntax-error.txt'
--- lib/lp/translations/doc/poimport-potemplate-syntax-error.txt 2009-08-13 15:12:16 +0000
+++ lib/lp/translations/doc/poimport-potemplate-syntax-error.txt 2009-11-13 18:41:14 +0000
@@ -7,13 +7,15 @@
77
8Here are some imports we need to get this test running.8Here are some imports we need to get this test running.
99
10 >>> from canonical.launchpad.interfaces import IPersonSet10 >>> from canonical.launchpad.interfaces import (
11 ... ILaunchpadCelebrities, IPersonSet)
11 >>> from lp.translations.interfaces.translationimportqueue import (12 >>> from lp.translations.interfaces.translationimportqueue import (
12 ... ITranslationImportQueue)13 ... ITranslationImportQueue)
13 >>> from lp.translations.model.potemplate import POTemplateSubset14 >>> from lp.translations.model.potemplate import POTemplateSubset
14 >>> import pytz15 >>> import pytz
15 >>> UTC = pytz.timezone('UTC')16 >>> UTC = pytz.timezone('UTC')
16 >>> translation_import_queue = getUtility(ITranslationImportQueue)17 >>> translation_import_queue = getUtility(ITranslationImportQueue)
18 >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
1719
18We need this for the Librarian to work properly.20We need this for the Librarian to work properly.
1921
@@ -59,7 +61,7 @@
59 ... potemplate.path, potemplate_contents, published, person,61 ... potemplate.path, potemplate_contents, published, person,
60 ... productseries=series, potemplate=potemplate)62 ... productseries=series, potemplate=potemplate)
61 >>> transaction.commit()63 >>> transaction.commit()
62 >>> entry.setStatus(RosettaImportStatus.APPROVED)64 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
63 >>> (subject, message) = potemplate.importFromQueue(entry)65 >>> (subject, message) = potemplate.importFromQueue(entry)
6466
65The import failed.67The import failed.
@@ -120,7 +122,7 @@
120 ... potemplate.path, potemplate_contents, published, person,122 ... potemplate.path, potemplate_contents, published, person,
121 ... productseries=series, potemplate=potemplate)123 ... productseries=series, potemplate=potemplate)
122 >>> transaction.commit()124 >>> transaction.commit()
123 >>> entry.setStatus(RosettaImportStatus.APPROVED)125 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
124 >>> (subject, message) = potemplate.importFromQueue(entry)126 >>> (subject, message) = potemplate.importFromQueue(entry)
125127
126The import failed.128The import failed.
127129
=== modified file 'lib/lp/translations/doc/poimport.txt'
--- lib/lp/translations/doc/poimport.txt 2009-10-29 17:46:00 +0000
+++ lib/lp/translations/doc/poimport.txt 2009-11-13 18:41:14 +0000
@@ -21,6 +21,7 @@
21 >>> import datetime21 >>> import datetime
22 >>> import pytz22 >>> import pytz
23 >>> UTC = pytz.timezone('UTC')23 >>> UTC = pytz.timezone('UTC')
24 >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
2425
25We need this for the Librarian to work properly.26We need this for the Librarian to work properly.
2627
@@ -125,7 +126,7 @@
125126
126The entry gets approved, so it can be imported.127The entry gets approved, so it can be imported.
127128
128 >>> entry.setStatus(RosettaImportStatus.APPROVED)129 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
129130
130 >>> import datetime131 >>> import datetime
131 >>> import pytz132 >>> import pytz
@@ -201,7 +202,7 @@
201The entry indicates what file it is to be imported to; importing it to202The entry indicates what file it is to be imported to; importing it to
202any other file would be an error.203any other file would be an error.
203204
204 >>> entry.setStatus(RosettaImportStatus.APPROVED)205 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
205 >>> from lp.translations.interfaces.potemplate import IPOTemplateSet206 >>> from lp.translations.interfaces.potemplate import IPOTemplateSet
206 >>> other_product = getUtility(IProductSet).getByName('netapplet')207 >>> other_product = getUtility(IProductSet).getByName('netapplet')
207 >>> other_productseries = other_product.getSeries('trunk')208 >>> other_productseries = other_product.getSeries('trunk')
@@ -306,7 +307,7 @@
306307
307We must approve the entry to be able to import it.308We must approve the entry to be able to import it.
308309
309 >>> entry.setStatus(RosettaImportStatus.APPROVED)310 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
310311
311And we do the import.312And we do the import.
312313
@@ -448,7 +449,7 @@
448 ... productseries=series, potemplate=potemplate,449 ... productseries=series, potemplate=potemplate,
449 ... pofile=sumerian_pofile)450 ... pofile=sumerian_pofile)
450 >>> transaction.commit()451 >>> transaction.commit()
451 >>> warning_entry.setStatus(RosettaImportStatus.APPROVED)452 >>> warning_entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
452 >>> (subject, message) = sumerian_pofile.importFromQueue(warning_entry)453 >>> (subject, message) = sumerian_pofile.importFromQueue(warning_entry)
453454
454The warning is noted in the confirmation email. Note that this455The warning is noted in the confirmation email. Note that this
@@ -477,14 +478,13 @@
477 <BLANKLINE>478 <BLANKLINE>
478 Line 12: We got a second header.479 Line 12: We got a second header.
479480
480 >>> warning_entry.setStatus(RosettaImportStatus.DELETED)481 >>> warning_entry.setStatus(RosettaImportStatus.DELETED, rosetta_experts)
481482
482483
483=== Import Without Errors ===484=== Import Without Errors ===
484485
485Now, let's import one without errors.486Now, let's import one without errors.
486487
487 >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
488 >>> pofile_without_errors = r'''488 >>> pofile_without_errors = r'''
489 ... msgid ""489 ... msgid ""
490 ... msgstr ""490 ... msgstr ""
@@ -522,7 +522,7 @@
522522
523We must approve the entry to be able to import it.523We must approve the entry to be able to import it.
524524
525 >>> entry.setStatus(RosettaImportStatus.APPROVED)525 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
526526
527At this point, the statistics note that we have 2 translations coming527At this point, the statistics note that we have 2 translations coming
528from imported files (currentCount), and none updated in Launchpad528from imported files (currentCount), and none updated in Launchpad
@@ -608,7 +608,7 @@
608The entry indicates what file it is to be imported to; importing it to608The entry indicates what file it is to be imported to; importing it to
609any other file would be an error.609any other file would be an error.
610610
611 >>> entry.setStatus(RosettaImportStatus.APPROVED)611 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
612 >>> other_pofile = potemplate.newPOFile('de')612 >>> other_pofile = potemplate.newPOFile('de')
613 >>> other_pofile.importFromQueue(entry)613 >>> other_pofile.importFromQueue(entry)
614 Traceback (most recent call last):614 Traceback (most recent call last):
@@ -638,7 +638,7 @@
638 ... if (entry.status == RosettaImportStatus.IMPORTED or638 ... if (entry.status == RosettaImportStatus.IMPORTED or
639 ... entry.status == RosettaImportStatus.FAILED) and (639 ... entry.status == RosettaImportStatus.FAILED) and (
640 ... entry.productseries == series):640 ... entry.productseries == series):
641 ... entry.setStatus(RosettaImportStatus.APPROVED)641 ... entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
642 ... syncUpdate(entry)642 ... syncUpdate(entry)
643 >>> transaction.commit()643 >>> transaction.commit()
644644
@@ -859,7 +859,7 @@
859 ... potemplate.path, potemplate_contents, True, potemplate.owner,859 ... potemplate.path, potemplate_contents, True, potemplate.owner,
860 ... sourcepackagename=firefox_name, distroseries=warty,860 ... sourcepackagename=firefox_name, distroseries=warty,
861 ... potemplate=potemplate)861 ... potemplate=potemplate)
862 >>> entry.setStatus(RosettaImportStatus.APPROVED)862 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
863 >>> syncUpdate(entry)863 >>> syncUpdate(entry)
864 >>> transaction.commit()864 >>> transaction.commit()
865865
@@ -941,7 +941,7 @@
941 >>> # Allow Librarian to see the change.941 >>> # Allow Librarian to see the change.
942 >>> transaction.commit()942 >>> transaction.commit()
943 >>> entry.pofile = firefox_dv943 >>> entry.pofile = firefox_dv
944 >>> entry.setStatus(RosettaImportStatus.APPROVED)944 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
945 >>> (subject, body) = firefox_dv.importFromQueue(entry, FakeLogger())945 >>> (subject, body) = firefox_dv.importFromQueue(entry, FakeLogger())
946 >>> flush_database_updates()946 >>> flush_database_updates()
947 >>> print entry.status.name947 >>> print entry.status.name
@@ -989,7 +989,7 @@
989 ... productseries=pofile.potemplate.productseries,989 ... productseries=pofile.potemplate.productseries,
990 ... potemplate=pofile.potemplate, pofile=pofile)990 ... potemplate=pofile.potemplate, pofile=pofile)
991 >>> transaction.commit()991 >>> transaction.commit()
992 >>> entry.setStatus(RosettaImportStatus.APPROVED)992 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
993 >>> (subject, message) = pofile.importFromQueue(entry)993 >>> (subject, message) = pofile.importFromQueue(entry)
994994
995Import succeeds but no email is sent out.995Import succeeds but no email is sent out.
@@ -1008,7 +1008,7 @@
1008 ... productseries=pofile.potemplate.productseries,1008 ... productseries=pofile.potemplate.productseries,
1009 ... potemplate=pofile.potemplate, pofile=pofile)1009 ... potemplate=pofile.potemplate, pofile=pofile)
1010 >>> transaction.commit()1010 >>> transaction.commit()
1011 >>> entry.setStatus(RosettaImportStatus.APPROVED)1011 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
1012 >>> (subject, message) = pofile.importFromQueue(entry)1012 >>> (subject, message) = pofile.importFromQueue(entry)
10131013
1014Import fails and email is sent out even though it's a published upload.1014Import fails and email is sent out even though it's a published upload.
@@ -1042,7 +1042,7 @@
1042 ... 'lo.po', 'Invalid content', True, hermit,1042 ... 'lo.po', 'Invalid content', True, hermit,
1043 ... pofile=pofile, potemplate=pofile.potemplate,1043 ... pofile=pofile, potemplate=pofile.potemplate,
1044 ... productseries=pofile.potemplate.productseries)1044 ... productseries=pofile.potemplate.productseries)
1045 >>> entry.setStatus(RosettaImportStatus.APPROVED)1045 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
1046 >>> transaction.commit()1046 >>> transaction.commit()
10471047
1048The import fails. The importer would like to send Kermit an email about1048The import fails. The importer would like to send Kermit an email about
10491049
=== modified file 'lib/lp/translations/doc/rosetta-karma.txt'
--- lib/lp/translations/doc/rosetta-karma.txt 2009-07-23 17:49:31 +0000
+++ lib/lp/translations/doc/rosetta-karma.txt 2009-11-13 18:41:14 +0000
@@ -58,7 +58,7 @@
5858
59 # Login as a rosetta expert to be able to change the import's status.59 # Login as a rosetta expert to be able to change the import's status.
60 >>> login('carlos@canonical.com')60 >>> login('carlos@canonical.com')
61 >>> entry.setStatus(RosettaImportStatus.APPROVED)61 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
62 >>> entry_id = entry.id62 >>> entry_id = entry.id
6363
64The file data is stored in the Librarian, so we have to commit the transaction64The file data is stored in the Librarian, so we have to commit the transaction
@@ -90,7 +90,7 @@
90 ... potemplate.path, potemplate_contents, comes_from_upstream,90 ... potemplate.path, potemplate_contents, comes_from_upstream,
91 ... foo_bar, productseries=potemplate.productseries,91 ... foo_bar, productseries=potemplate.productseries,
92 ... potemplate=potemplate)92 ... potemplate=potemplate)
93 >>> entry.setStatus(RosettaImportStatus.APPROVED)93 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
94 >>> entry_id = entry.id94 >>> entry_id = entry.id
9595
96The file data is stored in the Librarian, so we have to commit the transaction96The file data is stored in the Librarian, so we have to commit the transaction
@@ -146,7 +146,7 @@
146 ... pofile.path, pofile_contents, comes_from_upstream,146 ... pofile.path, pofile_contents, comes_from_upstream,
147 ... rosetta_experts, productseries=potemplate.productseries,147 ... rosetta_experts, productseries=potemplate.productseries,
148 ... potemplate=potemplate, pofile=pofile)148 ... potemplate=potemplate, pofile=pofile)
149 >>> entry.setStatus(RosettaImportStatus.APPROVED)149 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
150 >>> entry_id = entry.id150 >>> entry_id = entry.id
151151
152The file data is stored in the Librarian, so we have to commit the transaction152The file data is stored in the Librarian, so we have to commit the transaction
@@ -173,7 +173,7 @@
173 ... pofile.path, pofile_contents, comes_from_upstream, foo_bar,173 ... pofile.path, pofile_contents, comes_from_upstream, foo_bar,
174 ... productseries=potemplate.productseries, potemplate=potemplate,174 ... productseries=potemplate.productseries, potemplate=potemplate,
175 ... pofile=pofile)175 ... pofile=pofile)
176 >>> entry.setStatus(RosettaImportStatus.APPROVED)176 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
177 >>> entry_id = entry.id177 >>> entry_id = entry.id
178178
179The file data is stored in the Librarian, so we have to commit the transaction179The file data is stored in the Librarian, so we have to commit the transaction
@@ -210,7 +210,7 @@
210 ... pofile.path, pofile_contents, not comes_from_upstream, foo_bar,210 ... pofile.path, pofile_contents, not comes_from_upstream, foo_bar,
211 ... productseries=potemplate.productseries, potemplate=potemplate,211 ... productseries=potemplate.productseries, potemplate=potemplate,
212 ... pofile=pofile)212 ... pofile=pofile)
213 >>> entry.setStatus(RosettaImportStatus.APPROVED)213 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
214 >>> entry_id = entry.id214 >>> entry_id = entry.id
215215
216The file data is stored in the Librarian, so we have to commit the transaction216The file data is stored in the Librarian, so we have to commit the transaction
@@ -242,7 +242,7 @@
242 ... pofile.path, pofile_contents, not comes_from_upstream, foo_bar,242 ... pofile.path, pofile_contents, not comes_from_upstream, foo_bar,
243 ... productseries=potemplate.productseries, potemplate=potemplate,243 ... productseries=potemplate.productseries, potemplate=potemplate,
244 ... pofile=pofile)244 ... pofile=pofile)
245 >>> entry.setStatus(RosettaImportStatus.APPROVED)245 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
246 >>> entry_id = entry.id246 >>> entry_id = entry.id
247247
248The file data is stored in the Librarian, so we have to commit the transaction248The file data is stored in the Librarian, so we have to commit the transaction
249249
=== modified file 'lib/lp/translations/doc/rosetta-poimport-script.txt'
--- lib/lp/translations/doc/rosetta-poimport-script.txt 2009-07-02 17:16:50 +0000
+++ lib/lp/translations/doc/rosetta-poimport-script.txt 2009-11-13 18:41:14 +0000
@@ -1,7 +1,8 @@
1= PO import script =1= PO import script =
22
3 >>> from lp.translations.model.potemplate import POTemplate3 >>> from lp.translations.model.potemplate import POTemplate
4 >>> from canonical.launchpad.interfaces import IPersonSet4 >>> from canonical.launchpad.interfaces import (
5 ... ILaunchpadCelebrities, IPersonSet)
5 >>> from lp.translations.interfaces.translationimportqueue import (6 >>> from lp.translations.interfaces.translationimportqueue import (
6 ... ITranslationImportQueue,7 ... ITranslationImportQueue,
7 ... RosettaImportStatus)8 ... RosettaImportStatus)
@@ -9,6 +10,7 @@
9 >>> import datetime10 >>> import datetime
10 >>> import pytz11 >>> import pytz
11 >>> UTC = pytz.timezone('UTC')12 >>> UTC = pytz.timezone('UTC')
13 >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
1214
13Login as an admin to be able to do changes to the import queue.15Login as an admin to be able to do changes to the import queue.
1416
@@ -66,7 +68,7 @@
66 ... distroseries=pofile.potemplate.distroseries,68 ... distroseries=pofile.potemplate.distroseries,
67 ... productseries=pofile.potemplate.productseries)69 ... productseries=pofile.potemplate.productseries)
68 >>> entry.pofile = pofile70 >>> entry.pofile = pofile
69 >>> entry.setStatus(RosettaImportStatus.APPROVED)71 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
70 >>> transaction.commit()72 >>> transaction.commit()
71 >>> translation_import_queue.countEntries()73 >>> translation_import_queue.countEntries()
72 174 1
7375
=== modified file 'lib/lp/translations/doc/translationimportqueue.txt'
--- lib/lp/translations/doc/translationimportqueue.txt 2009-10-01 07:04:18 +0000
+++ lib/lp/translations/doc/translationimportqueue.txt 2009-11-13 18:41:14 +0000
@@ -136,7 +136,7 @@
136we need to be logged in as an admin.136we need to be logged in as an admin.
137137
138 >>> login('carlos@canonical.com')138 >>> login('carlos@canonical.com')
139 >>> entry.setStatus(RosettaImportStatus.IMPORTED)139 >>> entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
140140
141The status change updates date_status_changed as well.141The status change updates date_status_changed as well.
142142
@@ -202,7 +202,7 @@
202202
203But if that entry is imported, the guessing algorithm works.203But if that entry is imported, the guessing algorithm works.
204204
205 >>> pot_entry.setStatus(RosettaImportStatus.IMPORTED)205 >>> pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
206 >>> guessed_pofile = po_sr_entry.getGuessedPOFile()206 >>> guessed_pofile = po_sr_entry.getGuessedPOFile()
207 >>> guessed_pofile is None207 >>> guessed_pofile is None
208 False208 False
@@ -276,7 +276,7 @@
276276
277And set this entry as already imported.277And set this entry as already imported.
278278
279 >>> kde_pot_entry.setStatus(RosettaImportStatus.IMPORTED)279 >>> kde_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
280 >>> flush_database_updates()280 >>> flush_database_updates()
281281
282Let's attach a .po file from kde-i18n-es282Let's attach a .po file from kde-i18n-es
@@ -342,7 +342,7 @@
342342
343And set this entry as already imported.343And set this entry as already imported.
344344
345 >>> kde_pot_entry.setStatus(RosettaImportStatus.IMPORTED)345 >>> kde_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
346 >>> flush_database_updates()346 >>> flush_database_updates()
347347
348Let's attach a .po file from kde-i18n-es348Let's attach a .po file from kde-i18n-es
@@ -402,7 +402,7 @@
402402
403And set this entry as already imported.403And set this entry as already imported.
404404
405 >>> koffice_pot_entry.setStatus(RosettaImportStatus.IMPORTED)405 >>> koffice_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
406 >>> flush_database_updates()406 >>> flush_database_updates()
407407
408Let's attach a .po file from koffice-l10n408Let's attach a .po file from koffice-l10n
@@ -500,7 +500,7 @@
500500
501And set this entry as already imported.501And set this entry as already imported.
502502
503 >>> adept_pot_entry.setStatus(RosettaImportStatus.IMPORTED)503 >>> adept_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
504 >>> flush_database_updates()504 >>> flush_database_updates()
505505
506Let's attach a .po file now.506Let's attach a .po file now.
@@ -551,7 +551,7 @@
551551
552And set this entry as already imported.552And set this entry as already imported.
553553
554 >>> ktorrent_pot_entry.setStatus(RosettaImportStatus.IMPORTED)554 >>> ktorrent_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
555 >>> flush_database_updates()555 >>> flush_database_updates()
556556
557Let's attach a .po file now.557Let's attach a .po file now.
@@ -604,7 +604,7 @@
604604
605And set this entry as already imported.605And set this entry as already imported.
606606
607 >>> zope_pot_entry.setStatus(RosettaImportStatus.IMPORTED)607 >>> zope_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
608 >>> flush_database_updates()608 >>> flush_database_updates()
609609
610Let's attach a .po file now.610Let's attach a .po file now.
@@ -656,7 +656,7 @@
656656
657And set this entry as already imported.657And set this entry as already imported.
658658
659 >>> k3b_pot_entry.setStatus(RosettaImportStatus.IMPORTED)659 >>> k3b_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
660 >>> flush_database_updates()660 >>> flush_database_updates()
661661
662Let's attach a .po file now.662Let's attach a .po file now.
@@ -706,7 +706,7 @@
706706
707And set this entry as already imported.707And set this entry as already imported.
708708
709 >>> k3b_pot_entry.setStatus(RosettaImportStatus.IMPORTED)709 >>> k3b_pot_entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
710 >>> flush_database_updates()710 >>> flush_database_updates()
711711
712Let's attach a .po file now.712Let's attach a .po file now.
@@ -782,7 +782,7 @@
782782
783We need it blocked for this test.783We need it blocked for this test.
784784
785 >>> entry5.setStatus(RosettaImportStatus.BLOCKED)785 >>> entry5.setStatus(RosettaImportStatus.BLOCKED, rosetta_experts)
786786
787Let's see how many entries are blocked.787Let's see how many entries are blocked.
788788
@@ -873,7 +873,7 @@
873For the third entry, we have one .pot file on that directory, which is already873For the third entry, we have one .pot file on that directory, which is already
874in sample data.874in sample data.
875875
876 >>> entry3.setStatus(RosettaImportStatus.NEEDS_REVIEW)876 >>> entry3.setStatus(RosettaImportStatus.NEEDS_REVIEW, rosetta_experts)
877 >>> entries = entry3.getTemplatesOnSameDirectory()877 >>> entries = entry3.getTemplatesOnSameDirectory()
878 >>> entries.count()878 >>> entries.count()
879 1879 1
@@ -884,7 +884,7 @@
884884
885For the fourth entry, we have one.885For the fourth entry, we have one.
886886
887 >>> entry4.setStatus(RosettaImportStatus.NEEDS_REVIEW)887 >>> entry4.setStatus(RosettaImportStatus.NEEDS_REVIEW, rosetta_experts)
888 >>> entries = entry4.getTemplatesOnSameDirectory()888 >>> entries = entry4.getTemplatesOnSameDirectory()
889 >>> entries.count()889 >>> entries.count()
890 1890 1
@@ -898,7 +898,7 @@
898obviously, we are not returning it as being at the same directory as it makes898obviously, we are not returning it as being at the same directory as it makes
899no sense at all.899no sense at all.
900900
901 >>> entry5.setStatus(RosettaImportStatus.NEEDS_REVIEW)901 >>> entry5.setStatus(RosettaImportStatus.NEEDS_REVIEW, rosetta_experts)
902 >>> entries = entry5.getTemplatesOnSameDirectory()902 >>> entries = entry5.getTemplatesOnSameDirectory()
903 >>> entries.count()903 >>> entries.count()
904 0904 0
@@ -967,10 +967,17 @@
967 NEEDS_REVIEW967 NEEDS_REVIEW
968968
969Now we approve these entries (we need to be Rosetta administrator to do this).969Now we approve these entries (we need to be Rosetta administrator to do this).
970We also need to set an import target.
970971
971 >>> login('carlos@canonical.com')972 >>> login('carlos@canonical.com')
972 >>> entry1.setStatus(RosettaImportStatus.APPROVED)973 >>> entry1.potemplate = factory.makePOTemplate(
973 >>> entry2.setStatus(RosettaImportStatus.APPROVED)974 ... distroseries=hoary_distroseries,
975 ... sourcepackagename=evolution_sourcepackagename)
976 >>> entry1.pofile = factory.makePOFile('sr', potemplate=entry1.potemplate)
977 >>> entry1.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
978 >>> entry2.potemplate = factory.makePOTemplate(
979 ... productseries=productseries)
980 >>> entry2.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
974981
975 >>> flush_database_updates()982 >>> flush_database_updates()
976 >>> translationimportqueue = getUtility(ITranslationImportQueue)983 >>> translationimportqueue = getUtility(ITranslationImportQueue)
@@ -1100,8 +1107,8 @@
11001107
1101 >>> queue = getUtility(ITranslationImportQueue)1108 >>> queue = getUtility(ITranslationImportQueue)
1102 >>> print_queue_entries(queue)1109 >>> print_queue_entries(queue)
1103 hoary evolution | None | po/sr.po1110 hoary evolution | generic-string1 | po/sr.po
1104 firefox | None | foo/bar.pot1111 firefox | generic-string3 | foo/bar.pot
1105 evolution | evolution-2.2-test | po/evolution-2.2-test.pot1112 evolution | evolution-2.2-test | po/evolution-2.2-test.pot
1106 evolution | evolution-2.2-test | po/pt_BR.po1113 evolution | evolution-2.2-test | po/pt_BR.po
1107 firefox | None | foo/bar.po1114 firefox | None | foo/bar.po
@@ -1121,8 +1128,8 @@
1121And those new entries in the queue appear in the list.1128And those new entries in the queue appear in the list.
11221129
1123 >>> print_queue_entries(queue)1130 >>> print_queue_entries(queue)
1124 hoary evolution | None | po/sr.po1131 hoary evolution | generic-string1 | po/sr.po
1125 firefox | None | foo/bar.pot1132 firefox | generic-string3 | foo/bar.pot
1126 evolution | evolution-2.2-test | po/evolution-2.2-test.pot1133 evolution | evolution-2.2-test | po/evolution-2.2-test.pot
1127 evolution | evolution-2.2-test | po/pt_BR.po1134 evolution | evolution-2.2-test | po/pt_BR.po
1128 firefox | None | foo/bar.po1135 firefox | None | foo/bar.po
@@ -1320,7 +1327,8 @@
1320 >>> entry.import_into is None1327 >>> entry.import_into is None
1321 True1328 True
13221329
1323 >>> entry.setStatus(RosettaImportStatus.APPROVED)1330Set the entry to approved, which is only possible if we don't use setStatus.
1331 >>> removeSecurityProxy(entry).status = RosettaImportStatus.APPROVED
13241332
1325 >>> import logging1333 >>> import logging
1326 >>> from canonical.launchpad.scripts import FakeLogger1334 >>> from canonical.launchpad.scripts import FakeLogger
@@ -1355,7 +1363,8 @@
1355 >>> entry.import_into is None1363 >>> entry.import_into is None
1356 True1364 True
13571365
1358 >>> entry.setStatus(RosettaImportStatus.APPROVED)1366Set the entry to approved, which is only possible if we don't use setStatus.
1367 >>> removeSecurityProxy(entry).status = RosettaImportStatus.APPROVED
13591368
1360 >>> script = TranslationsImport('poimport', test_args=[])1369 >>> script = TranslationsImport('poimport', test_args=[])
1361 >>> script.logger.setLevel(logging.FATAL)1370 >>> script.logger.setLevel(logging.FATAL)
@@ -1382,11 +1391,13 @@
1382 ... distro = distroset[distro_name]1391 ... distro = distroset[distro_name]
1383 ... series = distro.getSeries(series_name)1392 ... series = distro.getSeries(series_name)
1384 ... package = packageset['pmount']1393 ... package = packageset['pmount']
1394 ... template = factory.makePOTemplate(distroseries=series,
1395 ... sourcepackagename=package)
1385 ... # In a completely arbitrary move, we make all import requests for1396 ... # In a completely arbitrary move, we make all import requests for
1386 ... # distro series imported.1397 ... # distro series imported.
1387 ... return translationimportqueue.addOrUpdateEntry('messages.pot',1398 ... return translationimportqueue.addOrUpdateEntry('messages.pot',
1388 ... 'dummy file', True, rosetta_experts, distroseries=series,1399 ... 'dummy file', True, rosetta_experts, distroseries=series,
1389 ... sourcepackagename=package)1400 ... sourcepackagename=package, potemplate=template)
13901401
1391 >>> def create_product_request(product_name, template_name):1402 >>> def create_product_request(product_name, template_name):
1392 ... """Enqueue an import request for given product and template."""1403 ... """Enqueue an import request for given product and template."""
@@ -1403,19 +1414,19 @@
14031414
1404 >>> # Populate import queue with wild mix of requests.1415 >>> # Populate import queue with wild mix of requests.
1405 >>> entry = create_product_request('evolution', 'evolution-2.2')1416 >>> entry = create_product_request('evolution', 'evolution-2.2')
1406 >>> entry.setStatus(RosettaImportStatus.APPROVED)1417 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
1407 >>> entry = create_distro_request('debian', 'sarge')1418 >>> entry = create_distro_request('debian', 'sarge')
1408 >>> entry.setStatus(RosettaImportStatus.NEEDS_REVIEW)1419 >>> entry.setStatus(RosettaImportStatus.NEEDS_REVIEW, rosetta_experts)
1409 >>> entry = create_product_request('alsa-utils', 'alsa-utils')1420 >>> entry = create_product_request('alsa-utils', 'alsa-utils')
1410 >>> entry.setStatus(RosettaImportStatus.FAILED)1421 >>> entry.setStatus(RosettaImportStatus.FAILED, rosetta_experts)
1411 >>> entry = create_distro_request('ubuntu', 'grumpy')1422 >>> entry = create_distro_request('ubuntu', 'grumpy')
1412 >>> entry.setStatus(RosettaImportStatus.BLOCKED)1423 >>> entry.setStatus(RosettaImportStatus.BLOCKED, rosetta_experts)
1413 >>> entry = create_distro_request('kubuntu', 'krunch')1424 >>> entry = create_distro_request('kubuntu', 'krunch')
1414 >>> entry.setStatus(RosettaImportStatus.APPROVED)1425 >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
1415 >>> entry = create_product_request('evolution', 'evolution-2.2-test')1426 >>> entry = create_product_request('evolution', 'evolution-2.2-test')
1416 >>> entry.setStatus(RosettaImportStatus.IMPORTED)1427 >>> entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
1417 >>> entry = create_distro_request('debian', 'woody')1428 >>> entry = create_distro_request('debian', 'woody')
1418 >>> entry.setStatus(RosettaImportStatus.NEEDS_REVIEW)1429 >>> entry.setStatus(RosettaImportStatus.NEEDS_REVIEW, rosetta_experts)
1419 >>> flush_database_updates()1430 >>> flush_database_updates()
14201431
1421TranslationImportQueue.getRequestTargets first lists distro series1432TranslationImportQueue.getRequestTargets first lists distro series
@@ -1479,7 +1490,7 @@
1479 >>> entry = translationimportqueue.addOrUpdateEntry(1490 >>> entry = translationimportqueue.addOrUpdateEntry(
1480 ... u'po/nl.po', 'hoi', True, rosetta_experts,1491 ... u'po/nl.po', 'hoi', True, rosetta_experts,
1481 ... productseries=evolution_productseries)1492 ... productseries=evolution_productseries)
1482 >>> entry.setStatus(RosettaImportStatus.DELETED)1493 >>> entry.setStatus(RosettaImportStatus.DELETED, rosetta_experts)
14831494
1484The entry stays on the queue long enough to make it a candidate for1495The entry stays on the queue long enough to make it a candidate for
1485purging.1496purging.
@@ -1507,7 +1518,7 @@
1507 >>> entry = translationimportqueue.addOrUpdateEntry(1518 >>> entry = translationimportqueue.addOrUpdateEntry(
1508 ... u'po/nl.po', 'hoi', True, rosetta_experts,1519 ... u'po/nl.po', 'hoi', True, rosetta_experts,
1509 ... productseries=evolution_productseries)1520 ... productseries=evolution_productseries)
1510 >>> entry.setStatus(RosettaImportStatus.IMPORTED)1521 >>> entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
1511 >>> print_queue_entries(translationimportqueue)1522 >>> print_queue_entries(translationimportqueue)
1512 evolution | None | po/nl.po1523 evolution | None | po/nl.po
15131524
15141525
=== modified file 'lib/lp/translations/interfaces/translationimportqueue.py'
--- lib/lp/translations/interfaces/translationimportqueue.py 2009-10-09 13:20:38 +0000
+++ lib/lp/translations/interfaces/translationimportqueue.py 2009-11-13 18:41:14 +0000
@@ -6,6 +6,7 @@
6from zope.interface import Interface, Attribute6from zope.interface import Interface, Attribute
7from zope.schema import (7from zope.schema import (
8 Bool, Choice, Datetime, Field, Int, Object, Text, TextLine)8 Bool, Choice, Datetime, Field, Int, Object, Text, TextLine)
9from zope.security.interfaces import Unauthorized
9from lazr.enum import DBEnumeratedType, DBItem, EnumeratedType, Item10from lazr.enum import DBEnumeratedType, DBItem, EnumeratedType, Item
1011
11from canonical.launchpad import _12from canonical.launchpad import _
@@ -18,11 +19,12 @@
1819
19from lazr.restful.interface import copy_field20from lazr.restful.interface import copy_field
20from lazr.restful.fields import Reference21from lazr.restful.fields import Reference
21from lazr.restful.declarations import (22from lazr.restful.declarations import (call_with,
22 collection_default_content, exported, export_as_webservice_collection,23 collection_default_content, exported, export_as_webservice_collection,
23 export_as_webservice_entry, export_read_operation, operation_parameters,24 export_as_webservice_entry, export_read_operation,
24 operation_returns_entry, operation_returns_collection_of)25 export_write_operation, operation_parameters,
2526 operation_returns_entry, operation_returns_collection_of,
27 REQUEST_USER, webservice_error)
26from lp.translations.interfaces.translationcommonformat import (28from lp.translations.interfaces.translationcommonformat import (
27 TranslationImportExportBaseException)29 TranslationImportExportBaseException)
2830
@@ -37,6 +39,7 @@
37 'RosettaImportStatus',39 'RosettaImportStatus',
38 'SpecialTranslationImportTargetFilter',40 'SpecialTranslationImportTargetFilter',
39 'TranslationFileType',41 'TranslationFileType',
42 'UserCannotSetTranslationImportStatus',
40 ]43 ]
4144
4245
@@ -46,6 +49,15 @@
46 conflicts with existing entries."""49 conflicts with existing entries."""
4750
4851
52class UserCannotSetTranslationImportStatus(Unauthorized):
53 """User not permitted to change status.
54
55 Raised when a user tries to transition to a new status who doesn't
56 have the necessary permissions.
57 """
58 webservice_error(401) # HTTP Error: 'Unauthorized'
59
60
49class RosettaImportStatus(DBEnumeratedType):61class RosettaImportStatus(DBEnumeratedType):
50 """Rosetta Import Status62 """Rosetta Import Status
5163
@@ -254,10 +266,27 @@
254 required=False,266 required=False,
255 readonly=True))267 readonly=True))
256268
257 def setStatus(status):269 def isUbuntuAndIsUserTranslationGroupOwner(self, user):
258 """Set status.270 """Check for special Ubuntu Translation Group.
259271
260 :param status: new status to set.272 Return true if the entry is targeted to Ubuntu and the user is in
273 the team owning the Ubuntu translation group.
274 """
275
276 def isUserUploaderOrOwner(user):
277 """Check for entry uploader or series owner."""
278
279 def canSetStatus(new_status, user):
280 """Check if the user can set this new status."""
281
282 @call_with(user=REQUEST_USER)
283 @operation_parameters(new_status=copy_field(status))
284 @export_write_operation()
285 def setStatus(new_status, user):
286 """Transition to a new status if possible.
287
288 :param new_status: Status to transition to.
289 :param user: The user that is doing the transition.
261 """290 """
262291
263 def setErrorOutput(output):292 def setErrorOutput(output):
264293
=== modified file 'lib/lp/translations/model/pofile.py'
--- lib/lp/translations/model/pofile.py 2009-10-29 12:36:32 +0000
+++ lib/lp/translations/model/pofile.py 2009-11-13 18:41:14 +0000
@@ -1140,10 +1140,12 @@
1140 subject = 'Translation import - %s - %s' % (1140 subject = 'Translation import - %s - %s' % (
1141 self.language.displayname, self.potemplate.displayname)1141 self.language.displayname, self.potemplate.displayname)
11421142
1143 rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
1143 if import_rejected:1144 if import_rejected:
1144 # There were no imports at all and the user needs to review that1145 # There were no imports at all and the user needs to review that
1145 # file, we tag it as FAILED.1146 # file, we tag it as FAILED.
1146 entry_to_import.setStatus(RosettaImportStatus.FAILED)1147 entry_to_import.setStatus(RosettaImportStatus.FAILED,
1148 rosetta_experts)
1147 else:1149 else:
1148 if (entry_to_import.is_published and1150 if (entry_to_import.is_published and
1149 not needs_notification_for_imported):1151 not needs_notification_for_imported):
@@ -1152,7 +1154,8 @@
1152 # are needed.1154 # are needed.
1153 subject = None1155 subject = None
11541156
1155 entry_to_import.setStatus(RosettaImportStatus.IMPORTED)1157 entry_to_import.setStatus(RosettaImportStatus.IMPORTED,
1158 rosetta_experts)
1156 # Assign karma to the importer if this is not an automatic import1159 # Assign karma to the importer if this is not an automatic import
1157 # (all automatic imports come from the rosetta expert user) and1160 # (all automatic imports come from the rosetta expert user) and
1158 # comes from upstream.1161 # comes from upstream.
11591162
=== modified file 'lib/lp/translations/model/potemplate.py'
--- lib/lp/translations/model/potemplate.py 2009-11-06 21:06:38 +0000
+++ lib/lp/translations/model/potemplate.py 2009-11-13 18:41:14 +0000
@@ -891,6 +891,7 @@
891891
892 translation_importer = getUtility(ITranslationImporter)892 translation_importer = getUtility(ITranslationImporter)
893893
894 rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
894 subject = 'Translation template import - %s' % self.displayname895 subject = 'Translation template import - %s' % self.displayname
895 template_mail = 'poimport-template-confirmation.txt'896 template_mail = 'poimport-template-confirmation.txt'
896 errors, warnings = None, None897 errors, warnings = None, None
@@ -908,7 +909,8 @@
908 template_mail = 'poimport-bad-encoding.txt'909 template_mail = 'poimport-bad-encoding.txt'
909 else:910 else:
910 template_mail = 'poimport-syntax-error.txt'911 template_mail = 'poimport-syntax-error.txt'
911 entry_to_import.setStatus(RosettaImportStatus.FAILED)912 entry_to_import.setStatus(RosettaImportStatus.FAILED,
913 rosetta_experts)
912 error_text = str(exception)914 error_text = str(exception)
913 entry_to_import.setErrorOutput(error_text)915 entry_to_import.setErrorOutput(error_text)
914 else:916 else:
@@ -926,7 +928,8 @@
926 entry_to_import.addWarningOutput(replacements['warnings'])928 entry_to_import.addWarningOutput(replacements['warnings'])
927929
928 if entry_to_import.status != RosettaImportStatus.FAILED:930 if entry_to_import.status != RosettaImportStatus.FAILED:
929 entry_to_import.setStatus(RosettaImportStatus.IMPORTED)931 entry_to_import.setStatus(RosettaImportStatus.IMPORTED,
932 rosetta_experts)
930933
931 # Assign karma to the importer if this is not an automatic import934 # Assign karma to the importer if this is not an automatic import
932 # (all automatic imports come from the rosetta expert team).935 # (all automatic imports come from the rosetta expert team).
933936
=== modified file 'lib/lp/translations/model/translationbranchapprover.py'
--- lib/lp/translations/model/translationbranchapprover.py 2009-10-10 19:37:03 +0000
+++ lib/lp/translations/model/translationbranchapprover.py 2009-11-13 18:41:14 +0000
@@ -11,6 +11,7 @@
1111
12from zope.component import getUtility12from zope.component import getUtility
1313
14from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
14from canonical.launchpad.validators.name import sanitize_name15from canonical.launchpad.validators.name import sanitize_name
15from lp.translations.interfaces.potemplate import IPOTemplateSet16from lp.translations.interfaces.potemplate import IPOTemplateSet
16from lp.translations.interfaces.translationimportqueue import (17from lp.translations.interfaces.translationimportqueue import (
@@ -165,6 +166,7 @@
165 # Approve the entry166 # Approve the entry
166 entry.potemplate = potemplate167 entry.potemplate = potemplate
167 if entry.status == RosettaImportStatus.NEEDS_REVIEW:168 if entry.status == RosettaImportStatus.NEEDS_REVIEW:
168 entry.setStatus(RosettaImportStatus.APPROVED)169 entry.setStatus(RosettaImportStatus.APPROVED,
170 getUtility(ILaunchpadCelebrities).rosetta_experts)
169 return entry171 return entry
170172
171173
=== modified file 'lib/lp/translations/model/translationimportqueue.py'
--- lib/lp/translations/model/translationimportqueue.py 2009-10-22 07:32:02 +0000
+++ lib/lp/translations/model/translationimportqueue.py 2009-11-13 18:41:14 +0000
@@ -56,11 +56,14 @@
56 ITranslationImportQueueEntry,56 ITranslationImportQueueEntry,
57 RosettaImportStatus,57 RosettaImportStatus,
58 SpecialTranslationImportTargetFilter,58 SpecialTranslationImportTargetFilter,
59 TranslationImportQueueConflictError)59 TranslationImportQueueConflictError,
60 UserCannotSetTranslationImportStatus)
60from lp.translations.interfaces.potemplate import IPOTemplate61from lp.translations.interfaces.potemplate import IPOTemplate
61from lp.translations.interfaces.translations import TranslationConstants62from lp.translations.interfaces.translations import TranslationConstants
62from lp.translations.utilities.gettext_po_importer import (63from lp.translations.utilities.gettext_po_importer import (
63 GettextPOImporter)64 GettextPOImporter)
65from lp.translations.utilities.permission_helpers import (
66 is_admin_or_rosetta_expert)
64from canonical.librarian.interfaces import ILibrarianClient67from canonical.librarian.interfaces import ILibrarianClient
6568
6669
@@ -271,9 +274,58 @@
271 distroseries=self.distroseries,274 distroseries=self.distroseries,
272 sourcepackagename=self.sourcepackagename)275 sourcepackagename=self.sourcepackagename)
273276
274 def setStatus(self, status):277 def isUbuntuAndIsUserTranslationGroupOwner(self, user):
275 """See `ITranslationImportQueueEntry`."""278 """See `ITranslationImportQueueEntry`."""
276 self.status = status279 # As a special case, the Ubuntu translation group owners can
280 # manage Ubuntu uploads.
281 if self.is_targeted_to_ubuntu:
282 group = self.distroseries.distribution.translationgroup
283 if group is not None and user.inTeam(group.owner):
284 return True
285 return False
286
287 def isUserUploaderOrOwner(self, user):
288 """See `ITranslationImportQueueEntry`."""
289 if user.inTeam(self.importer):
290 return True
291 if self.productseries is not None:
292 return user.inTeam(self.productseries.product.owner)
293 if self.distroseries is not None:
294 return user.inTeam(self.distroseries.distribution.owner)
295 return False
296
297 def canSetStatus(self, new_status, user):
298 """See `ITranslationImportQueueEntry`."""
299 if new_status == self.status:
300 # Leaving status as it is is always allowed.
301 return True
302 if user is None:
303 # Anonymous user cannot do anything.
304 return False
305 can_admin = (is_admin_or_rosetta_expert(user) or
306 self.isUbuntuAndIsUserTranslationGroupOwner(user))
307 if (new_status == RosettaImportStatus.APPROVED and
308 not (self.import_into is not None and can_admin)):
309 # Only administrators are able to set the APPROVED status, and
310 # that's only possible if we know where to import it
311 # (import_into not None).
312 return False
313 if (new_status == RosettaImportStatus.BLOCKED and not can_admin):
314 # Only administrators are able to set an entry to BLOCKED.
315 return False
316 if (new_status in (RosettaImportStatus.FAILED,
317 RosettaImportStatus.IMPORTED)
318 and not is_admin_or_rosetta_expert(user)):
319 # Only scripts set these statuses and they report as a rosetta
320 # expert.
321 return False
322 return (self.isUserUploaderOrOwner(user) or can_admin)
323
324 def setStatus(self, new_status, user):
325 """See `ITranslationImportQueueEntry`."""
326 if not self.canSetStatus(new_status, user):
327 raise UserCannotSetTranslationImportStatus()
328 self.status = new_status
277 self.date_status_changed = UTC_NOW329 self.date_status_changed = UTC_NOW
278330
279 def setErrorOutput(self, output):331 def setErrorOutput(self, output):
@@ -473,7 +525,8 @@
473 if guessed_language is None:525 if guessed_language is None:
474 # Custom language code says to ignore imports with this language526 # Custom language code says to ignore imports with this language
475 # code.527 # code.
476 self.setStatus(RosettaImportStatus.DELETED)528 self.setStatus(RosettaImportStatus.DELETED,
529 getUtility(ILaunchpadCelebrities).rosetta_experts)
477 return None530 return None
478 elif guessed_language == '':531 elif guessed_language == '':
479 # We don't recognize this as a translation file with a name532 # We don't recognize this as a translation file with a name
@@ -877,7 +930,7 @@
877 # We got an update for this entry. If the previous import is930 # We got an update for this entry. If the previous import is
878 # deleted or failed or was already imported we should retry931 # deleted or failed or was already imported we should retry
879 # the import now, just in case it can be imported now.932 # the import now, just in case it can be imported now.
880 entry.setStatus(RosettaImportStatus.NEEDS_REVIEW)933 entry.setStatus(RosettaImportStatus.NEEDS_REVIEW, importer)
881934
882 entry.date_status_changed = UTC_NOW935 entry.date_status_changed = UTC_NOW
883 entry.format = format936 entry.format = format
@@ -1130,7 +1183,8 @@
11301183
1131 # Already know where it should be imported. The entry is approved1184 # Already know where it should be imported. The entry is approved
1132 # automatically.1185 # automatically.
1133 entry.setStatus(RosettaImportStatus.APPROVED)1186 entry.setStatus(RosettaImportStatus.APPROVED,
1187 getUtility(ILaunchpadCelebrities).rosetta_experts)
11341188
1135 if txn is not None:1189 if txn is not None:
1136 txn.commit()1190 txn.commit()
@@ -1165,7 +1219,9 @@
1165 if has_templates and not has_templates_unblocked:1219 if has_templates and not has_templates_unblocked:
1166 # All templates on the same directory as this entry are1220 # All templates on the same directory as this entry are
1167 # blocked, so we can block it too.1221 # blocked, so we can block it too.
1168 entry.setStatus(RosettaImportStatus.BLOCKED)1222 entry.setStatus(
1223 RosettaImportStatus.BLOCKED,
1224 getUtility(ILaunchpadCelebrities).rosetta_experts)
1169 num_blocked += 11225 num_blocked += 1
1170 if txn is not None:1226 if txn is not None:
1171 txn.commit()1227 txn.commit()
11721228
=== modified file 'lib/lp/translations/scripts/po_import.py'
--- lib/lp/translations/scripts/po_import.py 2009-10-20 05:17:01 +0000
+++ lib/lp/translations/scripts/po_import.py 2009-11-13 18:41:14 +0000
@@ -73,7 +73,8 @@
73 def _registerFailure(self, entry, reason, traceback=False, abort=False):73 def _registerFailure(self, entry, reason, traceback=False, abort=False):
74 """Note that a queue entry is unusable in some way."""74 """Note that a queue entry is unusable in some way."""
75 reason_text = unicode(reason)75 reason_text = unicode(reason)
76 entry.setStatus(RosettaImportStatus.FAILED)76 entry.setStatus(RosettaImportStatus.FAILED,
77 getUtility(ILaunchpadCelebrities).rosetta_experts)
77 entry.setErrorOutput(reason_text)78 entry.setErrorOutput(reason_text)
7879
79 if abort:80 if abort:
8081
=== modified file 'lib/lp/translations/stories/webservice/xx-translationimportqueue.txt'
--- lib/lp/translations/stories/webservice/xx-translationimportqueue.txt 2009-07-01 20:45:39 +0000
+++ lib/lp/translations/stories/webservice/xx-translationimportqueue.txt 2009-11-13 18:41:14 +0000
@@ -114,8 +114,23 @@
114 ...114 ...
115 status: You tried to modify a read-only attribute.115 status: You tried to modify a read-only attribute.
116116
117An attempt to do so will leave the entry's status unchanged.117But you can set the status using the setStatus method.
118
119 >>> print webservice.named_post(
120 ... first_entry, 'setStatus', {}, new_status='Approved')
121 HTTP/1.1 200 Ok
122 ...
123
124The entry's status is changed.
118125
119 >>> queue = webservice.get("/+imports").jsonBody()126 >>> queue = webservice.get("/+imports").jsonBody()
120 >>> print queue['entries'][0]['status']127 >>> print queue['entries'][0]['status']
121 Imported128 Approved
129
130Unprivileged users cannot change the status.
131
132 >>> print user_webservice.named_post(
133 ... first_entry, 'setStatus', {}, new_status='Deleted')
134 HTTP/1.1 401 Unauthorized
135 ...
136
122137
=== modified file 'lib/lp/translations/tests/test_autoapproval.py'
--- lib/lp/translations/tests/test_autoapproval.py 2009-10-29 17:46:00 +0000
+++ lib/lp/translations/tests/test_autoapproval.py 2009-11-13 18:41:14 +0000
@@ -13,6 +13,9 @@
13import transaction13import transaction
14import unittest14import unittest
1515
16from zope.component import getUtility
17
18from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
16from canonical.launchpad.interfaces.lpstorm import IMasterStore19from canonical.launchpad.interfaces.lpstorm import IMasterStore
1720
18from lp.registry.interfaces.distroseries import DistroSeriesStatus21from lp.registry.interfaces.distroseries import DistroSeriesStatus
@@ -749,7 +752,8 @@
749752
750 def _setStatus(self, entry, status, when=None):753 def _setStatus(self, entry, status, when=None):
751 """Simulate status on queue entry having been set at a given time."""754 """Simulate status on queue entry having been set at a given time."""
752 entry.setStatus(status)755 entry.setStatus(status,
756 getUtility(ILaunchpadCelebrities).rosetta_experts)
753 if when is not None:757 if when is not None:
754 entry.date_status_changed = when758 entry.date_status_changed = when
755 entry.syncUpdate()759 entry.syncUpdate()
@@ -760,6 +764,8 @@
760 # are.764 # are.
761 one_year_ago = datetime.now(UTC) - timedelta(days=366)765 one_year_ago = datetime.now(UTC) - timedelta(days=366)
762 entry = self._makeProductEntry()766 entry = self._makeProductEntry()
767 entry.potemplate = (
768 self.factory.makePOTemplate(productseries=entry.productseries))
763 entry_id = entry.id769 entry_id = entry.id
764770
765 self._setStatus(entry, RosettaImportStatus.APPROVED, one_year_ago)771 self._setStatus(entry, RosettaImportStatus.APPROVED, one_year_ago)
766772
=== modified file 'lib/lp/translations/tests/test_translationbranchapprover.py'
--- lib/lp/translations/tests/test_translationbranchapprover.py 2009-10-19 10:57:28 +0000
+++ lib/lp/translations/tests/test_translationbranchapprover.py 2009-11-13 18:41:14 +0000
@@ -8,6 +8,7 @@
8from unittest import TestLoader8from unittest import TestLoader
9from zope.component import getUtility9from zope.component import getUtility
1010
11from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
11from canonical.launchpad.validators.name import valid_name12from canonical.launchpad.validators.name import valid_name
12from canonical.testing import LaunchpadZopelessLayer13from canonical.testing import LaunchpadZopelessLayer
13from lp.translations.interfaces.translationimportqueue import (14from lp.translations.interfaces.translationimportqueue import (
@@ -213,7 +214,8 @@
213 RosettaImportStatus.BLOCKED,214 RosettaImportStatus.BLOCKED,
214 )215 )
215 for status in not_approve_status:216 for status in not_approve_status:
216 entry.setStatus(status)217 entry.setStatus(
218 status, getUtility(ILaunchpadCelebrities).rosetta_experts)
217 approver = self._create_approver(pot_path)219 approver = self._create_approver(pot_path)
218 approver.approve(entry)220 approver.approve(entry)
219 self.assertEqual(status, entry.status)221 self.assertEqual(status, entry.status)
220222
=== added file 'lib/lp/translations/tests/test_translationimportqueue.py'
--- lib/lp/translations/tests/test_translationimportqueue.py 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/tests/test_translationimportqueue.py 2009-11-13 18:41:14 +0000
@@ -0,0 +1,125 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4# pylint: disable-msg=C0102
5
6__metaclass__ = type
7
8import unittest
9
10from zope.component import getUtility
11
12from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
13from lp.translations.interfaces.translationimportqueue import (
14 ITranslationImportQueue, RosettaImportStatus)
15
16from lp.testing import TestCaseWithFactory
17from canonical.testing import LaunchpadZopelessLayer
18
19
20class TestTranslationImpportQueueEntryStatus(TestCaseWithFactory):
21 """Test handling of the status of a queue entry."""
22
23 layer = LaunchpadZopelessLayer
24
25 def setUp(self):
26 """Set up context to test in."""
27 super(TestTranslationImpportQueueEntryStatus, self).setUp()
28
29 self.queue = getUtility(ITranslationImportQueue)
30 self.rosetta_experts = (
31 getUtility(ILaunchpadCelebrities).rosetta_experts)
32 self.productseries = self.factory.makeProductSeries()
33 self.uploaderperson = self.factory.makePerson()
34 self.potemplate = self.factory.makePOTemplate(
35 productseries=self.productseries)
36 self.entry = self.queue.addOrUpdateEntry(
37 'demo.pot', '#demo', False, self.uploaderperson,
38 productseries=self.productseries, potemplate=self.potemplate)
39
40 def _assertCanSetStatus(self, user, entry, expected_list):
41 # Helper to check for all statuses.
42 # Could iterate RosettaImportStatus.items but listing them here
43 # explicitely is better to read. They are sorted alphabetically.
44 possible_statuses = [
45 RosettaImportStatus.APPROVED,
46 RosettaImportStatus.BLOCKED,
47 RosettaImportStatus.DELETED,
48 RosettaImportStatus.FAILED,
49 RosettaImportStatus.IMPORTED,
50 RosettaImportStatus.NEEDS_REVIEW,
51 ]
52 # Do *not* use assertContentEqual here, as the order matters.
53 self.assertEqual(expected_list,
54 [entry.canSetStatus(status, user)
55 for status in possible_statuses])
56
57 def test_canSetStatus_non_admin(self):
58 # A non-privileged users cannot set any status except for retaining
59 # the current status of an entry.
60 some_user = self.factory.makePerson()
61 self._assertCanSetStatus(some_user, self.entry,
62 # A B D F I NR
63 [False, False, False, False, False, True])
64 self.entry.setStatus(
65 RosettaImportStatus.DELETED, self.rosetta_experts)
66 self._assertCanSetStatus(some_user, self.entry,
67 # A B D F I NR
68 [False, False, True, False, False, False])
69
70 def test_canSetStatus_rosetta_expert(self):
71 # Rosetta experts are all-powerful, didn't you know that?
72 self._assertCanSetStatus(self.rosetta_experts, self.entry,
73 # A B D F I NR
74 [True, True, True, True, True, True])
75
76 def test_canSetStatus_rosetta_expert_no_target(self):
77 # If the entry has no import target set, even Rosetta experts
78 # cannot set it to approved.
79 self.entry.potemplate = None
80 self._assertCanSetStatus(self.rosetta_experts, self.entry,
81 # A B D F I NR
82 [False, True, True, True, True, True])
83
84 def test_canSetStatus_uploader(self):
85 # The uploader can set some statuses.
86 self._assertCanSetStatus(self.uploaderperson, self.entry,
87 # A B D F I NR
88 [False, False, True, False, False, True])
89
90 def test_canSetStatus_owner(self):
91 # The owner gets the same permissions.
92 self._assertCanSetStatus(self.productseries.product.owner, self.entry,
93 # A B D F I NR
94 [False, False, True, False, False, True])
95
96 def _setUpUbuntu(self):
97 self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
98 self.ubuntu_group_owner = self.factory.makePerson()
99 self.ubuntu.translationgroup = (
100 self.factory.makeTranslationGroup(self.ubuntu_group_owner))
101
102 def test_canSetStatus_ubuntu_translation_group(self):
103 # Owners of the Ubuntu translation Groups can set entries
104 # that are targeted to Ubuntu.
105 self._setUpUbuntu()
106 ubuntu_entry = self.queue.addOrUpdateEntry(
107 'demo.pot', '#demo', False, self.uploaderperson,
108 distroseries=self.factory.makeDistroRelease(self.ubuntu),
109 sourcepackagename=self.factory.makeSourcePackageName(),
110 potemplate=self.potemplate)
111 self._assertCanSetStatus(self.ubuntu_group_owner, ubuntu_entry,
112 # A B D F I NR
113 [True, True, True, False, False, True])
114
115 def test_canSetStatus_ubuntu_translation_group_not_ubuntu(self):
116 # Outside of Ubuntu, owners of the Ubuntu translation Groups have no
117 # powers.
118 self._setUpUbuntu()
119 self._assertCanSetStatus(self.ubuntu_group_owner, self.entry,
120 # A B D F I NR
121 [False, False, False, False, False, True])
122
123
124def test_suite():
125 return unittest.TestLoader().loadTestsFromName(__name__)
0126
=== added file 'lib/lp/translations/utilities/permission_helpers.py'
--- lib/lp/translations/utilities/permission_helpers.py 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/utilities/permission_helpers.py 2009-11-13 18:41:14 +0000
@@ -0,0 +1,21 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Helpful functions to enforce permissions."""
5
6__metaclass__ = type
7
8__all__ = [
9 'is_admin_or_rosetta_expert',
10 ]
11
12from zope.component import getUtility
13
14from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
15
16
17def is_admin_or_rosetta_expert(user):
18 """Check if the user is a Launchpad admin or a Rosettta expert."""
19 celebrities = getUtility(ILaunchpadCelebrities)
20 return (user.inTeam(celebrities.admin) or
21 user.inTeam(celebrities.rosetta_experts))
022
=== modified file 'lib/lp/translations/utilities/tests/helpers.py'
--- lib/lp/translations/utilities/tests/helpers.py 2009-07-17 00:26:05 +0000
+++ lib/lp/translations/utilities/tests/helpers.py 2009-11-13 18:41:14 +0000
@@ -15,6 +15,7 @@
1515
16from zope.component import getUtility16from zope.component import getUtility
1717
18from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
18from lp.translations.interfaces.translationimportqueue import (19from lp.translations.interfaces.translationimportqueue import (
19 ITranslationImportQueue, RosettaImportStatus)20 ITranslationImportQueue, RosettaImportStatus)
20from canonical.launchpad.scripts import FakeLogger21from canonical.launchpad.scripts import FakeLogger
@@ -64,7 +65,8 @@
64 else:65 else:
65 commit()66 commit()
6667
67 entry.setStatus(RosettaImportStatus.APPROVED)68 entry.setStatus(RosettaImportStatus.APPROVED,
69 getUtility(ILaunchpadCelebrities).rosetta_experts)
68 (subject, body) = target.importFromQueue(entry, FakeLogger())70 (subject, body) = target.importFromQueue(entry, FakeLogger())
69 return entry71 return entry
7072
7173
=== modified file 'lib/lp/translations/utilities/tests/test_xpi_import.py'
--- lib/lp/translations/utilities/tests/test_xpi_import.py 2009-08-13 15:12:16 +0000
+++ lib/lp/translations/utilities/tests/test_xpi_import.py 2009-11-13 18:41:14 +0000
@@ -9,6 +9,7 @@
99
10from zope.component import getUtility10from zope.component import getUtility
1111
12from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
12from lp.registry.interfaces.person import IPersonSet13from lp.registry.interfaces.person import IPersonSet
13from lp.registry.interfaces.product import IProductSet14from lp.registry.interfaces.product import IProductSet
14from lp.translations.interfaces.potemplate import IPOTemplateSet15from lp.translations.interfaces.potemplate import IPOTemplateSet
@@ -204,7 +205,8 @@
204 ).count()205 ).count()
205206
206 # Force the entry to be imported again:207 # Force the entry to be imported again:
207 entry.setStatus(RosettaImportStatus.APPROVED)208 entry.setStatus(RosettaImportStatus.APPROVED,
209 getUtility(ILaunchpadCelebrities).rosetta_experts)
208 # Now, we tell the PO template to import from the file data it has.210 # Now, we tell the PO template to import from the file data it has.
209 (subject, body) = self.firefox_template.importFromQueue(entry)211 (subject, body) = self.firefox_template.importFromQueue(entry)
210212
211213
=== modified file 'lib/lp/translations/utilities/tests/test_xpi_po_exporter.py'
--- lib/lp/translations/utilities/tests/test_xpi_po_exporter.py 2009-08-13 15:12:16 +0000
+++ lib/lp/translations/utilities/tests/test_xpi_po_exporter.py 2009-11-13 18:41:14 +0000
@@ -10,6 +10,7 @@
1010
11from canonical.database.sqlbase import commit11from canonical.database.sqlbase import commit
12from canonical.launchpad.ftests import sync12from canonical.launchpad.ftests import sync
13from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
13from lp.registry.interfaces.person import IPersonSet14from lp.registry.interfaces.person import IPersonSet
14from lp.registry.interfaces.product import IProductSet15from lp.registry.interfaces.product import IProductSet
15from lp.translations.interfaces.potemplate import IPOTemplateSet16from lp.translations.interfaces.potemplate import IPOTemplateSet
@@ -78,7 +79,8 @@
78 potemplate=self.firefox_template)79 potemplate=self.firefox_template)
7980
80 # We must approve the entry to be able to import it.81 # We must approve the entry to be able to import it.
81 entry.setStatus(RosettaImportStatus.APPROVED)82 entry.setStatus(RosettaImportStatus.APPROVED,
83 getUtility(ILaunchpadCelebrities).rosetta_experts)
82 # The file data is stored in the Librarian, so we have to commit the84 # The file data is stored in the Librarian, so we have to commit the
83 # transaction to make sure it's stored properly.85 # transaction to make sure it's stored properly.
84 commit()86 commit()