← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~bryce/launchpad/lp-617698-forwarding into lp:launchpad

 

Bryce Harrington has proposed merging lp:~bryce/launchpad/lp-617698-forwarding into lp:launchpad with lp:~bryce/launchpad/lp-617695-linkui as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers): code
Related bugs:
  #617698 When forwarding a bug to an external bug tracker, the component field doesn't get filled in
  https://bugs.launchpad.net/bugs/617698


Implement remote_component support when forwarding bugs.

This causes the 'component' field to be properly filled in for the bugzilla submission page when forwarding a bug report to a remote project.


-- 
https://code.launchpad.net/~bryce/launchpad/lp-617698-forwarding/+merge/39835
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~bryce/launchpad/lp-617698-forwarding into lp:launchpad.
=== modified file 'lib/canonical/widgets/bugtask.py'
--- lib/canonical/widgets/bugtask.py	2010-10-03 15:30:06 +0000
+++ lib/canonical/widgets/bugtask.py	2010-11-02 10:22:01 +0000
@@ -491,6 +491,25 @@
         return distribution
 
 
+class UbuntuSourcePackageNameWidget(
+    BugTaskSourcePackageNameWidget):
+    """Package widget where the distribution can be assumed as Ubuntu
+
+    This widgets works the same as `BugTaskSourcePackageNameWidget`,
+    except that it assumes the distribution is 'ubuntu'.
+    """
+    distribution_name = "ubuntu"
+
+    def getDistribution(self):
+        """See `BugTaskSourcePackageNameWidget`"""
+        distribution = getUtility(IDistributionSet).getByName(
+            self.distribution_name)
+        if distribution is None:
+            raise UnexpectedFormData(
+                "No such distribution: %s" % self.distribution_name)
+        return distribution
+
+
 class AssigneeDisplayWidget(BrowserWidget):
     """A widget for displaying an assignee."""
 

=== modified file 'lib/lp/bugs/browser/bugalsoaffects.py'
--- lib/lp/bugs/browser/bugalsoaffects.py	2010-09-03 03:12:39 +0000
+++ lib/lp/bugs/browser/bugalsoaffects.py	2010-11-02 10:22:01 +0000
@@ -664,8 +664,17 @@
             title = bug.title
             description = u"Originally reported at:\n  %s\n\n%s" % (
                 canonical_url(bug), bug.description)
+            remote_component = ""
+            comp_group = target.bugtracker.getRemoteComponentGroup(
+                target.remote_product)
+            package_name = self.context.target.sourcepackagename.name
+            if comp_group is not None:
+                for component in comp_group.components:
+                    if (component.distro_source_package is not None and
+                        component.distro_source_package.name == package_name):
+                        remote_component = component.name
             return target.bugtracker.getBugFilingAndSearchLinks(
-                target.remote_product, title, description)
+                target.remote_product, remote_component, title, description)
 
 
 class BugTrackerCreationStep(AlsoAffectsStep):

=== modified file 'lib/lp/bugs/browser/bugtracker.py'
--- lib/lp/bugs/browser/bugtracker.py	2010-10-15 08:23:19 +0000
+++ lib/lp/bugs/browser/bugtracker.py	2010-11-02 10:22:01 +0000
@@ -10,6 +10,7 @@
     'BugTrackerBreadcrumb',
     'BugTrackerComponentGroupNavigation',
     'BugTrackerEditView',
+    'BugTrackerEditComponentView',
     'BugTrackerNavigation',
     'BugTrackerNavigationMenu',
     'BugTrackerSetBreadcrumb',
@@ -22,6 +23,7 @@
 
 from itertools import chain
 
+from zope.app.form import CustomWidgetFactory
 from zope.app.form.browser import TextAreaWidget
 from zope.component import getUtility
 from zope.formlib import form
@@ -67,13 +69,18 @@
     DelimitedListWidget,
     LaunchpadRadioWidget,
     )
