Merge lp:~sinzui/launchpad/action-menu-bug-403641 into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Merged at revision: not available
Proposed branch: lp:~sinzui/launchpad/action-menu-bug-403641
Merge into: lp:launchpad
Diff against target: None lines
To merge this branch: bzr merge lp:~sinzui/launchpad/action-menu-bug-403641
Reviewer Review Type Date Requested Status
Paul Hummer (community) Approve
Review via email: mp+9793@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :
Download full text (5.1 KiB)

This is my branch to add support for an action menu and involvement portlet.
This branch is extracted form my product index page branch.

    lp:~sinzui/launchpad/action-menu-bug-403641
    Diff size: 496
    Launchpad bug: https://bugs.launchpad.net/bugs/403641
                   https://bugs.launchpad.net/bugs/403651
    Test command: ./bin/test -vvt "(tales|menu|pillar-views).txt"
    Pre-implementation: beuno
    Target release: 2.2.8

= Add support for an action menu and involvement portlet =

Bug 403641 [base-layout must support a object action menu]
    The 3.0 design allows an object to have an action menu for links
    that affect the object that cannot be represented in the content.
    The examples links are "Change details", "Change password", and
    "Make private".

Bug 403651 [base-layout must support an Involvement menu]
    The 3.0 design has an involvement portlet to encourage users to
    participate.

Martin asked me to remove the 2.0 backward compatibility style hacks
and land it as soon as possible so that the engineers designing pages
see their work appear as the example designs.

== Rules ==

Bug 403641 [base-layout must support a object action menu]
    Reuse the NavigationMenu to present the menu. The nav menu is
    best because it can be used with with context object and views
    (which may be necessary)

Bug 403651 [base-layout must support an Involvement menu]
    Create a view to render the involvement menu for pillars. The
    menu should only list the officially supported applications.

== QA ==

Bug 403641 [base-layout must support a object action menu]
    Nothing uses this on edge/staging. It works with the product-page
    branch. We can get feedback from engineers who want to use this
    now.

Bug 403651 [base-layout must support an Involvement menu]
    Nothing uses this on edge/staging. It works with the product-page
    branch. We can get feedback from engineers who want to use this
    now.

As for the style changes, the search page will have smaller fonts.
Developers will stop complaining that the fonts looks to big and the
space is wrong.

== 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/browser/launchpad.py
  lib/canonical/launchpad/doc/tales.txt
  lib/canonical/launchpad/icing/style-3-0.css
  lib/canonical/launchpad/images/answers-arrow-right.png
  lib/canonical/launchpad/images/bg-project-downloads.png
  lib/canonical/launchpad/images/blueprints-arrow-right.png
  lib/canonical/launchpad/images/bugs-arrow-right.png
  lib/canonical/launchpad/images/code-arrow-right.png
  lib/canonical/launchpad/images/link-grey-arrow.png
  lib/canonical/launchpad/images/translations-arrow-right.png
  lib/canonical/launchpad/webapp/tales.py
  lib/lp/app/browser/configure.zcml
  lib/lp/app/browser/tests/menu.txt
  lib/lp/app/templates/navigationmenu-actions.pt
  lib/lp/registry/browser/configure.zcml
  lib/lp/registry/browser/pillar.py
  lib/lp/registry/browser/tests/pillar-views.txt
  lib/lp/registry/templates/pillar-involvement-portlet.pt

== Test ==

    * lib/canoni...

Read more...

