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
=== modified file 'lib/lp/bugs/browser/bugnomination.py'
--- lib/lp/bugs/browser/bugnomination.py 2011-02-02 15:43:31 +0000
+++ lib/lp/bugs/browser/bugnomination.py 2011-03-15 04:26:27 +0000
@@ -15,10 +15,7 @@
1515
16import pytz16import pytz
17from zope.component import getUtility17from zope.component import getUtility
18from zope.publisher.interfaces import (18from zope.publisher.interfaces import implements
19 implements,
20 NotFound,
21 )
2219
23from canonical.launchpad import _20from canonical.launchpad import _
24from canonical.launchpad.webapp import (21from canonical.launchpad.webapp import (
@@ -41,7 +38,6 @@
41 IBugNomination,38 IBugNomination,
42 IBugNominationForm,39 IBugNominationForm,
43 )40 )
44from lp.bugs.interfaces.bugtask import INullBugTask
45from lp.bugs.interfaces.cve import ICveSet41from lp.bugs.interfaces.cve import ICveSet
4642
4743
@@ -65,10 +61,6 @@
65 LaunchpadFormView.__init__(self, context, request)61 LaunchpadFormView.__init__(self, context, request)
6662
67 def initialize(self):63 def initialize(self):
68 if INullBugTask.providedBy(self.current_bugtask):
69 # It shouldn't be possible to nominate a bug that hasn't
70 # been reported yet.
71 raise NotFound(self.current_bugtask, '+nominate', self.request)
72 LaunchpadFormView.initialize(self)64 LaunchpadFormView.initialize(self)
73 # Update the submit label based on the user's permission.65 # Update the submit label based on the user's permission.
74 submit_action = self.__class__.actions.byname['actions.submit']66 submit_action = self.__class__.actions.byname['actions.submit']
7567
=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py 2011-03-10 16:15:57 +0000
+++ lib/lp/bugs/browser/bugtask.py 2011-03-15 04:26:27 +0000
@@ -236,7 +236,6 @@
236 IDistroSeriesBugTask,236 IDistroSeriesBugTask,
237 IFrontPageBugTaskSearch,237 IFrontPageBugTaskSearch,
238 INominationsReviewTableBatchNavigator,238 INominationsReviewTableBatchNavigator,
239 INullBugTask,
240 IPersonBugTaskSearch,239 IPersonBugTaskSearch,
241 IProductSeriesBugTask,240 IProductSeriesBugTask,
242 IRemoveQuestionFromBugTaskForm,241 IRemoveQuestionFromBugTaskForm,
@@ -490,7 +489,7 @@
490 """Return the IBugTask for this name in this context.489 """Return the IBugTask for this name in this context.
491490
492 If the bug has been reported, but not in this specific context, a491 If the bug has been reported, but not in this specific context, a
493 NullBugTask will be returned.492 redirect to the default context will be returned.
494493
495 Raises NotFoundError if no bug with the given name is found.494 Raises NotFoundError if no bug with the given name is found.
496495
@@ -514,59 +513,15 @@
514 # Security proxy this object on the way out.513 # Security proxy this object on the way out.
515 return getUtility(IBugTaskSet).get(bugtask.id)514 return getUtility(IBugTaskSet).get(bugtask.id)
516515
517 # If we've come this far, it means that no actual task exists in this516 # If we've come this far, there's no task for the requested
518 # context, so we'll return a null bug task. This makes it possible to,517 # context. Redirect to one that exists.
519 # for example, return a bug page for a context in which the bug hasn't518 return self.redirectSubTree(canonical_url(bug.default_bugtask))
520 # yet been reported.
521 if IProduct.providedBy(context):
522 null_bugtask = bug.getNullBugTask(product=context)
523 elif IProductSeries.providedBy(context):
524 null_bugtask = bug.getNullBugTask(productseries=context)
525 elif IDistribution.providedBy(context):
526 null_bugtask = bug.getNullBugTask(distribution=context)
527 elif IDistributionSourcePackage.providedBy(context):
528 null_bugtask = bug.getNullBugTask(
529 distribution=context.distribution,
530 sourcepackagename=context.sourcepackagename)
531 elif IDistroSeries.providedBy(context):
532 null_bugtask = bug.getNullBugTask(distroseries=context)
533 elif ISourcePackage.providedBy(context):
534 null_bugtask = bug.getNullBugTask(
535 distroseries=context.distroseries,
536 sourcepackagename=context.sourcepackagename)
537 else:
538 raise TypeError(
539 "Unknown context type for bug task: %s" % repr(context))
540
541 return null_bugtask
542519
543520
544class BugTaskNavigation(Navigation):521class BugTaskNavigation(Navigation):
545 """Navigation for the `IBugTask`."""522 """Navigation for the `IBugTask`."""
546 usedfor = IBugTask523 usedfor = IBugTask
547524
548 def traverse(self, name):
549 """Traverse the `IBugTask`."""
550 # Are we traversing to the view or edit status page of the
551 # bugtask? If so, and the task actually exists, return the
552 # appropriate page. If the task doesn't yet exist (i.e. it's a
553 # NullBugTask), then return a 404. In other words, the URL:
554 #
555 # /products/foo/+bug/1/+viewstatus
556 #
557 # will return the +viewstatus page if bug 1 has actually been
558 # reported in "foo". If bug 1 has not yet been reported in "foo",
559 # a 404 will be returned.
560 if name not in ("+viewstatus", "+editstatus"):
561 # You're going in the wrong direction.
562 return None
563 if INullBugTask.providedBy(self.context):
564 # The bug has not been reported in this context.
565 return None
566 # Yes! The bug has been reported in this context.
567 return getMultiAdapter((self.context, self.request),
568 name=(name + "-page"))
569
570 @stepthrough('attachments')525 @stepthrough('attachments')
571 def traverse_attachments(self, name):526 def traverse_attachments(self, name):
572 """traverse to an attachment by id."""527 """traverse to an attachment by id."""
@@ -655,13 +610,8 @@
655610
656 @property611 @property
657 def page_title(self):612 def page_title(self):
658 bugtask = self.context613 heading = 'Bug #%s in %s' % (
659 if INullBugTask.providedBy(bugtask):614 self.context.bug.id, self.context.bugtargetdisplayname)
660 heading = 'Bug #%s is not in %s' % (
661 bugtask.bug.id, bugtask.bugtargetdisplayname)
662 else:
663 heading = 'Bug #%s in %s' % (
664 bugtask.bug.id, bugtask.bugtargetdisplayname)
665 return smartquote('%s: "%s"') % (heading, self.context.bug.title)615 return smartquote('%s: "%s"') % (heading, self.context.bug.title)
666616
667 @property617 @property
@@ -698,12 +648,6 @@
698 # See render() for how this flag is used.648 # See render() for how this flag is used.
699 self._redirecting_to_bug_list = False649 self._redirecting_to_bug_list = False
700650
701 # If the bug is not reported in this context, redirect
702 # to the default bug task.
703 if not self.isReportedInContext():
704 self.request.response.redirect(
705 canonical_url(self.context.bug.default_bugtask))
706
707 self.bug_title_edit_widget = TextLineEditorWidget(651 self.bug_title_edit_widget = TextLineEditorWidget(
708 bug, IBug['title'], "Edit this summary", 'h1',652 bug, IBug['title'], "Edit this summary", 'h1',
709 edit_url=canonical_url(self.context, view_name='+edit'))653 edit_url=canonical_url(self.context, view_name='+edit'))
@@ -741,69 +685,6 @@
741 series.bugtargetdisplayname)685 series.bugtargetdisplayname)
742 self.request.response.redirect(canonical_url(self.context))686 self.request.response.redirect(canonical_url(self.context))
743687
744 def reportBugInContext(self):
745 """Report the bug affects the current context."""
746 fake_task = self.context
747 if self.request.form.get("reportbug"):
748 if self.isReportedInContext():
749 self.notices.append(
750 "The bug is already reported in this context.")
751 return
752 # The user has requested that the bug be reported in this
753 # context.
754 if IUpstreamBugTask.providedBy(fake_task):
755 # Create a real upstream task in this context.
756 real_task = fake_task.bug.addTask(
757 getUtility(ILaunchBag).user, fake_task.product)
758 elif IDistroBugTask.providedBy(fake_task):
759 # Create a real distro bug task in this context.
760 real_task = fake_task.bug.addTask(
761 getUtility(ILaunchBag).user, fake_task.target)
762 elif IDistroSeriesBugTask.providedBy(fake_task):
763 self._nominateBug(fake_task.distroseries)
764 return
765 elif IProductSeriesBugTask.providedBy(fake_task):
766 self._nominateBug(fake_task.productseries)
767 return
768 else:
769 raise TypeError(
770 "Unknown bug task type: %s" % repr(fake_task))
771
772 self.context = real_task
773
774 # Add an appropriate feedback message
775 self.notices.append("Thank you for your bug report.")
776
777 def isReportedInContext(self):
778 """Is the bug reported in this context? Returns True or False.
779
780 It considers a nominated bug to be reported.
781
782 This is particularly useful for views that may render a
783 NullBugTask.
784 """
785 if self.context.id is not None:
786 # Fast path for real bugtasks: they have a DB id.
787 return True
788 params = BugTaskSearchParams(user=self.user, bug=self.context.bug)
789 matching_bugtasks = self.context.target.searchTasks(params)
790 if self.context.productseries is not None:
791 nomination_target = self.context.productseries
792 elif self.context.distroseries is not None:
793 nomination_target = self.context.distroseries
794 else:
795 nomination_target = None
796 if nomination_target is not None:
797 try:
798 nomination = self.context.bug.getNominationFor(
799 nomination_target)
800 except NotFoundError:
801 nomination = None
802 else:
803 nomination = None
804
805 return nomination is not None or matching_bugtasks.count() > 0
806
807 def isSeriesTargetableContext(self):688 def isSeriesTargetableContext(self):
808 """Is the context something that supports Series targeting?689 """Is the context something that supports Series targeting?
809690
@@ -1744,17 +1625,12 @@
1744 The assignee is included.1625 The assignee is included.
1745 """1626 """
1746 bugtask = self.context1627 bugtask = self.context
1747
1748 if INullBugTask.providedBy(bugtask):
1749 return u"Not reported in %s" % bugtask.bugtargetname
1750
1751 assignee = bugtask.assignee1628 assignee = bugtask.assignee
1752 status = bugtask.status1629 status = bugtask.status
1753 status_title = status.title.capitalize()1630 status_title = status.title.capitalize()
17541631
1755 if not assignee:1632 if not assignee:
1756 return status_title + ' (unassigned)'1633 return status_title + ' (unassigned)'
1757
1758 assignee_html = PersonFormatterAPI(assignee).link('+assignedbugs')1634 assignee_html = PersonFormatterAPI(assignee).link('+assignedbugs')
17591635
1760 if status in (BugTaskStatus.INVALID,1636 if status in (BugTaskStatus.INVALID,
17611637
=== modified file 'lib/lp/bugs/browser/configure.zcml'
--- lib/lp/bugs/browser/configure.zcml 2011-03-11 21:31:10 +0000
+++ lib/lp/bugs/browser/configure.zcml 2011-03-15 04:26:27 +0000
@@ -637,35 +637,27 @@
637 class="lp.bugs.browser.bugalsoaffects.BugAlsoAffectsDistroMetaView"637 class="lp.bugs.browser.bugalsoaffects.BugAlsoAffectsDistroMetaView"
638 permission="launchpad.AnyPerson"/>638 permission="launchpad.AnyPerson"/>
639 <browser:page639 <browser:page
640 name="+editstatus-page"640 name="+edit-form"
641 for="lp.bugs.interfaces.bugtask.IUpstreamBugTask"641 for="lp.bugs.interfaces.bugtask.IUpstreamBugTask"
642 class="lp.bugs.browser.bugtask.BugTaskEditView"642 class="lp.bugs.browser.bugtask.BugTaskEditView"
643 permission="launchpad.Edit"643 permission="launchpad.Edit"
644 template="../templates/bugtask-edit.pt">644 template="../templates/bugtask-edit-form.pt">
645 </browser:page>645 </browser:page>
646 <browser:page646 <browser:page
647 name="+edit-form"647 name="+edit-form"
648 for="lp.bugs.interfaces.bugtask.IUpstreamBugTask"648 for="lp.bugs.interfaces.bugtask.IProductSeriesBugTask"
649 class="lp.bugs.browser.bugtask.BugTaskEditView"649 class="lp.bugs.browser.bugtask.BugTaskEditView"
650 permission="launchpad.Edit"650 permission="launchpad.Edit"
651 template="../templates/bugtask-edit-form.pt">651 template="../templates/bugtask-edit-form.pt">
652 </browser:page>652 </browser:page>
653 <browser:page653 <browser:page
654 name="+editstatus-page"654 name="+editstatus"
655 for="lp.bugs.interfaces.bugtask.IProductSeriesBugTask"655 for="lp.bugs.interfaces.bugtask.IBugTask"
656 class="lp.bugs.browser.bugtask.BugTaskEditView"656 class="lp.bugs.browser.bugtask.BugTaskEditView"
657 permission="launchpad.Edit"657 permission="launchpad.Edit"
658 template="../templates/bugtask-edit.pt">658 template="../templates/bugtask-edit.pt"/>
659 </browser:page>659 <browser:page
660 <browser:page660 name="+viewstatus"
661 name="+edit-form"
662 for="lp.bugs.interfaces.bugtask.IProductSeriesBugTask"
663 class="lp.bugs.browser.bugtask.BugTaskEditView"
664 permission="launchpad.Edit"
665 template="../templates/bugtask-edit-form.pt">
666 </browser:page>
667 <browser:page
668 name="+viewstatus-page"
669 for="lp.bugs.interfaces.bugtask.IBugTask"661 for="lp.bugs.interfaces.bugtask.IBugTask"
670 class="lp.bugs.browser.bugtask.BugTaskStatusView"662 class="lp.bugs.browser.bugtask.BugTaskStatusView"
671 permission="launchpad.View"663 permission="launchpad.View"
@@ -680,13 +672,6 @@
680 for="lp.bugs.interfaces.bugtask.IDistroBugTask"672 for="lp.bugs.interfaces.bugtask.IDistroBugTask"
681 name="+index"/>673 name="+index"/>
682 <browser:page674 <browser:page
683 name="+editstatus-page"
684 for="lp.bugs.interfaces.bugtask.IDistroBugTask"
685 class="lp.bugs.browser.bugtask.BugTaskEditView"
686 permission="launchpad.Edit"
687 template="../templates/bugtask-edit.pt">
688 </browser:page>
689 <browser:page
690 name="+edit-form"675 name="+edit-form"
691 for="lp.bugs.interfaces.bugtask.IDistroBugTask"676 for="lp.bugs.interfaces.bugtask.IDistroBugTask"
692 class="lp.bugs.browser.bugtask.BugTaskEditView"677 class="lp.bugs.browser.bugtask.BugTaskEditView"
@@ -697,13 +682,6 @@
697 for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"682 for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"
698 name="+index"/>683 name="+index"/>
699 <browser:page684 <browser:page
700 name="+editstatus-page"
701 for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"
702 class="lp.bugs.browser.bugtask.BugTaskEditView"
703 permission="launchpad.Edit"
704 template="../templates/bugtask-edit.pt">
705 </browser:page>
706 <browser:page
707 name="+edit-form"685 name="+edit-form"
708 for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"686 for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"
709 class="lp.bugs.browser.bugtask.BugTaskEditView"687 class="lp.bugs.browser.bugtask.BugTaskEditView"
710688
=== modified file 'lib/lp/bugs/browser/tests/bug-views.txt'
--- lib/lp/bugs/browser/tests/bug-views.txt 2011-02-15 09:31:20 +0000
+++ lib/lp/bugs/browser/tests/bug-views.txt 2011-03-15 04:26:27 +0000
@@ -777,7 +777,7 @@
777 ... 'testproduct.actions.save': 'Save Changes',777 ... 'testproduct.actions.save': 'Save Changes',
778 ... })778 ... })
779 >>> view = getMultiAdapter(779 >>> view = getMultiAdapter(
780 ... (bug.bugtasks[0], request), name="+editstatus-page")780 ... (bug.bugtasks[0], request), name="+editstatus")
781 >>> view.initialize()781 >>> view.initialize()
782782
783 >>> view = getMultiAdapter(783 >>> view = getMultiAdapter(
@@ -833,7 +833,7 @@
833 ... 'testproduct.actions.save': 'Save Changes',833 ... 'testproduct.actions.save': 'Save Changes',
834 ... })834 ... })
835 >>> view = getMultiAdapter(835 >>> view = getMultiAdapter(
836 ... (bug.bugtasks[0], request), name="+editstatus-page")836 ... (bug.bugtasks[0], request), name="+editstatus")
837 >>> view.initialize()837 >>> view.initialize()
838838
839 >>> view = getMultiAdapter(839 >>> view = getMultiAdapter(
840840
=== modified file 'lib/lp/bugs/browser/tests/bugtask-edit-views.txt'
--- lib/lp/bugs/browser/tests/bugtask-edit-views.txt 2010-10-18 22:24:59 +0000
+++ lib/lp/bugs/browser/tests/bugtask-edit-views.txt 2011-03-15 04:26:27 +0000
@@ -29,7 +29,7 @@
29 ... ubuntu_thunderbird_task.sourcepackagename.name}29 ... ubuntu_thunderbird_task.sourcepackagename.name}
30 >>> request = LaunchpadTestRequest(method='POST', form=edit_form)30 >>> request = LaunchpadTestRequest(method='POST', form=edit_form)
31 >>> edit_view = getMultiAdapter(31 >>> edit_view = getMultiAdapter(
32 ... (ubuntu_thunderbird_task, request), name='+editstatus-page')32 ... (ubuntu_thunderbird_task, request), name='+editstatus')
33 >>> edit_view.initialize()33 >>> edit_view.initialize()
34 >>> ubuntu_thunderbird_task.status.title34 >>> ubuntu_thunderbird_task.status.title
35 'In Progress'35 'In Progress'
@@ -50,7 +50,7 @@
50 >>> edit_form['ubuntu_thunderbird.sourcepackagename'] = u'linux-2.6.12'50 >>> edit_form['ubuntu_thunderbird.sourcepackagename'] = u'linux-2.6.12'
51 >>> request = LaunchpadTestRequest(method='POST', form=edit_form)51 >>> request = LaunchpadTestRequest(method='POST', form=edit_form)
52 >>> edit_view = getMultiAdapter(52 >>> edit_view = getMultiAdapter(
53 ... (ubuntu_thunderbird_task, request), name='+editstatus-page')53 ... (ubuntu_thunderbird_task, request), name='+editstatus')
54 >>> edit_view.initialize()54 >>> edit_view.initialize()
55 >>> ubuntu_thunderbird_task.sourcepackagename.name55 >>> ubuntu_thunderbird_task.sourcepackagename.name
56 u'linux-source-2.6.15'56 u'linux-source-2.6.15'
@@ -73,7 +73,7 @@
73 >>> edit_form['ubuntu_thunderbird.sourcepackagename'] = u'no-such-package'73 >>> edit_form['ubuntu_thunderbird.sourcepackagename'] = u'no-such-package'
74 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')74 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
75 >>> edit_view = getMultiAdapter(75 >>> edit_view = getMultiAdapter(
76 ... (ubuntu_thunderbird_task, request), name='+editstatus-page')76 ... (ubuntu_thunderbird_task, request), name='+editstatus')
77 >>> edit_view.initialize()77 >>> edit_view.initialize()
78 >>> for error in edit_view.errors:78 >>> for error in edit_view.errors:
79 ... print error79 ... print error
@@ -112,7 +112,7 @@
112 ... }112 ... }
113 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')113 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
114 >>> edit_view = getMultiAdapter(114 >>> edit_view = getMultiAdapter(
115 ... (ubuntu_task, request), name='+editstatus-page')115 ... (ubuntu_task, request), name='+editstatus')
116 >>> edit_view.initialize()116 >>> edit_view.initialize()
117 >>> for error in edit_view.errors:117 >>> for error in edit_view.errors:
118 ... print error118 ... print error
@@ -144,7 +144,7 @@
144 ... }144 ... }
145 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')145 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
146 >>> edit_view = getMultiAdapter(146 >>> edit_view = getMultiAdapter(
147 ... (ubuntu_grumpy_task, request), name='+editstatus-page')147 ... (ubuntu_grumpy_task, request), name='+editstatus')
148 >>> edit_view.initialize()148 >>> edit_view.initialize()
149 >>> for error in edit_view.errors:149 >>> for error in edit_view.errors:
150 ... print error150 ... print error
@@ -155,7 +155,7 @@
155155
156== Edit the Product ==156== Edit the Product ==
157157
158+editstatus-page allows a bug to be retargeted to another product.158+editstatus allows a bug to be retargeted to another product.
159159
160 >>> bug_seven = getUtility(IBugSet).get(7)160 >>> bug_seven = getUtility(IBugSet).get(7)
161 >>> product_task = bug_seven.bugtasks[0]161 >>> product_task = bug_seven.bugtasks[0]
@@ -173,7 +173,7 @@
173 ... }173 ... }
174 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')174 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
175 >>> edit_view = getMultiAdapter(175 >>> edit_view = getMultiAdapter(
176 ... (product_task, request), name='+editstatus-page')176 ... (product_task, request), name='+editstatus')
177 >>> edit_view.initialize()177 >>> edit_view.initialize()
178 >>> [str(error) for error in edit_view.errors]178 >>> [str(error) for error in edit_view.errors]
179 []179 []
@@ -193,7 +193,7 @@
193 ... }193 ... }
194 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')194 >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
195 >>> edit_view = getMultiAdapter(195 >>> edit_view = getMultiAdapter(
196 ... (product_task, request), name='+editstatus-page')196 ... (product_task, request), name='+editstatus')
197 >>> edit_view.initialize()197 >>> edit_view.initialize()
198 >>> for error in edit_view.errors:198 >>> for error in edit_view.errors:
199 ... print error199 ... print error
@@ -235,7 +235,7 @@
235 ... 'thunderbird.importance': 'Critical',235 ... 'thunderbird.importance': 'Critical',
236 ... 'thunderbird.bugwatch': '6'})236 ... 'thunderbird.bugwatch': '6'})
237 >>> edit_view = getMultiAdapter(237 >>> edit_view = getMultiAdapter(
238 ... (thunderbird_task, request), name='+editstatus-page')238 ... (thunderbird_task, request), name='+editstatus')
239 >>> edit_view.initialize()239 >>> edit_view.initialize()
240 >>> thunderbird_task.bugwatch == bugzilla_watch240 >>> thunderbird_task.bugwatch == bugzilla_watch
241 True241 True
@@ -251,7 +251,7 @@
251 ... 'thunderbird.actions.save': 'Save Changes',251 ... 'thunderbird.actions.save': 'Save Changes',
252 ... 'thunderbird.bugwatch-empty-marker': '1'})252 ... 'thunderbird.bugwatch-empty-marker': '1'})
253 >>> edit_view = getMultiAdapter(253 >>> edit_view = getMultiAdapter(
254 ... (thunderbird_task, request), name='+editstatus-page')254 ... (thunderbird_task, request), name='+editstatus')
255 >>> edit_view.initialize()255 >>> edit_view.initialize()
256 >>> thunderbird_task.bugwatch is None256 >>> thunderbird_task.bugwatch is None
257 True257 True
@@ -280,7 +280,7 @@
280 >>> request = LaunchpadTestRequest()280 >>> request = LaunchpadTestRequest()
281 >>> ubuntu_task = getUtility(IBugTaskSet).get(17)281 >>> ubuntu_task = getUtility(IBugTaskSet).get(17)
282 >>> bugtask_edit_view = getMultiAdapter(282 >>> bugtask_edit_view = getMultiAdapter(
283 ... (ubuntu_task, request), name="+editstatus-page")283 ... (ubuntu_task, request), name="+editstatus")
284 >>> bugtask_edit_view.initialize()284 >>> bugtask_edit_view.initialize()
285285
286 >>> IInputWidget.providedBy(bugtask_edit_view.widgets['milestone'])286 >>> IInputWidget.providedBy(bugtask_edit_view.widgets['milestone'])
@@ -293,7 +293,7 @@
293 >>> login("no-priv@canonical.com")293 >>> login("no-priv@canonical.com")
294294
295 >>> bugtask_edit_view = getMultiAdapter(295 >>> bugtask_edit_view = getMultiAdapter(
296 ... (ubuntu_task, request), name="+editstatus-page")296 ... (ubuntu_task, request), name="+editstatus")
297 >>> bugtask_edit_view.initialize()297 >>> bugtask_edit_view.initialize()
298298
299 >>> isinstance(bugtask_edit_view.widgets['milestone'], ItemDisplayWidget)299 >>> isinstance(bugtask_edit_view.widgets['milestone'], ItemDisplayWidget)
@@ -312,7 +312,7 @@
312Unlike before, no-priv can now edit the milestone.312Unlike before, no-priv can now edit the milestone.
313313
314 >>> bugtask_edit_view = getMultiAdapter(314 >>> bugtask_edit_view = getMultiAdapter(
315 ... (ubuntu_task, request), name="+editstatus-page")315 ... (ubuntu_task, request), name="+editstatus")
316 >>> bugtask_edit_view.initialize()316 >>> bugtask_edit_view.initialize()
317317
318 >>> IInputWidget.providedBy(bugtask_edit_view.widgets['milestone'])318 >>> IInputWidget.providedBy(bugtask_edit_view.widgets['milestone'])
319319
=== modified file 'lib/lp/bugs/configure.zcml'
--- lib/lp/bugs/configure.zcml 2011-03-08 01:56:34 +0000
+++ lib/lp/bugs/configure.zcml 2011-03-15 04:26:27 +0000
@@ -302,18 +302,6 @@
302 factory="lp.bugs.browser.bugcomment.BugCommentBreadcrumb"302 factory="lp.bugs.browser.bugcomment.BugCommentBreadcrumb"
303 permission="zope.Public"/>303 permission="zope.Public"/>
304304
305 <!-- NullBugTask -->
306
307 <class
308 class="lp.bugs.model.bugtask.NullBugTask">
309 <require
310 permission="launchpad.View"
311 interface="lp.bugs.interfaces.bugtask.IBugTask"/>
312 <require
313 permission="launchpad.Edit"
314 set_schema="lp.bugs.interfaces.bugtask.IBugTask"/>
315 </class>
316
317 <!-- BugTaskSearchParams -->305 <!-- BugTaskSearchParams -->
318 <class306 <class
319 class="lp.bugs.interfaces.bugtask.BugTaskSearchParams">307 class="lp.bugs.interfaces.bugtask.BugTaskSearchParams">
@@ -691,7 +679,6 @@
691 getDirectSubscribers679 getDirectSubscribers
692 getDirectSubscriptions680 getDirectSubscriptions
693 getIndirectSubscribers681 getIndirectSubscribers
694 getNullBugTask
695 is_complete682 is_complete
696 official_tags683 official_tags
697 who_made_private684 who_made_private
698685
=== modified file 'lib/lp/bugs/doc/bugtask.txt'
--- lib/lp/bugs/doc/bugtask.txt 2011-02-14 11:04:09 +0000
+++ lib/lp/bugs/doc/bugtask.txt 2011-03-15 04:26:27 +0000
@@ -732,83 +732,6 @@
732 False732 False
733733
734734
735== Null Bug Tasks ==
736
737Sometimes we need to be able to render a page for a bug in a context,
738when the bug hasn't actually been filed yet in that context. For cases
739like these, use the NullBugTask object.
740
741
742 >>> from lp.bugs.interfaces.bugtask import INullBugTask
743 >>> netapplet = productset.get(11)
744 >>> null_bugtask = bug_one.getNullBugTask(product=netapplet)
745 >>> verifyObject(INullBugTask, null_bugtask)
746 True
747 >>> IUpstreamBugTask.providedBy(null_bugtask)
748 True
749
750 >>> null_bugtask.id is None
751 True
752 >>> null_bugtask.title
753 u'Bug #1 is not in NetApplet: "Firefox does not support SVG"'
754 >>> null_bugtask.product is netapplet
755 True
756 >>> null_bugtask.bug == bug_one
757 True
758 >>> null_bugtask.datecreated is None
759 True
760 >>> null_bugtask.date_assigned is None
761 True
762 >>> null_bugtask.age is None
763 True
764 >>> null_bugtask.status is None
765 True
766 >>> null_bugtask.sourcepackagename is None
767 True
768 >>> null_bugtask.distribution is None
769 True
770 >>> null_bugtask.distroseries is None
771 True
772 >>> null_bugtask.milestone is None
773 True
774 >>> null_bugtask.importance is None
775 True
776 >>> null_bugtask.assignee is None
777 True
778 >>> null_bugtask.bugwatch is None
779 True
780 >>> null_bugtask.owner is None
781 True
782 >>> null_bugtask.target == netapplet
783 True
784 >>> null_bugtask.bugtargetname
785 u'netapplet'
786 >>> expected_related_task_ids = [
787 ... task.id for task in null_bugtask.related_tasks]
788 >>> actual_related_task_ids = [task.id for task in bug_one.bugtasks]
789 >>> expected_related_task_ids.sort()
790 >>> actual_related_task_ids.sort()
791 >>> expected_related_task_ids == actual_related_task_ids
792 True
793
794 >>> null_bugtask.conjoined_slave is None
795 True
796 >>> null_bugtask.conjoined_master is None
797 True
798
799The astute reader will have noticed that NullBugTask automatically
800"marked" itself as providing the correct IBugTask interface. Let's see
801two more examples:
802
803 >>> ubuntu_null_bugtask = bug_one.getNullBugTask(distribution=ubuntu)
804 >>> IDistroBugTask.providedBy(ubuntu_null_bugtask)
805 True
806
807 >>> warty_null_bugtask = bug_one.getNullBugTask(distroseries=warty)
808 >>> IDistroSeriesBugTask.providedBy(warty_null_bugtask)
809 True
810
811
812= Bug Privacy =735= Bug Privacy =
813736
814A bug is either private or public. Private bugs are only visible (e.g. in search737A bug is either private or public. Private bugs are only visible (e.g. in search
@@ -1080,6 +1003,7 @@
1080the latter is not exposed in `IBugTask`, so the `bugtargetdisplayname`1003the latter is not exposed in `IBugTask`, so the `bugtargetdisplayname`
1081is used here.1004is used here.
10821005
1006 >>> netapplet = productset.get(11)
1083 >>> upstream_task = bugtaskset.createTask(1007 >>> upstream_task = bugtaskset.createTask(
1084 ... bug=bug_one, product=netapplet, owner=mark,1008 ... bug=bug_one, product=netapplet, owner=mark,
1085 ... status=STATUS_NEW, importance=IMPORTANCE_MEDIUM)1009 ... status=STATUS_NEW, importance=IMPORTANCE_MEDIUM)
10861010
=== modified file 'lib/lp/bugs/doc/displaying-bugs-and-tasks.txt'
--- lib/lp/bugs/doc/displaying-bugs-and-tasks.txt 2010-11-01 15:46:48 +0000
+++ lib/lp/bugs/doc/displaying-bugs-and-tasks.txt 2011-03-15 04:26:27 +0000
@@ -150,17 +150,6 @@
150 >>> render_bugtask_status(test_task)150 >>> render_bugtask_status(test_task)
151 u'Fix released, assigned to ...Foo Bar...'151 u'Fix released, assigned to ...Foo Bar...'
152152
153This code also works for null bug tasks:
154
155 >>> from lp.bugs.model.bug import Bug
156 >>> from lp.bugs.model.bugtask import NullBugTask
157 >>> from lp.registry.model.product import Product
158 >>> bug_one = Bug.get(1)
159 >>> netapplet = Product.selectOneBy(name="netapplet")
160 >>> null_bugtask = NullBugTask(bug=bug_one, product=netapplet)
161 >>> render_bugtask_status(null_bugtask)
162 u'Not reported in netapplet'
163
164Lastly, some cleanup:153Lastly, some cleanup:
165154
166 >>> test_task.transitionToStatus(155 >>> test_task.transitionToStatus(
@@ -206,9 +195,3 @@
206195
207 >>> related_task.transitionToStatus(196 >>> related_task.transitionToStatus(
208 ... ORIGINAL_STATUS, getUtility(ILaunchBag).user)197 ... ORIGINAL_STATUS, getUtility(ILaunchBag).user)
209
210Null tasks are also supported:
211
212 >>> render_bugtask_status_elsewhere(null_bugtask)
213 'filed in 3 other places'
214
215198
=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py 2011-03-08 01:56:34 +0000
+++ lib/lp/bugs/interfaces/bug.py 2011-03-15 04:26:27 +0000
@@ -721,11 +721,6 @@
721 returned rows. The step parameter in each slice is ignored.721 returned rows. The step parameter in each slice is ignored.
722 """722 """
723723
724 def getNullBugTask(product=None, productseries=None,
725 sourcepackagename=None, distribution=None,
726 distroseries=None):
727 """Create an INullBugTask and return it for the given parameters."""
728
729 @operation_parameters(724 @operation_parameters(
730 target=Reference(schema=Interface, title=_('Target')))725 target=Reference(schema=Interface, title=_('Target')))
731 @call_with(owner=REQUEST_USER)726 @call_with(owner=REQUEST_USER)
732727
=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
--- lib/lp/bugs/interfaces/bugtask.py 2011-03-07 21:05:12 +0000
+++ lib/lp/bugs/interfaces/bugtask.py 2011-03-15 04:26:27 +0000
@@ -30,7 +30,6 @@
30 'IDistroSeriesBugTask',30 'IDistroSeriesBugTask',
31 'IFrontPageBugTaskSearch',31 'IFrontPageBugTaskSearch',
32 'INominationsReviewTableBatchNavigator',32 'INominationsReviewTableBatchNavigator',
33 'INullBugTask',
34 'IPersonBugTaskSearch',33 'IPersonBugTaskSearch',
35 'IProductSeriesBugTask',34 'IProductSeriesBugTask',
36 'IRemoveQuestionFromBugTaskForm',35 'IRemoveQuestionFromBugTaskForm',
@@ -839,16 +838,6 @@
839IBugWatch['bugtasks'].value_type.schema = IBugTask838IBugWatch['bugtasks'].value_type.schema = IBugTask
840839
841840
842class INullBugTask(IBugTask):
843 """A marker interface for an IBugTask that doesn't exist in a context.
844
845 An INullBugTask is useful when wanting to view a bug in a context
846 where that bug hasn't yet been reported. This might happen, for
847 example, when searching to see if a bug you want to report has
848 already been filed and finding matching reports that don't yet
849 have tasks reported in your context.
850 """
851
852UPSTREAM_STATUS_VOCABULARY = SimpleVocabulary(841UPSTREAM_STATUS_VOCABULARY = SimpleVocabulary(
853 [SimpleTerm(842 [SimpleTerm(
854 "pending_bugwatch",843 "pending_bugwatch",
855844
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2011-03-08 01:56:34 +0000
+++ lib/lp/bugs/model/bug.py 2011-03-15 04:26:27 +0000
@@ -168,7 +168,6 @@
168 BugTask,168 BugTask,
169 bugtask_sort_key,169 bugtask_sort_key,
170 get_bug_privacy_filter,170 get_bug_privacy_filter,
171 NullBugTask,
172 )171 )
173from lp.bugs.model.bugwatch import BugWatch172from lp.bugs.model.bugwatch import BugWatch
174from lp.bugs.model.structuralsubscription import (173from lp.bugs.model.structuralsubscription import (
@@ -1429,16 +1428,6 @@
1429 need_validity=True))1428 need_validity=True))
1430 return DecoratedResultSet(result, pre_iter_hook=eager_load_owners)1429 return DecoratedResultSet(result, pre_iter_hook=eager_load_owners)
14311430
1432 def getNullBugTask(self, product=None, productseries=None,
1433 sourcepackagename=None, distribution=None,
1434 distroseries=None):
1435 """See `IBug`."""
1436 return NullBugTask(bug=self, product=product,
1437 productseries=productseries,
1438 sourcepackagename=sourcepackagename,
1439 distribution=distribution,
1440 distroseries=distroseries)
1441
1442 def addNomination(self, owner, target):1431 def addNomination(self, owner, target):
1443 """See `IBug`."""1432 """See `IBug`."""
1444 if not self.canBeNominatedFor(target):1433 if not self.canBeNominatedFor(target):
14451434
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py 2011-03-11 03:06:43 +0000
+++ lib/lp/bugs/model/bugtask.py 2011-03-15 04:26:27 +0000
@@ -13,7 +13,6 @@
13 'BugTaskMixin',13 'BugTaskMixin',
14 'BugTask',14 'BugTask',
15 'BugTaskSet',15 'BugTaskSet',
16 'NullBugTask',
17 'bugtask_sort_key',16 'bugtask_sort_key',
18 'determine_target',17 'determine_target',
19 'get_bug_privacy_filter',18 'get_bug_privacy_filter',
@@ -119,7 +118,6 @@
119 IDistroSeriesBugTask,118 IDistroSeriesBugTask,
120 IllegalRelatedBugTasksParams,119 IllegalRelatedBugTasksParams,
121 IllegalTarget,120 IllegalTarget,
122 INullBugTask,
123 IProductSeriesBugTask,121 IProductSeriesBugTask,
124 IUpstreamBugTask,122 IUpstreamBugTask,
125 RESOLVED_BUGTASK_STATUSES,123 RESOLVED_BUGTASK_STATUSES,
@@ -374,68 +372,6 @@
374 return sorted(result, key=pillar_sort_key)372 return sorted(result, key=pillar_sort_key)
375373
376374
377class NullBugTask(BugTaskMixin):
378 """A null object for IBugTask.
379
380 This class is used, for example, to be able to render a URL like:
381
382 /products/evolution/+bug/5
383
384 when bug #5 isn't yet reported in evolution.
385 """
386 implements(INullBugTask)
387
388 def __init__(self, bug, product=None, productseries=None,
389 sourcepackagename=None, distribution=None,
390 distroseries=None):
391 """Initialize a NullBugTask."""
392 self.id = None
393 self.bug = bug
394 self.product = product
395 self.productseries = productseries
396 self.sourcepackagename = sourcepackagename
397 self.distribution = distribution
398 self.distroseries = distroseries
399
400 # Mark the task with the correct interface, depending on its
401 # context.
402 if self.product:
403 alsoProvides(self, IUpstreamBugTask)
404 elif self.distribution:
405 alsoProvides(self, IDistroBugTask)
406 elif self.distroseries:
407 alsoProvides(self, IDistroSeriesBugTask)
408 elif self.productseries:
409 alsoProvides(self, IProductSeriesBugTask)
410 else:
411 raise AssertionError('Unknown NullBugTask: %r.' % self)
412
413 # Make us provide the interface by setting all required attributes
414 # to None, and define the methods as raising NotImplementedError.
415 # The attributes are set to None because it doesn't make
416 # sense for these attributes to have a value when there is no
417 # real task there. (In fact, it may make sense for these
418 # values to be non-null, but I haven't yet found a use case
419 # for it, and I don't think there's any point on designing for
420 # that until we've encountered one.)
421 def this_is_a_null_bugtask_method(*args, **kwargs):
422 raise NotImplementedError
423
424 for name, spec in INullBugTask.namesAndDescriptions(True):
425 if not hasattr(self, name):
426 if IMethod.providedBy(spec):
427 value = this_is_a_null_bugtask_method
428 else:
429 value = None
430 setattr(self, name, value)
431
432 @property
433 def title(self):
434 """See `IBugTask`."""
435 return 'Bug #%s is not in %s: "%s"' % (
436 self.bug.id, self.bugtargetdisplayname, self.bug.title)
437
438
439def BugTaskToBugAdapter(bugtask):375def BugTaskToBugAdapter(bugtask):
440 """Adapt an IBugTask to an IBug."""376 """Adapt an IBugTask to an IBug."""
441 return bugtask.bug377 return bugtask.bug
@@ -467,9 +403,10 @@
467 else:403 else:
468 target_params[attr[:-2]] = getUtility(utility_iface).get(value)404 target_params[attr[:-2]] = getUtility(utility_iface).get(value)
469405
470 # Use a NullBugTask to determine the new target.406 # Update the target name cache with the potential new target. The
471 nulltask = NullBugTask(self.bug, **target_params)407 # attribute changes haven't been made yet, so we need to calculate the
472 self.updateTargetNameCache(nulltask.target)408 # target manually.
409 self.updateTargetNameCache(determine_target(**target_params))
473410
474 return value411 return value
475412
476413
=== modified file 'lib/lp/bugs/templates/bugtask-index.pt'
--- lib/lp/bugs/templates/bugtask-index.pt 2011-02-25 22:09:46 +0000
+++ lib/lp/bugs/templates/bugtask-index.pt 2011-03-15 04:26:27 +0000
@@ -72,7 +72,6 @@
72 <div id="tags-autocomplete">72 <div id="tags-autocomplete">
73 <div id="tags-autocomplete-content"></div>73 <div id="tags-autocomplete-content"></div>
74 </div>74 </div>
75 <tal:block condition="view/reportBugInContext" />
7675
77 <p class="informational message"76 <p class="informational message"
78 tal:condition="view/notices"77 tal:condition="view/notices"