Merge lp://staging/~henninge/launchpad/bug-434055-combined into lp://staging/launchpad

Proposed by Henning Eggers
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp://staging/~henninge/launchpad/bug-434055-combined
Merge into: lp://staging/launchpad
Diff against target: None lines
To merge this branch: bzr merge lp://staging/~henninge/launchpad/bug-434055-combined
Reviewer Review Type Date Requested Status
Brad Crittenden (community) release-critical Approve
Graham Binns (community) code Approve
Review via email: mp+12229@code.staging.launchpad.net
To post a comment you must log in.
Revision history for this message
Henning Eggers (henninge) wrote :

= Summary =

Bug 434055 and bug 434195.
Convert 'hasspefications-specs.pt' to 3.0 design. Convert specificationstarget-assignments to UI 3.0.

This is a combined branch, jtv's part for bug 434195 has already been reviewed.

== Implementation details ==

The trouble with hasspecificaitons-spece.pt was that it is used in many different contexts and that each hat its own ApplicationMenu. The menus shared most links in varying combinations, so I aggregated the links into one Mixin that is now used in all Menus.

I converted the ApplicationMenus to NavigationMenus to use them as global-actions. This was the most straight-forward way of conversion without loosing any functionality. I also changed that for specificationstarget-assignments where jtv had removed the menu completely.

