Merge lp:~wgrant/launchpad/unuse-nullbugtask into lp:launchpad

Proposed by William Grant
Status: Merged
Approved by: William Grant
Approved revision: no longer in the source branch.
Merged at revision: 12607
Proposed branch: lp:~wgrant/launchpad/unuse-nullbugtask
Merge into: lp:launchpad
Diff against target: 479 lines (+43/-198)
6 files modified
lib/lp/bugs/browser/bugnomination.py (+1/-9)
lib/lp/bugs/browser/bugtask.py (+6/-130)
lib/lp/bugs/browser/configure.zcml (+21/-43)
lib/lp/bugs/browser/tests/bug-views.txt (+2/-2)
lib/lp/bugs/browser/tests/bugtask-edit-views.txt (+13/-13)
lib/lp/bugs/templates/bugtask-index.pt (+0/-1)
To merge this branch: bzr merge lp:~wgrant/launchpad/unuse-nullbugtask
Reviewer Review Type Date Requested Status
Tim Penhey (community) code Approve
Steve Kowalik (community) code* Approve
Review via email: mp+53363@code.launchpad.net

Commit message

[r=stevenk,thumper][bug=589701,712094] Remove traversal to NullBugTasks, and get rid of some now-unreachable special cases.

Description of the change

NullBugTask was created to provide a button to report a bug in a new context, if the bug was navigated to below a context for which a task did not yet exist. In early 2009 this behaviour was largely removed; non-existent tasks now redirect to the default task instead.

But one case was missed: DistributionSourcePackages where there is a task for the Distribution or another DistributionSourcePackage for the same distro. BugTargetTraversalMixin.traverse_bug returns a NullBugTask, but BugTaskView.initialise allows the null task through without redirecting.

This branch fixes that case by redirecting during traversal, preventing a NullBugTask from ever being encountered. This allows us to remove a lot of special cases, mostly trivially. One non-trivial case is BugTaskTraversal.traverse: it was used to turn +editstatus and +viewstatus into +editstatus-page and +viewstatus-page, presumably to prevent them from rendering on a NullBugTask. I've renamed the views to their exposed names, and removed the custom traversal method.

Removal of NullBugTask itself is a little more difficult, so that will be done in a later branch.

