Merge lp:~sinzui/launchpad/base-layout-bug-407052 into lp:launchpad

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

This is my branch to address two base-layout issues.

    lp:~sinzui/launchpad/base-layout-bug-407052
    Diff size: 226
    Launchpad bug: https://bugs.launchpad.net/bugs/407052
                   https://bugs.launchpad.net/bugs/407055
    Test command: ./bin/test -vvt "lp/app/.*/(base-layout|menu)"
    Pre-implementation: no one.
    Target release: 2.2.8

= Two base-layout issues =

Bug #407052 base-layout heading slot breaks the title edit widget

    The title edit widget creates it's own <h1> with an id and class. When
    used with the heading slot a <h1> is generated inside the <h1>.

    The solution is that the <h1> only be created when base-layout is
    automatically creating a title for the context object.

Bug #407055 NavigationMenu is still needed but not supported by base-layout

    While working on the project edit pages in an experiment to move
    them to the 3.0 layout. I realised I had dismantled the edit
    NavigationMenu, then added a section to the page that had the same
    links. We should be reusing the navigation menus

    Since many pages need to have a releated pages section with a list
    of links, there should be a view that can be called to make the
    standard markup. This will enforce consistency, reduce the amount of
    typing, and allow us to *not* remove the NavigationMenu from views.

== Rules ==

Bug #407052 base-layout heading slot breaks the title edit widget

    Make the template pass the <h1> to the slot. If the slot is not
    called by the template, base-layout makes a <h1> using the
    context.title

Bug #407055 NavigationMenu is still needed but not supported by base-layout

    There is already a view for the NavigationMenu. We need a new
    template that renders the portlet, heading, and list. This structure
    is the same as the one used on series and milestones now.

== QA ==

Bug #407052 base-layout heading slot breaks the title edit widget

    No page has encountered this yet on edge staging. This fix can only
    verified by a page in development (namely the product-index.pt) that
    I am working on.

Bug #407055 NavigationMenu is still needed but not supported by base-layout

    No page uses this yet, but I have branch that does.

== Lint ==

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/app/browser/configure.zcml
  lib/lp/app/browser/tests/base-layout.txt
  lib/lp/app/browser/tests/menu.txt
  lib/lp/app/browser/tests/testfiles/main-only.pt
  lib/lp/app/browser/tests/testfiles/main-side.pt
  lib/lp/app/browser/tests/testfiles/searchless.pt
  lib/lp/app/templates/base-layout.pt
  lib/lp/app/templates/navigationmenu-related-pages.pt

== Test ==

    * lib/lp/app/browser/tests/menu.txt
      * Created a test to verify the behaviour of the related pages template.
    * lib/lp/app/browser/tests/base-layout.txt
      * I Updated the narative to explain that the <h1> must be passed by
        the callsite.
    * lib/lp/app/browser/tests/testfiles/main-only.pt
    * lib/lp/app/browser/tests/testfiles/main-side.pt
    * lib/lp/app/browser/tests/testfiles/search...

Read more...

Revision history for this message
Abel Deuring (adeuring) wrote :
Download full text (3.4 KiB)

Hi Curtis,

A nice branch; thanks for the detailed cover letter!

I have only a few cosmetic remarks, see below.

