Merge lp:~sinzui/launchpad/announcement-heading into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~sinzui/launchpad/announcement-heading
Merge into: lp:launchpad
Diff against target: 345 lines
7 files modified
lib/canonical/launchpad/pagetitles.py (+0/-17)
lib/lp/registry/browser/announcement.py (+15/-8)
lib/lp/registry/browser/configure.zcml (+6/-1)
lib/lp/registry/browser/tests/announcement-views.txt (+47/-0)
lib/lp/registry/stories/announcements/xx-announcements.txt (+10/-10)
lib/lp/registry/templates/announcement-index.pt (+0/-4)
lib/lp/testing/views.py (+8/-6)
To merge this branch: bzr merge lp:~sinzui/launchpad/announcement-heading
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+12898@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

This is my branch to fix announcement headings.

    lp:~sinzui/launchpad/announcement-heading
    Diff size: 346
    Launchpad bug: https://bugs.launchpad.net/bugs/434851
    Test command: ./bin/test -vvt "announcement-views|xx-announcements"
    Pre-implementation: barry
    Target release: 3.1.10

= Fix announcement headings =

Fix for bug 297877 is not working as expected. None of these urls have the
announcement name visible anywhere on the page:
 - https://launchpad.dev/mozilla/+announcement/22/+retarget
 - https://launchpad.dev/mozilla/+announcement/22/+publish
 - https://launchpad.dev/mozilla/+announcement/22/+delete

== Rules ==

These were not fully converted.
    * Register TitleBreadcrumb for IAnnouncement to make the breadcrumbs
      render.
    * Move pagetitles into the view.
    * Update the announcement view mixin to provide a sane label and
      add a simple page_title to each view.

== QA ==

    * Visit https://edge.launchpad.net/launchpad-project/+announcement/3451
      * Verify it had breadcrumbs that list pillar
      * Verify the title contains the announcement title
    * Choose Modify
      * Verify it had breadcrumbs that list pillar
      * Verify the title contains the announcement title
    * Visit https://edge.launchpad.net/+announcements
      * Verify its title is
        Announcements from all projects hosted in Launchpad

== Lint ==

Linting changed files:
  lib/canonical/launchpad/pagetitles.py
  lib/lp/registry/browser/announcement.py
  lib/lp/registry/browser/configure.zcml
  lib/lp/registry/browser/tests/announcement-views.txt
  lib/lp/registry/stories/announcements/xx-announcements.txt
  lib/lp/registry/templates/announcement-index.pt
  lib/lp/testing/views.py

== Test ==

    * lib/lp/registry/browser/tests/announcement-views.txt
      * Added a new module to test that the announcement generates
        expect breadcrumbs (and titles).
      * Added a test for the menus because I wanted to make sure they
        worked since I saw errors as I was making changes. They were
        fine, my code was not.
    * lib/lp/registry/stories/announcements/xx-announcements.txt
      * Updated the tests to verify the page title changes.

== Implementation ==

    * lib/canonical/launchpad/pagetitles.py
      * Removed the old page titles.
    * lib/lp/registry/browser/announcement.py
      * Updated the Announcement mixing to provide consistent heading, added
        unique page titles for that last breadcrumb.
    * lib/lp/registry/browser/configure.zcml
      * Corrected IHasAnnouncements +announcements. It was using the Set view
        instead of the HasAnnouncementsView view. The Set view extends the
        HasAnnouncementsView and its page_title broke the tests, I realised
        the broken tests were only testing IHasAnnouncements objects.
      * Registered TitleBreadcrumb as an adapter for IAnnouncement.
    * lib/lp/registry/templates/announcement-index.pt
      * Removed the heading because the that is now managed by the header
        macros and tales.
    * lib/lp/testing/views.py
      * Updated create_view and create_initialized_view to allow me to
        pass a fake request for testing.

Revision history for this message
Brad Crittenden (bac) wrote :

