Merge lp:~abentley/launchpad/fix-dummy-translations into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Merged at revision: 12822
Proposed branch: lp:~abentley/launchpad/fix-dummy-translations
Merge into: lp:launchpad
Diff against target: 122 lines (+51/-15)
3 files modified
lib/lp/testing/factory.py (+2/-1)
lib/lp/translations/browser/tests/test_pofile_view.py (+41/-1)
lib/lp/translations/templates/currenttranslationmessage-translate-one.pt (+8/-13)
To merge this branch: bzr merge lp:~abentley/launchpad/fix-dummy-translations
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+57208@code.launchpad.net

Commit message

Fix credit display for translators who cannot edit.

Description of the change

= Summary =
Fix bug #702477: Translation credits on new POFiles show the dummy string for the other side

== Proposed fix ==
Use the same translation credit display for all viewers, regardless of whether
they can edit the POFile.

== Pre-implementation notes ==
Discussed with henninge

== Implementation details ==
This bug turns out to be simpler than it appeared. We had two ways of
displaying translation credits; one for translators with write access, and one
for translators without write access. The first was suitable for both, and the
second was unsuitable for anyone. So this branch uses the first way for both
cases.

Also, I had to change self.factory.makeTranslator because it got Unauthorized
trying to set .translations_relicensing_agreement. Presumably, it has only
been used with Zopeless tests so far. But LaunchpadFunctionalLayer is a better
simulation of actual user interaction, so I used that.

== Tests ==
bin/test -t test_unwritable_translation_credits test_pofile_view

== Demo and Q/A ==
View a POFile that you do not have write access to. If you are not already a
translator, accept the agreement. The translation credits should be shown
correctly, and you should not be able to edit any translations.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/translations/browser/tests/test_pofile_view.py
  lib/lp/testing/factory.py
  lib/lp/translations/templates/currenttranslationmessage-translate-one.pt

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch looks good.

A thought regarding this bit of code:

    insecure_tx_person = removeSecurityProxy(tx_person)
    insecure_tx_person.translations_relicensing_agreement = license

You might want to consider avoiding storing the reference to the
unproxied object in the hope that it'll avoid someone changing the code
in an insecure way in the future. Like so:

    removeSecurityProxy(tx_person).translations_relicensing_agreement = (
        license)

Although, your variable naming might be just as effective.

review: Approve (code)
Revision history for this message
Aaron Bentley (abentley) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 11-04-11 05:00 PM, Benji York wrote:
> Review: Approve code
> This branch looks good.
>
> A thought regarding this bit of code:
>
> insecure_tx_person = removeSecurityProxy(tx_person)
> insecure_tx_person.translations_relicensing_agreement = license
>
> You might want to consider avoiding storing the reference to the
> unproxied object in the hope that it'll avoid someone changing the code
> in an insecure way in the future. Like so:
>
> removeSecurityProxy(tx_person).translations_relicensing_agreement = (
> license)

I've certainly used this approach in the past, but in this case, it
produces a multiple-line statement. I believe that impairs readability,
and for me that trumps the slight chance of avoiding security errors.

