Merge lp:~henninge/launchpad/devel-705176-sharing-info-link into lp:launchpad

Proposed by Henning Eggers
Status: Merged
Approved by: Henning Eggers
Approved revision: no longer in the source branch.
Merged at revision: 12559
Proposed branch: lp:~henninge/launchpad/devel-705176-sharing-info-link
Merge into: lp:launchpad
Prerequisite: lp:~henninge/launchpad/devel-705176-sharing-info-on-page
Diff against target: 414 lines (+204/-8)
9 files modified
lib/lp/translations/browser/potemplate.py (+19/-1)
lib/lp/translations/browser/productseries.py (+12/-1)
lib/lp/translations/browser/sourcepackage.py (+13/-1)
lib/lp/translations/browser/tests/test_sharing_information.py (+78/-5)
lib/lp/translations/browser/translationsharing.py (+57/-0)
lib/lp/translations/stories/standalone/xx-potemplate-index.txt (+15/-0)
lib/lp/translations/templates/potemplate-index.pt (+3/-0)
lib/lp/translations/templates/productseries-translations.pt (+4/-0)
lib/lp/translations/templates/sourcepackage-translations.pt (+3/-0)
To merge this branch: bzr merge lp:~henninge/launchpad/devel-705176-sharing-info-link
Reviewer Review Type Date Requested Status
Deryck Hodge (community) code Approve
Curtis Hovey (community) ui Approve
Review via email: mp+51891@code.launchpad.net

Commit message

[r=deryck][ui=sinzui][bug=705176] Add links to sharing details page to translation pages.

Description of the change

= Summary =

This is the final step in adding sharing information to translation pages. It adds a link to each "Sharing Information" portlet which were introduced in the last branch. The link points to a yet-to-be-created page that will provide detailed information about translation sharing and a place to take the necessary configuration step. Thus the link is named differently depending on the user's permissions to configure these things.

Here are the screenshots used in the UI review.
http://people.canonical.com/~henninge/screenshots/sharing-information/sharing-details-info.png
http://people.canonical.com/~henninge/screenshots/sharing-information/sharing-details-edit.png
http://people.canonical.com/~henninge/screenshots/sharing-information/sharing-details-setup.png

== Proposed fix ==

I added a Mixin to be used in all the view classes that creates an HTML snippet for the link. This was far easier than putting it together in TAL. The views that use this mixing implement some methods to provide the specific objects.

== Pre-implementation notes ==

Same as before.

== Implementation details ==

A lot of the implementation is extending the test to work with different users to verify that the correct link text is displayed. One trick here was to make sure that the user's password is 'test' because that is what getViewBrowser expects.

== Tests ==

bin/test -vvcm lp.translations.browser.test.test_sharing_information
bin/test -vvct xx-potemplate-index.txt

== Demo and Q/A ==

Try these as a privileged and an unprivileged user. The link should be "Edit sharing details" and "View sharing details" respectively.

You will have to enable the feature:
translations.sharing_information.enabled

View sharing information for a product series here:
http://translations.launchpad.dev/evolution/trunk

View sharing information for a template here:
http://translations.launchpad.dev/evolution/trunk/+pots/evolution-2.2

View sharing information for a pofile here:
http://translations.launchpad.dev/evolution/trunk/+pots/evolution-2.2/es
This will not show the link!

Once on the other side, you'll see the equivalent links there.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/translations/browser/potemplate.py
  lib/lp/translations/browser/productseries.py
  lib/lp/translations/browser/sourcepackage.py
  lib/lp/translations/browser/translationsharing.py
  lib/lp/translations/browser/tests/test_sharing_information.py
  lib/lp/translations/stories/standalone/xx-potemplate-index.txt
  lib/lp/translations/templates/potemplate-index.pt
  lib/lp/translations/templates/productseries-translations.pt
  lib/lp/translations/templates/sourcepackage-translations.pt

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

The exclamation mark in sharing-details-setup.png looks odd (now!). Links do not have punctuation. Why does this one need an exclamation mark? Launchpad does not have any links that say "now". This link looks like it wants to be a sentence. There are no links that use "Set up". I see "Set thing" and "Configure thing" I would expect this to be presented as
a sentence:
    _(/) Configure sharing_ now!
or a navigational link
    _(/) Configure sharing_

review: Needs Information (ui)
Revision history for this message
Henning Eggers (henninge) wrote :