+from canonical.widgets.bugtask import (
+    UbuntuSourcePackageNameWidget,
+    )
 from lp.bugs.interfaces.bugtracker import (
     BugTrackerType,
     IBugTracker,
     IBugTrackerSet,
     IRemoteBug,
+    IBugTrackerComponent,
     IBugTrackerComponentGroup,
     )
+from lp.registry.interfaces.distribution import IDistributionSet
 from lp.services.propertycache import cachedproperty
 
 # A set of bug tracker types for which there can only ever be one bug
@@ -229,6 +236,14 @@
         return shortlist(chain(self.context.projects,
                                self.context.products), 100)
 
+    @property
+    def related_component_groups(self):
+        """Return all component groups and components
+
+        This property was created for the Related components portlet in
+        the bug tracker's page.
+        """
+        return self.context.getAllRemoteComponentGroups()
 
 BUG_TRACKER_ACTIVE_VOCABULARY = SimpleVocabulary.fromItems(
     [('On', True), ('Off', False)])
@@ -451,6 +466,83 @@
         return self.context.getRemoteComponentGroup(name)
 
 
+class BugTrackerEditComponentView(LaunchpadEditFormView):
+
+    schema = IBugTrackerComponent
+    custom_widget('sourcepackagename',
+                  UbuntuSourcePackageNameWidget)
+    #edit_form = ViewPageTemplateFile(
+    #    '../templates/bugtask-component-edit-form.pt')
+
+    @property
+    def page_title(self):
+        return smartquote(
+            u'Link a distribution source package to the %s component'
+            % self.context.name)
+
+    @property
+    def field_names(self):
+        field_names = [
+            'sourcepackagename',
+            ]
+        return field_names
+
+    #def setUpFields(self):
+    #    super(BugTrackerEditComponentView, self).setUpFields()
+
+    def setUpWidgets(self, context=None):
+        for field in self.form_fields:
+            if (field.custom_widget is None and
+                field.__name__ in self.custom_widgets):
+                field.custom_widget = self.custom_widgets[field.__name__]
+        self.widgets = form.setUpWidgets(
+            self.form_fields, self.prefix, self.context, self.request,
+            data=self.initial_values, adapters=self.adapters,
+            ignore_request=False)
+
+    @property
+    def initial_values(self):
+        """See `LaunchpadFormView.`"""
+        field_values = {}
+        for name in self.field_names:
+            if name == 'sourcepackagename':
+                pkg = self.context.distro_source_package
+                if pkg is not None:
+                    field_values['sourcepackagename'] = pkg.name
+                else:
+                    field_values['sourcepackagename'] = ""
+            else:
+                field_values[name] = getattr(self.context, name)
+
+        return field_values
+
+    def updateContextFromData(self, data, context=None):
+        """TODO"""
+        if context is None:
+            context = self.context
+        component = context
+
+        sourcepackagename = self.request.form.get(
+            self.widgets['sourcepackagename'].name)
+        
+        distro_name = self.widgets['sourcepackagename'].distribution_name
+        distribution = getUtility(IDistributionSet).getByName(distro_name)
+        pkg = distribution.getSourcePackage(sourcepackagename)
+        #pkg = self.widgets['sourcepackagename']
+        component.distro_source_package = pkg
+        self.request.response.addNotification(
+            "%s:%s is now linked to the %s source package in %s" %(
+                component.component_group.name, component.name,
+                sourcepackagename, distro_name))
+
+    @action('Save Changes', name='save')
+    def save_action(self, action, data):
+        """Update the component with the form data."""
+        self.updateContextFromData(data)
+
+        self.next_url = canonical_url(
+            self.context.component_group.bug_tracker)
+
 class BugTrackerComponentGroupNavigation(Navigation):
 
     usedfor = IBugTrackerComponentGroup