Very nice fix, Curtis.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/pagetitles.py'
2--- lib/canonical/launchpad/pagetitles.py 2009-09-28 13:56:17 +0000
3+++ lib/canonical/launchpad/pagetitles.py 2009-10-06 02:10:27 +0000
4@@ -487,23 +487,6 @@
5
6 milestone_delete = ContextTitle('Delete %s')
7
8-announcement_add = 'Make an announcement'
9-
10-announcement_delete = 'Permanently delete this announcement'
11-
12-announcement_edit = 'Modify this announcement'
13-
14-def announcement_index(context, view):
15- return '%s announcement' % context.target.displayname
16-
17-announcement_publish = 'Publish this announcement'
18-
19-announcement_retarget = 'Move this announcement to a different project'
20-
21-announcement_retract = 'Retract this announcement'
22-
23-announcements_all = 'Announcements from all projects hosted in Launchpad'
24-
25 oauth_authorize = 'Authorize application to access Launchpad on your behalf'
26
27 def object_driver(context, view):
28
29=== modified file 'lib/lp/registry/browser/announcement.py'
30--- lib/lp/registry/browser/announcement.py 2009-09-10 20:49:25 +0000
31+++ lib/lp/registry/browser/announcement.py 2009-10-06 02:10:27 +0000
32@@ -102,9 +102,8 @@
33 """A mixin to provide the common form features."""
34
35 @property
36- def page_title(self):
37- """The html page title."""
38- return self.label
39+ def label(self):
40+ return self.context.title
41
42 @property
43 def cancel_url(self):
44@@ -127,6 +126,7 @@
45
46 schema = AddAnnouncementForm
47 label = "Make an announcement"
48+ page_title = label
49
50 custom_widget('publication_date', AnnouncementDateWidget)
51
52@@ -157,7 +157,7 @@
53
54 schema = AddAnnouncementForm
55 field_names = ['title', 'summary', 'url', ]
56- label = _('Modify this announcement')
57+ page_title = 'Modify announcement'
58
59 @property
60 def initial_values(self):
61@@ -189,7 +189,7 @@
62
63 schema = AnnouncementRetargetForm
64 field_names = ['target']
65- label = _('Move this announcement to a different project')
66+ page_title = 'Move announcement'
67
68 def validate(self, data):
69 """Ensure that the person can publish announcement at the new
70@@ -224,7 +224,7 @@
71
72 schema = AddAnnouncementForm
73 field_names = ['publication_date']
74- label = _('Publish this announcement')
75+ page_title = 'Publish announcement'
76
77 custom_widget('publication_date', AnnouncementDateWidget)
78
79@@ -239,7 +239,7 @@
80 """A view to unpublish an announcement."""
81
82 schema = IAnnouncement
83- label = _('Retract this announcement')
84+ page_title = 'Retract announcement'
85
86 @action(_('Retract'), name='retract')
87 def retract_action(self, action, data):
88@@ -251,7 +251,7 @@
89 """A view to delete an annoucement."""
90
91 schema = IAnnouncement
92- label = _('Delete this announcement')
93+ page_title = 'Delete announcement'
94
95 @action(_("Delete"), name="delete", validator='validate_cancel')
96 def action_delete(self, action, data):
97@@ -309,6 +309,13 @@
98 RootAnnouncementsFeedLink,
99 )
100
101+ page_title = 'Announcements from all projects hosted in Launchpad'
102+ label = page_title
103+
104
105 class AnnouncementView(LaunchpadView):
106 """A view class for a single announcement."""
107+
108+ @property
109+ def label(self):
110+ return self.context.title
111
112=== modified file 'lib/lp/registry/browser/configure.zcml'
113--- lib/lp/registry/browser/configure.zcml 2009-09-22 13:11:13 +0000
114+++ lib/lp/registry/browser/configure.zcml 2009-10-06 02:10:27 +0000
115@@ -689,7 +689,7 @@
116 template="../templates/announcement-delete.pt"/>
117 <browser:pages
118 for="lp.registry.interfaces.announcement.IHasAnnouncements"
119- class="lp.registry.browser.announcement.AnnouncementSetView"
120+ class="lp.registry.browser.announcement.HasAnnouncementsView"
121 facet="overview"
122 permission="zope.Public">
123 <browser:page
124@@ -725,6 +725,11 @@
125 name="+announcement-macros"
126 permission="zope.Public"
127 template="../templates/announcement-macros.pt" />
128+ <adapter
129+ provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
130+ for="lp.registry.interfaces.announcement.IAnnouncement"
131+ factory="canonical.launchpad.webapp.breadcrumb.TitleBreadcrumb"
132+ permission="zope.Public"/>
133 <browser:menus
134 module="lp.registry.browser.announcement"
135 classes="
136
137=== added file 'lib/lp/registry/browser/tests/announcement-views.txt'
138--- lib/lp/registry/browser/tests/announcement-views.txt 1970-01-01 00:00:00 +0000
139+++ lib/lp/registry/browser/tests/announcement-views.txt 2009-10-06 02:10:27 +0000
140@@ -0,0 +1,47 @@
141+Announcement Pages
142+==================
143+
144+Tests for breadcrumbs, menus, and pages.
145+
146+
147+Breadcrumbs
148+-----------
149+
150+Announcement breadcrumbs use the announcement title.
151+
152+ >>> from canonical.lazr.testing.menus import make_fake_request
153+
154+ >>> product = factory.makeProduct(name='cube')
155+ >>> owner = product.owner
156+ >>> announcement = product.announce(
157+ ... user=owner, title='A title', summary='A summary')
158+ >>> view = create_initialized_view(announcement, '+index')
159+
160+ >>> request = make_fake_request(
161+ ... 'http://launchpad.dev/joy-of-cooking/spam',
162+ ... [product, announcement, view])
163+ >>> hierarchy = create_initialized_view(
164+ ... announcement, '+hierarchy', request=request)
165+ >>> hierarchy.items
166+ [<DisplaynameBreadcrumb ... text='Cube'>,
167+ <TitleBreadcrumb ... text='A title'>]
168+
169+
170+Menus
171+-----
172+
173+The announcement module provides two menus. There is an edit menu for an
174+announcement.
175+
176+ >>> from lp.registry.browser.announcement import (
177+ ... AnnouncementEditNavigationMenu, AnnouncementCreateNavigationMenu)
178+ >>> from lp.testing.menu import check_menu_links
179+
180+ >>> check_menu_links(AnnouncementEditNavigationMenu(announcement))
181+ True
182+
183+There is a menu for views for IHasAnnouncements objects to list there
184+announcements. The product implements IHasAnnouncements.
185+
186+ >>> check_menu_links(AnnouncementCreateNavigationMenu(product))
187+ True
188
189=== modified file 'lib/lp/registry/stories/announcements/xx-announcements.txt'
190--- lib/lp/registry/stories/announcements/xx-announcements.txt 2009-09-18 15:24:30 +0000
191+++ lib/lp/registry/stories/announcements/xx-announcements.txt 2009-10-06 02:10:27 +0000
192@@ -263,7 +263,7 @@
193 >>> anon_browser.open('http://launchpad.dev/apache/+announcements')
194 >>> anon_browser.getLink('Derby announcement headline').click()
195 >>> print anon_browser.title
196- Derby announcement
197+ Derby announcement headline : Derby
198
199 The page shows the announcement and it has a link back to the announcements
200 page that any user can navigate.
201@@ -365,7 +365,7 @@
202 >>> priv_browser.getLink('Kubuntu announcement headline').click()
203 >>> priv_browser.getLink('Publish announcement').click()
204 >>> print priv_browser.title
205- Publish this announcement...
206+ Publish announcement : Kubuntu announcement headline : Kubuntu
207 >>> print priv_browser.url
208 http://launchpad.dev/kubuntu/+announceme.../+publish
209 >>> radio = priv_browser.getControl(name="field.publication_date.action")
210@@ -475,7 +475,7 @@
211 >>> priv_browser.getLink('Apache announcement headline').click()
212 >>> priv_browser.getLink('Modify announcement').click()
213 >>> print priv_browser.title
214- Modify this announcement...
215+ Modify announcement : Apache announcement headline : Apache
216 >>> headline = priv_browser.getControl('Headline')
217 >>> print headline.value
218 Apache announcement headline
219@@ -515,7 +515,7 @@
220 >>> priv_browser.getLink('Delete announcement').click()
221 >>> priv_browser.getLink('retracting the announcement').click()
222 >>> print priv_browser.title
223- Retract this announcement...
224+ Retract announcement : Kubuntu announcement headline : Kubuntu
225
226 Actually clicking "Retract" takes us back to the listing page. The item
227 is shown as having been retracted if you are a privileged user.
228@@ -547,7 +547,7 @@
229 >>> priv_browser.getLink('Kubuntu announcement headline').click()
230 >>> priv_browser.getLink('Publish announcement').click()
231 >>> print priv_browser.title
232- Publish this announcement...
233+ Publish announcement : Kubuntu announcement headline : Kubuntu
234 >>> radio = priv_browser.getControl(name="field.publication_date.action")
235 >>> radio.value = ['immediately']
236 >>> priv_browser.getControl(
237@@ -576,7 +576,7 @@
238 >>> priv_browser.getLink('Kubuntu announcement headline').click()
239 >>> priv_browser.getLink('Move announcement').click()
240 >>> print priv_browser.title
241- Move this announcement to a different project...
242+ Move announcement : Kubuntu announcement headline : Kubuntu
243 >>> priv_browser.getControl('For').value = 'guadalinex'
244 >>> priv_browser.getControl('Retarget').click()
245 >>> print priv_browser.title
246@@ -593,13 +593,13 @@
247 >>> kamion_browser.getLink('Kubuntu announcement headline').click()
248 >>> kamion_browser.getLink('Move announcement').click()
249 >>> print kamion_browser.title
250- Move this announcement to a different project...
251+ Move announcement : Kubuntu announcement headline : GuadaLinex
252 >>> kamion_browser.getControl('For').value = 'kubuntu'
253 >>> kamion_browser.getControl('Retarget').click()
254 >>> "don't have permission" in extract_text(find_main_content(kamion_browser.contents))
255 True
256 >>> print kamion_browser.title
257- Move this announcement to a different project...
258+ Move announcement : Kubuntu announcement headline : GuadaLinex
259
260
261 Atom/RSS Feeds
262@@ -665,7 +665,7 @@
263 >>> priv_browser.getLink('Delete announcement').click()
264 >>> priv_browser.getLink('retracting the announcement').click()
265 >>> print priv_browser.title
266- Retract this announcement...
267+ Retract announcement : Kubuntu announcement headline : GuadaLinex
268 >>> priv_browser.getControl('Retract').click()
269 >>> nopriv_browser.reload()
270 >>> 'Kubuntu announcement ' in nopriv_browser.contents
271@@ -758,7 +758,7 @@
272 >>> kamion_browser.getLink('Kubuntu announcement headline').click()
273 >>> kamion_browser.getLink('Delete announcement').click()
274 >>> print kamion_browser.title
275- Delete this announcement...
276+ Delete announcement : Kubuntu announcement headline : GuadaLinex
277 >>> kamion_browser.getControl('Delete').click()
278 >>> print priv_browser.title
279 GuadaLinex news and announcements...
280
281=== modified file 'lib/lp/registry/templates/announcement-index.pt'
282--- lib/lp/registry/templates/announcement-index.pt 2009-08-31 13:37:42 +0000
283+++ lib/lp/registry/templates/announcement-index.pt 2009-10-06 02:10:27 +0000
284@@ -8,10 +8,6 @@
285 >
286
287 <body>
288- <tal:heading metal:fill-slot="heading">
289- <h1 tal:content="context/title">Announcement headline</h1>
290- </tal:heading>
291-
292 <div metal:fill-slot="main">
293 <tal:body define="announcement context;
294 show_title nothing">
295
296=== modified file 'lib/lp/testing/views.py'
297--- lib/lp/testing/views.py 2009-09-24 02:24:23 +0000
298+++ lib/lp/testing/views.py 2009-10-06 02:10:27 +0000
299@@ -20,7 +20,7 @@
300
301 def create_view(context, name, form=None, layer=None, server_url=None,
302 method='GET', principal=None, query_string='', cookie='',
303- path_info='/', current_request=False, **kwargs):
304+ request=None, path_info='/', current_request=False, **kwargs):
305 """Return a view based on the given arguments.
306
307 :param context: The context for the view.
308@@ -33,15 +33,17 @@
309 unauthenticated principal.
310 :param query_string: The query string for the request.
311 :patam cookie: The HTTP_COOKIE value for the request.
312+ :param request: Use this request instead of creating a new one.
313 :param path_info: The PATH_INFO value for the request.
314 :param current_request: If True, the request will be set as the current
315 interaction.
316 :param **kwargs: Any other parameter for the request.
317 :return: The view class for the given context and the name.
318 """
319- request = LaunchpadTestRequest(
320- form=form, SERVER_URL=server_url, QUERY_STRING=query_string,
321- HTTP_COOKIE=cookie, method=method, **kwargs)
322+ if request is None:
323+ request = LaunchpadTestRequest(
324+ form=form, SERVER_URL=server_url, QUERY_STRING=query_string,
325+ HTTP_COOKIE=cookie, method=method, **kwargs)
326 if principal is not None:
327 request.setPrincipal(principal)
328 else:
329@@ -57,7 +59,7 @@
330
331 def create_initialized_view(context, name, form=None, layer=None,
332 server_url=None, method=None, principal=None,
333- query_string=None, cookie=None):
334+ query_string=None, cookie=None, request=None):
335 """Return a view that has already been initialized."""
336 if method is None:
337 if form is None:
338@@ -66,6 +68,6 @@
339 method = 'POST'
340 view = create_view(
341 context, name, form, layer, server_url, method, principal,
342- query_string, cookie)
343+ query_string, cookie, request)
344 view.initialize()
345 return view