← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/sanitise-labels into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/sanitise-labels into lp:launchpad.

Commit message:
Remove the duplicate search title from bug listings, and massively refactor the Person bug views.

Requested reviews:
  William Grant (wgrant): code

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/sanitise-labels/+merge/243202

Remove the duplicate search title from bug listings, and massively refactor the Person bug views.
-- 
Your team Launchpad code reviewers is subscribed to branch lp:launchpad.
=== modified file 'lib/lp/app/interfaces/launchpad.py'
--- lib/lp/app/interfaces/launchpad.py	2013-05-10 06:28:17 +0000
+++ lib/lp/app/interfaces/launchpad.py	2014-11-29 04:56:30 +0000
@@ -14,6 +14,7 @@
     'IHasIcon',
     'IHasLogo',
     'IHasMugshot',
+    'IHeadingContext',
     'ILaunchpadCelebrities',
     'ILaunchpadUsage',
     'IPrivacy',
@@ -179,3 +180,13 @@
     """Something created on a certain date."""
 
     datecreated = Attribute("The date on which I was created.")
+
+
+class IHeadingContext(Interface):
+    """Something that appears in a page's header section.
+
+    This is a marker to allow views to avoid duplicating header
+    information in the body. The header is generated from
+    IHeadingBreadcrumbs, so you also need to define one of those for
+    each of these.
+    """

=== modified file 'lib/lp/blueprints/interfaces/sprint.py'
--- lib/lp/blueprints/interfaces/sprint.py	2014-11-17 18:36:16 +0000
+++ lib/lp/blueprints/interfaces/sprint.py	2014-11-29 04:56:30 +0000
@@ -30,6 +30,7 @@
 
 from lp import _
 from lp.app.validators.name import name_validator