> === added file 'lib/lp/app/browser/tests/menu.txt'
> --- lib/lp/app/browser/tests/menu.txt 1970-01-01 00:00:00 +0000
> +++ lib/lp/app/browser/tests/menu.txt 2009-07-31 02:42:44 +0000
> @@ -0,0 +1,114 @@
> +====================
> +NavigationMenu views
> +====================
> +
> +Navigation menus are used to create links to related pages. The menus
> +are usually bound to a view.
> +
> + >>> from zope.component import provideAdapter
> + >>> from zope.interface import implements, Interface
> + >>> from canonical.launchpad.webapp.interfaces import INavigationMenu
> + >>> from canonical.launchpad.webapp.menu import (
> + ... enabled_with_permission, Link, NavigationMenu)
> + >>> from canonical.launchpad.webapp.publisher import LaunchpadView
> + >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
> +
> + >>> class IEditMenuMarker(Interface):
> + ... """A marker for a menu and a view."""
> +
> + >>> class EditMenu(NavigationMenu):
> + ... """A simple menu."""
> + ... usedfor = IEditMenuMarker
> + ... facet = 'overview'
> + ... title = 'Related pages'
> + ... links = ('edit_thing', 'edit_people', 'admin')
> + ...
> + ... def edit_thing(self):
> + ... return Link('+edit', 'Edit thing', icon='edit')
> + ...
> + ... def edit_people(self):
> + ... return Link('+edit-people', 'Edit people related to thing')
> + ...
> + ... @enabled_with_permission('launchpad.Admin')
> + ... def admin(self):
> + ... return Link('+admin', 'Administer this user')
> +
> + >>> class EditView(LaunchpadView):
> + ... """A simple view."""
> + ... implements(IEditMenuMarker)
> + ... # A hack that reveals secret of how facet work.
> + ... __launchpad_facetname__ = 'overview'
> +
> + # Menu's are normally registered using the menu ZCML directive.

s/Menu's/Menus/

> + >>> provideAdapter(
> + ... EditMenu, [IEditMenuMarker], INavigationMenu, name="overview")
> +
> +The related pages portlet is rendered using a TALES call passing the view
> +to the named adapter: <tal:menu replace="structure view/@@+related-pages" />
> +
> + >>> user = factory.makePerson(name='beaker')
> + >>> view = EditView(user, LaunchpadTestRequest())
> + >>> menu_view = create_initialized_view(
> + ... view, '+related-pages', principal=user)
> + >>> print menu_view.template.filename
> + /.../navigationmenu-related-pages.pt
> +
> +The view provides access the menu's title and links. The both enabled
> +and disabled links are included.

s/The both/Both the/

> +
> + >>> print menu_view.title
> + Related pages
> + >>> for link in menu_view.links:
> + ... print link.enabled, link.url
> + True http://launchpad.dev/~beaker/+edit
> + True http://launchpad.dev/~beaker/+edit-people
> + False http://launchpad.dev/~beaker/+admin
> +
> +The view renders the a heading using the menu title and a list of the
> +enabled and linked links. The template uses t...

Read more...

review: Approve
Revision history for this message
Curtis Hovey (sinzui) wrote :
Download full text (4.5 KiB)

Thanks for the review Abel.

On Fri, 2009-07-31 at 11:33 +0000, Abel Deuring wrote:
> Review: Approve
> Hi Curtis,
>
> A nice branch; thanks for the detailed cover letter!
>
> I have only a few cosmetic remarks, see below.
>
>
> > === added file 'lib/lp/app/browser/tests/menu.txt'
> > --- lib/lp/app/browser/tests/menu.txt 1970-01-01 00:00:00 +0000
> > +++ lib/lp/app/browser/tests/menu.txt 2009-07-31 02:42:44 +0000

...

> > + >>> class EditView(LaunchpadView):
> > + ... """A simple view."""
> > + ... implements(IEditMenuMarker)
> > + ... # A hack that reveals secret of how facet work.

Fixed facet, should be factets

> > + ... __launchpad_facetname__ = 'overview'
> > +
> > + # Menu's are normally registered using the menu ZCML directive.
>
> s/Menu's/Menus/

fixed

> > + >>> provideAdapter(
> > + ... EditMenu, [IEditMenuMarker], INavigationMenu, name="overview")
> > +
> > +The related pages portlet is rendered using a TALES call passing the view
> > +to the named adapter: <tal:menu replace="structure view/@@+related-pages" />
> > +
> > + >>> user = factory.makePerson(name='beaker')
> > + >>> view = EditView(user, LaunchpadTestRequest())
> > + >>> menu_view = create_initialized_view(
> > + ... view, '+related-pages', principal=user)
> > + >>> print menu_view.template.filename
> > + /.../navigationmenu-related-pages.pt
> > +
> > +The view provides access the menu's title and links. The both enabled
> > +and disabled links are included.
>
> s/The both/Both the/