I fixed the Breadcrumb for the specifications vhost as it was repeating the context name (like "Foo Bar >> Specifications involving Foo Bar", now it's "Foo Bar >> Blueprints") and I normalized on using the term "blueprint" ;-)

Some label and page_title work was also done.

Did I forget to mention anything?

== Code ==

I will paste a separate diff to show the differences to jtv's branch.

Revision history for this message
Henning Eggers (henninge) wrote :
Download full text (29.6 KiB)

=== modified file 'lib/canonical/launchpad/pagetitles.py'
--- lib/canonical/launchpad/pagetitles.py 2009-09-22 00:49:37 +0000
+++ lib/canonical/launchpad/pagetitles.py 2009-09-22 15:03:45 +0000
@@ -402,13 +402,6 @@

 hasannouncements_index = ContextDisplayName('%s news and announcements')

-def hasspecifications_specs(context, view):
- """Return the secifications title for the context."""
- if IPerson.providedBy(context):
- return "Blueprints involving %s" % context.title
- else:
- return "Blueprints for %s" % context.title
-
 hassprints_sprints = ContextTitle("Events related to %s")

 # launchpad_debug doesn't need a title.

=== modified file 'lib/lp/blueprints/browser/specificationgoal.py'
--- lib/lp/blueprints/browser/specificationgoal.py 2009-09-18 19:47:24 +0000
+++ lib/lp/blueprints/browser/specificationgoal.py 2009-09-22 14:54:36 +0000
@@ -30,6 +30,7 @@
     """

     label = "Set feature goals"
+ page_title = "Feature goals"

     @cachedproperty
     def spec_filter(self):

=== modified file 'lib/lp/blueprints/browser/specificationtarget.py'
--- lib/lp/blueprints/browser/specificationtarget.py 2009-09-22 09:23:42 +0000
+++ lib/lp/blueprints/browser/specificationtarget.py 2009-09-22 14:54:36 +0000
@@ -6,6 +6,7 @@
 __metaclass__ = type

 __all__ = [
+ 'HasSpecificationsMenuMixin',
     'HasSpecificationsView',
     'RegisterABlueprintButtonView',
     'SpecificationAssignmentsView',
@@ -30,7 +31,7 @@

 from canonical.config import config
 from canonical.launchpad import _
-from canonical.launchpad.webapp import LaunchpadView
+from canonical.launchpad.webapp import LaunchpadView, Link
 from canonical.launchpad.webapp.batching import BatchNavigator
 from canonical.launchpad.webapp.breadcrumb import Breadcrumb
 from canonical.launchpad.helpers import shortlist
@@ -39,6 +40,48 @@
 from canonical.lazr.utils import smartquote

+class HasSpecificationsMenuMixin(object):
+
+ def listall(self):
+ """Return a link to show all blueprints."""
+ text = 'List all blueprints'
+ return Link('+specs?show=all', text, icon='blueprint')
+
+ def listaccepted(self):
+ """Return a link to show the approved goals."""
+ text = 'List approved blueprints'
+ return Link('+specs?acceptance=accepted', text, icon='blueprint')
+
+ def listproposed(self):
+ """Return a link to show the proposed goals."""
+ text = 'List proposed blueprints'
+ return Link('+specs?acceptance=proposed', text, icon='blueprint')
+
+ def listdeclined(self):
+ """Return a link to show the declined goals."""
+ text = 'List declined blueprints'
+ return Link('+specs?acceptance=declined', text, icon='blueprint')
+
+ def doc(self):
+ text = 'List documentation'
+ return Link('+documentation', text, icon='info')
+
+ def setgoals(self):
+ """Return a link to set the series goals."""
+ text = 'Set series goals'
+ return Link('+setgoals', text, icon='edit')
+
+ def assignments(self):
+ """Return a link to show the people assigned to the blueprint."""
+ text = 'Assignments'
+ return Link('+assignments', t...

Revision history for this message
Graham Binns (gmb) wrote :

I'm happy for this to land once the no-arg methods of HasSpecificationsMenuMixin are made into properties, as discussed on IRC.

review: Approve (code)
Revision history for this message
Brad Crittenden (bac) :
review: Approve (release-critical)

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-20 19:40:47 +0000
3+++ lib/canonical/launchpad/pagetitles.py 2009-09-22 15:10:46 +0000
4@@ -402,13 +402,6 @@
5
6 hasannouncements_index = ContextDisplayName('%s news and announcements')
7
8-def hasspecifications_specs(context, view):
9- """Return the secifications title for the context."""
10- if IPerson.providedBy(context):
11- return "Blueprints involving %s" % context.title
12- else:
13- return "Blueprints for %s" % context.title
14-
15 hassprints_sprints = ContextTitle("Events related to %s")
16
17 # launchpad_debug doesn't need a title.
18@@ -848,8 +841,6 @@
19 """Return the page title for a specificationtarget."""
20 return view.title
21
22-specificationtarget_assignments = ContextTitle('Blueprint assignments for %s')
23-
24 specificationtarget_workload = ContextTitle('Blueprint workload in %s')
25
26 sprint_attend = ContextTitle('Register your attendance at %s')
27
28=== modified file 'lib/lp/blueprints/browser/configure.zcml'
29--- lib/lp/blueprints/browser/configure.zcml 2009-09-21 12:39:15 +0000
30+++ lib/lp/blueprints/browser/configure.zcml 2009-09-22 10:20:47 +0000
31@@ -56,13 +56,17 @@
32 name="+specs"
33 template="../templates/hasspecifications-specs.pt"/>
34 <browser:page
35- name="+assignments"
36- template="../templates/specificationtarget-assignments.pt"/>
37- <browser:page
38 name="+portlet-latestspecs"
39 template="../templates/specificationtarget-portlet-latestspecs.pt"/>
40 </browser:pages>
41 <browser:page
42+ name="+assignments"
43+ for="lp.blueprints.interfaces.sprint.ISprint"
44+ class="canonical.launchpad.browser.SpecificationAssignmentsView"
45+ facet="specifications"
46+ permission="zope.Public"
47+ template="../templates/specificationtarget-assignments.pt"/>
48+ <browser:page
49 name="+edit"
50 for="lp.blueprints.interfaces.sprint.ISprint"
51 class="lp.blueprints.browser.sprint.SprintEditView"
52@@ -537,13 +541,16 @@
53 name="+specs"
54 template="../templates/hasspecifications-specs.pt"/>
55 <browser:page
56- name="+assignments"
57- template="../templates/specificationtarget-assignments.pt"/>
58- <browser:page
59 name="+portlet-latestspecs"
60 template="../templates/specificationtarget-portlet-latestspecs.pt"/>
61 </browser:pages>
62 <browser:page
63+ name="+assignments"
64+ for="lp.blueprints.interfaces.specificationtarget.IHasSpecifications"
65+ class="lp.blueprints.browser.specificationtarget.SpecificationAssignmentsView"
66+ permission="zope.Public"
67+ template="../templates/specificationtarget-assignments.pt"/>
68+ <browser:page
69 name="+register-a-blueprint-button"
70 for="lp.blueprints.interfaces.specificationtarget.ISpecificationTarget"
71 class="lp.blueprints.browser.specificationtarget.RegisterABlueprintButtonView"
72@@ -585,12 +592,16 @@
73 name="+specs"
74 template="../templates/hasspecifications-specs.pt"/>
75 <browser:page
76- name="+assignments"
77- template="../templates/specificationtarget-assignments.pt"/>
78- <browser:page
79 name="+portlet-latestspecs"
80 template="../templates/specificationtarget-portlet-latestspecs.pt"/>
81 </browser:pages>
82+ <browser:page
83+ name="+assignments"
84+ for="lp.registry.interfaces.project.IProject"
85+ class="lp.blueprints.browser.specificationtarget.SpecificationAssignmentsView"
86+ facet="specifications"
87+ permission="zope.Public"
88+ template="../templates/specificationtarget-assignments.pt"/>
89
90 <browser:page
91 name="+addspec"
92
93=== modified file 'lib/lp/blueprints/browser/specificationgoal.py'
94--- lib/lp/blueprints/browser/specificationgoal.py 2009-09-18 19:47:24 +0000
95+++ lib/lp/blueprints/browser/specificationgoal.py 2009-09-22 15:02:41 +0000
96@@ -30,6 +30,7 @@
97 """
98
99 label = "Set feature goals"
100+ page_title = "Feature goals"
101
102 @cachedproperty
103 def spec_filter(self):
104
105=== modified file 'lib/lp/blueprints/browser/specificationtarget.py'
106--- lib/lp/blueprints/browser/specificationtarget.py 2009-09-17 21:20:14 +0000
107+++ lib/lp/blueprints/browser/specificationtarget.py 2009-09-22 15:02:41 +0000
108@@ -6,8 +6,10 @@
109 __metaclass__ = type
110
111 __all__ = [
112+ 'HasSpecificationsMenuMixin',
113 'HasSpecificationsView',
114 'RegisterABlueprintButtonView',
115+ 'SpecificationAssignmentsView',
116 'SpecificationDocumentationView',
117 ]
118
119@@ -29,7 +31,7 @@
120
121 from canonical.config import config
122 from canonical.launchpad import _
123-from canonical.launchpad.webapp import LaunchpadView
124+from canonical.launchpad.webapp import LaunchpadView, Link
125 from canonical.launchpad.webapp.batching import BatchNavigator
126 from canonical.launchpad.webapp.breadcrumb import Breadcrumb
127 from canonical.launchpad.helpers import shortlist
128@@ -38,6 +40,48 @@
129 from canonical.lazr.utils import smartquote
130
131
132+class HasSpecificationsMenuMixin(object):
133+
134+ def listall(self):
135+ """Return a link to show all blueprints."""
136+ text = 'List all blueprints'
137+ return Link('+specs?show=all', text, icon='blueprint')
138+
139+ def listaccepted(self):
140+ """Return a link to show the approved goals."""
141+ text = 'List approved blueprints'
142+ return Link('+specs?acceptance=accepted', text, icon='blueprint')
143+
144+ def listproposed(self):
145+ """Return a link to show the proposed goals."""
146+ text = 'List proposed blueprints'
147+ return Link('+specs?acceptance=proposed', text, icon='blueprint')
148+
149+ def listdeclined(self):
150+ """Return a link to show the declined goals."""
151+ text = 'List declined blueprints'
152+ return Link('+specs?acceptance=declined', text, icon='blueprint')
153+
154+ def doc(self):
155+ text = 'List documentation'
156+ return Link('+documentation', text, icon='info')
157+
158+ def setgoals(self):
159+ """Return a link to set the series goals."""
160+ text = 'Set series goals'
161+ return Link('+setgoals', text, icon='edit')
162+
163+ def assignments(self):
164+ """Return a link to show the people assigned to the blueprint."""
165+ text = 'Assignments'
166+ return Link('+assignments', text, icon='person')
167+
168+ def new(self):
169+ """Return a link to register a blueprint."""
170+ text = 'Register a blueprint'
171+ return Link('+addspec', text, icon='add')
172+
173+
174 class HasSpecificationsView(LaunchpadView):
175 """Base class for several context-specific views that involve lists of
176 specifications.
177@@ -77,7 +121,6 @@
178 # replacing the conditional execution with polymorphism.
179 # See https://bugs.launchpad.net/blueprint/+bug/173972.
180 def initialize(self):
181- mapping = {'name': self.context.displayname}
182 if IPerson.providedBy(self.context):
183 self.is_person = True
184 elif (IDistribution.providedBy(self.context) or
185@@ -104,11 +147,6 @@
186 else:
187 raise AssertionError, 'Unknown blueprint listing site'
188
189- if self.is_person:
190- self.title = _('Specifications involving $name', mapping=mapping)
191- else:
192- self.title = _('Specifications for $name', mapping=mapping)
193-
194 if IHasDrivers.providedBy(self.context):
195 self.has_drivers = True
196
197@@ -116,6 +154,14 @@
198 self.specs, self.request,
199 size=config.launchpad.default_batch_size)
200
201+ @property
202+ def label(self):
203+ mapping = {'name': self.context.displayname}
204+ if self.is_person:
205+ return _('Blueprints involving $name', mapping=mapping)
206+ else:
207+ return _('Blueprints for $name', mapping=mapping)
208+
209 def mdzCsv(self):
210 """Quick hack for mdz, to get csv dump of specs."""
211 import csv
212@@ -313,8 +359,19 @@
213 quantity=quantity, prejoin_people=False)
214
215
216+class SpecificationAssignmentsView(HasSpecificationsView):
217+ """View for +assignments pages."""
218+ page_title = "Assignments"
219+
220+ @property
221+ def label(self):
222+ return smartquote(
223+ 'Blueprint assignments for "%s"' % self.context.displayname)
224+
225+
226 class SpecificationDocumentationView(HasSpecificationsView):
227 """View for blueprints +documentation page."""
228+ page_title = "Documentation"
229
230 @property
231 def label(self):
232@@ -344,17 +401,7 @@
233 """ % canonical_url(target, rootsite='blueprints')
234
235
236-class HasSpecificationsOnBlueprintsVHostBreadcrumb(Breadcrumb):
237- rootsite = 'blueprints'
238-
239- @property
240- def text(self):
241- return 'Blueprints for %s' % self.context.title
242-
243-
244-class PersonOnBlueprintsVHostBreadcrumb(Breadcrumb):
245- rootsite = 'blueprints'
246-
247- @property
248- def text(self):
249- return 'Blueprints involving %s' % self.context.displayname
250+class BlueprintsVHostBreadcrumb(Breadcrumb):
251+ rootsite = 'blueprints'
252+ text = 'Blueprints'
253+
254
255=== modified file 'lib/lp/blueprints/browser/sprint.py'
256--- lib/lp/blueprints/browser/sprint.py 2009-09-21 12:39:15 +0000
257+++ lib/lp/blueprints/browser/sprint.py 2009-09-22 15:02:41 +0000
258@@ -46,7 +46,7 @@
259
260 from lp.app.interfaces.headings import IMajorHeadingView
261 from lp.blueprints.browser.specificationtarget import (
262- HasSpecificationsView)
263+ HasSpecificationsMenuMixin, HasSpecificationsView)
264 from lp.blueprints.interfaces.specification import (
265 SpecificationDefinitionStatus, SpecificationFilter, SpecificationPriority,
266 SpecificationSort)
267@@ -110,32 +110,16 @@
268 return Link('+branding', text, summary, icon='edit')
269
270
271-class SprintSpecificationsMenu(ApplicationMenu):
272-
273+class SprintSpecificationsMenu(NavigationMenu,
274+ HasSpecificationsMenuMixin):
275 usedfor = ISprint
276 facet = 'specifications'
277- links = ['assignments', 'declined', 'settopics', 'addspec']
278-
279- def assignments(self):
280- text = 'Assignments'
281- summary = 'View the specification assignments'
282- return Link('+assignments', text, summary, icon='info')
283-
284- def declined(self):
285- text = 'List declined blueprints'
286- summary = 'Show topics that were not accepted for discussion'
287- return Link('+specs?acceptance=declined', text, summary, icon='info')
288+ links = ['assignments', 'listdeclined', 'settopics', 'new']
289
290 @enabled_with_permission('launchpad.Driver')
291 def settopics(self):
292 text = 'Set agenda'
293- summary = 'Approve or defer topics for discussion'
294- return Link('+settopics', text, summary, icon='edit')
295-
296- def addspec(self):
297- text = 'Register a blueprint'
298- summary = 'Register a new blueprint for this meeting'
299- return Link('+addspec', text, summary, icon='info')
300+ return Link('+settopics', text, icon='edit')
301
302
303 class SprintSetNavigation(GetitemNavigation):
304
305=== modified file 'lib/lp/blueprints/browser/tests/test_breadcrumbs.py'
306--- lib/lp/blueprints/browser/tests/test_breadcrumbs.py 2009-09-16 03:37:47 +0000
307+++ lib/lp/blueprints/browser/tests/test_breadcrumbs.py 2009-09-22 15:02:41 +0000
308@@ -29,16 +29,14 @@
309 self.product_specs_url, [self.root, self.product])
310 last_crumb = crumbs[-1]
311 self.assertEquals(last_crumb.url, self.product_specs_url)
312- self.assertEquals(
313- last_crumb.text, 'Blueprints for %s' % self.product.title)
314+ self.assertEquals(last_crumb.text, 'Blueprints')
315
316 def test_person(self):
317 crumbs = self._getBreadcrumbs(
318 self.person_specs_url, [self.root, self.person])
319 last_crumb = crumbs[-1]
320 self.assertEquals(last_crumb.url, self.person_specs_url)
321- self.assertEquals(last_crumb.text,
322- 'Blueprints involving %s' % self.person.displayname)
323+ self.assertEquals(last_crumb.text, 'Blueprints')
324
325
326 class TestSpecificationBreadcrumb(BaseBreadcrumbTestCase):
327
328=== modified file 'lib/lp/blueprints/configure.zcml'
329--- lib/lp/blueprints/configure.zcml 2009-09-16 03:37:47 +0000
330+++ lib/lp/blueprints/configure.zcml 2009-09-22 15:02:41 +0000
331@@ -236,12 +236,12 @@
332 name="blueprints"
333 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
334 for="lp.blueprints.interfaces.specificationtarget.IHasSpecifications"
335- factory="lp.blueprints.browser.specificationtarget.HasSpecificationsOnBlueprintsVHostBreadcrumb"
336+ factory="lp.blueprints.browser.specificationtarget.BlueprintsVHostBreadcrumb"
337 permission="zope.Public"/>
338 <adapter
339 name="blueprints"
340 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
341 for="lp.registry.interfaces.person.IPerson"
342- factory="lp.blueprints.browser.specificationtarget.PersonOnBlueprintsVHostBreadcrumb"
343+ factory="lp.blueprints.browser.specificationtarget.BlueprintsVHostBreadcrumb"
344 permission="zope.Public"/>
345 </configure>
346
347=== modified file 'lib/lp/blueprints/stories/blueprints/01-creation.txt'
348--- lib/lp/blueprints/stories/blueprints/01-creation.txt 2009-09-19 01:54:04 +0000
349+++ lib/lp/blueprints/stories/blueprints/01-creation.txt 2009-09-22 15:02:41 +0000
350@@ -64,7 +64,7 @@
351 ... user_browser.contents, 'menu-link-new'):
352 ... print tag
353 <a href="http://blueprints.launchpad.dev/ubuntu/+addspec"
354- class="menu-link-new" ...>Register a blueprint</a>
355+ class="menu-link-new...>Register a blueprint</a>
356
357
358 === From a distribution series ===
359@@ -85,7 +85,7 @@
360 ... user_browser.contents, 'menu-link-new'):
361 ... print tag
362 <a href="http://blueprints.launchpad.dev/ubuntu/hoary/+addspec"
363- class="menu-link-new" ...>Register a blueprint</a>
364+ class="menu-link-new...>Register a blueprint</a>
365
366
367 === From a product ===
368@@ -106,7 +106,7 @@
369 ... user_browser.contents, 'menu-link-new'):
370 ... print tag
371 <a href="http://blueprints.launchpad.dev/bzr/+addspec"
372- class="menu-link-new" ...>Register a blueprint</a>
373+ class="menu-link-new...>Register a blueprint</a>
374
375 For products without any blueprints, users can follow the special "register
376 it here as a blueprint" link:
377@@ -137,7 +137,7 @@
378 ... user_browser.contents, 'menu-link-new'):
379 ... print tag
380 <a href="http://blueprints.launchpad.dev/firefox/1.0/+addspec"
381- class="menu-link-new" ...>Register a blueprint</a>
382+ class="menu-link-new...>Register a blueprint</a>
383
384
385 === From a project ===
386@@ -158,7 +158,7 @@
387 ... user_browser.contents, 'menu-link-new'):
388 ... print tag
389 <a href="http://blueprints.launchpad.dev/mozilla/+addspec"
390- class="menu-link-new" ...>Register a blueprint</a>
391+ class="menu-link-new...>Register a blueprint</a>
392
393
394 === From a sprint ===
395@@ -176,10 +176,10 @@
396 Users can also follow the textual "Register a blueprint" link:
397
398 >>> for tag in find_tags_by_class(
399- ... user_browser.contents, 'menu-link-addspec'):
400+ ... user_browser.contents, 'menu-link-new'):
401 ... print tag
402 <a href="http://blueprints.launchpad.dev/sprints/futurista/+addspec"
403- class="menu-link-addspec" ...>Register a blueprint</a>
404+ class="menu-link-new...>Register a blueprint</a>
405
406
407 == Registering a blueprint ==
408
409=== modified file 'lib/lp/blueprints/stories/blueprints/07-milestones.txt'
410--- lib/lp/blueprints/stories/blueprints/07-milestones.txt 2009-09-21 19:15:03 +0000
411+++ lib/lp/blueprints/stories/blueprints/07-milestones.txt 2009-09-22 15:02:41 +0000
412@@ -17,7 +17,7 @@
413 >>> admin_browser.getLink('Target milestone').click()
414 >>> print admin_browser.title
415 Target to a milestone : Support <canvas> Objects :
416- Blueprints for Mozilla Firefox : Mozilla Firefox
417+ Blueprints : Mozilla Firefox
418 >>> back_link = admin_browser.getLink('Support <canvas> Objects')
419 >>> back_link.url
420 'http://blueprints.launchpad.dev/firefox/+spec/canvas'
421
422=== modified file 'lib/lp/blueprints/stories/blueprints/xx-overview.txt'
423--- lib/lp/blueprints/stories/blueprints/xx-overview.txt 2009-09-21 19:15:03 +0000
424+++ lib/lp/blueprints/stories/blueprints/xx-overview.txt 2009-09-22 15:02:41 +0000
425@@ -44,6 +44,7 @@
426 >>> main = find_main_content(user_browser.contents)
427 >>> print extract_text(main).encode('ascii', 'backslashreplace')
428 Blueprints for 1.0
429+ ...
430 Launchpad lets projects track the features they intend to implement...
431
432 Let's target an existing Mozilla Firefox blueprint to the 1.0 series:
433@@ -144,6 +145,7 @@
434 >>> main = find_main_content(user_browser.contents)
435 >>> print extract_text(main).encode('ascii', 'backslashreplace')
436 Blueprints for Grumpy
437+ ...
438 Launchpad lets projects track the features they intend to implement...
439
440 Let's target an existing Ubuntu blueprint to the Grumpy series:
441
442=== modified file 'lib/lp/blueprints/stories/blueprints/xx-personviews.txt'
443--- lib/lp/blueprints/stories/blueprints/xx-personviews.txt 2009-09-18 12:47:26 +0000
444+++ lib/lp/blueprints/stories/blueprints/xx-personviews.txt 2009-09-22 15:02:41 +0000
445@@ -8,8 +8,8 @@
446 involving that person.
447
448 >>> browser.open('http://blueprints.launchpad.dev/~name16')
449- >>> browser.title
450- 'Blueprints involving Foo Bar'
451+ >>> print browser.title
452+ Blueprints : Foo Bar
453 >>> soup = find_main_content(browser.contents)
454 >>> soup('h1')
455 [<h1>Blueprints involving Foo Bar</h1>]
456@@ -73,9 +73,10 @@
457 are part of the workload for the team. It then shows the workloads
458 for each member of the team, using batching.
459
460+ >>> from canonical.launchpad.helpers import backslashreplace
461 >>> browser.open('http://blueprints.launchpad.dev/~admins')
462- >>> browser.title
463- 'Blueprints involving Launchpad Administrators'
464+ >>> print backslashreplace(browser.title)
465+ Blueprints : \u201cLaunchpad Administrators\u201d team
466 >>> browser.getLink('Workload').click()
467 >>> browser.url
468 '.../~admins/+specworkload'
469
470=== modified file 'lib/lp/blueprints/stories/sprints/15-sprint-tabular-view.txt'
471--- lib/lp/blueprints/stories/sprints/15-sprint-tabular-view.txt 2009-05-11 18:19:21 +0000
472+++ lib/lp/blueprints/stories/sprints/15-sprint-tabular-view.txt 2009-09-22 00:13:14 +0000
473@@ -1,12 +1,12 @@
474 We should be able to see the workload of a sprint:
475
476 >>> anon_browser.open('http://launchpad.dev/sprints/ubz/+assignments')
477- >>> anon_browser.title
478- 'Blueprint assignments for Ubuntu Below Zero'
479+ >>> print anon_browser.title
480+ Assignments : Ubuntu Below Zero : Meetings
481
482 We should be able to see the spec assignment table of a sprint:
483
484- >>> mainarea = find_tag_by_id(anon_browser.contents, 'mainarea')
485+ >>> mainarea = find_main_content(anon_browser.contents)
486 >>> for header in mainarea.findAll('th'):
487 ... print header.renderContents()
488 Priority
489@@ -21,6 +21,7 @@
490 no spec assigned to people.
491
492 >>> anon_browser.open('http://launchpad.dev/sprints/ltsponsteroids/+assignments')
493- >>> 'No blueprints to display' in anon_browser.contents
494- True
495+ >>> notice = find_tag_by_id(anon_browser.contents, 'no-blueprints')
496+ >>> print extract_text(notice)
497+ There are no open blueprints.
498
499
500=== modified file 'lib/lp/blueprints/stories/sprints/sprint-settopics.txt'
501--- lib/lp/blueprints/stories/sprints/sprint-settopics.txt 2009-09-21 12:43:33 +0000
502+++ lib/lp/blueprints/stories/sprints/sprint-settopics.txt 2009-09-22 15:02:41 +0000
503@@ -70,7 +70,7 @@
504
505 >>> print cprov_browser.title
506 Review discussion topics for “Ubuntu DevSummit Guacamole” sprint :
507- Blueprints for Ubuntu DevSummit Guacamole :
508+ Blueprints :
509 Ubuntu DevSummit Guacamole :
510 Meetings
511
512
513=== modified file 'lib/lp/blueprints/templates/hasspecifications-specs.pt'
514--- lib/lp/blueprints/templates/hasspecifications-specs.pt 2009-07-17 17:59:07 +0000
515+++ lib/lp/blueprints/templates/hasspecifications-specs.pt 2009-09-22 15:02:41 +0000
516@@ -3,18 +3,16 @@
517 xmlns:tal="http://xml.zope.org/namespaces/tal"
518 xmlns:metal="http://xml.zope.org/namespaces/metal"
519 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
520- xml:lang="en"
521- lang="en"
522- dir="ltr"
523- metal:use-macro="context/@@main_template/master"
524+ metal:use-macro="view/macro:page/main_side"
525 i18n:domain="launchpad"
526 >
527
528 <body>
529
530- <metal:portlets fill-slot="portlets">
531- <div tal:replace="structure context/@@+portlet-latestspecs" />
532- </metal:portlets>
533+<tal:side metal:fill-slot="side">
534+ <tal:menu replace="structure context/@@+global-actions" />
535+ <div tal:replace="structure context/@@+portlet-latestspecs" />
536+</tal:side>
537
538 <div metal:fill-slot="main"
539 tal:define="specs view/specs;
540@@ -33,11 +31,6 @@
541 </tal:block>
542 </tal:block>
543
544- <h1 tal:condition="view/is_person"
545- >Blueprints involving <tal:person replace="context/title" /></h1>
546- <h1 tal:condition="not:view/is_person"
547- >Blueprints for <tal:software replace="context/displayname" /></h1>
548-
549 <div style="float: right;"
550 tal:content="structure context/@@+register-a-blueprint-button|nothing" />
551
552
553=== modified file 'lib/lp/blueprints/templates/specificationtarget-assignments.pt'
554--- lib/lp/blueprints/templates/specificationtarget-assignments.pt 2009-07-17 17:59:07 +0000
555+++ lib/lp/blueprints/templates/specificationtarget-assignments.pt 2009-09-22 15:02:41 +0000
556@@ -3,91 +3,95 @@
557 xmlns:tal="http://xml.zope.org/namespaces/tal"
558 xmlns:metal="http://xml.zope.org/namespaces/metal"
559 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
560- xml:lang="en"
561- lang="en"
562- dir="ltr"
563- metal:use-macro="context/@@main_template/master"
564+ metal:use-macro="view/macro:page/main_side"
565 i18n:domain="launchpad"
566 >
567
568 <body>
569
570-<metal:leftportlets fill-slot="portlets_one">
571+<tal:side metal:fill-slot="side">
572+ <tal:menu replace="structure context/@@+global-actions" />
573 <div tal:replace="structure context/@@+portlet-latestspecs" />
574-</metal:leftportlets>
575-
576-<metal:heading fill-slot="pageheading">
577- <h1>Assignment of work on blueprints</h1>
578-</metal:heading>
579+</tal:side>
580
581 <div metal:fill-slot="main">
582
583- <p class="informational message" tal:condition="not: view/specs">
584- No blueprints to display.
585- </p>
586-
587- <table class="listing sortable" tal:condition="view/specs" id="work">
588- <thead>
589- <tr>
590- <th>Priority</th>
591- <th>Name</th>
592- <th>Definition</th>
593- <th>Delivery</th>
594- <th>Assignee</th>
595- <th>Drafter</th>
596- <th>Approver</th>
597- </tr>
598- </thead>
599- <tbody class="lesser">
600- <tr tal:repeat="spec view/specs">
601- <td>
602- <span class="sortkey" tal:content="spec/priority/sortkey" />
603- <span tal:content="spec/priority/title"
604- tal:attributes="
605- class string:specpriority${spec/priority/name}">High</span>
606- </td>
607- <td><a tal:content="spec/name/fmt:shorten/35"
608- tal:attributes="
609- href spec/fmt:url;
610- title spec/title">foo-bar-baz</a>
611- <img src="/@@/info" alt="Informational"
612- tal:condition="spec/informational" />
613- </td>
614- <td>
615- <span class="sortkey" tal:content="spec/definition_status/sortkey" />
616- <span tal:content="spec/definition_status/title"
617- tal:attributes="
618- class string:specstatus${spec/definition_status/name}">Approved</span>
619- </td>
620- <td>
621- <span class="sortkey" tal:content="spec/implementation_status/sortkey" />
622- <span tal:content="spec/implementation_status/title"
623- tal:attributes="
624- class string:specdelivery${spec/implementation_status/name}">Slow
625- </span>
626- </td>
627- <td><a tal:condition="spec/assignee"
628- tal:content="spec/assignee/name"
629- tal:attributes="href spec/assignee/fmt:url">Assignee Bar</a></td>
630- <td><a tal:condition="spec/drafter"
631- tal:content="spec/drafter/name"
632- tal:attributes="href spec/drafter/fmt:url">Drafter Baz</a></td>
633- <td><a tal:condition="spec/approver"
634- tal:content="spec/approver/name"
635- tal:attributes="href spec/approver/fmt:url">Approver Foo</a></td>
636- </tr>
637- </tbody>
638- </table>
639-
640- <p tal:condition="view/specs">
641- This listing shows the assignment of work for
642- blueprints currently associated with
643- <span tal:replace="context/displayname">Mozilla</span>.
644- The drafter is responsible for getting the specification correctly
645- written up and approved.
646- The approver is usually the person who would
647- sign off on the specification.
648- </p>
649+ <p class="portlet" tal:condition="not: view/specs" id="no-blueprints">
650+ There are no open blueprints.
651+ </p>
652+
653+ <div tal:condition="view/specs" class="portlet">
654+ <p>
655+ This listing shows the assignment of work for
656+ blueprints currently associated with
657+ <span tal:replace="context/displayname">Mozilla</span>.
658+ The drafter is responsible for getting the specification correctly
659+ written up and approved.
660+ The approver is usually the person who would
661+ sign off on the specification.
662+ </p>
663+
664+ <table class="listing sortable" id="work">
665+ <thead>
666+ <tr>
667+ <th>Priority</th>
668+ <th>Name</th>
669+ <th>Definition</th>
670+ <th>Delivery</th>
671+ <th>Assignee</th>
672+ <th>Drafter</th>
673+ <th>Approver</th>
674+ </tr>
675+ </thead>
676+ <tbody class="lesser">
677+ <tr tal:repeat="spec view/specs">
678+ <td>
679+ <span class="sortkey" tal:content="spec/priority/sortkey" />
680+ <span tal:content="spec/priority/title"
681+ tal:attributes="
682+ class string:specpriority${spec/priority/name}">High</span>
683+ </td>
684+ <td>
685+ <a tal:replace="structure spec/fmt:link">Better mousetrap</a>
686+ <span tal:condition="spec/informational"
687+ class="info sprite"
688+ alt="Informational" />
689+ </td>
690+ <td>
691+ <span class="sortkey" tal:content="spec/definition_status/sortkey" />
692+ <span tal:content="spec/definition_status/title"
693+ tal:attributes="
694+ class string:specstatus${spec/definition_status/name}">Approved</span>
695+ </td>
696+ <td>
697+ <span class="sortkey" tal:content="spec/implementation_status/sortkey" />
698+ <span tal:content="spec/implementation_status/title"
699+ tal:attributes="
700+ class string:specdelivery${spec/implementation_status/name}">Slow
701+ </span>
702+ </td>
703+ <td>
704+ <a tal:condition="spec/assignee"
705+ tal:replace="structure spec/assignee/fmt:link">
706+ Assignee Bar
707+ </a>
708+ </td>
709+ <td>
710+ <a tal:condition="spec/drafter"
711+ tal:replace="structure spec/drafter/fmt:link">
712+ Drafter Baz
713+ </a>
714+ </td>
715+ <td>
716+ <a tal:condition="spec/approver"
717+ tal:replace="structure spec/approver/fmt:link">
718+ Approver Foo
719+ </a>
720+ </td>
721+ </tr>
722+ </tbody>
723+ </table>
724+ </div>
725
726 </div>
727 </body>
728
729=== modified file 'lib/lp/registry/browser/distribution.py'
730--- lib/lp/registry/browser/distribution.py 2009-09-16 00:40:53 +0000
731+++ lib/lp/registry/browser/distribution.py 2009-09-22 15:02:41 +0000
732@@ -47,6 +47,8 @@
733 from zope.security.interfaces import Unauthorized
734
735 from canonical.cachedproperty import cachedproperty
736+from lp.blueprints.browser.specificationtarget import (
737+ HasSpecificationsMenuMixin)
738 from lp.registry.browser.announcement import HasAnnouncementsView
739 from lp.registry.browser.menu import (
740 IRegistryCollectionNavigationMenu, RegistryCollectionActionMenuBase)
741@@ -454,31 +456,12 @@
742 return Link('+subscribe', text, icon='edit')
743
744
745-class DistributionSpecificationsMenu(ApplicationMenu):
746-
747+class DistributionSpecificationsMenu(NavigationMenu,
748+ HasSpecificationsMenuMixin):
749 usedfor = IDistribution
750 facet = 'specifications'
751 links = ['listall', 'doc', 'assignments', 'new']
752
753- def listall(self):
754- text = 'List all blueprints'
755- return Link('+specs?show=all', text, icon='info')
756-
757- def assignments(self):
758- text = 'Assignments'
759- return Link('+assignments', text, icon='info')
760-
761- def doc(self):
762- text = 'Documentation'
763- summary = 'List all complete informational specifications'
764- return Link('+documentation', text, summary,
765- icon='info')
766-
767- def new(self):
768- text = 'Register a blueprint'
769- summary = 'Register a new blueprint for %s' % self.context.title
770- return Link('+addspec', text, summary, icon='add')
771-
772
773 class DistributionPackageSearchView(PackageSearchViewBase):
774 """Customised PackageSearchView for Distribution"""
775
776=== modified file 'lib/lp/registry/browser/distroseries.py'
777--- lib/lp/registry/browser/distroseries.py 2009-09-15 01:17:46 +0000
778+++ lib/lp/registry/browser/distroseries.py 2009-09-22 15:02:41 +0000
779@@ -28,6 +28,8 @@
780 from canonical.database.constants import UTC_NOW
781 from canonical.launchpad import _
782 from canonical.launchpad import helpers
783+from lp.blueprints.browser.specificationtarget import (
784+ HasSpecificationsMenuMixin)
785 from lp.bugs.browser.bugtask import BugTargetTraversalMixin
786 from lp.soyuz.browser.build import BuildRecordsView
787 from canonical.launchpad.browser.packagesearch import PackageSearchViewBase
788@@ -49,7 +51,7 @@
789 from canonical.launchpad.webapp.launchpadform import (
790 LaunchpadEditFormView, LaunchpadFormView)
791 from canonical.launchpad.webapp.menu import (
792- ApplicationMenu, Link, enabled_with_permission)
793+ ApplicationMenu, Link, NavigationMenu, enabled_with_permission)
794 from canonical.launchpad.webapp.publisher import (
795 canonical_url, stepthrough, stepto)
796 from canonical.widgets.itemswidgets import LaunchpadDropdownWidget
797@@ -225,43 +227,12 @@
798 return Link('+subscribe', 'Subscribe to bug mail')
799
800
801-class DistroSeriesSpecificationsMenu(ApplicationMenu):
802+class DistroSeriesSpecificationsMenu(NavigationMenu,
803+ HasSpecificationsMenuMixin):
804
805 usedfor = IDistroSeries
806 facet = 'specifications'
807- links = ['listall', 'table', 'setgoals', 'listdeclined', 'new']
808-
809- def listall(self):
810- text = 'List all blueprints'
811- return Link('+specs?show=all', text, icon='info')
812-
813- def listapproved(self):
814- text = 'List approved blueprints'
815- return Link('+specs?acceptance=accepted', text, icon='info')
816-
817- def listproposed(self):
818- text = 'List proposed blueprints'
819- return Link('+specs?acceptance=proposed', text, icon='info')
820-
821- def listdeclined(self):
822- text = 'List declined blueprints'
823- summary = 'Show the goals which have been declined'
824- return Link('+specs?acceptance=declined', text, icon='info')
825-
826- def setgoals(self):
827- text = 'Set series goals'
828- summary = 'Approve or decline feature goals that have been proposed'
829- return Link('+setgoals', text, icon='info')
830-
831- def table(self):
832- text = 'Assignments'
833- summary = 'Show the assignee, drafter and approver of these specs'
834- return Link('+assignments', text, icon='info')
835-
836- def new(self):
837- text = 'Register a blueprint'
838- summary = 'Register a new blueprint for %s' % self.context.title
839- return Link('+addspec', text, summary, icon='add')
840+ links = ['listall', 'listdeclined', 'assignments', 'setgoals', 'new']
841
842
843 class DistroSeriesPackageSearchView(PackageSearchViewBase):
844
845=== modified file 'lib/lp/registry/browser/person.py'
846--- lib/lp/registry/browser/person.py 2009-09-20 07:13:52 +0000
847+++ lib/lp/registry/browser/person.py 2009-09-22 15:02:41 +0000
848@@ -775,7 +775,7 @@
849 return Link('+commentedbugs', text, summary=summary)
850
851
852-class PersonSpecsMenu(ApplicationMenu):
853+class PersonSpecsMenu(NavigationMenu):
854
855 usedfor = IPerson
856 facet = 'specifications'
857@@ -786,28 +786,28 @@
858 def registrant(self):
859 text = 'Registrant'
860 summary = 'List specs registered by %s' % self.context.displayname
861- return Link('+specs?role=registrant', text, summary, icon='spec')
862+ return Link('+specs?role=registrant', text, summary, icon='blueprint')
863
864 def approver(self):
865 text = 'Approver'
866 summary = 'List specs with %s is supposed to approve' % (
867 self.context.displayname)
868- return Link('+specs?role=approver', text, summary, icon='spec')
869+ return Link('+specs?role=approver', text, summary, icon='blueprint')
870
871 def assignee(self):
872 text = 'Assignee'
873 summary = 'List specs for which %s is the assignee' % (
874 self.context.displayname)
875- return Link('+specs?role=assignee', text, summary, icon='spec')
876+ return Link('+specs?role=assignee', text, summary, icon='blueprint')
877
878 def drafter(self):
879 text = 'Drafter'
880 summary = 'List specs drafted by %s' % self.context.displayname
881- return Link('+specs?role=drafter', text, summary, icon='spec')
882+ return Link('+specs?role=drafter', text, summary, icon='blueprint')
883
884 def subscriber(self):
885 text = 'Subscriber'
886- return Link('+specs?role=subscriber', text, icon='spec')
887+ return Link('+specs?role=subscriber', text, icon='blueprint')
888
889 def feedback(self):
890 text = 'Feedback requests'
891
892=== modified file 'lib/lp/registry/browser/product.py'
893--- lib/lp/registry/browser/product.py 2009-09-18 01:34:06 +0000
894+++ lib/lp/registry/browser/product.py 2009-09-22 15:02:41 +0000
895@@ -58,6 +58,8 @@
896 from canonical.launchpad import _
897 from canonical.launchpad.fields import PillarAliases, PublicPersonChoice
898 from lp.app.interfaces.headings import IEditableContextTitle
899+from lp.blueprints.browser.specificationtarget import (
900+ HasSpecificationsMenuMixin)
901 from lp.bugs.interfaces.bugtask import RESOLVED_BUGTASK_STATUSES
902 from lp.bugs.interfaces.bugwatch import IBugTracker
903 from lp.services.worlddata.interfaces.country import ICountry
904@@ -475,32 +477,11 @@
905 return Link('+subscribe', text, icon='edit')
906
907
908-class ProductSpecificationsMenu(ApplicationMenu):
909-
910+class ProductSpecificationsMenu(NavigationMenu,
911+ HasSpecificationsMenuMixin):
912 usedfor = IProduct
913 facet = 'specifications'
914- links = ['listall', 'doc', 'table', 'new']
915-
916- def listall(self):
917- text = 'List all blueprints'
918- summary = 'Show all specifications for %s' % self.context.title
919- return Link('+specs?show=all', text, summary, icon='info')
920-
921- def doc(self):
922- text = 'List documentation'
923- summary = 'List all complete informational specifications'
924- return Link('+documentation', text, summary,
925- icon='info')
926-
927- def table(self):
928- text = 'Assignments'
929- summary = 'Show the full assignment of work, drafting and approving'
930- return Link('+assignments', text, summary, icon='info')
931-
932- def new(self):
933- text = 'Register a blueprint'
934- summary = 'Register a new blueprint for %s' % self.context.title
935- return Link('+addspec', text, summary, icon='add')
936+ links = ['listall', 'doc', 'assignments', 'new']
937
938
939 def _sort_distros(a, b):
940
941=== modified file 'lib/lp/registry/browser/productseries.py'
942--- lib/lp/registry/browser/productseries.py 2009-09-15 02:16:19 +0000
943+++ lib/lp/registry/browser/productseries.py 2009-09-22 15:02:41 +0000
944@@ -39,6 +39,8 @@
945 from canonical.cachedproperty import cachedproperty
946 from canonical.launchpad import _
947 from lp.code.browser.branchref import BranchRef
948+from lp.blueprints.browser.specificationtarget import (
949+ HasSpecificationsMenuMixin)
950 from lp.blueprints.interfaces.specification import (
951 ISpecificationSet, SpecificationImplementationStatus)
952 from lp.bugs.interfaces.bugtask import BugTaskStatus
953@@ -247,7 +249,8 @@
954 return Link('+subscribe', 'Subscribe to bug mail')
955
956
957-class ProductSeriesSpecificationsMenu(ApplicationMenu):
958+class ProductSeriesSpecificationsMenu(NavigationMenu,
959+ HasSpecificationsMenuMixin):
960 """Specs menu for ProductSeries.
961
962 This menu needs to keep track of whether we are showing all the
963@@ -258,46 +261,7 @@
964
965 usedfor = IProductSeries
966 facet = 'specifications'
967- links = ['listall', 'table', 'setgoals', 'listdeclined', 'new']
968-
969- def listall(self):
970- """Return a link to show all blueprints."""
971- text = 'List all blueprints'
972- return Link('+specs?show=all', text, icon='info')
973-
974- def listaccepted(self):
975- """Return a link to show the approved goals."""
976- text = 'List approved blueprints'
977- return Link('+specs?acceptance=accepted', text, icon='info')
978-
979- def listproposed(self):
980- """Return a link to show the proposed goals."""
981- text = 'List proposed blueprints'
982- return Link('+specs?acceptance=proposed', text, icon='info')
983-
984- def listdeclined(self):
985- """Return a link to show the declined goals."""
986- text = 'List declined blueprints'
987- summary = 'Show the goals which have been declined'
988- return Link('+specs?acceptance=declined', text, summary, icon='info')
989-
990- def setgoals(self):
991- """Return a link to set the series goals."""
992- text = 'Set series goals'
993- summary = 'Approve or decline feature goals that have been proposed'
994- return Link('+setgoals', text, summary, icon='edit')
995-
996- def table(self):
997- """Return a link to show the people assigned to the blueprint."""
998- text = 'Assignments'
999- summary = 'Show the assignee, drafter and approver of these specs'
1000- return Link('+assignments', text, summary, icon='info')
1001-
1002- def new(self):
1003- """Return a link to register a blueprint."""
1004- text = 'Register a blueprint'
1005- summary = 'Register a new blueprint for %s' % self.context.title
1006- return Link('+addspec', text, summary, icon='add')
1007+ links = ['listall', 'assignments', 'setgoals', 'listdeclined', 'new']
1008
1009
1010 class ProductSeriesOverviewNavigationMenu(NavigationMenu):
1011
1012=== modified file 'lib/lp/registry/browser/project.py'
1013--- lib/lp/registry/browser/project.py 2009-09-15 20:41:36 +0000
1014+++ lib/lp/registry/browser/project.py 2009-09-22 15:02:41 +0000
1015@@ -44,6 +44,8 @@
1016 from canonical.launchpad import _
1017 from canonical.launchpad.webapp.interfaces import NotFoundError
1018 from canonical.launchpad.webapp.menu import NavigationMenu
1019+from lp.blueprints.browser.specificationtarget import (
1020+ HasSpecificationsMenuMixin)
1021 from lp.registry.interfaces.product import IProductSet
1022 from lp.registry.interfaces.project import (
1023 IProject, IProjectSeries, IProjectSet)
1024@@ -267,30 +269,12 @@
1025 links = ('branding', 'reassign', 'driver', 'administer')
1026
1027
1028-class ProjectSpecificationsMenu(ApplicationMenu):
1029-
1030+class ProjectSpecificationsMenu(NavigationMenu,
1031+ HasSpecificationsMenuMixin):
1032 usedfor = IProject
1033 facet = 'specifications'
1034 links = ['listall', 'doc', 'assignments', 'new']
1035
1036- def listall(self):
1037- text = 'List all blueprints'
1038- return Link('+specs?show=all', text, icon='info')
1039-
1040- def doc(self):
1041- text = 'List documentation'
1042- summary = 'Show all completed informational specifications'
1043- return Link('+documentation', text, summary, icon="info")
1044-
1045- def assignments(self):
1046- text = 'Assignments'
1047- return Link('+assignments', text, icon='info')
1048-
1049- def new(self):
1050- text = 'Register a blueprint'
1051- summary = 'Register a new blueprint for %s' % self.context.title
1052- return Link('+addspec', text, summary, icon='add')
1053-
1054
1055 class ProjectAnswersMenu(QuestionCollectionAnswersMenu):
1056 """Menu for the answers facet of projects."""