Aaron
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk2kZiEACgkQ0F+nu1YWqI0AsQCfVIv3RRNBP4dFaQcLxOVuoSuw
p6IAni0bzcXvfyKE1ax74zrB4unB89Do
=XHNq
-----END PGP SIGNATURE-----

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/testing/factory.py'
2--- lib/lp/testing/factory.py 2011-04-07 20:37:22 +0000
3+++ lib/lp/testing/factory.py 2011-04-12 18:56:43 +0000
4@@ -800,7 +800,8 @@
5 if person is None:
6 person = self.makePerson()
7 tx_person = ITranslationsPerson(person)
8- tx_person.translations_relicensing_agreement = license
9+ insecure_tx_person = removeSecurityProxy(tx_person)
10+ insecure_tx_person.translations_relicensing_agreement = license
11 return getUtility(ITranslatorSet).new(group, language, person)
12
13 def makeMilestone(self, product=None, distribution=None,
14
15=== modified file 'lib/lp/translations/browser/tests/test_pofile_view.py'
16--- lib/lp/translations/browser/tests/test_pofile_view.py 2010-12-09 19:49:36 +0000
17+++ lib/lp/translations/browser/tests/test_pofile_view.py 2011-04-12 18:56:43 +0000
18@@ -11,16 +11,22 @@
19 import pytz
20
21 from canonical.launchpad.webapp.servers import LaunchpadTestRequest
22-from canonical.testing.layers import ZopelessDatabaseLayer
23+from canonical.testing.layers import (
24+ DatabaseFunctionalLayer,
25+ ZopelessDatabaseLayer,
26+ )
27 from lp.app.errors import UnexpectedFormData
28 from lp.testing import (
29+ BrowserTestCase,
30 login,
31+ person_logged_in,
32 TestCaseWithFactory,
33 )
34 from lp.translations.browser.pofile import (
35 POFileBaseView,
36 POFileTranslateView,
37 )
38+from lp.translations.enums import TranslationPermission
39
40
41 class TestPOFileBaseViewFiltering(TestCaseWithFactory):
42@@ -435,3 +441,37 @@
43 DocumentationScenarioMixin):
44 layer = ZopelessDatabaseLayer
45 view_class = POFileTranslateView
46+
47+
48+class TestBrowser(BrowserTestCase):
49+
50+ layer = DatabaseFunctionalLayer
51+
52+ def test_unwritable_translation_credits(self):
53+ """Text of credits should be sane for non-editors."""
54+ # Make the user a translator so they can see translations.
55+ self.factory.makeTranslator(person=self.user)
56+ pofile = self.factory.makePOFile()
57+ # Restrict translations so that the translator cannot change it.
58+ product = pofile.potemplate.productseries.product
59+ with person_logged_in(product.owner):
60+ product.translationpermission = TranslationPermission.CLOSED
61+ # Add credits so that they show in the UI
62+ credits = self.factory.makePOTMsgSet(
63+ potemplate=pofile.potemplate, singular='translator-credits')
64+ browser = self.getViewBrowser(pofile)
65+ self.assertNotIn('This is a dummy translation', browser.contents)
66+ self.assertIn('(no translation yet)', browser.contents)
67+
68+ def test_anonymous_translation_credits(self):
69+ """Credits should be hidden for non-logged-in users."""
70+ pofile = self.factory.makePOFile()
71+ # Restrict translations so that the translator cannot change it.
72+ product = pofile.potemplate.productseries.product
73+ # Add credits so that they show in the UI
74+ credits = self.factory.makePOTMsgSet(
75+ potemplate=pofile.potemplate, singular='translator-credits')
76+ browser = self.getViewBrowser(pofile, no_login=True)
77+ self.assertTextMatchesExpressionIgnoreWhitespace(
78+ 'To prevent privacy issues, this translation is not available to'
79+ ' anonymous users', browser.contents)
80
81=== modified file 'lib/lp/translations/templates/currenttranslationmessage-translate-one.pt'
82--- lib/lp/translations/templates/currenttranslationmessage-translate-one.pt 2010-12-18 09:10:37 +0000
83+++ lib/lp/translations/templates/currenttranslationmessage-translate-one.pt 2011-04-12 18:56:43 +0000
84@@ -274,7 +274,7 @@
85 <tal:locked condition="context/potmsgset/is_translation_credit">
86 <td class="icon left right" />
87 <td>
88- <tal:form-writeable condition="view/form_is_writeable">
89+ <tal:not-hidden condition="not:view/message_must_be_hidden">
90 <tal:automatic condition="view/translation_credits">
91 <div tal:condition="not: view/user_is_official_translator"
92 tal:attributes="
93@@ -305,22 +305,17 @@
94 (no translation yet)
95 </label>
96 </tal:no-automatic>
97- </tal:form-writeable>
98- <tal:form-not-writeable condition="not:view/form_is_writeable">
99- <div tal:condition="not: view/user_is_official_translator"
100- tal:attributes="
101+ </tal:not-hidden>
102+ <tal:hidden condition="view/message_must_be_hidden">
103+ <tal:comment condition="nothing">
104+ Display the "privacy issues" message generated by the view.
105+ </tal:comment>
106+ <div tal:attributes="
107 id string:${translation_dictionary/html_id_translation}"
108 tal:content="structure translation_dictionary/current_translation">
109 translation credits
110 </div>
111- <label tal:condition="view/user_is_official_translator"
112- tal:attributes="
113- id string:${translation_dictionary/html_id_translation};
114- for string:${translation_dictionary/html_id_translation}_radiobutton"
115- tal:content="structure translation_dictionary/current_translation">
116- translation credits
117- </label>
118- </tal:form-not-writeable>
119+ </tal:hidden>
120 </td>
121 </tal:locked>
122 </tr>