Fixed

> > +
> > + >>> print menu_view.title
> > + Related pages
> > + >>> for link in menu_view.links:
> > + ... print link.enabled, link.url
> > + True http://launchpad.dev/~beaker/+edit
> > + True http://launchpad.dev/~beaker/+edit-people
> > + False http://launchpad.dev/~beaker/+admin
> > +
> > +The view renders the a heading using the menu title and a list of the
> > +enabled and linked links. The template uses the inline-link rules, if the
> > +link has a icon, the classes are set, otherwise a style attribute is used.
>
> s/renders the a/renders the/
> s/a icon/an icon/
>
> "linked links" sounds a bit odd to me ;) What about "list of enabled links"?

The links `enabled` and `linked` properties should not be confused. The
former is about permission, the later is about relevance.

This is the diff of my changes:

=== modified file 'lib/lp/app/browser/tests/menu.txt'
--- lib/lp/app/browser/tests/menu.txt 2009-07-31 02:42:44 +0000
+++ lib/lp/app/browser/tests/menu.txt 2009-07-31 13:23:20 +0000
@@ -36,10 +36,10 @@
     >>> class EditView(LaunchpadView):
     ... """A simple view."""
     ... implements(IEditMenuMarker)
- ... # A hack that reveals secret of how facet work.
+ ... # A hack that reveals secret of how facets work.
     ... __launchpad_facetname__ = 'overview'

- # Menu's are normally registered using the menu ZCML directive.
+ # Menus are normally registered using the menu ZCML directive.
     >>> provideAdapter(
     ... EditMenu, [IEditMenuMarker], INavigationMenu, name="overview")

@@ -53,7 +53,7 @@
     >>> print menu_view.template...

