Merge lp:~adiroiban/launchpad/bug-406477 into lp:launchpad

Proposed by Adi Roiban
Status: Merged
Approved by: Данило Шеган
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~adiroiban/launchpad/bug-406477
Merge into: lp:launchpad
Diff against target: 708 lines (+380/-101)
11 files modified
lib/canonical/launchpad/icing/style-3-0.css (+13/-0)
lib/canonical/launchpad/security.py (+74/-33)
lib/lp/testing/factory.py (+4/-1)
lib/lp/translations/browser/configure.zcml (+1/-1)
lib/lp/translations/browser/potemplate.py (+2/-2)
lib/lp/translations/configure.zcml (+1/-1)
lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt (+72/-15)
lib/lp/translations/stories/standalone/xx-potemplate-admin.txt (+42/-2)
lib/lp/translations/stories/standalone/xx-potemplate-index.txt (+70/-1)
lib/lp/translations/templates/object-templates.pt (+100/-44)
lib/lp/translations/templates/potemplate-index.pt (+1/-1)
To merge this branch: bzr merge lp:~adiroiban/launchpad/bug-406477
Reviewer Review Type Date Requested Status
Данило Шеган (community) Approve
Review via email: mp+15793@code.launchpad.net

Commit message

Use launchpad.TranslationsAdmin privileges for IPOTemplate and IPOTemplateSubset. Allow owners of distribution translation group to also administer disabled templates.

To post a comment you must log in.
Revision history for this message
Adi Roiban (adiroiban) wrote :

= Bug 406477 =

Members of the ubuntu-translations-coordinators team can edit templates through the +admin pages.

However, once a template has been disabled, the +admin page is no longer accessible to them, which stops them from e.g. enabling the template again in case of a mistake.

== Implementation details ==

After asking a pre-implementation review from Danilo, he suggest I should use launchpad.TranslationsAdmin, instead of the more generic launchpad.Admin translation.

The previous administration privileges for IPOTemplate was assigned only to RosettaAdmins.
Now it was change to all RosettaAdmin + owner of distro.translationgroup

== Tests ==

There were no test covering the use case of distribution translations coordinator admin rights to templates, so this was added to distroseries-templates

Also the previous rosetta-potemplate-index was not checking the administrative links.

./bin/test -ct "distroseries-templates"
./bin/test -ct "rosetta-potemplate-index"

== Demo and Q/A ==

Make sure you are a member of Ubuntu Translation Coordinators team (ubuntu-l10n-coordinator).
https://launchpad.dev/~ubuntu-l10n-coordinator

Go to the Distro +template page
https://translations.launchpad.dev/ubuntu/hoary/+templates

You should see the Administer page for Evolution, disabled-template .

As a member of Ubuntu Translation Coordinators team you should be able to administer it, just like any other template from that table.

Login as a normal user, you should not see the administration links (only Download), and when trying to manually enter the admin url (https://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/disabled-template/+admin) you should see an access denied page.

= 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/configure.zcml
  lib/lp/translations/browser/potemplate.py
  lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt
  lib/lp/translations/stories/standalone/xx-rosetta-potemplate-index.txt
  lib/lp/translations/templates/object-templates.pt
  lib/lp/translations/templates/potemplate-index.pt

Revision history for this message
Данило Шеган (danilo) wrote :
Download full text (14.0 KiB)

Adi, thanks a lot for working on this. In general, there's one major
complaint about how you implemented permissions. There's a number of
narrative/stylistic suggestions as well, though they are obviously of
lower priority (though still important).

If I sound patronising anywhere, please forgive me: I am not sure how
much of our code base or styling guides you have already had a chance to
figure out yourself, so I might be saying things you already know :)

Anyway, this would need further improvements, but is overall a great
step in the right direction!

  review needsfixing

У уто, 08. 12 2009. у 03:15 +0000, Adi Roiban пише:

> == Implementation details ==
>
> After asking a pre-implementation review from Danilo, he suggest I
> should use launchpad.TranslationsAdmin, instead of the more generic
> launchpad.Admin translation.
>
> The previous administration privileges for IPOTemplate was assigned
> only to RosettaAdmins.
> Now it was change to all RosettaAdmin + owner of
> distro.translationgroup
>
> == Tests ==
>
> There were no test covering the use case of distribution translations
> coordinator admin rights to templates, so this was added to
> distroseries-templates
>
> Also the previous rosetta-potemplate-index was not checking the
> administrative links.

FWIW, sometimes we avoid having too many tests in "pagetests" (i.e.
stories), because they are very fragile. Just moving stuff around in
the layout sometimes breaks them, so we do not test for all possible
cases in them.

> ./bin/test -ct "distroseries-templates"
> ./bin/test -ct "rosetta-potemplate-index"

FWIW, you can pass -t to bin/test multiple times, eg.

  bin/test -vvct distroseries-templates -t rosetta-potemplate-index

(if you really must have colorized output; -vv is basically a standard
verbose mode LP developers use, and -vvv gives you test timings as well)

> == Demo and Q/A ==
>
> Make sure you are a member of Ubuntu Translation Coordinators team
> (ubuntu-l10n-coordinator).
> https://launchpad.dev/~ubuntu-l10n-coordinator
>
> Go to the Distro +template page
> https://translations.launchpad.dev/ubuntu/hoary/+templates
>
> You should see the Administer page for Evolution, disabled-template .
>
> As a member of Ubuntu Translation Coordinators team you should be able
> to administer it, just like any other template from that table.
>
> Login as a normal user, you should not see the administration links
> (only Download), and when trying to manually enter the admin url
> (https://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/disabled-template/+admin) you should see an access denied page.

While playing with it, I discovered that you see disabled templates even
if you are not logged in, or if you don't have sufficient privileges.
Clicking on it gives you a 'not found' error. It's probably not your
code, but it'd be useful to fix it along the way and show disabled
templates only if someone has TranslationsAdmin privilege on them.

> === modified file 'lib/canonical/launchpad/security.py'
> --- lib/canonical/launchpad/security.py 2009-11-19 15:14:53 +0000
> +++ lib/canonical/launchpad/security.py 2009-12-08 03:15:30 +0000
> @@ -479,6 +479,28 @@...

review: Needs Fixing
Revision history for this message
Adi Roiban (adiroiban) wrote :

Hi,

Thank you very much for taking the time and reviewing this branch.
It was really ugly. So don't worry, I deserve it :)

I agree that is hard to cover all test paths, is just that right now I don't know what should be tested and what not, and where is the border between a useful test and a bloated test.
Is a test is not needed, feel free to comment and I will delete it.

I'm testing using a single command line, but in MP I put them in different lines to improve readability... stupid thing to do :). For the future I'll put them in a single line.

Disabled templates are only displayed for TranslationsAdmins.
The previous behavior was validated by test stories.

I didn't knew Ubuntu is already a celebrity.
I prefer to keep with Ubuntu, without creating a new distribution. It will just make the story to complicated.
Added a "license=yes" to MakeTranslator , to also sign the translation license.. otherwise that translator has no edit rights.

I'm sorry for failing to write proper stories. Hope I can improve my narrator skills.

Hope the permission are right.

I added stories for testing administration and editing of POTemplate and PoTemplateSubset.

Thank you again for taking the time reviewing this branch.
If there are further issues, have no mercy! :)