=== modified file 'lib/lp/bugs/browser/configure.zcml'
--- lib/lp/bugs/browser/configure.zcml	2010-10-28 09:11:36 +0000
+++ lib/lp/bugs/browser/configure.zcml	2010-11-02 10:22:01 +0000
@@ -811,6 +811,12 @@
             path_expression="name"
             attribute_to_parent="component_group"
             rootsite="bugs"/>
+        <browser:page
+            name="+edit"
+            for="lp.bugs.interfaces.bugtracker.IBugTrackerComponent"
+            class="lp.bugs.browser.bugtracker.BugTrackerEditComponentView"
+            permission="launchpad.AnyPerson"
+            template="../templates/bugtracker-edit-component.pt"/>
         <browser:pages
             for="lp.bugs.interfaces.bugtracker.IBugTracker"
             class="lp.bugs.browser.bugtracker.BugTrackerView"
@@ -822,6 +828,9 @@
                 name="+portlet-details"
                 template="../templates/bugtracker-portlet-details.pt"/>
             <browser:page
+                name="+portlet-components"
+                template="../templates/bugtracker-portlet-components.pt"/>
+            <browser:page
                 name="+portlet-projects"
                 template="../templates/bugtracker-portlet-projects.pt"/>
             <browser:page

=== modified file 'lib/lp/bugs/doc/bugtracker.txt'
--- lib/lp/bugs/doc/bugtracker.txt	2010-10-18 22:24:59 +0000
+++ lib/lp/bugs/doc/bugtracker.txt	2010-11-02 10:22:01 +0000
@@ -356,26 +356,28 @@
 Filing a bug on the remote tracker
 ----------------------------------
 
-The IBugTracker interface defines a method, getBugFilingAndSearchLinks(),
-which returns the URLs of the bug filing form and the bug search on
-the remote bug tracker as a dict. It accepts three parameters:
-remote_product, which is the name of the product on the remote
-tracker; summary, which is the bug summary to be passed to the remote
-bug tracker for searching or filing and description, which is the full
-description of the bug. This is only passed to the bug filing form as
-it is too specific for the search form.
+The IBugTracker interface defines a method,
+getBugFilingAndSearchLinks(), which returns the URLs of the bug filing
+form and the bug search on the remote bug tracker as a dict. It accepts
+fource parameters: remote_product, which is the name of the product on
+the remote tracker; remote_component, which corresponds to the component
+field; summary, which is the bug summary to be passed to the remote bug
+tracker for searching or filing and description, which is the full
+description of the bug. This is only passed to the bug filing form as it
+is too specific for the search form.
 
     >>> def print_links(links_dict):
     ...     for key in sorted(links_dict):
     ...         print "%s: %s" % (key, links_dict[key])
 
     >>> links = mozilla_bugzilla.getBugFilingAndSearchLinks(
-    ...     remote_product='testproduct', summary="Foo", description="Bar")
+    ...     remote_product='testproduct', remote_componet='testcomponent',
+    ...     summary="Foo", description="Bar")
     >>> print_links(links)
     bug_filing_url:
-    https://.../enter_bug.cgi?product=testproduct&short_desc=Foo&long_desc=Bar
+    https://.../enter_bug.cgi?product=testproduct&component=testcomponent&short_desc=Foo&long_desc=Bar
     bug_search_url:
-    https://.../query.cgi?product=testproduct&short_desc=Foo
+    https://.../query.cgi?product=testproduct&component=testcomponent&short_desc=Foo
 
 For the RT tracker we specify a Queue in which to file a ticket.
 
@@ -462,7 +464,8 @@
     >>> example_roundup = factory.makeBugTracker(
     ...     'http://roundup.example.com', BugTrackerType.ROUNDUP)
     >>> links = example_roundup.getBugFilingAndSearchLinks(
-    ...     remote_product='testproduct', summary="Foo", description="Bar")
+    ...     remote_product='testproduct', remote_component='testcomponent',
+    ...     summary="Foo", description="Bar")
     >>> print_links(links)
     bug_filing_url: http://.../issue?@template=item&title=Foo&@note=Bar
     bug_search_url: http://.../issue?@template=search&@search_text=Foo