Revision history for this message
Paul Hummer (rockstar) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/browser/launchpad.py'
2--- lib/canonical/launchpad/browser/launchpad.py 2009-07-29 00:30:50 +0000
3+++ lib/canonical/launchpad/browser/launchpad.py 2009-08-06 14:09:56 +0000
4@@ -173,6 +173,7 @@
5 menu = queryAdapter(self.context, INavigationMenu, name=facet)
6 if menu is not None:
7 self.title = menu.title
8+ self.enabled_links = [link for link in self.links if link.enabled]
9
10 def render(self):
11 if not self.links:
12
13=== modified file 'lib/canonical/launchpad/doc/tales.txt'
14--- lib/canonical/launchpad/doc/tales.txt 2009-08-05 19:34:07 +0000
15+++ lib/canonical/launchpad/doc/tales.txt 2009-08-06 15:14:54 +0000
16@@ -481,6 +481,11 @@
17 http://.../+download/test.txt: test file (4 bytes)
18 http://.../+download/test.txt/+md5:
19
20+The url for the release file can be retrieved using fmt:url.
21+
22+ >>> print test_tales("release_file/fmt:url", release_file=release_file)
23+ http://launchpad.dev/.../+download/test.txt
24+
25
26 === Product series ===
27
28
29=== modified file 'lib/canonical/launchpad/icing/style-3-0.css'
30--- lib/canonical/launchpad/icing/style-3-0.css 2009-08-05 19:34:07 +0000
31+++ lib/canonical/launchpad/icing/style-3-0.css 2009-08-06 15:02:25 +0000
32@@ -183,7 +183,7 @@
33
34 /* Page layout */
35 .yui-d0 {
36- margin: 10px 20px; /* 20px sides are 2.0 backward compatability */
37+ margin: 10px 20px;
38 }
39 .yui-t4 .yui-b {
40 width: 21%;
41@@ -199,6 +199,7 @@
42 padding-top: 0.5em;
43 }
44 .portlet, .aside {
45+ clear: both;
46 border-top: 1px solid #d6d6d6;
47 padding: 0.5em 0;
48 }
49@@ -236,7 +237,8 @@
50 /* Default text presentation */
51 html, body {
52 font-family: bitstream vera sans, arial, helvetica, clean, sans-serif;
53- font-size: 97%; /* 2.0 backward compatability */
54+ font-family: sans-serif;
55+ font-size: 93%;
56 }
57 body.private {
58 /* It must be obvious to the user that the context is private */
59@@ -246,7 +248,6 @@
60 clear: none;
61 padding-top: 6px; /* An offset from the logo. */
62 font-size: 197%;
63- margin-bottom: 0.5em; /* 2.0 backward compatability */
64 }
65 h2 {
66 margin-bottom: 0.3em;
67@@ -270,7 +271,10 @@
68 list-style: decimal outside;
69 }
70 li {
71- padding-bottom: .2em;
72+ padding-bottom: .3em;
73+ }
74+dt {
75+ margin-bottom: .1em;
76 }
77 dd {
78 margin-bottom: .8em;
79@@ -305,13 +309,13 @@
80 }
81 .see-all a {
82 padding-left: 8px;
83- background: url(../images/link-grey-arrow.gif) left center no-repeat;
84+ background: url(/@@/link-grey-arrow.gif) left center no-repeat;
85 font-size: 72%;
86 color: #484848;
87 text-decoration: underline;
88 }
89 .rss-right {
90- background: url(https://launchpad.net/@@/rss.png) right center no-repeat;
91+ background: url(/@@/rss.png) right center no-repeat;
92 }
93 .logo {
94 float: left;
95@@ -321,7 +325,6 @@
96 margin: 0 5px 0 0;
97 }
98 .registered {
99- margin-top: -0.5em; /* 2.0 backward compatability */
100 font-size: 85%;
101 color: #666;
102 font-style: italic;
103@@ -330,15 +333,23 @@
104 clear: both;
105 font-size: 100%;
106 }
107+.summary {
108+ margin: 0 0 1em 0;
109+ font-size: 138.5%;
110+ }
111+
112
113 /* Common list presentations. */
114 .bulleted {
115 margin-bottom: 0.5em;
116 }
117 .bulleted li {
118- margin: auto auto 0.2em 2em;
119+ margin: 0 0 0 2em;
120 list-style-type: disc;
121 }
122+.horizontal {
123+ margin: 1em 0 0 0;
124+ }
125 .horizontal li {
126 display: inline;
127 padding: 0 1.5em 0 0;
128@@ -444,15 +455,13 @@
129 background: #fbfbfb;
130 }
131
132-.downloads {
133- font-weight: bold;
134- }
135 .downloads a {
136 color: #4f843c;
137 }
138 .downloads li {
139 margin: 0;
140 padding: 2px 0 0;
141+ font-weight: bold;
142 }
143 .downloads li a {
144 display: block;
145@@ -461,18 +470,26 @@
146 -moz-border-radius: 1.5%;
147 -webkit-border-radius: 1.5%;
148 border-radius: 1.5%;
149- background: #4f843c url(../images/bg-project-downloads.gif) center right no-repeat;
150+ background: #4f843c url(/@@/bg-project-downloads.png) center right no-repeat;
151 padding: 6%;
152+ padding-right: 50px;
153 color: #fff;
154 font-size: 108%;
155 text-decoration: underline;
156 }
157 .downloads .released {
158- margin: 0 0 0.5em 0;
159- background: #f3f3f3 url(../images/bg-project-released.gif) right bottom no-repeat;
160- padding: 0.4em 0.2em;
161- font-size: 85%;
162- font-weight: normal;
163+ margin: .3em 0 1em 0;
164+ padding: 0 .2em 0 0;
165+ text-align: right;
166+ }
167+.downloads .released span {
168+ -moz-border-radius: 0 0 3% 3%;
169+ -webkit-border-radius: 0 0 5% 5%;
170+ border-radius: 0 0 3% 3%;
171+ background: #d3e3c7;
172+ padding: 0.2em 1em;
173+ }
174+.downloads .alternate {
175 text-align: right;
176 }
177
178@@ -491,27 +508,26 @@
179 }
180 .involvement a.bugs {
181 color: #b9413e;
182- background: url(../images/red-arrow.gif) right center no-repeat;
183+ background: url(/@@/bugs-arrow-right.png) right center no-repeat;
184 }
185 .involvement a.question {
186 color: #5265b2;
187- background: url(../images/blue-arrow.gif) right center no-repeat;
188+ background: url(/@@/answers-arrow-right.png) right center no-repeat;
189 }
190 .involvement a.translate {
191 color: #c5458e;
192- background: url(../images/pink-arrow.gif) right center no-repeat;
193+ background: url(/@@/translations-arrow-right.png) right center no-repeat;
194 }
195 .involvement a.code {
196 color: #d39f57;
197- background: url(../images/yellow-arrow.gif) right center no-repeat;
198+ background: url(/@@/images/code-arrow-right.png) right center no-repeat;
199 }
200 .involvement a.blueprint {
201 color: #5ba4c6;
202- background: url(../images/lightblue-arrow.gif) right center no-repeat;
203+ background: url(/@@/images/blueprints-arrow-right.png) right center no-repeat;
204 }
205
206 .announcements li {
207- font-size: 85%;
208 margin-bottom: 0.5em;
209 }
210 .announcements li strong,
211@@ -525,14 +541,6 @@
212 border-bottom: 1px solid #d0d0d0;
213 }
214
215-
216-/* Should not be needed with real data */
217-.timeline {
218- width: 100%;
219- color: #484848;
220- background: #f7f7f7;
221- }
222-
223 /* From nice_pre in tales.py */
224 pre.wrap {
225 white-space: -moz-pre-wrap;
226@@ -540,3 +548,4 @@
227 white-space: pre-wrap;
228 word-wrap: break-word;
229 }
230+
231
232=== added file 'lib/canonical/launchpad/images/answers-arrow-right.png'
233Binary files lib/canonical/launchpad/images/answers-arrow-right.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/answers-arrow-right.png 2009-08-06 14:09:56 +0000 differ
234=== added file 'lib/canonical/launchpad/images/bg-project-downloads.png'
235Binary files lib/canonical/launchpad/images/bg-project-downloads.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bg-project-downloads.png 2009-08-06 14:09:56 +0000 differ
236=== added file 'lib/canonical/launchpad/images/blueprints-arrow-right.png'
237Binary files lib/canonical/launchpad/images/blueprints-arrow-right.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/blueprints-arrow-right.png 2009-08-06 14:09:56 +0000 differ
238=== added file 'lib/canonical/launchpad/images/bugs-arrow-right.png'
239Binary files lib/canonical/launchpad/images/bugs-arrow-right.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bugs-arrow-right.png 2009-08-06 14:09:56 +0000 differ
240=== added file 'lib/canonical/launchpad/images/code-arrow-right.png'
241Binary files lib/canonical/launchpad/images/code-arrow-right.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/code-arrow-right.png 2009-08-06 14:09:56 +0000 differ
242=== added file 'lib/canonical/launchpad/images/link-grey-arrow.png'
243Binary files lib/canonical/launchpad/images/link-grey-arrow.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/link-grey-arrow.png 2009-08-06 14:09:56 +0000 differ
244=== added file 'lib/canonical/launchpad/images/translations-arrow-right.png'
245Binary files lib/canonical/launchpad/images/translations-arrow-right.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/translations-arrow-right.png 2009-08-06 14:09:56 +0000 differ
246=== modified file 'lib/canonical/launchpad/webapp/tales.py'
247--- lib/canonical/launchpad/webapp/tales.py 2009-08-05 19:34:07 +0000
248+++ lib/canonical/launchpad/webapp/tales.py 2009-08-06 14:09:56 +0000
249@@ -1190,7 +1190,7 @@
250 class ProductReleaseFileFormatterAPI(ObjectFormatterAPI):
251 """Adapter for `IProductReleaseFile` objects to a formatted string."""
252
253- traversable_names = {'link': 'link'}
254+ traversable_names = {'link': 'link', 'url': 'url'}
255
256 def link(self, view_name):
257 """A hyperlinked ProductReleaseFile.
258@@ -1226,6 +1226,10 @@
259 html += ')'
260 return html % replacements
261
262+ def url(self, view_name):
263+ """Return the URL to download the file."""
264+ return self._getDownloadURL(self._context.libraryfile)
265+
266 @property
267 def _release(self):
268 return self._context.productrelease
269
270=== modified file 'lib/lp/app/browser/configure.zcml'
271--- lib/lp/app/browser/configure.zcml 2009-07-31 17:14:35 +0000
272+++ lib/lp/app/browser/configure.zcml 2009-08-06 14:11:31 +0000
273@@ -21,4 +21,11 @@
274 template="../templates/navigationmenu-related-pages.pt"
275 permission="zope.Public"
276 />
277+ <browser:page
278+ for="*"
279+ name="+global-actions"
280+ class="canonical.launchpad.browser.launchpad.NavigationMenuTabs"
281+ template="../templates/navigationmenu-actions.pt"
282+ permission="zope.Public"
283+ />
284 </configure>
285
286=== modified file 'lib/lp/app/browser/tests/menu.txt'
287--- lib/lp/app/browser/tests/menu.txt 2009-07-31 13:25:26 +0000
288+++ lib/lp/app/browser/tests/menu.txt 2009-08-06 15:02:25 +0000
289@@ -43,6 +43,10 @@
290 >>> provideAdapter(
291 ... EditMenu, [IEditMenuMarker], INavigationMenu, name="overview")
292
293+
294+Related pages
295+-------------
296+
297 The related pages portlet is rendered using a TALES call passing the view
298 to the named adapter: <tal:menu replace="structure view/@@+related-pages" />
299
300@@ -113,3 +117,45 @@
301 </div>
302
303
304+Action menus
305+------------
306+
307+A navigation menu can be presented as an action menu is the side portlets.
308+The action menu uses the view's enabled_links property to get the list of
309+links.
310+
311+ >>> menu_view = create_initialized_view(
312+ ... view, '+global-actions', principal=user)
313+ >>> for link in menu_view.enabled_links:
314+ ... print link.enabled, link.linked, link.url
315+ True False http://launchpad.dev/~beaker/+edit
316+ True True http://launchpad.dev/~beaker/+edit-people
317+
318+The generated markup is for a portlet with the global-actions id.
319+
320+ >>> print menu_view.render()
321+ <div id="global-actions" class="portlet">
322+ <ul>
323+ <li>
324+ <a href="..."
325+ class="menu-link-edit_thing sprite modify edit">Edit thing</a>
326+ </li>
327+ <li>
328+ <a style="..." href="..."
329+ class="menu-link-edit_people">Edit people related to thing</a>
330+ </li>
331+ </ul>
332+ </div>
333+
334+If there are no enabled links, no markup is rendered. For example, a menu
335+may contain links that require special privileges to access.
336+
337+ >>> EditMenu.links = ('admin',)
338+
339+ >>> menu_view = create_initialized_view(
340+ ... view, '+global-actions', principal=user)
341+ >>> menu_view.enabled_links
342+ []
343+
344+ >>> print menu_view.render()
345+ <BLANKLINE>
346
347=== added file 'lib/lp/app/templates/navigationmenu-actions.pt'
348--- lib/lp/app/templates/navigationmenu-actions.pt 1970-01-01 00:00:00 +0000
349+++ lib/lp/app/templates/navigationmenu-actions.pt 2009-08-06 14:11:31 +0000
350@@ -0,0 +1,11 @@
351+<div
352+ xmlns:tal="http://xml.zope.org/namespaces/tal"
353+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
354+ id="global-actions" class="portlet"
355+ tal:condition="view/enabled_links">
356+ <ul>
357+ <li tal:repeat="link view/enabled_links">
358+ <a tal:replace="structure link/fmt:icon-link" />
359+ </li>
360+ </ul>
361+</div>
362
363=== modified file 'lib/lp/registry/browser/configure.zcml'
364--- lib/lp/registry/browser/configure.zcml 2009-08-03 14:38:55 +0000
365+++ lib/lp/registry/browser/configure.zcml 2009-08-06 18:17:49 +0000
366@@ -556,6 +556,13 @@
367 name="+listing-simple"
368 permission="zope.Public"
369 template="../templates/pillar-listing-simple.pt"/>
370+ <browser:page
371+ name="+get-involved"
372+ for="lp.registry.interfaces.pillar.IPillar"
373+ class="lp.registry.browser.pillar.PillarView"
374+ facet="overview"
375+ permission="zope.Public"
376+ template="../templates/pillar-involvement-portlet.pt"/>
377 <facet
378 facet="overview">
379 <browser:url
380
381=== added file 'lib/lp/registry/browser/pillar.py'
382--- lib/lp/registry/browser/pillar.py 1970-01-01 00:00:00 +0000
383+++ lib/lp/registry/browser/pillar.py 2009-08-06 18:17:49 +0000
384@@ -0,0 +1,26 @@
385+# Copyright 2009 Canonical Ltd. This software is licensed under the
386+# GNU Affero General Public License version 3 (see the file LICENSE).
387+
388+"""Common views for objects that implement `IPillar`."""
389+
390+__metaclass__ = type
391+
392+__all__ = [
393+ 'PillarView',
394+ ]
395+
396+
397+from canonical.launchpad.webapp.publisher import LaunchpadView
398+
399+
400+class PillarView(LaunchpadView):
401+ """A view for any `IPillar`."""
402+
403+ @property
404+ def has_involvement(self):
405+ """This `IPillar` uses Launchpad."""
406+ pillar = self.context
407+ return (
408+ pillar.official_codehosting or pillar.official_malone
409+ or pillar.official_answers or pillar.official_blueprints
410+ or pillar.official_rosetta)
411
412=== added file 'lib/lp/registry/browser/tests/pillar-views.txt'
413--- lib/lp/registry/browser/tests/pillar-views.txt 1970-01-01 00:00:00 +0000
414+++ lib/lp/registry/browser/tests/pillar-views.txt 2009-08-06 18:17:49 +0000
415@@ -0,0 +1,45 @@
416+Pillar views
417+============
418+
419+Pillar views are used to display IPillar objects link distributions and
420+products in a consistent fashion.
421+
422+The +get-involved presentation creates a portlet of links to encourage
423+project involvement. Only links to official applications are rendered.
424+
425+
426+ >>> distribution = factory.makeDistribution(name='umbra')
427+ >>> login_person(distribution.owner)
428+ >>> view = create_view(
429+ ... distribution, '+get-involved', principal=distribution.owner)
430+
431+The has_involvement property is used to determine if the portlet should
432+be rendered. The newly created pillar does not use any launchpad applications.
433+
434+ >>> view.has_involvement
435+ False
436+
437+ >>> print view.render()
438+ <BLANKLINE>
439+
440+Pillars that do use launchpad applications have an involvement menu.
441+
442+ >>> distribution.official_answers = True
443+ >>> distribution.official_malone = True
444+ >>> view = create_view(
445+ ... distribution, '+get-involved', principal=distribution.owner)
446+ >>> view.has_involvement
447+ True
448+
449+ >>> print view.render()
450+ <div id="involvement" class="portlet involvement">
451+ <h2>Get Involved</h2>
452+ <ul>
453+ <li>
454+ <a class="bugs" href="...">Report a Bug</a>
455+ </li>
456+ <li>
457+ <a class="question" href="...">Ask a question</a>
458+ </li>
459+ </ul>
460+ </div>
461
462=== added file 'lib/lp/registry/templates/pillar-involvement-portlet.pt'
463--- lib/lp/registry/templates/pillar-involvement-portlet.pt 1970-01-01 00:00:00 +0000
464+++ lib/lp/registry/templates/pillar-involvement-portlet.pt 2009-08-06 18:17:49 +0000
465@@ -0,0 +1,30 @@
466+<div
467+ xmlns:tal="http://xml.zope.org/namespaces/tal"
468+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
469+ id="involvement" class="portlet involvement"
470+ tal:condition="view/has_involvement">
471+ <h2>Get Involved</h2>
472+
473+ <ul>
474+ <li tal:condition="context/official_malone">
475+ <a class="bugs"
476+ tal:attributes="href context/menu:bugs/filebug/url">Report a Bug</a>
477+ </li>
478+ <li tal:condition="context/official_answers">
479+ <a class="question"
480+ tal:attributes="href context/menu:answers/new/url">Ask a question</a>
481+ </li>
482+ <li tal:condition="context/official_rosetta">
483+ <a class="translate"
484+ tal:attributes="href context/menu:translations/overview/url">Help translate</a>
485+ </li>
486+ <li tal:condition="context/official_codehosting">
487+ <a class="code"
488+ tal:attributes="href context/menu:branches/branch_add/url">Submit code</a>
489+ </li>
490+ <li tal:condition="context/official_blueprints">
491+ <a class="blueprint"
492+ tal:attributes="href context/menu:specications/new/url">Register a Blueprint</a>
493+ </li>
494+ </ul>
495+</div>
496