It is ok to have these filenames?
xx-potemplate-admin.txt
xx-potemplate-edit.txt
xx-rosetta-potemplate-export.txt
xx-rosetta-potemplate-index.txt

I was thinking at removing rosetta from the filename.

Revision history for this message
Данило Шеган (danilo) wrote :
Download full text (18.7 KiB)

Hi Adi,

Thanks for all the effort you are putting into this: you are not just
solving the bug, you are helping improve the Launchpad code quality as
well (which is why it's a bit harder than expected :). There's still
some work needed on this branch.

У сре, 09. 12 2009. у 01:03 +0000, Adi Roiban пише:

> Thank you very much for taking the time and reviewing this branch.
> It was really ugly. So don't worry, I deserve it :)

You are most welcome - and it wasn't ugly, it just needed further
polish :)

Btw, it's useful to post an incremental diff (i.e. only the changes
since the last review) so it's easier for a reviewer to only go over
that without going through things they have already approved. This
time it's simple because it's only one revision, but sometimes it's
harder than that and then an incremental diff helps.

> I'm testing using a single command line, but in MP I put them in
> different lines to improve readability... stupid thing to do :). For
> the future I'll put them in a single line.

Thanks!

> Disabled templates are only displayed for TranslationsAdmins.
> The previous behavior was validated by test stories.

Cool, thanks for this improvement!

> I didn't knew Ubuntu is already a celebrity.
> I prefer to keep with Ubuntu, without creating a new
> distribution. It will just make the story to complicated.

Well, "complicated" is a relative term. If you create a new
distribution and at least two new templates (one disabled, another
enabled), you'd have a fully self-contained test. That's means that
extra 5 lines of test set-up, you'll get a story which a reader can
understand fully without resorting to "make run" to see what data is
already there.

However, this branch is already too big: do not worry about this and
keep it as-is. For future work, I do suggest you keep the above in
mind.

> Added a "license=yes" to MakeTranslator , to also sign the
> translation license.. otherwise that translator has no edit rights.

Cool, thanks for doing this.

> I'm sorry for failing to write proper stories. Hope I can improve my
> narrator skills.

Don't worry about it. None of us is great at it, and we've got
years of experience. With every review though, we are always getting
better :)

There are still some suggestions on how to improve it, and some tips
on how to think about it when writing stories inline below.

> Hope the permission are right.

Not yet. I'll comment on the actual code below.

> I added stories for testing administration and editing of POTemplate
> and PoTemplateSubset.

Thanks, they look good.

> Thank you again for taking the time reviewing this branch.
> If there are further issues, have no mercy! :)

This is complicated stuff, so don't feel bad if I have no mercy :)

> It is ok to have these filenames?
> xx-potemplate-admin.txt
> xx-potemplate-edit.txt
> xx-rosetta-potemplate-export.txt
> xx-rosetta-potemplate-index.txt
>
> I was thinking at removing rosetta from the filename.

Sure, that'd be great :)

And now, your code with my comments inline:

=== modified file 'lib/canonical/launchpad/security.py'
> --- lib/canonical/launchpad/security.py 2009-12-07 21:58:53 +0000
> +++ lib/canonical/launchpad/secur...

Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (17.8 KiB)

Here is the diff from the last commit.
I will leave the other tasks for another branch.

I looked for all the other places where factory.makeTranslator is used and the change should not affect the current tests.

bin/test -m translations looks ok.

=== modified file 'lib/canonical/launchpad/icing/style-3-0.css'
--- lib/canonical/launchpad/icing/style-3-0.css 2009-12-05 03:11:48 +0000
+++ lib/canonical/launchpad/icing/style-3-0.css 2009-12-10 12:03:04 +0000
@@ -674,6 +674,18 @@
     padding: 2px 1em 2px 2px;
 }

+/* Templates listing.
+ *
+ * Examples:
+ * https://translations.launchpad.dev/ubuntu/hoary/+templates
+ * https://translations.launchpad.dev/evolution/trunk/+templates
+ */
+
+.inactive-template td {
+ background-color: #DCDCDC;
+}
+
+
 /* Translations statistics and legend.
  *
  * Examples:

=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2009-12-09 01:03:22 +0000
+++ lib/canonical/launchpad/security.py 2009-12-10 11:31:11 +0000
@@ -1123,13 +1123,21 @@
         to edit distribution details are able to change translation settings
         for a distribution.
         """
- return (
+ # Translation group owner for a distribution is also a
+ # translations administrator for it.
+ translation_group = self.obj.translationgroup
+ if translation_group and user.inTeam(translation_group.owner):
+ return True
+ else:
+ return (
             OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user) or
             EditDistributionByDistroOwnersOrAdmins.checkAuthenticated(
                 self, user))

-class AdminPOTemplateDetails(AdminDistributionTranslations):
+# Please keep AdminPOTemplateSubset in sync with this, unless you
+# know exactly what you are doing.
+class AdminPOTemplateDetails(OnlyRosettaExpertsAndAdmins):
     """Controls administration of an `IPOTemplate`.

     Allow all persons that can also administer the translations to
@@ -1143,16 +1151,17 @@
     def checkAuthenticated(self, user):
         template = self.obj
         if template.distroseries is not None:
- distro = template.distroseries.distribution
- translation_group = distro.translationgroup
- if translation_group and user.inTeam(translation_group.owner):
- return True
-
+ # Template is on a distribution.
+ distribution = template.distroseries.distribution
             return (
                 AdminDistributionTranslations(
- template.distroseries.distribution).checkAuthenticated(user))
+ template.distroseries.distribution).checkAuthenticated(
+ user))

- return False
+ else:
+ # Template is on a product.
+ return OnlyRosettaExpertsAndAdmins.checkAuthenticated(
+ self, user)

 class EditPOTemplateDetails(AdminPOTemplateDetails, EditByOwnersOrAdmins):
@@ -1636,7 +1645,11 @@
                 user.inTeam(celebs.bazaar_experts))

-class AdminPOTemplateSubset(AdminDistributionTranslations):
+# Please keep this in sync with AdminPOTemplateDetails. Note that
+# th...

Revision history for this message
Данило Шеган (danilo) wrote :
Download full text (5.4 KiB)

У чет, 10. 12 2009. у 12:44 +0000, Adi Roiban пише:
> Here is the diff from the last commit.
> I will leave the other tasks for another branch.

Cool, thanks. We are almost there, only a few bits remaining.

> === modified file 'lib/canonical/launchpad/icing/style-3-0.css'
> --- lib/canonical/launchpad/icing/style-3-0.css 2009-12-05 03:11:48 +0000
> +++ lib/canonical/launchpad/icing/style-3-0.css 2009-12-10 12:03:04 +0000
> @@ -674,6 +674,18 @@
> padding: 2px 1em 2px 2px;
> }
>
> +/* Templates listing.
> + *
> + * Examples:
> + * https://translations.launchpad.dev/ubuntu/hoary/+templates
> + * https://translations.launchpad.dev/evolution/trunk/+templates
> + */
> +
> +.inactive-template td {
> + background-color: #DCDCDC;
> +}
> +
> +