Well, the exclamation mark and the "now" were meant to encourage people to do the configuration. But I see your point that this is not common to do. Using "set up" is used on the same page, though: "Set up branch synchronization". Should that be "Configure branch synchronization", too? I understand "set up" to be more about "inital configuration" which is what this link points to.

I would like to do this:
    _(/) Set up sharing_

Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you for explaining more about what the user is going to do. _(/) Set up sharing_
is better than my suggestion.

review: Approve (ui)
Revision history for this message
Deryck Hodge (deryck) wrote :

Looks good. Approved with the following request for a minor update:

<deryck> henninge, looking at your MP.... should TranslationSharingDetailsMixin also raise NotImplementedError for is_sharing and can_edit_sharing_details?
<deryck> henninge, so the API is clear?
<henninge> deryck: Yes, I think that makes sense. I thought about it, too, but I don't know why I discarded the thought.

Cheers,
deryck

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/translations/browser/potemplate.py'
2--- lib/lp/translations/browser/potemplate.py 2011-03-01 17:40:06 +0000
3+++ lib/lp/translations/browser/potemplate.py 2011-03-08 17:15:57 +0000
4@@ -71,6 +71,9 @@
5 from lp.services.worlddata.interfaces.language import ILanguageSet
6 from lp.translations.browser.poexportrequest import BaseExportView
7 from lp.translations.browser.translations import TranslationsMixin
8+from lp.translations.browser.translationsharing import (
9+ TranslationSharingDetailsMixin,
10+ )
11 from lp.translations.interfaces.pofile import IPOFileSet
12 from lp.translations.interfaces.potemplate import (
13 IPOTemplate,
14@@ -229,7 +232,8 @@
15 self.request.response.redirect('../+translations')
16
17
18-class POTemplateView(LaunchpadView, TranslationsMixin):
19+class POTemplateView(LaunchpadView,
20+ TranslationsMixin, TranslationSharingDetailsMixin):
21
22 SHOW_RELATED_TEMPLATES = 4
23
24@@ -373,6 +377,20 @@
25 obj, template = infos[0]
26 return template
27
28+ def getTranslationTarget(self):
29+ """See `TranslationSharingDetailsMixin`."""
30+ if self.is_upstream_template:
31+ return self.context.productseries
32+ else:
33+ return self.context.sourcepackage
34+
35+ def can_edit_sharing_details(self):
36+ if self.is_upstream_template:
37+ obj = self.context.productseries
38+ else:
39+ obj = self.context.distroseries
40+ return check_permission('launchpad.Edit', obj)
41+
42
43 class POTemplateUploadView(LaunchpadView, TranslationsMixin):
44 """Upload translations and updated template."""
45
46=== modified file 'lib/lp/translations/browser/productseries.py'
47--- lib/lp/translations/browser/productseries.py 2011-02-25 11:45:11 +0000
48+++ lib/lp/translations/browser/productseries.py 2011-03-08 17:15:57 +0000
49@@ -49,6 +49,9 @@
50 from lp.translations.browser.poexportrequest import BaseExportView
51 from lp.translations.browser.potemplate import BaseSeriesTemplatesView
52 from lp.translations.browser.translations import TranslationsMixin
53+from lp.translations.browser.translationsharing import (
54+ TranslationSharingDetailsMixin,
55+ )
56 from lp.translations.interfaces.productserieslanguage import (
57 IProductSeriesLanguageSet,
58 )
59@@ -344,7 +347,9 @@
60 return check_permission("launchpad.Edit", self.context)
61
62
63-class ProductSeriesView(LaunchpadView, ProductSeriesTranslationsMixin):
64+class ProductSeriesView(LaunchpadView,
65+ ProductSeriesTranslationsMixin,
66+ TranslationSharingDetailsMixin):
67 """A view to show a series with translations."""
68
69 label = "Translation status by language"
70@@ -462,6 +467,12 @@
71 sourcepackage, template = infos[0]
72 return sourcepackage
73
74+ def getTranslationTarget(self):
75+ """See `TranslationSharingDetailsMixin`."""
76+ return self.context
77+
78+ can_edit_sharing_details = can_configure_translations
79+
80
81 class SettingsRadioWidget(LaunchpadRadioWidgetWithDescription):
82 """Remove the confusing hint under the widget."""
83
84=== modified file 'lib/lp/translations/browser/sourcepackage.py'
85--- lib/lp/translations/browser/sourcepackage.py 2011-02-25 11:45:11 +0000
86+++ lib/lp/translations/browser/sourcepackage.py 2011-03-08 17:15:57 +0000
87@@ -16,16 +16,21 @@
88 Link,
89 NavigationMenu,
90 )
91+from canonical.launchpad.webapp.authorization import check_permission
92 from lp.registry.interfaces.sourcepackage import ISourcePackage
93 from lp.translations.browser.poexportrequest import BaseExportView
94 from lp.translations.browser.translations import TranslationsMixin
95+from lp.translations.browser.translationsharing import (
96+ TranslationSharingDetailsMixin,
97+ )
98 from lp.translations.utilities.translationsharinginfo import (
99 has_upstream_template,
100 get_upstream_sharing_info,
101 )
102
103
104-class SourcePackageTranslationsView(TranslationsMixin):
105+class SourcePackageTranslationsView(TranslationsMixin,
106+ TranslationSharingDetailsMixin):
107
108 @property
109 def potemplates(self):
110@@ -47,6 +52,13 @@
111 productseries, template = infos[0]
112 return productseries
113
114+ def getTranslationTarget(self):
115+ """See `TranslationSharingDetailsMixin`."""
116+ return self.context
117+
118+ def can_edit_sharing_details(self):
119+ return check_permission('launchpad.Edit', self.context.distroseries)
120+
121
122 class SourcePackageTranslationsMenu(NavigationMenu):
123 usedfor = ISourcePackage
124
125=== modified file 'lib/lp/translations/browser/tests/test_sharing_information.py'
126--- lib/lp/translations/browser/tests/test_sharing_information.py 2011-03-01 20:05:22 +0000
127+++ lib/lp/translations/browser/tests/test_sharing_information.py 2011-03-08 17:15:57 +0000
128@@ -62,12 +62,28 @@
129
130 SHARING_TEXT = None
131
132- def _test_sharing_information(self, obj, expected_text):
133+ def getAuthorizedUser(self, obj):
134+ """Get a user that is authorized to edit sharing details on obj."""
135+ raise NotImplementedError
136+
137+ SHARING_DETAILS_INFO = "View sharing details"
138+ SHARING_DETAILS_SETUP = "Set up sharing"
139+ SHARING_DETAILS_EDIT = "Edit sharing details"
140+
141+ def _test_sharing_information(self, obj,
142+ id_under_test, expected_text,
143+ authorized=False):
144 self.useFixture(FeatureFixture(
145 {'translations.sharing_information.enabled': 'on'}))
146+ if authorized:
147+ user = self.getAuthorizedUser(obj)
148+ else:
149+ user = None
150 browser = self.getViewBrowser(
151- obj, no_login=True, rootsite="translations")
152- sharing_info = find_tag_by_id(browser.contents, 'sharing-information')
153+ obj, user=user, no_login=(not authorized),
154+ rootsite="translations")
155+
156+ sharing_info = find_tag_by_id(browser.contents, id_under_test)
157 if expected_text is None:
158 self.assertIs(None, sharing_info)
159 else:
160@@ -77,11 +93,36 @@
161
162 def test_not_sharing(self):
163 self._test_sharing_information(
164- self.makeNotSharingObject(), self.NOT_SHARING_TEXT)
165+ self.makeNotSharingObject(),
166+ 'sharing-information', self.NOT_SHARING_TEXT)
167
168 def test_sharing(self):
169 self._test_sharing_information(
170- self.makeSharingObject(), self.SHARING_TEXT)
171+ self.makeSharingObject(),
172+ 'sharing-information', self.SHARING_TEXT)
173+
174+ def test_sharing_details_info(self):
175+ # For unauthorized users, the link to the sharing details page is
176+ # informational.
177+ self._test_sharing_information(
178+ self.makeSharingObject(),
179+ 'sharing-details', self.SHARING_DETAILS_INFO)
180+
181+ def test_sharing_details_setup(self):
182+ # For authorized users of not sharing objects, the link to the
183+ # sharing details page encourages action.
184+ self._test_sharing_information(
185+ self.makeNotSharingObject(),
186+ 'sharing-details', self.SHARING_DETAILS_SETUP,
187+ authorized=True)
188+
189+ def test_sharing_details_edit(self):
190+ # For authorized users, the link to the sharing details page is for
191+ # editing
192+ self._test_sharing_information(
193+ self.makeSharingObject(),
194+ 'sharing-details', self.SHARING_DETAILS_EDIT,
195+ authorized=True)
196
197
198 class TestUpstreamPOTemplateSharingInfo(BrowserTestCase,
199@@ -104,6 +145,9 @@
200 SHARING_TEXT = """
201 This template is sharing translations with .*"""
202
203+ def getAuthorizedUser(self, potemplate):
204+ return potemplate.productseries.product.owner
205+
206
207 class TestPOFileSharingInfo(BrowserTestCase, TestSharingInfoMixin):
208 """Test display of POFile sharing info."""
209@@ -125,6 +169,13 @@
210 SHARING_TEXT = """
211 These translations are shared with .*"""
212
213+ def getAuthorizedUser(self, productseries):
214+ return None
215+
216+ SHARING_DETAILS_INFO = None
217+ SHARING_DETAILS_SETUP = None
218+ SHARING_DETAILS_EDIT = None
219+
220
221 class TestDummyPOFileSharingInfo(BrowserTestCase, TestSharingInfoMixin):
222 """Test display of DummyPOFile sharing info."""
223@@ -145,6 +196,13 @@
224 SHARING_TEXT = """
225 These translations are shared with .*"""
226
227+ def getAuthorizedUser(self, productseries):
228+ return None
229+
230+ SHARING_DETAILS_INFO = None
231+ SHARING_DETAILS_SETUP = None
232+ SHARING_DETAILS_EDIT = None
233+
234
235 class TestUpstreamSharingInfo(BrowserTestCase, TestSharingInfoMixin):
236 """Test display of product series sharing info."""
237@@ -167,6 +225,9 @@
238 SHARING_TEXT = """
239 This project series is sharing translations with .*"""
240
241+ def getAuthorizedUser(self, productseries):
242+ return productseries.product.owner
243+
244
245 class TestUbuntuPOTemplateSharingInfo(BrowserTestCase, TestSharingInfoMixin):
246 """Test display of template sharing info in an Ubuntu source package."""
247@@ -190,6 +251,12 @@
248 SHARING_TEXT = """
249 This template is sharing translations with .*"""
250
251+ def getAuthorizedUser(self, potemplate):
252+ with celebrity_logged_in('admin'):
253+ potemplate.distroseries.owner = self.factory.makePerson(
254+ password='test')
255+ return potemplate.distroseries.owner
256+
257
258 class TestUbuntuSharingInfo(BrowserTestCase, TestSharingInfoMixin):
259 """Test display of source package sharing info."""
260@@ -213,3 +280,9 @@
261
262 SHARING_TEXT = """
263 This source package is sharing translations with .*"""
264+
265+ def getAuthorizedUser(self, sourcepackage):
266+ with celebrity_logged_in('admin'):
267+ sourcepackage.distroseries.owner = self.factory.makePerson(
268+ password='test')
269+ return sourcepackage.distroseries.owner
270
271=== added file 'lib/lp/translations/browser/translationsharing.py'
272--- lib/lp/translations/browser/translationsharing.py 1970-01-01 00:00:00 +0000
273+++ lib/lp/translations/browser/translationsharing.py 2011-03-08 17:15:57 +0000
274@@ -0,0 +1,57 @@
275+# Copyright 2011 Canonical Ltd. This software is licensed under the
276+# GNU Affero General Public License version 3 (see the file LICENSE).
277+
278+"""Views and mixins to use for translation sharing."""
279+
280+__metaclass__ = type
281+
282+__all__ = [
283+ 'TranslationSharingDetailsMixin',
284+ ]
285+
286+
287+from canonical.launchpad.webapp import canonical_url
288+
289+
290+class TranslationSharingDetailsMixin:
291+ """Mixin for views that need to display translation details link.
292+
293+ View using this need to implement is_sharing, can_edit_sharing_details
294+ and getTranslationTarget().
295+ """
296+
297+ def is_sharing(self):
298+ """Whether this object is sharing translations or not."""
299+ raise NotImplementedError
300+
301+ def can_edit_sharing_details(self):
302+ """If the current user can edit sharing details."""
303+ raise NotImplementedError
304+
305+ def getTranslationTarget(self):
306+ """Return either a productseries or a sourcepackage."""
307+ raise NotImplementedError
308+
309+ def sharing_details(self):
310+ """Construct the link to the sharing details page."""
311+ tag_template = (
312+ '<a class="sprite %(icon)s" id="sharing-details"'
313+ ' href="%(href)s/+sharing-details">%(text)s</a>')
314+
315+ if self.can_edit_sharing_details():
316+ icon = 'edit'
317+ if self.is_sharing():
318+ text = "Edit sharing details"
319+ else:
320+ text = "Set up sharing"
321+ else:
322+ if self.is_sharing():
323+ icon = 'info'
324+ text = "View sharing details"
325+ else:
326+ return ""
327+ href = canonical_url(
328+ self.getTranslationTarget(),
329+ rootsite='translations',
330+ )
331+ return tag_template % dict(icon=icon, text=text, href=href)
332
333=== modified file 'lib/lp/translations/stories/standalone/xx-potemplate-index.txt'
334--- lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2011-03-02 09:20:38 +0000
335+++ lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2011-03-08 17:15:57 +0000
336@@ -80,6 +80,7 @@
337 Sharing Information
338 This template is sharing translations with
339 evolution in Ubuntu Hoary template evolution-2.2.
340+ View sharing details
341 >>> print sharing_info
342 <div...<a href="/ubuntu/hoary/+source/evolution/+pots/evolution-2.2"...
343
344@@ -95,9 +96,23 @@
345 Sharing Information
346 This template is sharing translations with
347 Evolution trunk series template evolution-2.2.
348+ View sharing details
349 >>> print sharing_info
350 <div...<a href="/evolution/trunk/+pots/evolution-2.2"...
351
352+If the user has the right permissions, they are offered to edit the sharing
353+information.
354+
355+ >>> with FeatureFixture(feature_flag):
356+ ... admin_browser.open('http://translations.launchpad.dev/evolution'
357+ ... '/trunk/+pots/evolution-2.2')
358+ >>> sharing_details = find_tag_by_id(
359+ ... admin_browser.contents, 'sharing-details')
360+ >>> print extract_text(sharing_details)
361+ Edit sharing details
362+ >>> print sharing_details['href']
363+ http://.../evolution/trunk/+sharing-details
364+
365
366 Finding related templates
367 -------------------------
368
369=== modified file 'lib/lp/translations/templates/potemplate-index.pt'
370--- lib/lp/translations/templates/potemplate-index.pt 2011-03-01 17:39:16 +0000
371+++ lib/lp/translations/templates/potemplate-index.pt 2011-03-08 17:15:57 +0000
372@@ -80,6 +80,9 @@
373 tal:content="template/name"
374 >evolution-2.2</a>.
375 </p>
376+ <a tal:replace="structure view/sharing_details">
377+ View sharing details
378+ </a>
379 </div>
380 </div>
381
382
383=== modified file 'lib/lp/translations/templates/productseries-translations.pt'
384--- lib/lp/translations/templates/productseries-translations.pt 2011-02-28 17:06:03 +0000
385+++ lib/lp/translations/templates/productseries-translations.pt 2011-03-08 17:15:57 +0000
386@@ -1,3 +1,4 @@
387+
388 <html
389 xmlns="http://www.w3.org/1999/xhtml"
390 xmlns:tal="http://xml.zope.org/namespaces/tal"
391@@ -90,6 +91,9 @@
392 tal:attributes="href package/fmt:url"
393 tal:content="package/displayname">apache in ubuntu hoary</a>.
394 </p>
395+ <a tal:replace="structure view/sharing_details">
396+ View sharing details
397+ </a>
398 </div>
399 </div>
400
401
402=== modified file 'lib/lp/translations/templates/sourcepackage-translations.pt'
403--- lib/lp/translations/templates/sourcepackage-translations.pt 2011-02-28 17:06:03 +0000
404+++ lib/lp/translations/templates/sourcepackage-translations.pt 2011-03-08 17:15:57 +0000
405@@ -38,6 +38,9 @@
406 tal:attributes="href productseries/fmt:url"
407 tal:content="productseries/title">Evolution trunk</a>.
408 </p>
409+ <a tal:replace="structure view/sharing_details">
410+ View sharing details
411+ </a>
412 </div>
413 </div>
414 <div class="yui-u">