+from lp.app.interfaces.launchpad import IHeadingContext
 from lp.blueprints.interfaces.specificationtarget import IHasSpecifications
 from lp.registry.interfaces.role import (
     IHasDrivers,
@@ -56,7 +57,7 @@
         return getUtility(ISprintSet)[name]
 
 
-class ISprint(IHasOwner, IHasDrivers, IHasSpecifications):
+class ISprint(IHasOwner, IHasDrivers, IHasSpecifications, IHeadingContext):
     """A sprint, or conference, or meeting."""
 
     id = Int(title=_('The Sprint ID'))

=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py	2014-11-14 22:20:18 +0000
+++ lib/lp/bugs/browser/bugtask.py	2014-11-29 04:56:30 +0000
@@ -3082,10 +3082,6 @@
         """Should the reporter widget be shown on the advanced search page?"""
         return True
 
-    def shouldShowTagsCombinatorWidget(self):
-        """Should the tags combinator widget show on the search page?"""
-        return True
-
     def shouldShowReleaseCriticalPortlet(self):
         """Should the page include a portlet showing release-critical bugs
         for different series.
@@ -3241,21 +3237,6 @@
         else:
             return None
 
-    @property
-    def search_macro_title(self):
-        """The search macro's title text."""
-        return u"Search bugs %s" % self.context_description
-
-    @property
-    def context_description(self):
-        """A phrase describing the context of the bug.
-
-        The phrase is intended to be used for headings like
-        "Bugs in $context", "Search bugs in $context". This
-        property should be overridden for person related views.
-        """
-        return "in %s" % self.context.displayname
-
 
 class BugNominationsView(BugTaskSearchListingView):
     """View for accepting/declining bug nominations."""
@@ -4023,9 +4004,6 @@
         """Return the heading to search all Bugs."""
         return "Search all bug reports"
 
-    def search_macro_title(self):
-        return u'Search all bugs'
-
     @property
     def label(self):
         return self.getSearchPageHeading()

=== modified file 'lib/lp/bugs/browser/person.py'
--- lib/lp/bugs/browser/person.py	2014-11-29 00:03:36 +0000
+++ lib/lp/bugs/browser/person.py	2014-11-29 04:56:30 +0000
@@ -189,29 +189,36 @@
         return sorted(L, key=itemgetter('package_name'))
 
 
-class PersonAssignedBugTaskSearchListingView(RelevantMilestonesMixin,
-                                             BugTaskSearchListingView):
+class FilteredSearchListingViewMixin(RelevantMilestonesMixin,
+                                     BugTaskSearchListingView):
+    columns_to_show = ["id", "summary", "bugtargetdisplayname",
+                       "importance", "status"]
+
+    @property
+    def page_title(self):
+        return self.label
+
+    def searchUnbatched(self, searchtext=None, context=None,
+                        extra_params=None):
+        context = context or self.context
+        extra_params = extra_params or {}
+        extra_params.update(self.getExtraParams(context))
+        return super(FilteredSearchListingViewMixin, self).searchUnbatched(
+            searchtext, context, extra_params)
+
+    def getSimpleSearchURL(self):
+        """Return a URL that can be used as an href to the simple search."""
+        return canonical_url(self.context, view_name=self.view_name)
+
+
+class PersonAssignedBugTaskSearchListingView(FilteredSearchListingViewMixin):
     """All bugs assigned to someone."""
 
-    columns_to_show = ["id", "summary", "bugtargetdisplayname",
-                       "importance", "status"]
-    page_title = 'Assigned bugs'
+    label = 'Assigned bugs'
     view_name = '+assignedbugs'
 
-    def searchUnbatched(self, searchtext=None, context=None,
-                        extra_params=None):
-        """Return the open bugs assigned to a person."""
-        if context is None:
-            context = self.context
-
-        if extra_params is None:
-            extra_params = dict()
-        else:
-            extra_params = dict(extra_params)
-        extra_params['assignee'] = context
-
-        sup = super(PersonAssignedBugTaskSearchListingView, self)
-        return sup.searchUnbatched(searchtext, context, extra_params)
+    def getExtraParams(self, context):
+        return {'assignee': context}
 
     def shouldShowAssigneeWidget(self):
         """Should the assignee widget be shown on the advanced search page?"""
@@ -221,100 +228,25 @@
         """Should the team assigned bugs portlet be shown?"""
         return True
 
-    def shouldShowTagsCombinatorWidget(self):
-        """Should the tags combinator widget show on the search page?"""
-        return False
-
-    @property
-    def context_description(self):
-        """See `BugTaskSearchListingView`."""
-        return "assigned to %s" % self.context.displayname
-
-    def getSearchPageHeading(self):
-        """The header for the search page."""
-        return "Bugs %s" % self.context_description
-
-    def getAdvancedSearchButtonLabel(self):
-        """The Search button for the advanced search page."""
-        return "Search bugs %s" % self.context_description
-
-    def getSimpleSearchURL(self):
-        """Return a URL that can be used as an href to the simple search."""
-        return canonical_url(self.context, view_name="+assignedbugs")
-
-    @property
-    def label(self):
-        return self.getSearchPageHeading()
-
-
-class PersonCommentedBugTaskSearchListingView(RelevantMilestonesMixin,
-                                              BugTaskSearchListingView):
+
+class PersonCommentedBugTaskSearchListingView(FilteredSearchListingViewMixin):
     """All bugs commented on by a Person."""
 
-    columns_to_show = ["id", "summary", "bugtargetdisplayname",
-                       "importance", "status"]
-    page_title = 'Commented bugs'
-
-    def searchUnbatched(self, searchtext=None, context=None,
-                        extra_params=None):
-        """Return the open bugs commented on by a person."""
-        if context is None:
-            context = self.context
-
-        if extra_params is None:
-            extra_params = dict()
-        else:
-            extra_params = dict(extra_params)
-        extra_params['bug_commenter'] = context
-
-        sup = super(PersonCommentedBugTaskSearchListingView, self)
-        return sup.searchUnbatched(searchtext, context, extra_params)
-
-    @property
-    def context_description(self):
-        """See `BugTaskSearchListingView`."""
-        return "commented on by %s" % self.context.displayname
-
-    def getSearchPageHeading(self):
-        """The header for the search page."""
-        return "Bugs %s" % self.context_description
-
-    def getAdvancedSearchButtonLabel(self):
-        """The Search button for the advanced search page."""
-        return "Search bugs %s" % self.context_description
-
-    def getSimpleSearchURL(self):
-        """Return a URL that can be used as an href to the simple search."""
-        return canonical_url(self.context, view_name="+commentedbugs")
-
-    @property
-    def label(self):
-        return self.getSearchPageHeading()
-
-
-class PersonAffectingBugTaskSearchListingView(
-    RelevantMilestonesMixin, BugTaskSearchListingView):
+    label = 'Commented bugs'
+    view_name = '+commentedbugs'
+
+    def getExtraParams(self, context):
+        return {'bug_commenter': context}
+
+
+class PersonAffectingBugTaskSearchListingView(FilteredSearchListingViewMixin):
     """All bugs affecting someone."""
 
-    columns_to_show = ["id", "summary", "bugtargetdisplayname",
-                       "importance", "status"]
+    label = 'Bugs affecting'
     view_name = '+affectingbugs'
-    page_title = 'Bugs affecting'   # The context is added externally.
-
-    def searchUnbatched(self, searchtext=None, context=None,
-                        extra_params=None):
-        """Return the open bugs assigned to a person."""
-        if context is None:
-            context = self.context
-
-        if extra_params is None:
-            extra_params = dict()
-        else:
-            extra_params = dict(extra_params)
-        extra_params['affected_user'] = context
-
-        sup = super(PersonAffectingBugTaskSearchListingView, self)
-        return sup.searchUnbatched(searchtext, context, extra_params)
+
+    def getExtraParams(self, context):
+        return {'affected_user': context}
 
     def shouldShowAssigneeWidget(self):
         """Should the assignee widget be shown on the advanced search page?"""
@@ -324,40 +256,13 @@
         """Should the team assigned bugs portlet be shown?"""
         return True
 
-    def shouldShowTagsCombinatorWidget(self):
-        """Should the tags combinator widget show on the search page?"""
-        return False
-
-    @property
-    def context_description(self):
-        """See `BugTaskSearchListingView`."""
-        return "affecting %s" % self.context.displayname
-
-    def getSearchPageHeading(self):
-        """The header for the search page."""
-        return "Bugs %s" % self.context_description
-
-    def getAdvancedSearchButtonLabel(self):
-        """The Search button for the advanced search page."""
-        return "Search bugs %s" % self.context_description
-
-    def getSimpleSearchURL(self):
-        """Return a URL that can be used as an href to the simple search."""
-        return canonical_url(self.context, view_name=self.view_name)
-
-    @property
-    def label(self):
-        return self.getSearchPageHeading()
-
-
-class PersonRelatedBugTaskSearchListingView(RelevantMilestonesMixin,
-                                            BugTaskSearchListingView,
+
+class PersonRelatedBugTaskSearchListingView(FilteredSearchListingViewMixin,
                                             FeedsMixin):
     """All bugs related to someone."""
 
-    columns_to_show = ["id", "summary", "bugtargetdisplayname",
-                       "importance", "status"]
-    page_title = 'Related bugs'
+    label = 'Related bugs'
+    view_name = '+bugs'
 
     def searchUnbatched(self, searchtext=None, context=None,
                         extra_params=None):
@@ -392,130 +297,36 @@
         return context.searchTasks(
             assignee_params, subscriber_params, owner_params, commenter_params)
 
-    @property
-    def context_description(self):
-        """See `BugTaskSearchListingView`."""
-        return "related to %s" % self.context.displayname
-
-    def getSearchPageHeading(self):
-        return "Bugs %s" % self.context_description
-
-    def getAdvancedSearchButtonLabel(self):
-        return "Search bugs %s" % self.context_description
-
-    def getSimpleSearchURL(self):
-        return canonical_url(self.context, view_name="+bugs")
-
-    @property
-    def label(self):
-        return self.getSearchPageHeading()
-
-
-class PersonReportedBugTaskSearchListingView(RelevantMilestonesMixin,
-                                             BugTaskSearchListingView):
+
+class PersonReportedBugTaskSearchListingView(FilteredSearchListingViewMixin):
     """All bugs reported by someone."""
 
-    columns_to_show = ["id", "summary", "bugtargetdisplayname",
-                       "importance", "status"]
-    page_title = 'Reported bugs'
-
-    def searchUnbatched(self, searchtext=None, context=None,
-                        extra_params=None):
-        """Return the bugs reported by a person."""
-        if context is None:
-            context = self.context
-
-        if extra_params is None:
-            extra_params = dict()
-        else:
-            extra_params = dict(extra_params)
+    label = 'Reported bugs'
+    view_name = '+reportedbugs'
+
+    def getExtraParams(self, context):
         # Specify both owner and bug_reporter to try to prevent the same
         # bug (but different tasks) being displayed.
-        extra_params['owner'] = context
-        extra_params['bug_reporter'] = context
-
-        sup = super(PersonReportedBugTaskSearchListingView, self)
-        return sup.searchUnbatched(searchtext, context, extra_params)
-
-    @property
-    def context_description(self):
-        """See `BugTaskSearchListingView`."""
-        return "reported by %s" % self.context.displayname
-
-    def getSearchPageHeading(self):
-        """The header for the search page."""
-        return "Bugs %s" % self.context_description
-
-    def getAdvancedSearchButtonLabel(self):
-        """The Search button for the advanced search page."""
-        return "Search bugs %s" % self.context_description
-
-    def getSimpleSearchURL(self):
-        """Return a URL that can be used as an href to the simple search."""
-        return canonical_url(self.context, view_name="+reportedbugs")
+        return {'owner': context, 'bug_reporter': context}
 
     def shouldShowReporterWidget(self):
         """Should the reporter widget be shown on the advanced search page?"""
         return False
 
-    def shouldShowTagsCombinatorWidget(self):
-        """Should the tags combinator widget show on the search page?"""
-        return False
-
-    @property
-    def label(self):
-        return self.getSearchPageHeading()
-
-
-class PersonSubscribedBugTaskSearchListingView(RelevantMilestonesMixin,
-                                               BugTaskSearchListingView):
+
+class PersonSubscribedBugTaskSearchListingView(FilteredSearchListingViewMixin):
     """All bugs someone is subscribed to."""
 
-    columns_to_show = ["id", "summary", "bugtargetdisplayname",
-                       "importance", "status"]
-    page_title = 'Subscribed bugs'
+    label = 'Subscribed bugs'
     view_name = '+subscribedbugs'
 
-    def searchUnbatched(self, searchtext=None, context=None,
-                        extra_params=None):
-        """Return the bugs subscribed to by a person."""
-        if context is None:
-            context = self.context
-
-        if extra_params is None:
-            extra_params = dict()
-        else:
-            extra_params = dict(extra_params)
-        extra_params['subscriber'] = context
-
-        sup = super(PersonSubscribedBugTaskSearchListingView, self)
-        return sup.searchUnbatched(searchtext, context, extra_params)
+    def getExtraParams(self, context):
+        return {'subscriber': context}
 
     def shouldShowTeamPortlet(self):
         """Should the team subscribed bugs portlet be shown?"""
         return True
 
-    @property
-    def context_description(self):
-        """See `BugTaskSearchListingView`."""
-        return "%s is subscribed to" % self.context.displayname
-
-    def getSearchPageHeading(self):
-        """The header for the search page."""
-        return "Bugs %s" % self.context_description
-
-    def getAdvancedSearchButtonLabel(self):
-        """The Search button for the advanced search page."""
-        return "Search bugs %s is Cc'd to" % self.context.displayname
-
-    def getSimpleSearchURL(self):
-        """Return a URL that can be used as an href to the simple search."""
-        return canonical_url(self.context, view_name="+subscribedbugs")
-
-    @property
-    def label(self):
-        return self.getSearchPageHeading()
-
 
 class PersonSubscriptionsView(LaunchpadView):
     """All the subscriptions for a person."""

=== modified file 'lib/lp/bugs/browser/tests/test_buglisting.py'
--- lib/lp/bugs/browser/tests/test_buglisting.py	2014-11-14 23:21:57 +0000
+++ lib/lp/bugs/browser/tests/test_buglisting.py	2014-11-29 04:56:30 +0000
@@ -207,20 +207,6 @@
         self.assertFalse(
             'Y.lp.app.batchnavigator.BatchNavigatorHooks' in view())
 
-    def test_search_macro_title(self):
-        # The title text is displayed for the macro `simple-search-form`.
-        product = self.factory.makeProduct(
-            displayname='Test Product', official_malone=True)
-        view = create_initialized_view(product, '+bugs')
-        self.assertEqual(
-            'Search bugs in Test Product', view.search_macro_title)
-
-        # The title is shown.
-        form_title_matches = Tag(
-            'Search form title', 'h3', text=view.search_macro_title)
-        view = create_initialized_view(product, '+bugs')
-        self.assertThat(view.render(), HTMLContains(form_title_matches))
-
     def test_search_macro_div_node_with_css_class(self):
         # The <div> enclosing the search form in the macro
         # `simple-search-form` has the CSS class "dynamic_bug_listing".

=== modified file 'lib/lp/bugs/browser/tests/test_bugs.py'
--- lib/lp/bugs/browser/tests/test_bugs.py	2014-02-19 04:01:46 +0000
+++ lib/lp/bugs/browser/tests/test_bugs.py	2014-11-29 04:56:30 +0000
@@ -100,7 +100,7 @@
             self.application, '+bugs', rootsite='bugs')
         content = view.render()
         # we should get some valid content out of this
-        self.assertIn('Search all bugs', content)
+        self.assertIn('Search all bug reports', content)
 
     def _assert_getBugData(self, related_bug=None):
         # The getBugData method works as expected.

=== modified file 'lib/lp/bugs/templates/bugtarget-macros-search.pt'
--- lib/lp/bugs/templates/bugtarget-macros-search.pt	2012-10-02 06:36:44 +0000
+++ lib/lp/bugs/templates/bugtarget-macros-search.pt	2014-11-29 04:56:30 +0000
@@ -83,9 +83,6 @@
      tal:attributes="action search_url|string:">
   <form method="get" name="search" class="primary search dynamic_bug_listing"
         tal:attributes="action search_url|string:">
-    <h3 tal:content="view/search_macro_title">
-      Search bugs in Ubuntu
-    </h3>
     <p>
     <tal:searchbox replace="structure view/widgets/searchtext" />
     <input type="submit" name="search" value="Search" />

=== modified file 'lib/lp/bugs/templates/bugtask-macros-tableview.pt'
--- lib/lp/bugs/templates/bugtask-macros-tableview.pt	2014-02-18 11:40:52 +0000
+++ lib/lp/bugs/templates/bugtask-macros-tableview.pt	2014-11-29 04:56:30 +0000
@@ -307,8 +307,7 @@
               >An error message.</div>
           </div>
 
-          <div condition="view/shouldShowTagsCombinatorWidget"
-               class="value">
+          <div class="value">
             <label style="font-weight: normal">
               <input id="field.tags_combinator.0"
                      name="field.tags_combinator"

=== modified file 'lib/lp/registry/browser/tests/test_person.py'
--- lib/lp/registry/browser/tests/test_person.py	2014-07-24 07:56:06 +0000
+++ lib/lp/registry/browser/tests/test_person.py	2014-11-29 04:56:30 +0000
@@ -1411,21 +1411,12 @@
             self.assertEqual(expected, view.getMilestoneWidgetValues())
         self.assertThat(recorder, HasQueryCount(LessThan(6)))
 
-    def test_context_description(self):
-        # view.context_description returns a string that can be used
-        # in texts like "Bugs in $context_descirption"
-        view = create_initialized_view(self.person, self.view_name)
-        self.assertEqual(
-            self.expected_context_description % self.person.displayname,
-            view.context_description)
-
 
 class TestPersonRelatedBugTaskSearchListingView(
     BugTaskViewsTestBase, TestCaseWithFactory):
     """Tests for PersonRelatedBugTaskSearchListingView."""
 
     view_name = '+bugs'
-    expected_context_description = 'related to %s'
 
     def setUp(self):
         super(TestPersonRelatedBugTaskSearchListingView, self).setUp()
@@ -1442,7 +1433,6 @@
     """Tests for PersonAssignedBugTaskSearchListingView."""
 
     view_name = '+assignedbugs'
-    expected_context_description = 'assigned to %s'
 
     def setUp(self):
         super(TestPersonAssignedBugTaskSearchListingView, self).setUp()
@@ -1456,7 +1446,6 @@
     """Tests for PersonAssignedBugTaskSearchListingView."""
 
     view_name = '+commentedbugs'
-    expected_context_description = 'commented on by %s'
 
     def setUp(self):
         super(TestPersonCommentedBugTaskSearchListingView, self).setUp()
@@ -1470,7 +1459,6 @@
     """Tests for PersonAssignedBugTaskSearchListingView."""
 
     view_name = '+reportedbugs'
-    expected_context_description = 'reported by %s'
 
     def setUp(self):
         super(TestPersonReportedBugTaskSearchListingView, self).setUp()
@@ -1484,7 +1472,6 @@
     """Tests for PersonAssignedBugTaskSearchListingView."""
 
     view_name = '+subscribedbugs'
-    expected_context_description = '%s is subscribed to'
 
     def setUp(self):
         super(TestPersonSubscribedBugTaskSearchListingView, self).setUp()
@@ -1499,7 +1486,6 @@
     """Tests for PersonAffectingBugTaskSearchListingView."""
 
     view_name = '+affectingbugs'
-    expected_context_description = 'affecting %s'
 
     def setUp(self):
         super(TestPersonAffectingBugTaskSearchListingView, self).setUp()

=== modified file 'lib/lp/registry/interfaces/distributionsourcepackage.py'
--- lib/lp/registry/interfaces/distributionsourcepackage.py	2013-10-01 00:57:56 +0000
+++ lib/lp/registry/interfaces/distributionsourcepackage.py	2014-11-29 04:56:30 +0000
@@ -22,6 +22,7 @@
 
 from lp import _
 from lp.answers.interfaces.questiontarget import IQuestionTarget
+from lp.app.interfaces.launchpad import IHeadingContext
 from lp.bugs.interfaces.bugtarget import (
     IBugTarget,
     IHasOfficialBugTags,
@@ -38,8 +39,8 @@
 from lp.soyuz.enums import ArchivePurpose
 
 
-class IDistributionSourcePackage(IBugTarget, IHasBranches, IHasMergeProposals,
-                                 IHasOfficialBugTags,
+class IDistributionSourcePackage(IHeadingContext, IBugTarget, IHasBranches,
+                                 IHasMergeProposals, IHasOfficialBugTags,
                                  IStructuralSubscriptionTarget,
                                  IQuestionTarget, IHasDrivers):
     """Represents a source package in a distribution.

=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py	2014-11-18 00:07:27 +0000
+++ lib/lp/registry/interfaces/person.py	2014-11-29 04:56:30 +0000
@@ -98,6 +98,7 @@
     IHasIcon,
     IHasLogo,
     IHasMugshot,
+    IHeadingContext,
     IPrivacy,
     )
 from lp.app.validators import LaunchpadValidationError
@@ -1844,7 +1845,7 @@
 
 class IPerson(IPersonPublic, IPersonLimitedView, IPersonViewRestricted,
               IPersonEditRestricted, IPersonSpecialRestricted, IHasStanding,
-              ISetLocation):
+              ISetLocation, IHeadingContext):
     """A Person."""
     export_as_webservice_entry(plural_name='people')
 

=== modified file 'lib/lp/registry/interfaces/pillar.py'
--- lib/lp/registry/interfaces/pillar.py	2013-01-07 02:40:55 +0000
+++ lib/lp/registry/interfaces/pillar.py	2014-11-29 04:56:30 +0000
@@ -32,6 +32,7 @@
     )
 
 from lp import _
+from lp.app.interfaces.launchpad import IHeadingContext
 from lp.registry.enums import (
     BranchSharingPolicy,
     BugSharingPolicy,
@@ -50,7 +51,7 @@
     ]
 
 
-class IPillar(Interface):
+class IPillar(IHeadingContext):
     """An object that might be a project, a project group, or a distribution.
 
     This is a polymorphic object served by the pillar set. Check the


References