=== modified file 'lib/lp/bugs/interfaces/bugtracker.py'
--- lib/lp/bugs/interfaces/bugtracker.py	2010-10-15 01:37:04 +0000
+++ lib/lp/bugs/interfaces/bugtracker.py	2010-11-02 10:22:01 +0000
@@ -289,12 +289,14 @@
     watches_needing_update = Attribute(
         "The set of bug watches that need updating.")
 
-    def getBugFilingAndSearchLinks(remote_product, summary=None,
-                                   description=None):
+    def getBugFilingAndSearchLinks(remote_product, remote_component=None,
+                                   summary=None, description=None):
         """Return the bug filing and search links for the tracker.
 
         :param remote_product: The name of the product on which the bug
             is to be filed or search for.
+        :param remote_product: The name of the component on which the bug
+            is to be filed or search for.
         :param summary: The string with which to pre-filly the summary
             field of the upstream bug tracker's search and bug filing forms.
         :param description: The string with which to pre-filly the description
@@ -521,6 +523,10 @@
             title=_('Name'),
             description=_("The name of a software component "
                           "as shown in Launchpad.")))
+    sourcepackagename = Choice(
+        title=_("Package"), required=False, vocabulary='SourcePackageName')
+    distribution = Choice(
+        title=_("Distribution"), required=False, vocabulary='Distribution')
 
     distro_source_package = exported(
         Reference(

=== modified file 'lib/lp/bugs/model/bugtracker.py'
--- lib/lp/bugs/model/bugtracker.py	2010-10-19 01:21:14 +0000
+++ lib/lp/bugs/model/bugtracker.py	2010-11-02 10:22:01 +0000
@@ -249,7 +249,6 @@
 
     def addComponent(self, component_name):
         """Adds a component that is synced from a remote bug tracker"""
-
         component = BugTrackerComponent()
         component.name = component_name
         component.component_group = self
@@ -266,18 +265,17 @@
         None is returned if there is no component by that name in the
         group.
         """
-
         if component_name is None:
             return None
         else:
             return Store.of(self).find(
                 BugTrackerComponent,
-                (BugTrackerComponent.name == component_name)).one()
+                BugTrackerComponent.name == component_name,
+                BugTrackerComponent.component_group == self.id).one()
 
     def addCustomComponent(self, component_name):
         """Adds a component locally that isn't synced from a remote tracker
         """
-
         component = BugTrackerComponent()
         component.name = component_name
         component.component_group = self
@@ -289,7 +287,6 @@
 
         return component
 