Read more...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/browser/configure.zcml'
2--- lib/lp/app/browser/configure.zcml 2009-07-17 02:25:09 +0000
3+++ lib/lp/app/browser/configure.zcml 2009-07-31 02:42:44 +0000
4@@ -14,4 +14,11 @@
5 template="../templates/base-layout-macros.pt"
6 permission="zope.Public"
7 />
8+ <browser:page
9+ for="*"
10+ name="+related-pages"
11+ class="canonical.launchpad.browser.launchpad.NavigationMenuTabs"
12+ template="../templates/navigationmenu-related-pages.pt"
13+ permission="zope.Public"
14+ />
15 </configure>
16
17=== modified file 'lib/lp/app/browser/tests/base-layout.txt'
18--- lib/lp/app/browser/tests/base-layout.txt 2009-07-27 20:57:01 +0000
19+++ lib/lp/app/browser/tests/base-layout.txt 2009-07-30 20:38:50 +0000
20@@ -237,7 +237,8 @@
21 >>> print content.h1
22 <h1>Heading</h1>
23
24-The page heading slot's default behaviour is to use the context.title.
25+The page heading slot's default behaviour is to use the context.title and
26+place it in a <h1>.
27
28 >>> class DefaultHeadingView(LaunchpadView):
29 ... """A simple view to test base-layout."""
30
31=== added file 'lib/lp/app/browser/tests/menu.txt'
32--- lib/lp/app/browser/tests/menu.txt 1970-01-01 00:00:00 +0000
33+++ lib/lp/app/browser/tests/menu.txt 2009-07-31 02:42:44 +0000
34@@ -0,0 +1,114 @@
35+====================
36+NavigationMenu views
37+====================
38+
39+Navigation menus are used to create links to related pages. The menus
40+are usually bound to a view.
41+
42+ >>> from zope.component import provideAdapter
43+ >>> from zope.interface import implements, Interface
44+ >>> from canonical.launchpad.webapp.interfaces import INavigationMenu
45+ >>> from canonical.launchpad.webapp.menu import (
46+ ... enabled_with_permission, Link, NavigationMenu)
47+ >>> from canonical.launchpad.webapp.publisher import LaunchpadView
48+ >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
49+
50+ >>> class IEditMenuMarker(Interface):
51+ ... """A marker for a menu and a view."""
52+
53+ >>> class EditMenu(NavigationMenu):
54+ ... """A simple menu."""
55+ ... usedfor = IEditMenuMarker
56+ ... facet = 'overview'
57+ ... title = 'Related pages'
58+ ... links = ('edit_thing', 'edit_people', 'admin')
59+ ...
60+ ... def edit_thing(self):
61+ ... return Link('+edit', 'Edit thing', icon='edit')
62+ ...
63+ ... def edit_people(self):
64+ ... return Link('+edit-people', 'Edit people related to thing')
65+ ...
66+ ... @enabled_with_permission('launchpad.Admin')
67+ ... def admin(self):
68+ ... return Link('+admin', 'Administer this user')
69+
70+ >>> class EditView(LaunchpadView):
71+ ... """A simple view."""
72+ ... implements(IEditMenuMarker)
73+ ... # A hack that reveals secret of how facet work.
74+ ... __launchpad_facetname__ = 'overview'
75+
76+ # Menu's are normally registered using the menu ZCML directive.
77+ >>> provideAdapter(
78+ ... EditMenu, [IEditMenuMarker], INavigationMenu, name="overview")
79+
80+The related pages portlet is rendered using a TALES call passing the view
81+to the named adapter: <tal:menu replace="structure view/@@+related-pages" />
82+
83+ >>> user = factory.makePerson(name='beaker')
84+ >>> view = EditView(user, LaunchpadTestRequest())
85+ >>> menu_view = create_initialized_view(
86+ ... view, '+related-pages', principal=user)
87+ >>> print menu_view.template.filename
88+ /.../navigationmenu-related-pages.pt
89+
90+The view provides access the menu's title and links. The both enabled
91+and disabled links are included.
92+
93+ >>> print menu_view.title
94+ Related pages
95+ >>> for link in menu_view.links:
96+ ... print link.enabled, link.url
97+ True http://launchpad.dev/~beaker/+edit
98+ True http://launchpad.dev/~beaker/+edit-people
99+ False http://launchpad.dev/~beaker/+admin
100+
101+The view renders the a heading using the menu title and a list of the
102+enabled and linked links. The template uses the inline-link rules, if the
103+link has a icon, the classes are set, otherwise a style attribute is used.
104+
105+ >>> print menu_view.render()
106+ <div id="related-pages" class="portlet">
107+ <h2>Related pages</h2>
108+ <ul>
109+ <li>
110+ <a href="..."
111+ class="menu-link-edit_thing sprite modify edit">Edit thing</a>
112+ </li>
113+ <li>
114+ <a style="..." href="..."
115+ class="menu-link-edit_people">Edit people related to thing</a>
116+ </li>
117+ </ul>
118+ </div>
119+
120+The link is not displayed if the link matches the requested URL (not linked).
121+The page will not render a link to itself.
122+
123+ >>> request = LaunchpadTestRequest(
124+ ... SERVER_URL='http://launchpad.dev/~beaker/+edit')
125+ >>> print request.getURL()
126+ http://launchpad.dev/~beaker/+edit
127+
128+ >>> view = EditView(user, request)
129+ >>> menu_view = create_initialized_view(
130+ ... view, '+related-pages', principal=user)
131+ >>> for link in menu_view.links:
132+ ... print link.enabled, link.linked, link.url
133+ True False http://launchpad.dev/~beaker/+edit
134+ True True http://launchpad.dev/~beaker/+edit-people
135+ False True http://launchpad.dev/~beaker/+admin
136+
137+ >>> print menu_view.render()
138+ <div id="related-pages" class="portlet">
139+ <h2>Related pages</h2>
140+ <ul>
141+ <li>
142+ <a style="..." href="..."
143+ class="menu-link-edit_people">Edit people related to thing</a>
144+ </li>
145+ </ul>
146+ </div>
147+
148+
149
150=== modified file 'lib/lp/app/browser/tests/testfiles/main-only.pt'
151--- lib/lp/app/browser/tests/testfiles/main-only.pt 2009-07-24 04:26:14 +0000
152+++ lib/lp/app/browser/tests/testfiles/main-only.pt 2009-07-30 20:38:50 +0000
153@@ -12,7 +12,7 @@
154 </head>
155
156 <body>
157- <tal:heading metal:fill-slot="heading">Heading</tal:heading>
158+ <tal:heading metal:fill-slot="heading"><h1>Heading</h1></tal:heading>
159 <tal:main metal:fill-slot="main">
160 <div class="top-portlet">
161 <p class="registered">
162
163=== modified file 'lib/lp/app/browser/tests/testfiles/main-side.pt'
164--- lib/lp/app/browser/tests/testfiles/main-side.pt 2009-07-24 04:26:14 +0000
165+++ lib/lp/app/browser/tests/testfiles/main-side.pt 2009-07-30 20:38:50 +0000
166@@ -12,7 +12,7 @@
167 </head>
168
169 <body>
170- <tal:heading metal:fill-slot="heading">Heading</tal:heading>
171+ <tal:heading metal:fill-slot="heading"><h1>Heading</h1></tal:heading>
172 <tal:main metal:fill-slot="main">
173 <div class="top-portlet">
174 <p class="registered">
175
176=== modified file 'lib/lp/app/browser/tests/testfiles/searchless.pt'
177--- lib/lp/app/browser/tests/testfiles/searchless.pt 2009-07-24 04:26:14 +0000
178+++ lib/lp/app/browser/tests/testfiles/searchless.pt 2009-07-30 20:38:50 +0000
179@@ -12,7 +12,7 @@
180 </head>
181
182 <body>
183- <tal:heading metal:fill-slot="heading">Heading</tal:heading>
184+ <tal:heading metal:fill-slot="heading"><h1>Heading</h1></tal:heading>
185 <tal:main metal:fill-slot="main">
186 <div class="top-portlet">
187 <p class="registered">
188
189=== modified file 'lib/lp/app/templates/base-layout.pt'
190--- lib/lp/app/templates/base-layout.pt 2009-07-27 20:57:01 +0000
191+++ lib/lp/app/templates/base-layout.pt 2009-07-30 20:38:50 +0000
192@@ -137,10 +137,10 @@
193 xml:lang view/lang|default_language|default;
194 dir view/dir|string:ltr">
195 <tal:location condition="view/macro:pagehas/applicationtabs">
196- <h1><tal:heading
197+ <h1
198 tal:condition="context/title|nothing"
199 tal:content="context/title"
200- metal:define-slot="heading" /></h1>
201+ metal:define-slot="heading" />
202 <tal:breadcrumbs>
203 <!-- future breadcrumb rule -->
204 </tal:breadcrumbs>
205
206=== added file 'lib/lp/app/templates/navigationmenu-related-pages.pt'
207--- lib/lp/app/templates/navigationmenu-related-pages.pt 1970-01-01 00:00:00 +0000
208+++ lib/lp/app/templates/navigationmenu-related-pages.pt 2009-07-31 02:42:44 +0000
209@@ -0,0 +1,16 @@
210+<div
211+ xmlns:tal="http://xml.zope.org/namespaces/tal"
212+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
213+ id="related-pages" class="portlet">
214+ <h2 tal:content="view/title">Menu title</h2>
215+
216+ <ul>
217+ <tal:link tal:repeat="link view/links">
218+ <tal:enabled tal:condition="link/enabled">
219+ <li tal:condition="link/linked">
220+ <a tal:replace="structure link/fmt:icon-link" />
221+ </li>
222+ </tal:enabled>
223+ </tal:link>
224+ </ul>
225+</div>
226