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

Proposed by William Grant
Status: Superseded
Proposed branch: lp:~wgrant/launchpad/delete-nullbugtask
Merge into: lp:launchpad
Diff against target: 815 lines (+48/-399)
13 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/configure.zcml (+0/-13)
lib/lp/bugs/doc/bugtask.txt (+1/-77)
lib/lp/bugs/doc/displaying-bugs-and-tasks.txt (+0/-17)
lib/lp/bugs/interfaces/bug.py (+0/-5)
lib/lp/bugs/interfaces/bugtask.py (+0/-11)
lib/lp/bugs/model/bug.py (+0/-11)
lib/lp/bugs/model/bugtask.py (+4/-67)
lib/lp/bugs/templates/bugtask-index.pt (+0/-1)
To merge this branch: bzr merge lp:~wgrant/launchpad/delete-nullbugtask
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+53367@code.launchpad.net

Commit message

Remove NullBugTask.

Description of the change

This branch removes NullBugTask's final callsite, using determine_target in validate_target_attribute instead.

With that gone, NullBugTask and its tests have also been deleted.

To post a comment you must log in.

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 04:26:27 +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 04:26:27 +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 04:26:27 +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 04:26:27 +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 04:26:27 +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/configure.zcml'
470--- lib/lp/bugs/configure.zcml 2011-03-08 01:56:34 +0000
471+++ lib/lp/bugs/configure.zcml 2011-03-15 04:26:27 +0000
472@@ -302,18 +302,6 @@
473 factory="lp.bugs.browser.bugcomment.BugCommentBreadcrumb"
474 permission="zope.Public"/>
475
476- <!-- NullBugTask -->
477-
478- <class
479- class="lp.bugs.model.bugtask.NullBugTask">
480- <require
481- permission="launchpad.View"
482- interface="lp.bugs.interfaces.bugtask.IBugTask"/>
483- <require
484- permission="launchpad.Edit"
485- set_schema="lp.bugs.interfaces.bugtask.IBugTask"/>
486- </class>
487-
488 <!-- BugTaskSearchParams -->
489 <class
490 class="lp.bugs.interfaces.bugtask.BugTaskSearchParams">
491@@ -691,7 +679,6 @@
492 getDirectSubscribers
493 getDirectSubscriptions
494 getIndirectSubscribers
495- getNullBugTask
496 is_complete
497 official_tags
498 who_made_private
499
500=== modified file 'lib/lp/bugs/doc/bugtask.txt'
501--- lib/lp/bugs/doc/bugtask.txt 2011-02-14 11:04:09 +0000
502+++ lib/lp/bugs/doc/bugtask.txt 2011-03-15 04:26:27 +0000
503@@ -732,83 +732,6 @@
504 False
505
506
507-== Null Bug Tasks ==
508-
509-Sometimes we need to be able to render a page for a bug in a context,
510-when the bug hasn't actually been filed yet in that context. For cases
511-like these, use the NullBugTask object.
512-
513-
514- >>> from lp.bugs.interfaces.bugtask import INullBugTask
515- >>> netapplet = productset.get(11)
516- >>> null_bugtask = bug_one.getNullBugTask(product=netapplet)
517- >>> verifyObject(INullBugTask, null_bugtask)
518- True
519- >>> IUpstreamBugTask.providedBy(null_bugtask)
520- True
521-
522- >>> null_bugtask.id is None
523- True
524- >>> null_bugtask.title
525- u'Bug #1 is not in NetApplet: "Firefox does not support SVG"'
526- >>> null_bugtask.product is netapplet
527- True
528- >>> null_bugtask.bug == bug_one
529- True
530- >>> null_bugtask.datecreated is None
531- True
532- >>> null_bugtask.date_assigned is None
533- True
534- >>> null_bugtask.age is None
535- True
536- >>> null_bugtask.status is None
537- True
538- >>> null_bugtask.sourcepackagename is None
539- True
540- >>> null_bugtask.distribution is None
541- True
542- >>> null_bugtask.distroseries is None
543- True
544- >>> null_bugtask.milestone is None
545- True
546- >>> null_bugtask.importance is None
547- True
548- >>> null_bugtask.assignee is None
549- True
550- >>> null_bugtask.bugwatch is None
551- True
552- >>> null_bugtask.owner is None
553- True
554- >>> null_bugtask.target == netapplet
555- True
556- >>> null_bugtask.bugtargetname
557- u'netapplet'
558- >>> expected_related_task_ids = [
559- ... task.id for task in null_bugtask.related_tasks]
560- >>> actual_related_task_ids = [task.id for task in bug_one.bugtasks]
561- >>> expected_related_task_ids.sort()
562- >>> actual_related_task_ids.sort()
563- >>> expected_related_task_ids == actual_related_task_ids
564- True
565-
566- >>> null_bugtask.conjoined_slave is None
567- True
568- >>> null_bugtask.conjoined_master is None
569- True
570-
571-The astute reader will have noticed that NullBugTask automatically
572-"marked" itself as providing the correct IBugTask interface. Let's see
573-two more examples:
574-
575- >>> ubuntu_null_bugtask = bug_one.getNullBugTask(distribution=ubuntu)
576- >>> IDistroBugTask.providedBy(ubuntu_null_bugtask)
577- True
578-
579- >>> warty_null_bugtask = bug_one.getNullBugTask(distroseries=warty)
580- >>> IDistroSeriesBugTask.providedBy(warty_null_bugtask)
581- True
582-
583-
584 = Bug Privacy =
585
586 A bug is either private or public. Private bugs are only visible (e.g. in search
587@@ -1080,6 +1003,7 @@
588 the latter is not exposed in `IBugTask`, so the `bugtargetdisplayname`
589 is used here.
590
591+ >>> netapplet = productset.get(11)
592 >>> upstream_task = bugtaskset.createTask(
593 ... bug=bug_one, product=netapplet, owner=mark,
594 ... status=STATUS_NEW, importance=IMPORTANCE_MEDIUM)
595
596=== modified file 'lib/lp/bugs/doc/displaying-bugs-and-tasks.txt'
597--- lib/lp/bugs/doc/displaying-bugs-and-tasks.txt 2010-11-01 15:46:48 +0000
598+++ lib/lp/bugs/doc/displaying-bugs-and-tasks.txt 2011-03-15 04:26:27 +0000
599@@ -150,17 +150,6 @@
600 >>> render_bugtask_status(test_task)
601 u'Fix released, assigned to ...Foo Bar...'
602
603-This code also works for null bug tasks:
604-
605- >>> from lp.bugs.model.bug import Bug
606- >>> from lp.bugs.model.bugtask import NullBugTask
607- >>> from lp.registry.model.product import Product
608- >>> bug_one = Bug.get(1)
609- >>> netapplet = Product.selectOneBy(name="netapplet")
610- >>> null_bugtask = NullBugTask(bug=bug_one, product=netapplet)
611- >>> render_bugtask_status(null_bugtask)
612- u'Not reported in netapplet'
613-
614 Lastly, some cleanup:
615
616 >>> test_task.transitionToStatus(
617@@ -206,9 +195,3 @@
618
619 >>> related_task.transitionToStatus(
620 ... ORIGINAL_STATUS, getUtility(ILaunchBag).user)
621-
622-Null tasks are also supported:
623-
624- >>> render_bugtask_status_elsewhere(null_bugtask)
625- 'filed in 3 other places'
626-
627
628=== modified file 'lib/lp/bugs/interfaces/bug.py'
629--- lib/lp/bugs/interfaces/bug.py 2011-03-08 01:56:34 +0000
630+++ lib/lp/bugs/interfaces/bug.py 2011-03-15 04:26:27 +0000
631@@ -721,11 +721,6 @@
632 returned rows. The step parameter in each slice is ignored.
633 """
634
635- def getNullBugTask(product=None, productseries=None,
636- sourcepackagename=None, distribution=None,
637- distroseries=None):
638- """Create an INullBugTask and return it for the given parameters."""
639-
640 @operation_parameters(
641 target=Reference(schema=Interface, title=_('Target')))
642 @call_with(owner=REQUEST_USER)
643
644=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
645--- lib/lp/bugs/interfaces/bugtask.py 2011-03-07 21:05:12 +0000
646+++ lib/lp/bugs/interfaces/bugtask.py 2011-03-15 04:26:27 +0000
647@@ -30,7 +30,6 @@
648 'IDistroSeriesBugTask',
649 'IFrontPageBugTaskSearch',
650 'INominationsReviewTableBatchNavigator',
651- 'INullBugTask',
652 'IPersonBugTaskSearch',
653 'IProductSeriesBugTask',
654 'IRemoveQuestionFromBugTaskForm',
655@@ -839,16 +838,6 @@
656 IBugWatch['bugtasks'].value_type.schema = IBugTask
657
658
659-class INullBugTask(IBugTask):
660- """A marker interface for an IBugTask that doesn't exist in a context.
661-
662- An INullBugTask is useful when wanting to view a bug in a context
663- where that bug hasn't yet been reported. This might happen, for
664- example, when searching to see if a bug you want to report has
665- already been filed and finding matching reports that don't yet
666- have tasks reported in your context.
667- """
668-
669 UPSTREAM_STATUS_VOCABULARY = SimpleVocabulary(
670 [SimpleTerm(
671 "pending_bugwatch",
672
673=== modified file 'lib/lp/bugs/model/bug.py'
674--- lib/lp/bugs/model/bug.py 2011-03-08 01:56:34 +0000
675+++ lib/lp/bugs/model/bug.py 2011-03-15 04:26:27 +0000
676@@ -168,7 +168,6 @@
677 BugTask,
678 bugtask_sort_key,
679 get_bug_privacy_filter,
680- NullBugTask,
681 )
682 from lp.bugs.model.bugwatch import BugWatch
683 from lp.bugs.model.structuralsubscription import (
684@@ -1429,16 +1428,6 @@
685 need_validity=True))
686 return DecoratedResultSet(result, pre_iter_hook=eager_load_owners)
687
688- def getNullBugTask(self, product=None, productseries=None,
689- sourcepackagename=None, distribution=None,
690- distroseries=None):
691- """See `IBug`."""
692- return NullBugTask(bug=self, product=product,
693- productseries=productseries,
694- sourcepackagename=sourcepackagename,
695- distribution=distribution,
696- distroseries=distroseries)
697-
698 def addNomination(self, owner, target):
699 """See `IBug`."""
700 if not self.canBeNominatedFor(target):
701
702=== modified file 'lib/lp/bugs/model/bugtask.py'
703--- lib/lp/bugs/model/bugtask.py 2011-03-11 03:06:43 +0000
704+++ lib/lp/bugs/model/bugtask.py 2011-03-15 04:26:27 +0000
705@@ -13,7 +13,6 @@
706 'BugTaskMixin',
707 'BugTask',
708 'BugTaskSet',
709- 'NullBugTask',
710 'bugtask_sort_key',
711 'determine_target',
712 'get_bug_privacy_filter',
713@@ -119,7 +118,6 @@
714 IDistroSeriesBugTask,
715 IllegalRelatedBugTasksParams,
716 IllegalTarget,
717- INullBugTask,
718 IProductSeriesBugTask,
719 IUpstreamBugTask,
720 RESOLVED_BUGTASK_STATUSES,
721@@ -374,68 +372,6 @@
722 return sorted(result, key=pillar_sort_key)
723
724
725-class NullBugTask(BugTaskMixin):
726- """A null object for IBugTask.
727-
728- This class is used, for example, to be able to render a URL like:
729-
730- /products/evolution/+bug/5
731-
732- when bug #5 isn't yet reported in evolution.
733- """
734- implements(INullBugTask)
735-
736- def __init__(self, bug, product=None, productseries=None,
737- sourcepackagename=None, distribution=None,
738- distroseries=None):
739- """Initialize a NullBugTask."""
740- self.id = None
741- self.bug = bug
742- self.product = product
743- self.productseries = productseries
744- self.sourcepackagename = sourcepackagename
745- self.distribution = distribution
746- self.distroseries = distroseries
747-
748- # Mark the task with the correct interface, depending on its
749- # context.
750- if self.product:
751- alsoProvides(self, IUpstreamBugTask)
752- elif self.distribution:
753- alsoProvides(self, IDistroBugTask)
754- elif self.distroseries:
755- alsoProvides(self, IDistroSeriesBugTask)
756- elif self.productseries:
757- alsoProvides(self, IProductSeriesBugTask)
758- else:
759- raise AssertionError('Unknown NullBugTask: %r.' % self)
760-
761- # Make us provide the interface by setting all required attributes
762- # to None, and define the methods as raising NotImplementedError.
763- # The attributes are set to None because it doesn't make
764- # sense for these attributes to have a value when there is no
765- # real task there. (In fact, it may make sense for these
766- # values to be non-null, but I haven't yet found a use case
767- # for it, and I don't think there's any point on designing for
768- # that until we've encountered one.)
769- def this_is_a_null_bugtask_method(*args, **kwargs):
770- raise NotImplementedError
771-
772- for name, spec in INullBugTask.namesAndDescriptions(True):
773- if not hasattr(self, name):
774- if IMethod.providedBy(spec):
775- value = this_is_a_null_bugtask_method
776- else:
777- value = None
778- setattr(self, name, value)
779-
780- @property
781- def title(self):
782- """See `IBugTask`."""
783- return 'Bug #%s is not in %s: "%s"' % (
784- self.bug.id, self.bugtargetdisplayname, self.bug.title)
785-
786-
787 def BugTaskToBugAdapter(bugtask):
788 """Adapt an IBugTask to an IBug."""
789 return bugtask.bug
790@@ -467,9 +403,10 @@
791 else:
792 target_params[attr[:-2]] = getUtility(utility_iface).get(value)
793
794- # Use a NullBugTask to determine the new target.
795- nulltask = NullBugTask(self.bug, **target_params)
796- self.updateTargetNameCache(nulltask.target)
797+ # Update the target name cache with the potential new target. The
798+ # attribute changes haven't been made yet, so we need to calculate the
799+ # target manually.
800+ self.updateTargetNameCache(determine_target(**target_params))
801
802 return value
803
804
805=== modified file 'lib/lp/bugs/templates/bugtask-index.pt'
806--- lib/lp/bugs/templates/bugtask-index.pt 2011-02-25 22:09:46 +0000
807+++ lib/lp/bugs/templates/bugtask-index.pt 2011-03-15 04:26:27 +0000
808@@ -72,7 +72,6 @@
809 <div id="tags-autocomplete">
810 <div id="tags-autocomplete-content"></div>
811 </div>
812- <tal:block condition="view/reportBugInContext" />
813
814 <p class="informational message"
815 tal:condition="view/notices"