-
 class BugTracker(SQLBase):
     """A class to access the BugTracker table in the database.
 
@@ -327,6 +324,7 @@
     _filing_url_patterns = {
         BugTrackerType.BUGZILLA: (
             "%(base_url)s/enter_bug.cgi?product=%(remote_product)s"
+            "&component=%(remote_component)s"
             "&short_desc=%(summary)s&long_desc=%(description)s"),
         BugTrackerType.GOOGLE_CODE: (
             "%(base_url)s/entry?summary=%(summary)s&"
@@ -383,6 +381,7 @@
         return {
             gnome_bugzilla: (
                 "%(base_url)s/enter_bug.cgi?product=%(remote_product)s"
+                "&component=%(remote_component)s"
                 "&short_desc=%(summary)s&comment=%(description)s"),
             }
 
@@ -399,7 +398,8 @@
         else:
             return False
 
-    def getBugFilingAndSearchLinks(self, remote_product, summary=None,
+    def getBugFilingAndSearchLinks(self, remote_product,
+                                   remote_component=None, summary=None,
                                    description=None):
         """See `IBugTracker`."""
         bugtracker_urls = {'bug_filing_url': None, 'bug_search_url': None}
@@ -414,6 +414,10 @@
             # quote() doesn't blow up later on.
             remote_product = ''
 
+        if remote_component is None:
+            # Ditto for remote component.
+            remote_component = ''
+
         if self in self._custom_filing_url_patterns:
             # Some bugtrackers are customised to accept different
             # querystring parameters from the default. We special-case
@@ -471,6 +475,7 @@
             url_components = {
                 'base_url': base_url,
                 'remote_product': quote(remote_product),
+                'remote_component': quote(remote_component),
                 'summary': quote(summary),
                 'description': quote(description),
                 }

=== added file 'lib/lp/bugs/templates/bugtracker-edit-component.pt'
--- lib/lp/bugs/templates/bugtracker-edit-component.pt	1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/templates/bugtracker-edit-component.pt	2010-11-02 10:22:01 +0000
@@ -0,0 +1,16 @@
+<bug-tracker-edit-component
+  xmlns="http://www.w3.org/1999/xhtml";
+  xmlns:tal="http://xml.zope.org/namespaces/tal";
+  xmlns:metal="http://xml.zope.org/namespaces/metal";
+  xmlns:i18n="http://xml.zope.org/namespaces/i18n";
+  metal:use-macro="view/macro:page/main_only"
+  i18n:domain="malone">
+
+  <div metal:fill-slot="main">
+    <h1>Link <span tal:replace="context/component_group/name"/> component '<span tal:replace="context/name"/>'</h1>
+    <div metal:use-macro="context/@@launchpad_form/form">
+      <p>Configure component</p>
+    </div>
+  </div>
+
+</bug-tracker-edit-component>

=== modified file 'lib/lp/bugs/templates/bugtracker-index.pt'
--- lib/lp/bugs/templates/bugtracker-index.pt	2009-09-01 15:58:46 +0000
+++ lib/lp/bugs/templates/bugtracker-index.pt	2010-11-02 10:22:01 +0000
@@ -34,6 +34,7 @@
     <div class="yui-g">
       <div class="first yui-u">
         <div tal:replace="structure context/@@+portlet-details" />
+        <div tal:replace="structure context/@@+portlet-components" />
       </div>
       <div class="yui-u">
         <div tal:replace="structure context/@@+portlet-projects" />

=== added file 'lib/lp/bugs/templates/bugtracker-portlet-components.pt'
--- lib/lp/bugs/templates/bugtracker-portlet-components.pt	1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/templates/bugtracker-portlet-components.pt	2010-11-02 10:22:01 +0000
@@ -0,0 +1,37 @@
+<div
+    xmlns:tal="http://xml.zope.org/namespaces/tal";
+    xmlns:metal="http://xml.zope.org/namespaces/metal";
+    xmlns:i18n="http://xml.zope.org/namespaces/i18n";
+    class="portlet" id="portlet-components">
+  <h2>Components</h2>
+  <p>
+    You can link components from this bug tracker to their corresponding
+    distribution source packages in the project's &ldquo;Change
+    components&rdquo; page.
+  </p>
+  <ul tal:define="related_component_groups view/related_component_groups">
+    <li tal:repeat="component_group related_component_groups">
+      <strong><span tal:replace="component_group/name" /></strong>
+      <ul tal:define="components component_group/components">
+        <li tal:repeat="component components">
+          <span tal:replace="component/name" />
+          &nbsp;
+          <span tal:condition="component/distro_source_package">
+            <a class="sprite edit"
+               tal:attributes="href string:${context/fmt:url}/+components/${component_group/name}/${component/name}/+edit">
+               <span tal:replace="structure component/distro_source_package/name"/></a>
+          </span>
+          <a class="sprite add"
+             tal:condition="not: component/distro_source_package"
+             tal:attributes="href string:${context/fmt:url}/+components/${component_group/name}/${component/name}/+edit"></a>
+        </li>
+        <li tal:condition="not: components">
+          <i>This bug tracker has no components for this group</i>
+        </li>
+      </ul>
+    </li>
+    <li tal:condition="not: related_component_groups">
+      <i>This bug tracker has no components</i>
+    </li>
+  </ul>
+</div>