To post a comment you must log in.
Revision history for this message
Steve Kowalik (stevenk) :
review: Approve (code*)
Revision history for this message
Tim Penhey (thumper) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/browser/bugnomination.py'
2--- lib/lp/bugs/browser/bugnomination.py 2011-02-02 15:43:31 +0000
3+++ lib/lp/bugs/browser/bugnomination.py 2011-03-15 03:31:14 +0000
4@@ -15,10 +15,7 @@
5
6 import pytz
7 from zope.component import getUtility
8-from zope.publisher.interfaces import (
9- implements,
10- NotFound,
11- )
12+from zope.publisher.interfaces import implements
13
14 from canonical.launchpad import _
15 from canonical.launchpad.webapp import (
16@@ -41,7 +38,6 @@
17 IBugNomination,
18 IBugNominationForm,
19 )
20-from lp.bugs.interfaces.bugtask import INullBugTask
21 from lp.bugs.interfaces.cve import ICveSet
22
23
24@@ -65,10 +61,6 @@
25 LaunchpadFormView.__init__(self, context, request)
26
27 def initialize(self):
28- if INullBugTask.providedBy(self.current_bugtask):
29- # It shouldn't be possible to nominate a bug that hasn't
30- # been reported yet.
31- raise NotFound(self.current_bugtask, '+nominate', self.request)
32 LaunchpadFormView.initialize(self)
33 # Update the submit label based on the user's permission.
34 submit_action = self.__class__.actions.byname['actions.submit']
35
36=== modified file 'lib/lp/bugs/browser/bugtask.py'
37--- lib/lp/bugs/browser/bugtask.py 2011-03-10 16:15:57 +0000
38+++ lib/lp/bugs/browser/bugtask.py 2011-03-15 03:31:14 +0000
39@@ -236,7 +236,6 @@
40 IDistroSeriesBugTask,
41 IFrontPageBugTaskSearch,
42 INominationsReviewTableBatchNavigator,
43- INullBugTask,
44 IPersonBugTaskSearch,
45 IProductSeriesBugTask,
46 IRemoveQuestionFromBugTaskForm,
47@@ -490,7 +489,7 @@
48 """Return the IBugTask for this name in this context.
49
50 If the bug has been reported, but not in this specific context, a
51- NullBugTask will be returned.
52+ redirect to the default context will be returned.
53
54 Raises NotFoundError if no bug with the given name is found.
55
56@@ -514,59 +513,15 @@
57 # Security proxy this object on the way out.
58 return getUtility(IBugTaskSet).get(bugtask.id)
59
60- # If we've come this far, it means that no actual task exists in this
61- # context, so we'll return a null bug task. This makes it possible to,
62- # for example, return a bug page for a context in which the bug hasn't
63- # yet been reported.
64- if IProduct.providedBy(context):
65- null_bugtask = bug.getNullBugTask(product=context)
66- elif IProductSeries.providedBy(context):
67- null_bugtask = bug.getNullBugTask(productseries=context)
68- elif IDistribution.providedBy(context):
69- null_bugtask = bug.getNullBugTask(distribution=context)
70- elif IDistributionSourcePackage.providedBy(context):
71- null_bugtask = bug.getNullBugTask(
72- distribution=context.distribution,
73- sourcepackagename=context.sourcepackagename)
74- elif IDistroSeries.providedBy(context):
75- null_bugtask = bug.getNullBugTask(distroseries=context)
76- elif ISourcePackage.providedBy(context):
77- null_bugtask = bug.getNullBugTask(
78- distroseries=context.distroseries,
79- sourcepackagename=context.sourcepackagename)
80- else:
81- raise TypeError(
82- "Unknown context type for bug task: %s" % repr(context))
83-
84- return null_bugtask
85+ # If we've come this far, there's no task for the requested
86+ # context. Redirect to one that exists.
87+ return self.redirectSubTree(canonical_url(bug.default_bugtask))
88
89
90 class BugTaskNavigation(Navigation):
91 """Navigation for the `IBugTask`."""
92 usedfor = IBugTask
93
94- def traverse(self, name):
95- """Traverse the `IBugTask`."""
96- # Are we traversing to the view or edit status page of the
97- # bugtask? If so, and the task actually exists, return the
98- # appropriate page. If the task doesn't yet exist (i.e. it's a
99- # NullBugTask), then return a 404. In other words, the URL:
100- #
101- # /products/foo/+bug/1/+viewstatus
102- #
103- # will return the +viewstatus page if bug 1 has actually been
104- # reported in "foo". If bug 1 has not yet been reported in "foo",
105- # a 404 will be returned.
106- if name not in ("+viewstatus", "+editstatus"):
107- # You're going in the wrong direction.
108- return None
109- if INullBugTask.providedBy(self.context):
110- # The bug has not been reported in this context.
111- return None
112- # Yes! The bug has been reported in this context.
113- return getMultiAdapter((self.context, self.request),
114- name=(name + "-page"))
115-
116 @stepthrough('attachments')
117 def traverse_attachments(self, name):
118 """traverse to an attachment by id."""
119@@ -655,13 +610,8 @@
120
121 @property
122 def page_title(self):
123- bugtask = self.context
124- if INullBugTask.providedBy(bugtask):
125- heading = 'Bug #%s is not in %s' % (
126- bugtask.bug.id, bugtask.bugtargetdisplayname)
127- else:
128- heading = 'Bug #%s in %s' % (
129- bugtask.bug.id, bugtask.bugtargetdisplayname)
130+ heading = 'Bug #%s in %s' % (
131+ self.context.bug.id, self.context.bugtargetdisplayname)
132 return smartquote('%s: "%s"') % (heading, self.context.bug.title)
133
134 @property
135@@ -698,12 +648,6 @@
136 # See render() for how this flag is used.
137 self._redirecting_to_bug_list = False
138
139- # If the bug is not reported in this context, redirect
140- # to the default bug task.
141- if not self.isReportedInContext():
142- self.request.response.redirect(
143- canonical_url(self.context.bug.default_bugtask))
144-
145 self.bug_title_edit_widget = TextLineEditorWidget(
146 bug, IBug['title'], "Edit this summary", 'h1',
147 edit_url=canonical_url(self.context, view_name='+edit'))
148@@ -741,69 +685,6 @@
149 series.bugtargetdisplayname)
150 self.request.response.redirect(canonical_url(self.context))
151
152- def reportBugInContext(self):
153- """Report the bug affects the current context."""
154- fake_task = self.context
155- if self.request.form.get("reportbug"):
156- if self.isReportedInContext():
157- self.notices.append(
158- "The bug is already reported in this context.")
159- return
160- # The user has requested that the bug be reported in this
161- # context.
162- if IUpstreamBugTask.providedBy(fake_task):
163- # Create a real upstream task in this context.
164- real_task = fake_task.bug.addTask(
165- getUtility(ILaunchBag).user, fake_task.product)
166- elif IDistroBugTask.providedBy(fake_task):
167- # Create a real distro bug task in this context.
168- real_task = fake_task.bug.addTask(
169- getUtility(ILaunchBag).user, fake_task.target)
170- elif IDistroSeriesBugTask.providedBy(fake_task):
171- self._nominateBug(fake_task.distroseries)
172- return
173- elif IProductSeriesBugTask.providedBy(fake_task):
174- self._nominateBug(fake_task.productseries)
175- return
176- else:
177- raise TypeError(
178- "Unknown bug task type: %s" % repr(fake_task))
179-
180- self.context = real_task
181-
182- # Add an appropriate feedback message
183- self.notices.append("Thank you for your bug report.")
184-
185- def isReportedInContext(self):
186- """Is the bug reported in this context? Returns True or False.
187-
188- It considers a nominated bug to be reported.
189-
190- This is particularly useful for views that may render a
191- NullBugTask.
192- """
193- if self.context.id is not None:
194- # Fast path for real bugtasks: they have a DB id.
195- return True
196- params = BugTaskSearchParams(user=self.user, bug=self.context.bug)
197- matching_bugtasks = self.context.target.searchTasks(params)
198- if self.context.productseries is not None:
199- nomination_target = self.context.productseries
200- elif self.context.distroseries is not None:
201- nomination_target = self.context.distroseries
202- else:
203- nomination_target = None
204- if nomination_target is not None:
205- try:
206- nomination = self.context.bug.getNominationFor(
207- nomination_target)
208- except NotFoundError:
209- nomination = None
210- else:
211- nomination = None
212-
213- return nomination is not None or matching_bugtasks.count() > 0
214-
215 def isSeriesTargetableContext(self):
216 """Is the context something that supports Series targeting?
217
218@@ -1744,17 +1625,12 @@
219 The assignee is included.
220 """
221 bugtask = self.context
222-
223- if INullBugTask.providedBy(bugtask):
224- return u"Not reported in %s" % bugtask.bugtargetname
225-
226 assignee = bugtask.assignee
227 status = bugtask.status
228 status_title = status.title.capitalize()
229
230 if not assignee:
231 return status_title + ' (unassigned)'
232-
233 assignee_html = PersonFormatterAPI(assignee).link('+assignedbugs')
234
235 if status in (BugTaskStatus.INVALID,
236
237=== modified file 'lib/lp/bugs/browser/configure.zcml'
238--- lib/lp/bugs/browser/configure.zcml 2011-03-11 21:31:10 +0000
239+++ lib/lp/bugs/browser/configure.zcml 2011-03-15 03:31:14 +0000
240@@ -637,35 +637,27 @@
241 class="lp.bugs.browser.bugalsoaffects.BugAlsoAffectsDistroMetaView"
242 permission="launchpad.AnyPerson"/>
243 <browser:page
244- name="+editstatus-page"
245- for="lp.bugs.interfaces.bugtask.IUpstreamBugTask"
246- class="lp.bugs.browser.bugtask.BugTaskEditView"
247- permission="launchpad.Edit"
248- template="../templates/bugtask-edit.pt">
249- </browser:page>
250- <browser:page
251- name="+edit-form"
252- for="lp.bugs.interfaces.bugtask.IUpstreamBugTask"
253- class="lp.bugs.browser.bugtask.BugTaskEditView"
254- permission="launchpad.Edit"
255- template="../templates/bugtask-edit-form.pt">
256- </browser:page>
257- <browser:page
258- name="+editstatus-page"
259- for="lp.bugs.interfaces.bugtask.IProductSeriesBugTask"
260- class="lp.bugs.browser.bugtask.BugTaskEditView"
261- permission="launchpad.Edit"
262- template="../templates/bugtask-edit.pt">
263- </browser:page>
264- <browser:page
265- name="+edit-form"
266- for="lp.bugs.interfaces.bugtask.IProductSeriesBugTask"
267- class="lp.bugs.browser.bugtask.BugTaskEditView"
268- permission="launchpad.Edit"
269- template="../templates/bugtask-edit-form.pt">
270- </browser:page>
271- <browser:page
272- name="+viewstatus-page"
273+ name="+edit-form"
274+ for="lp.bugs.interfaces.bugtask.IUpstreamBugTask"
275+ class="lp.bugs.browser.bugtask.BugTaskEditView"
276+ permission="launchpad.Edit"
277+ template="../templates/bugtask-edit-form.pt">
278+ </browser:page>
279+ <browser:page
280+ name="+edit-form"
281+ for="lp.bugs.interfaces.bugtask.IProductSeriesBugTask"
282+ class="lp.bugs.browser.bugtask.BugTaskEditView"
283+ permission="launchpad.Edit"
284+ template="../templates/bugtask-edit-form.pt">
285+ </browser:page>
286+ <browser:page
287+ name="+editstatus"
288+ for="lp.bugs.interfaces.bugtask.IBugTask"
289+ class="lp.bugs.browser.bugtask.BugTaskEditView"
290+ permission="launchpad.Edit"
291+ template="../templates/bugtask-edit.pt"/>
292+ <browser:page
293+ name="+viewstatus"
294 for="lp.bugs.interfaces.bugtask.IBugTask"
295 class="lp.bugs.browser.bugtask.BugTaskStatusView"
296 permission="launchpad.View"
297@@ -680,13 +672,6 @@
298 for="lp.bugs.interfaces.bugtask.IDistroBugTask"
299 name="+index"/>
300 <browser:page
301- name="+editstatus-page"
302- for="lp.bugs.interfaces.bugtask.IDistroBugTask"
303- class="lp.bugs.browser.bugtask.BugTaskEditView"
304- permission="launchpad.Edit"
305- template="../templates/bugtask-edit.pt">
306- </browser:page>
307- <browser:page
308 name="+edit-form"
309 for="lp.bugs.interfaces.bugtask.IDistroBugTask"
310 class="lp.bugs.browser.bugtask.BugTaskEditView"
311@@ -697,13 +682,6 @@
312 for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"
313 name="+index"/>
314 <browser:page
315- name="+editstatus-page"
316- for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"
317- class="lp.bugs.browser.bugtask.BugTaskEditView"
318- permission="launchpad.Edit"
319- template="../templates/bugtask-edit.pt">
320- </browser:page>
321- <browser:page
322 name="+edit-form"
323 for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"
324 class="lp.bugs.browser.bugtask.BugTaskEditView"
325
326=== modified file 'lib/lp/bugs/browser/tests/bug-views.txt'
327--- lib/lp/bugs/browser/tests/bug-views.txt 2011-02-15 09:31:20 +0000
328+++ lib/lp/bugs/browser/tests/bug-views.txt 2011-03-15 03:31:14 +0000
329@@ -777,7 +777,7 @@
330 ... 'testproduct.actions.save': 'Save Changes',
331 ... })
332 >>> view = getMultiAdapter(
333- ... (bug.bugtasks[0], request), name="+editstatus-page")
334+ ... (bug.bugtasks[0], request), name="+editstatus")
335 >>> view.initialize()
336
337 >>> view = getMultiAdapter(
338@@ -833,7 +833,7 @@
339 ... 'testproduct.actions.save': 'Save Changes',
340 ... })
341 >>> view = getMultiAdapter(
342- ... (bug.bugtasks[0], request), name="+editstatus-page")
343+ ... (bug.bugtasks[0], request), name="+editstatus")
344 >>> view.initialize()
345
346 >>> view = getMultiAdapter(
347
348=== modified file 'lib/lp/bugs/browser/tests/bugtask-edit-views.txt'
349--- lib/lp/bugs/browser/tests/bugtask-edit-views.txt 2010-10-18 22:24:59 +0000
350+++ lib/lp/bugs/browser/tests/bugtask-edit-views.txt 2011-03-15 03:31:14 +0000
351@@ -29,7 +29,7 @@
352 ... ubuntu_thunderbird_task.sourcepackagename.name}
353 >>> request = LaunchpadTestRequest(method='POST', form=edit_form)
354 >>> edit_view = getMultiAdapter(
355- ... (ubuntu_thunderbird_task, request), name='+editstatus-page')
356+ ... (ubuntu_thunderbird_task, request), name='+editstatus')
357 >>> edit_view.initialize()
358 >>> ubuntu_thunderbird_task.status.title
359 'In Progress'
360@@ -50,7 +50,7 @@
361 >>> edit_form['ubuntu_thunderbird.sourcepackagename'] = u'linux-2.6.12'
362 >>> request = LaunchpadTestRequest(method='POST', form=edit_form)
363 >>> edit_view = getMultiAdapter(
364- ... (ubuntu_thunderbird_task, request), name='+editstatus-page')
365+ ... (ubuntu_thunderbird_task, request), name='+editstatus')
366 >>> edit_view.initialize()
367 >>> ubuntu_thunderbird_task.sourcepackagename.name
368 u'linux-source-2.6.15'
369@@ -73,7 +73,7 @@
370 >>> edit_form['ubuntu_thunderbird.sourcepackagename'] = u'no-such-package'
371 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
372 >>> edit_view = getMultiAdapter(
373- ... (ubuntu_thunderbird_task, request), name='+editstatus-page')
374+ ... (ubuntu_thunderbird_task, request), name='+editstatus')
375 >>> edit_view.initialize()
376 >>> for error in edit_view.errors:
377 ... print error
378@@ -112,7 +112,7 @@
379 ... }
380 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
381 >>> edit_view = getMultiAdapter(
382- ... (ubuntu_task, request), name='+editstatus-page')
383+ ... (ubuntu_task, request), name='+editstatus')
384 >>> edit_view.initialize()
385 >>> for error in edit_view.errors:
386 ... print error
387@@ -144,7 +144,7 @@
388 ... }
389 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
390 >>> edit_view = getMultiAdapter(
391- ... (ubuntu_grumpy_task, request), name='+editstatus-page')
392+ ... (ubuntu_grumpy_task, request), name='+editstatus')
393 >>> edit_view.initialize()
394 >>> for error in edit_view.errors:
395 ... print error
396@@ -155,7 +155,7 @@
397
398 == Edit the Product ==
399
400-+editstatus-page allows a bug to be retargeted to another product.
401++editstatus allows a bug to be retargeted to another product.
402
403 >>> bug_seven = getUtility(IBugSet).get(7)
404 >>> product_task = bug_seven.bugtasks[0]
405@@ -173,7 +173,7 @@
406 ... }
407 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
408 >>> edit_view = getMultiAdapter(
409- ... (product_task, request), name='+editstatus-page')
410+ ... (product_task, request), name='+editstatus')
411 >>> edit_view.initialize()
412 >>> [str(error) for error in edit_view.errors]
413 []
414@@ -193,7 +193,7 @@
415 ... }
416 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
417 >>> edit_view = getMultiAdapter(
418- ... (product_task, request), name='+editstatus-page')
419+ ... (product_task, request), name='+editstatus')
420 >>> edit_view.initialize()
421 >>> for error in edit_view.errors:
422 ... print error
423@@ -235,7 +235,7 @@
424 ... 'thunderbird.importance': 'Critical',
425 ... 'thunderbird.bugwatch': '6'})
426 >>> edit_view = getMultiAdapter(
427- ... (thunderbird_task, request), name='+editstatus-page')
428+ ... (thunderbird_task, request), name='+editstatus')
429 >>> edit_view.initialize()
430 >>> thunderbird_task.bugwatch == bugzilla_watch
431 True
432@@ -251,7 +251,7 @@
433 ... 'thunderbird.actions.save': 'Save Changes',
434 ... 'thunderbird.bugwatch-empty-marker': '1'})
435 >>> edit_view = getMultiAdapter(
436- ... (thunderbird_task, request), name='+editstatus-page')
437+ ... (thunderbird_task, request), name='+editstatus')
438 >>> edit_view.initialize()
439 >>> thunderbird_task.bugwatch is None
440 True
441@@ -280,7 +280,7 @@
442 >>> request = LaunchpadTestRequest()
443 >>> ubuntu_task = getUtility(IBugTaskSet).get(17)
444 >>> bugtask_edit_view = getMultiAdapter(
445- ... (ubuntu_task, request), name="+editstatus-page")
446+ ... (ubuntu_task, request), name="+editstatus")
447 >>> bugtask_edit_view.initialize()
448
449 >>> IInputWidget.providedBy(bugtask_edit_view.widgets['milestone'])
450@@ -293,7 +293,7 @@
451 >>> login("no-priv@canonical.com")
452
453 >>> bugtask_edit_view = getMultiAdapter(
454- ... (ubuntu_task, request), name="+editstatus-page")
455+ ... (ubuntu_task, request), name="+editstatus")
456 >>> bugtask_edit_view.initialize()
457
458 >>> isinstance(bugtask_edit_view.widgets['milestone'], ItemDisplayWidget)
459@@ -312,7 +312,7 @@
460 Unlike before, no-priv can now edit the milestone.
461
462 >>> bugtask_edit_view = getMultiAdapter(
463- ... (ubuntu_task, request), name="+editstatus-page")
464+ ... (ubuntu_task, request), name="+editstatus")
465 >>> bugtask_edit_view.initialize()
466
467 >>> IInputWidget.providedBy(bugtask_edit_view.widgets['milestone'])
468
469=== modified file 'lib/lp/bugs/templates/bugtask-index.pt'
470--- lib/lp/bugs/templates/bugtask-index.pt 2011-02-25 22:09:46 +0000
471+++ lib/lp/bugs/templates/bugtask-index.pt 2011-03-15 03:31:14 +0000
472@@ -72,7 +72,6 @@
473 <div id="tags-autocomplete">
474 <div id="tags-autocomplete-content"></div>
475 </div>
476- <tal:block condition="view/reportBugInContext" />
477
478 <p class="informational message"
479 tal:condition="view/notices"