I tried it out: this looks great. I've also played with

.inactive-template td {
    background-color: #fee;
    color: #855;
}

(to give it a slight redish appearance, to indicate something is wrong
with it), but I am not so sure about it. Your call about that, but if
you are in doubt about something, JFDI. :)

> === modified file 'lib/canonical/launchpad/security.py'
> --- lib/canonical/launchpad/security.py 2009-12-09 01:03:22 +0000
> +++ lib/canonical/launchpad/security.py 2009-12-10 11:31:11 +0000

> @@ -1123,13 +1123,21 @@
> to edit distribution details are able to change translation settings
> for a distribution.
> """
> - return (
> + # Translation group owner for a distribution is also a
> + # translations administrator for it.
> + translation_group = self.obj.translationgroup
> + if translation_group and user.inTeam(translation_group.owner):
> + return True
> + else:
> + return (
> OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user) or
> EditDistributionByDistroOwnersOrAdmins.checkAuthenticated(
> self, user))

A minor nitpick: please fix the indentation here as well. In general,
if you are using Emacs, python-mode should do it correctly for you if
you just press Tab key once on each of these lines.

> +# Please keep AdminPOTemplateSubset in sync with this, unless you
> +# know exactly what you are doing.
> +class AdminPOTemplateDetails(OnlyRosettaExpertsAndAdmins):
> """Controls administration of an `IPOTemplate`.
>
> Allow all persons that can also administer the translations to
> @@ -1143,16 +1151,17 @@
> def checkAuthenticated(self, user):
> template = self.obj
> if template.distroseries is not None:
> - distro = template.distroseries.distribution
> - translation_group = distro.translationgroup
> - if translation_group and
> user.inTeam(translation_group.owner):
> - return True
> -
> + # Template is on a distribution.
> + distribution = template.distroseries.distribution
> return (
> AdminDistributionTranslations(
> -
> template.distroseries.distribution).checkAuthenticated(user))
> +
> template.distroseries.distribution).checkAuthenticated(
> + user))

Let's replace this `template.d...

Read more...

Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (4.6 KiB)

Many thanks for the review... there we a couple of stupid errors :(
For the future I will try to read the whole diff at least twice.

Latest diff.

=== modified file 'lib/canonical/launchpad/icing/style-3-0.css'
--- lib/canonical/launchpad/icing/style-3-0.css 2009-12-10 12:46:11 +0000
+++ lib/canonical/launchpad/icing/style-3-0.css 2009-12-11 11:33:39 +0000
@@ -682,7 +682,8 @@
  */

 .inactive-template td {
- background-color: #DCDCDC;
+ background-color: #fee;
+ color: #855;
 }

=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2009-12-10 12:46:11 +0000
+++ lib/canonical/launchpad/security.py 2009-12-11 11:33:39 +0000
@@ -1130,9 +1130,9 @@
             return True
         else:
             return (
- OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user) or
- EditDistributionByDistroOwnersOrAdmins.checkAuthenticated(
- self, user))
+ OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user) or
+ EditDistributionByDistroOwnersOrAdmins.checkAuthenticated(
+ self, user))

 # Please keep AdminPOTemplateSubset in sync with this, unless you
@@ -1155,8 +1155,7 @@
             distribution = template.distroseries.distribution
             return (
                 AdminDistributionTranslations(
- template.distroseries.distribution).checkAuthenticated(
- user))
+ distribution).checkAuthenticated(user))

         else:
             # Template is on a product.
@@ -1663,16 +1662,13 @@
     def checkAuthenticated(self, user):
         template_set = self.obj
         if template_set.distroseries is not None:
- distro = template_set.distroseries.distribution
- translation_group = distro.translationgroup
- if translation_group and user.inTeam(translation_group.owner):
- return True
-
+ distribution = template_set.distroseries.distribution
             return (
                 AdminDistributionTranslations(
- template_set.distroseries.distribution).checkAuthenticated(user))
-
- return False
+ distribution).checkAuthenticated(user))
+ else:
+ # Template is on a product.
+ return OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user)

 class AdminDistroSeriesLanguage(OnlyRosettaExpertsAndAdmins):

=== modified file 'lib/lp/translations/stories/standalone/xx-potemplate-admin.txt'
--- lib/lp/translations/stories/standalone/xx-potemplate-admin.txt 2009-08-12 05:09:36 +0000
+++ lib/lp/translations/stories/standalone/xx-potemplate-admin.txt 2009-12-11 11:33:39 +0000
@@ -1,3 +1,10 @@
+Administering POTemplates
+=========================
+
+
+Product templates
+-----------------
+
 The POTemplate admin page lets us to edit any aspect of that object, that's
 why we need to be a Rosetta Expert or a Launchpad admin to use it.

@@ -155,8 +162,29 @@
   >>> print admin_browser.url
   http://translations.launchpad.dev/evolution/trunk/+pots/evolution-renamed

-
-== Distribution templates ==
+Administrators can disable and t...

Read more...

Revision history for this message
Данило Шеган (danilo) wrote :

У пет, 11. 12 2009. у 11:40 +0000, Adi Roiban пише:
> Many thanks for the review... there we a couple of stupid errors :(
> For the future I will try to read the whole diff at least twice.

Great work, thanks again!

 review approve
 merge approve

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/icing/style-3-0.css'
--- lib/canonical/launchpad/icing/style-3-0.css 2009-12-07 13:53:47 +0000
+++ lib/canonical/launchpad/icing/style-3-0.css 2009-12-11 11:37:16 +0000
@@ -694,6 +694,19 @@
694 padding: 2px 1em 2px 2px;694 padding: 2px 1em 2px 2px;
695}695}
696696
697/* Templates listing.
698 *
699 * Examples:
700 * https://translations.launchpad.dev/ubuntu/hoary/+templates
701 * https://translations.launchpad.dev/evolution/trunk/+templates
702 */
703
704.inactive-template td {
705 background-color: #fee;
706 color: #855;
707}
708
709
697/* Translations statistics and legend.710/* Translations statistics and legend.
698 *711 *
699 * Examples:712 * Examples:
700713
=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2009-11-19 15:14:53 +0000
+++ lib/canonical/launchpad/security.py 2009-12-11 11:37:16 +0000
@@ -479,6 +479,7 @@
479 """Allow Launchpad's admins and Rosetta experts edit all fields."""479 """Allow Launchpad's admins and Rosetta experts edit all fields."""
480 return is_admin_or_rosetta_expert(user)480 return is_admin_or_rosetta_expert(user)
481481
482
482class AdminProductTranslations(AuthorizationBase):483class AdminProductTranslations(AuthorizationBase):
483 permission = 'launchpad.TranslationsAdmin'484 permission = 'launchpad.TranslationsAdmin'
484 usedfor = IProduct485 usedfor = IProduct
@@ -1103,27 +1104,63 @@
1103 usedfor = ICodeImportMachine1104 usedfor = ICodeImportMachine
11041105
11051106
1107class AdminDistributionTranslations(OnlyRosettaExpertsAndAdmins,
1108 EditDistributionByDistroOwnersOrAdmins):
1109 """Class for deciding who can administer distribution translations.
1110
1111 This class is used for `launchpad.TranslationsAdmin` privilege on
1112 `IDistribution` and `IDistroSeries` and corresponding `IPOTemplate`s,
1113 and limits access to Rosetta experts, Launchpad admins and distribution
1114 translation group owner.
1115 """
1116 permission = 'launchpad.TranslationsAdmin'
1117 usedfor = IDistribution
1118
1119 def checkAuthenticated(self, user):
1120 """Is the user able to manage `IDistribution` translations settings?
1121
1122 Any Launchpad/Launchpad Translations administrator or people allowed
1123 to edit distribution details are able to change translation settings
1124 for a distribution.
1125 """
1126 # Translation group owner for a distribution is also a
1127 # translations administrator for it.
1128 translation_group = self.obj.translationgroup
1129 if translation_group and user.inTeam(translation_group.owner):
1130 return True
1131 else:
1132 return (
1133 OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user) or
1134 EditDistributionByDistroOwnersOrAdmins.checkAuthenticated(
1135 self, user))
1136
1137
1138# Please keep AdminPOTemplateSubset in sync with this, unless you
1139# know exactly what you are doing.
1106class AdminPOTemplateDetails(OnlyRosettaExpertsAndAdmins):1140class AdminPOTemplateDetails(OnlyRosettaExpertsAndAdmins):
1107 permission = 'launchpad.Admin'1141 """Controls administration of an `IPOTemplate`.
1142
1143 Allow all persons that can also administer the translations to
1144 which this template belongs to and also translation group owners.
1145
1146 Product owners does not have administrative privileges.
1147 """
1148 permission = 'launchpad.TranslationsAdmin'
1108 usedfor = IPOTemplate1149 usedfor = IPOTemplate
11091150
1110 def checkAuthenticated(self, user):1151 def checkAuthenticated(self, user):
1111 """Allow LP/Translations admins, and for distros, owners and
1112 translation group owners.
1113 """
1114 if OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user):
1115 return True
1116
1117 template = self.obj1152 template = self.obj
1118 if template.distroseries is not None:1153 if template.distroseries is not None:
1119 distro = template.distroseries.distribution1154 # Template is on a distribution.
1120 if user.inTeam(distro.owner):1155 distribution = template.distroseries.distribution
1121 return True1156 return (
1122 translation_group = distro.translationgroup1157 AdminDistributionTranslations(
1123 if translation_group and user.inTeam(translation_group.owner):1158 distribution).checkAuthenticated(user))
1124 return True
11251159
1126 return False1160 else:
1161 # Template is on a product.
1162 return OnlyRosettaExpertsAndAdmins.checkAuthenticated(
1163 self, user)
11271164
11281165
1129class EditPOTemplateDetails(AdminPOTemplateDetails, EditByOwnersOrAdmins):1166class EditPOTemplateDetails(AdminPOTemplateDetails, EditByOwnersOrAdmins):
@@ -1607,10 +1644,32 @@
1607 user.inTeam(celebs.bazaar_experts))1644 user.inTeam(celebs.bazaar_experts))
16081645
16091646
1647# Please keep this in sync with AdminPOTemplateDetails. Note that
1648# this permission controls access to browsing into individual
1649# potemplates, but it's on a different object (POTemplateSubset)
1650# from AdminPOTemplateDetails, even though it looks almost identical
1610class AdminPOTemplateSubset(OnlyRosettaExpertsAndAdmins):1651class AdminPOTemplateSubset(OnlyRosettaExpertsAndAdmins):
1611 permission = 'launchpad.Admin'1652 """Controls administration of an `IPOTemplateSubset`.
1653
1654 Allow all persons that can also administer the translations to
1655 which this template belongs to and also translation group owners.
1656
1657 Product owners does not have administrative privileges.
1658 """
1659 permission = 'launchpad.TranslationsAdmin'
1612 usedfor = IPOTemplateSubset1660 usedfor = IPOTemplateSubset
16131661
1662 def checkAuthenticated(self, user):
1663 template_set = self.obj
1664 if template_set.distroseries is not None:
1665 distribution = template_set.distroseries.distribution
1666 return (
1667 AdminDistributionTranslations(
1668 distribution).checkAuthenticated(user))
1669 else:
1670 # Template is on a product.
1671 return OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user)
1672
16141673
1615class AdminDistroSeriesLanguage(OnlyRosettaExpertsAndAdmins):1674class AdminDistroSeriesLanguage(OnlyRosettaExpertsAndAdmins):
1616 permission = 'launchpad.Admin'1675 permission = 'launchpad.Admin'
@@ -1791,24 +1850,6 @@
1791 user.inTeam(self.obj.distribution.language_pack_admin))1850 user.inTeam(self.obj.distribution.language_pack_admin))
17921851
17931852
1794class AdminDistributionTranslations(OnlyRosettaExpertsAndAdmins,
1795 EditDistributionByDistroOwnersOrAdmins):
1796 permission = 'launchpad.TranslationsAdmin'
1797 usedfor = IDistribution
1798
1799 def checkAuthenticated(self, user):
1800 """Is the user able to manage `IDistribution` translations settings?
1801
1802 Any Launchpad/Launchpad Translations administrator or people allowed
1803 to edit distribution details are able to change translation settings
1804 for a distribution.
1805 """
1806 return (
1807 OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user) or
1808 EditDistributionByDistroOwnersOrAdmins.checkAuthenticated(
1809 self, user))
1810
1811
1812class AdminLanguagePack(OnlyRosettaExpertsAndAdmins):1853class AdminLanguagePack(OnlyRosettaExpertsAndAdmins):
1813 permission = 'launchpad.LanguagePacksAdmin'1854 permission = 'launchpad.LanguagePacksAdmin'
1814 usedfor = ILanguagePack1855 usedfor = ILanguagePack
18151856
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2009-12-10 20:23:49 +0000
+++ lib/lp/testing/factory.py 2009-12-11 11:37:16 +0000
@@ -76,6 +76,7 @@
76from lp.translations.interfaces.translationgroup import (76from lp.translations.interfaces.translationgroup import (
77 ITranslationGroupSet)77 ITranslationGroupSet)
78from lp.translations.interfaces.translator import ITranslatorSet78from lp.translations.interfaces.translator import ITranslatorSet
79from lp.translations.interfaces.translationsperson import ITranslationsPerson
79from canonical.launchpad.ftests._sqlobject import syncUpdate80from canonical.launchpad.ftests._sqlobject import syncUpdate
80from lp.services.mail.signedmessage import SignedMessage81from lp.services.mail.signedmessage import SignedMessage
81from lp.services.worlddata.interfaces.country import ICountrySet82from lp.services.worlddata.interfaces.country import ICountrySet
@@ -502,13 +503,15 @@
502 return getUtility(ITranslationGroupSet).new(503 return getUtility(ITranslationGroupSet).new(
503 name, title, summary, url, owner)504 name, title, summary, url, owner)
504505
505 def makeTranslator(self, language_code, group=None, person=None):506 def makeTranslator(
507 self, language_code, group=None, person=None, license=True):
506 """Create a new, arbitrary `Translator`."""508 """Create a new, arbitrary `Translator`."""
507 language = getUtility(ILanguageSet).getLanguageByCode(language_code)509 language = getUtility(ILanguageSet).getLanguageByCode(language_code)
508 if group is None:510 if group is None:
509 group = self.makeTranslationGroup()511 group = self.makeTranslationGroup()
510 if person is None:512 if person is None:
511 person = self.makePerson()513 person = self.makePerson()
514 ITranslationsPerson(person).translations_relicensing_agreement = license
512 return getUtility(ITranslatorSet).new(group, language, person)515 return getUtility(ITranslatorSet).new(group, language, person)
513516
514 def makeMilestone(517 def makeMilestone(
515518
=== modified file 'lib/lp/translations/browser/configure.zcml'
--- lib/lp/translations/browser/configure.zcml 2009-12-07 18:42:21 +0000
+++ lib/lp/translations/browser/configure.zcml 2009-12-11 11:37:16 +0000
@@ -420,7 +420,7 @@
420 name="+admin"420 name="+admin"
421 for="lp.translations.interfaces.potemplate.IPOTemplate"421 for="lp.translations.interfaces.potemplate.IPOTemplate"
422 class="lp.translations.browser.potemplate.POTemplateAdminView"422 class="lp.translations.browser.potemplate.POTemplateAdminView"
423 permission="launchpad.Admin"423 permission="launchpad.TranslationsAdmin"
424 template="../../app/templates/generic-edit.pt"424 template="../../app/templates/generic-edit.pt"
425 layer="canonical.launchpad.layers.TranslationsLayer"/>425 layer="canonical.launchpad.layers.TranslationsLayer"/>
426 <browser:page426 <browser:page
427427
=== modified file 'lib/lp/translations/browser/potemplate.py'
--- lib/lp/translations/browser/potemplate.py 2009-11-27 12:50:16 +0000
+++ lib/lp/translations/browser/potemplate.py 2009-12-11 11:37:16 +0000
@@ -184,7 +184,7 @@
184 text = 'Settings'184 text = 'Settings'
185 return Link('+edit', text)185 return Link('+edit', text)
186186
187 @enabled_with_permission('launchpad.Admin')187 @enabled_with_permission('launchpad.TranslationsAdmin')
188 def administer(self):188 def administer(self):
189 text = 'Administer'189 text = 'Administer'
190 return Link('+admin', text)190 return Link('+admin', text)
@@ -688,7 +688,7 @@
688 raise AssertionError('Unknown context for %s' % potemplate.title)688 raise AssertionError('Unknown context for %s' % potemplate.title)
689689
690 if ((official_rosetta and potemplate.iscurrent) or690 if ((official_rosetta and potemplate.iscurrent) or
691 check_permission('launchpad.Admin', self.context)):691 check_permission('launchpad.TranslationsAdmin', self.context)):
692 # The target is using officially Launchpad Translations and the692 # The target is using officially Launchpad Translations and the
693 # template is available to be translated, or the user is a is a693 # template is available to be translated, or the user is a is a
694 # Launchpad administrator in which case we show everything.694 # Launchpad administrator in which case we show everything.
695695
=== modified file 'lib/lp/translations/configure.zcml'
--- lib/lp/translations/configure.zcml 2009-09-26 10:03:31 +0000
+++ lib/lp/translations/configure.zcml 2009-12-11 11:37:16 +0000
@@ -411,7 +411,7 @@
411 permission="launchpad.Edit"411 permission="launchpad.Edit"
412 set_attributes="owner priority description"/>412 set_attributes="owner priority description"/>
413 <require413 <require
414 permission="launchpad.Admin"414 permission="launchpad.TranslationsAdmin"
415 set_attributes="name translation_domain productseries distroseries sourcepackagename sourcepackageversion binarypackagename languagepack path header iscurrent from_sourcepackagename date_last_updated source_file source_file_format"/>415 set_attributes="name translation_domain productseries distroseries sourcepackagename sourcepackageversion binarypackagename languagepack path header iscurrent from_sourcepackagename date_last_updated source_file source_file_format"/>
416 </class>416 </class>
417 <adapter417 <adapter
418418
=== modified file 'lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt'
--- lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt 2009-11-30 18:18:08 +0000
+++ lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt 2009-12-11 11:37:16 +0000
@@ -1,5 +1,3 @@
1
2
3Templates view for DistroSeries1Templates view for DistroSeries
4===============================2===============================
53
@@ -20,6 +18,7 @@
20 >>> print user_browser.url18 >>> print user_browser.url
21 http://translations.launchpad.dev/ubuntu/hoary/+templates19 http://translations.launchpad.dev/ubuntu/hoary/+templates
2220
21
23The templates table22The templates table
24-------------------23-------------------
2524
@@ -35,8 +34,7 @@
3534
36 >>> table = find_tag_by_id(anon_browser.contents, 'templates_table')35 >>> table = find_tag_by_id(anon_browser.contents, 'templates_table')
37 >>> print extract_text(table)36 >>> print extract_text(table)
38 Source package Template name Last update 37 Source package Template name Last update
39 evolution disabled-template 2007-01-05
40 evolution evolution-2.2 2005-05-0638 evolution evolution-2.2 2005-05-06
41 evolution man 2006-08-1439 evolution man 2006-08-14
42 mozilla pkgconf-mozilla 2005-05-0640 mozilla pkgconf-mozilla 2005-05-06
@@ -44,18 +42,19 @@
44 pmount pmount 2005-05-0642 pmount pmount 2005-05-06
4543
4644
47Logged-in users will see a link from distro series45Logged-in users see a link to all the active translation templates
46on a distribution series translation page.
48 >>> user_browser.open(47 >>> user_browser.open(
49 ... 'http://translations.launchpad.dev/ubuntu/hoary')48 ... 'http://translations.launchpad.dev/ubuntu/hoary')
50 >>> user_browser.getLink('full list of templates').click()49 >>> user_browser.getLink('full list of templates').click()
5150
52Logged-in users can also choose to download all translations for each51Regular users only see the option to download translations for each of
53of the templates.52the active templates.
5453
55 >>> table = find_tag_by_id(user_browser.contents, 'templates_table')54 >>> table = find_tag_by_id(user_browser.contents, 'templates_table')
56 >>> print extract_text(table)55 >>> print extract_text(table)
57 Source package Template name Last update Actions56 Source package Template name Last update Actions
58 evolution disabled-template 2007-01-05 Download57 evolution evolution-2.2 2005-05-06 Download
59 ...58 ...
60 mozilla pkgconf-mozilla 2005-05-06 Download59 mozilla pkgconf-mozilla 2005-05-06 Download
61 ...60 ...
@@ -70,13 +69,70 @@
7069
71 >>> table = find_tag_by_id(admin_browser.contents, 'templates_table')70 >>> table = find_tag_by_id(admin_browser.contents, 'templates_table')
72 >>> print extract_text(table)71 >>> print extract_text(table)
73 Source package Template name Last update Actions72 Source package Template name Last update Actions
74 evolution disabled-template 2007-01-05 Edit Upload Download Administer73 evolution disabled-template (inactive) 2007-01-05 Edit Upload Download Administer
75 evolution evolution-2.2 2005-05-06 Edit Upload Download Administer74 evolution evolution-2.2 2005-05-06 Edit Upload Download Administer
76 evolution man 2006-08-14 Edit Upload Download Administer75 evolution man 2006-08-14 Edit Upload Download Administer
77 mozilla pkgconf-mozilla 2005-05-06 Edit Upload Download Administer76 mozilla pkgconf-mozilla 2005-05-06 Edit Upload Download Administer
78 pmount man 2006-08-14 Edit Upload Download Administer77 pmount man 2006-08-14 Edit Upload Download Administer
79 pmount pmount 2005-05-06 Edit Upload Download Administer78 pmount pmount 2005-05-06 Edit Upload Download Administer
79
80Ubuntu Translations Coordinator can administer all templates, including
81those that are currently disabled.
82
83New user is made an owner of an Ubuntu translation group, thus being
84considered a translations administrator for Ubuntu.
85
86 >>> from zope.component import getUtility
87 >>> from canonical.launchpad.ftests import login, logout
88 >>> from canonical.launchpad.interfaces import (
89 ... IDistributionSet, IPersonSet, ILaunchpadCelebrities)
90 >>> login('foo.bar@canonical.com')
91 >>> a_person = factory.makePerson(email='utc-member@example.com',
92 ... name='utc-member', password='test', displayname='Some Guy')
93 >>> utc_member = factory.makeTranslator('ro', person=a_person)
94 >>> utc_team = factory.makeTeam(owner=a_person)
95 >>> utg = factory.makeTranslationGroup(owner=utc_team,
96 ... name="Ubuntu Translation Group")
97 >>> ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
98 >>> ubuntu.translationgroup = utg
99 >>> # Log out so we can go back to using test browsers.
100 >>> logout()
101
102Visiting the Ubuntu Hoary templates page, translation administrators
103will see "Edit" and "Administer" links for all templates.
104
105Trying to edit/administer enabled templates brings them to the
106appropriate page.
107
108 >>> utc_browser = setupBrowser(auth='Basic utc-member@example.com:test')
109 >>> utc_browser.open(
110 ... 'http://translations.launchpad.dev/ubuntu/hoary/+templates')
111 >>> utc_browser.getLink(url='ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+edit').click()
112 >>> print utc_browser.url
113 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+edit
114
115 >>> utc_browser.open(
116 ... 'http://translations.launchpad.dev/ubuntu/hoary/+templates')
117 >>> utc_browser.getLink(url='/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+admin').click()
118 >>> print utc_browser.url
119 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+admin
120
121Trying to edit/administer disabled templates brings them to the
122appropriate page.
123
124 >>> utc_browser = setupBrowser(auth='Basic utc-member@example.com:test')
125 >>> utc_browser.open(
126 ... 'http://translations.launchpad.dev/ubuntu/hoary/+templates')
127 >>> utc_browser.getLink(url='ubuntu/hoary/+source/evolution/+pots/disabled-template/+edit').click()
128 >>> print utc_browser.url
129 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/disabled-template/+edit
130
131 >>> utc_browser.open(
132 ... 'http://translations.launchpad.dev/ubuntu/hoary/+templates')
133 >>> utc_browser.getLink(url='/ubuntu/hoary/+source/evolution/+pots/disabled-template/+admin').click()
134 >>> print utc_browser.url
135 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/disabled-template/+admin
80136
81137
82Links to the templates138Links to the templates
@@ -97,3 +153,4 @@
97 >>> admin_browser.getLink('Edit').click()153 >>> admin_browser.getLink('Edit').click()
98 >>> print admin_browser.url154 >>> print admin_browser.url
99 http://translations.../evolution/+pots/disabled-template/+edit155 http://translations.../evolution/+pots/disabled-template/+edit
156
100157
=== renamed file 'lib/lp/translations/stories/standalone/xx-rosetta-pofile-export.txt' => 'lib/lp/translations/stories/standalone/xx-pofile-export.txt'
=== modified file 'lib/lp/translations/stories/standalone/xx-potemplate-admin.txt'
--- lib/lp/translations/stories/standalone/xx-potemplate-admin.txt 2009-08-12 05:09:36 +0000
+++ lib/lp/translations/stories/standalone/xx-potemplate-admin.txt 2009-12-11 11:37:16 +0000
@@ -1,3 +1,10 @@
1Administering POTemplates
2=========================
3
4
5Product templates
6-----------------
7
1The POTemplate admin page lets us to edit any aspect of that object, that's8The POTemplate admin page lets us to edit any aspect of that object, that's
2why we need to be a Rosetta Expert or a Launchpad admin to use it.9why we need to be a Rosetta Expert or a Launchpad admin to use it.
310
@@ -155,8 +162,29 @@
155 >>> print admin_browser.url162 >>> print admin_browser.url
156 http://translations.launchpad.dev/evolution/trunk/+pots/evolution-renamed163 http://translations.launchpad.dev/evolution/trunk/+pots/evolution-renamed
157164
158165Administrators can disable and then make changes to a disabled template.
159== Distribution templates ==166
167 >>> admin_browser.open(
168 ... 'http://translations.launchpad.dev/evolution/trunk/+pots/'
169 ... 'evolution-renamed/+admin')
170 >>> admin_browser.getControl(name='field.iscurrent').value = False
171 >>> admin_browser.getControl('Change').click()
172 >>> print admin_browser.url
173 http://translations.launchpad.dev/evolution/trunk/+pots/evolution-renamed
174
175Now we will reenable the template.
176
177 >>> admin_browser.open(
178 ... 'http://translations.launchpad.dev/evolution/trunk/+pots/'
179 ... 'evolution-renamed/+admin')
180 >>> admin_browser.getControl(name='field.iscurrent').value = True
181 >>> admin_browser.getControl('Change').click()
182 >>> print admin_browser.url
183 http://translations.launchpad.dev/evolution/trunk/+pots/evolution-renamed
184
185
186Distribution templates
187----------------------
160188
161Distributions get slightly wider permissions to manage their templates189Distributions get slightly wider permissions to manage their templates
162autonomously.190autonomously.
@@ -214,3 +242,15 @@
214242
215 >>> print template.path243 >>> print template.path
216 splat.pot244 splat.pot
245
246Distribution translation coordinators can disable and manage disabled
247templates.
248
249 >>> group_owner_browser.open(template_admin_url)
250 >>> group_owner_browser.getControl(name='field.iscurrent').value = False
251 >>> group_owner_browser.getControl('Change').click()
252 >>> group_owner_browser.open(template_admin_url)
253 >>> group_owner_browser.getControl(name='field.iscurrent').value = True
254 >>> group_owner_browser.getControl('Change').click()
255
256
217257
=== renamed file 'lib/lp/translations/stories/standalone/xx-rosetta-potemplate-export.txt' => 'lib/lp/translations/stories/standalone/xx-potemplate-export.txt'
=== renamed file 'lib/lp/translations/stories/standalone/xx-rosetta-potemplate-index.txt' => 'lib/lp/translations/stories/standalone/xx-potemplate-index.txt'
--- lib/lp/translations/stories/standalone/xx-rosetta-potemplate-index.txt 2009-11-09 17:08:21 +0000
+++ lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-11 11:37:16 +0000
@@ -1,4 +1,9 @@
1= POTemplate index page =1POTemplate index page
2=====================
3
4
5DistoSeries
6-----------
27
3The index page for a POTemplate lists all available translations8The index page for a POTemplate lists all available translations
4for a source package. No Privileges Person visits the 9for a source package. No Privileges Person visits the
@@ -74,6 +79,10 @@
74 Dutch ... ... ... ... Luk Claes79 Dutch ... ... ... ... Luk Claes
75 Finnish ... ... ... ... P\xf6ll\xe480 Finnish ... ... ... ... P\xf6ll\xe4
7681
82
83DistroSeries and ProductSeries links to related templates
84---------------------------------------------------------
85
77We are presented not only with links to alternate templates from the same86We are presented not only with links to alternate templates from the same
78source, but also with links to the same template in (other) distroseries.87source, but also with links to the same template in (other) distroseries.
7988
@@ -95,3 +104,63 @@
95 >>> print alternate_notice104 >>> print alternate_notice
96 <p...<a href="/evolution/trunk/+pots/evolution-2.2"...105 <p...<a href="/evolution/trunk/+pots/evolution-2.2"...
97106
107
108Administering templates
109-----------------------
110
111Anonymous visitors see only a list of all existing templates, with no
112administration or download/upload links.
113
114 >>> anon_browser.open('http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
115 >>> anon_browser.getLink('upload')
116 Traceback (most recent call last):
117 ...
118 LinkNotFoundError
119
120 >>> anon_browser.getLink('download').click()
121 Traceback (most recent call last):
122 ...
123 LinkNotFoundError
124
125As an authenticated user, you should see the download link,
126but not the one for uploading file to this potemplate.
127
128 >>> user_browser.open('http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
129 >>> user_browser.getLink('upload')
130 Traceback (most recent call last):
131 ...
132 LinkNotFoundError
133
134 >>> user_browser.getLink('download').click()
135 >>> print user_browser.url
136 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+export
137
138Translation administrators will see both download and upload links.
139Beside administering this template, "Change permissions"
140and "Change details" should be also accessible.
141
142 >>> admin_browser.open('http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
143 >>> admin_browser.getLink('upload').click()
144 >>> print admin_browser.url
145 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+upload
146
147 >>> admin_browser.open('http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
148 >>> admin_browser.getLink('download').click()
149 >>> print admin_browser.url
150 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+export
151
152 >>> admin_browser.open('http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
153 >>> admin_browser.getLink('Administer this template').click()
154 >>> print admin_browser.url
155 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+admin
156
157 >>> admin_browser.open('http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
158 >>> admin_browser.getLink('Change details').click()
159 >>> print admin_browser.url
160 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+edit
161
162 >>> admin_browser.open('http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
163 >>> admin_browser.getLink('Change permissions').click()
164 >>> print admin_browser.url
165 http://translations.launchpad.dev/ubuntu/+settings
166
98167
=== modified file 'lib/lp/translations/templates/object-templates.pt'
--- lib/lp/translations/templates/object-templates.pt 2009-12-07 14:19:02 +0000
+++ lib/lp/translations/templates/object-templates.pt 2009-12-11 11:37:16 +0000
@@ -82,51 +82,107 @@
82 </tr>82 </tr>
83 </thead>83 </thead>
84 <tbody>84 <tbody>
85 <tr tal:repeat="template view/iter_templates" class="template_row">85 <tal:templates repeat="template view/iter_templates">
86 <td tal:condition="view/is_distroseries"86 <tal:not-current condition="not: template/iscurrent">
87 tal:content="template/sourcepackagename/name"87 <tal:admin condition="template/required:launchpad.TranslationsAdmin">
88 class="sourcepackage_column">Source package88 <tr class="template_row inactive-template">
89 </td>89 <td tal:condition="view/is_distroseries"
90 <td class="template_column"><a tal:attributes="href template/fmt:url"90 tal:content="template/sourcepackagename/name"
91 tal:content="template/name">Template name</a></td>91 class="sourcepackage_column">Source package
92 <td class="lastupdate_column">92 </td>
93 <span class="sortkey"93 <td class="template_column">
94 tal:condition="template/date_last_updated"94 <a tal:attributes="href template/fmt:url">
95 tal:content="template/date_last_updated/fmt:datetime">95 <span tal:content="template/name">Template name</span>
96 time sort key96 </a> (inactive)
97 </span>97 </td>
98 <span class="lastupdate_column"98 <td class="lastupdate_column">
99 tal:condition="template/date_last_updated"99 <span class="sortkey"
100 tal:attributes="100 tal:condition="template/date_last_updated"
101 title template/date_last_updated/fmt:datetime"101 tal:content="template/date_last_updated/fmt:datetime">
102 tal:content="102 time sort key
103 template/date_last_updated/fmt:approximatedate"103 </span>
104 >104 <span class="lastupdate_column"
105 2009-09-23105 tal:condition="template/date_last_updated"
106 </span>106 tal:attributes="
107 </td>107 title template/date_last_updated/fmt:datetime"
108 <td class="actions_column"108 tal:content="
109 tal:condition="context/required:launchpad.AnyPerson">109 template/date_last_updated/fmt:approximatedate"
110 <div class="template_links">110 >
111 <tal:maintainer condition="template/required:launchpad.Edit">111 2009-09-23
112 <a tal:attributes="href string:${template/fmt:url}/+edit;112 </span>
113 title string:Edit ${template/name}'s details">113 </td>
114 <img src="/@@/edit" />&nbsp;Edit</a>114 <td class="actions_column"
115 <a tal:attributes="href string:${template/fmt:url}/+upload;115 tal:condition="context/required:launchpad.AnyPerson">
116 title string:Upload translations to ${template/name}">116 <div class="template_links">
117 <img src="/@@/add" />&nbsp;Upload</a>117 <tal:maintainer condition="template/required:launchpad.Edit">
118 </tal:maintainer>118 <a tal:attributes="href string:${template/fmt:url}/+edit;
119 <a tal:attributes="href string:${template/fmt:url}/+export;119 title string:Edit ${template/name}'s details">
120 title string:Download translations from ${template/name}">120 <img src="/@@/edit" />&nbsp;Edit</a>
121 <img src="/@@/download" />&nbsp;Download</a>121 <a tal:attributes="href string:${template/fmt:url}/+upload;
122 <tal:admin condition="template/required:launchpad.Admin">122 title string:Upload translations to ${template/name}">
123 <a tal:attributes="href string:${template/fmt:url}/+admin;123 <img src="/@@/add" />&nbsp;Upload</a>
124 title string:Administer ${template/name}">124 </tal:maintainer>
125 <img src="/@@/edit" />&nbsp;Administer</a>125 <a tal:attributes="href string:${template/fmt:url}/+export;
126 title string:Download translations from ${template/name}">
127 <img src="/@@/download" />&nbsp;Download</a>
128 <tal:admin condition="template/required:launchpad.TranslationsAdmin">
129 <a tal:attributes="href string:${template/fmt:url}/+admin;
130 title string:Administer ${template/name}">
131 <img src="/@@/edit" />&nbsp;Administer</a>
132 </tal:admin>
133 </div>
134 </td>
135 </tr>
126 </tal:admin>136 </tal:admin>
127 </div>137 </tal:not-current>
128 </td>138 <tal:current condition="template/iscurrent">
129 </tr>139 <tr class="template_row">
140 <td tal:condition="view/is_distroseries"
141 tal:content="template/sourcepackagename/name"
142 class="sourcepackage_column">Source package
143 </td>
144 <td class="template_column"><a tal:attributes="href template/fmt:url"
145 tal:content="template/name">Template name</a></td>
146 <td class="lastupdate_column">
147 <span class="sortkey"
148 tal:condition="template/date_last_updated"
149 tal:content="template/date_last_updated/fmt:datetime">
150 time sort key
151 </span>
152 <span class="lastupdate_column"
153 tal:condition="template/date_last_updated"
154 tal:attributes="
155 title template/date_last_updated/fmt:datetime"
156 tal:content="
157 template/date_last_updated/fmt:approximatedate"
158 >
159 2009-09-23
160 </span>
161 </td>
162 <td class="actions_column"
163 tal:condition="context/required:launchpad.AnyPerson">
164 <div class="template_links">
165 <tal:maintainer condition="template/required:launchpad.Edit">
166 <a tal:attributes="href string:${template/fmt:url}/+edit;
167 title string:Edit ${template/name}'s details">
168 <img src="/@@/edit" />&nbsp;Edit</a>
169 <a tal:attributes="href string:${template/fmt:url}/+upload;
170 title string:Upload translations to ${template/name}">
171 <img src="/@@/add" />&nbsp;Upload</a>
172 </tal:maintainer>
173 <a tal:attributes="href string:${template/fmt:url}/+export;
174 title string:Download translations from ${template/name}">
175 <img src="/@@/download" />&nbsp;Download</a>
176 <tal:admin condition="template/required:launchpad.TranslationsAdmin">
177 <a tal:attributes="href string:${template/fmt:url}/+admin;
178 title string:Administer ${template/name}">
179 <img src="/@@/edit" />&nbsp;Administer</a>
180 </tal:admin>
181 </div>
182 </td>
183 </tr>
184 </tal:current>
185 </tal:templates>
130 </tbody>186 </tbody>
131 </table>187 </table>
132 </div>188 </div>
133189
=== modified file 'lib/lp/translations/templates/potemplate-index.pt'
--- lib/lp/translations/templates/potemplate-index.pt 2009-10-15 14:04:48 +0000
+++ lib/lp/translations/templates/potemplate-index.pt 2009-12-11 11:37:16 +0000
@@ -115,7 +115,7 @@
115 class="download sprite">download</a>115 class="download sprite">download</a>
116 translation tarballs.116 translation tarballs.
117 </p>117 </p>
118 <div tal:condition="context/required:launchpad.Admin">118 <div tal:condition="context/required:launchpad.TranslationsAdmin">
119 <a tal:attributes="href119 <a tal:attributes="href
120 context/menu:navigation/administer/url"120 context/menu:navigation/administer/url"
121 class="edit sprite">121 class="edit sprite">