~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/bugs/browser/bugtarget.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-05-05 15:37:29 UTC
  • mfrom: (12811.2.20 filebug-loses-data)
  • Revision ID: launchpad@pqm.canonical.com-20110505153729-6azkc87xoesv15rn
[r=sinzui][bug=513591,
        735290] Fix data loss issue on bug entry form and fix assignee picker.

Show diffs side-by-side

added added

removed removed

Lines of Context:
109
109
from lp.bugs.interfaces.apportjob import IProcessApportBlobJobSource
110
110
from lp.bugs.interfaces.bug import (
111
111
    CreateBugParams,
 
112
    IBug,
112
113
    IBugAddForm,
113
114
    IBugSet,
114
115
    IProjectGroupBugAddForm,
234
235
        self.updateContextFromData(data)
235
236
 
236
237
 
237
 
class FileBugViewBase(LaunchpadFormView):
 
238
class FileBugReportingGuidelines(LaunchpadFormView):
 
239
    """Provides access to common bug reporting attributes.
 
240
 
 
241
    Attributes provided are: security_related and bug_reporting_guidelines.
 
242
 
 
243
    This view is a superclass of `FileBugViewBase` so that non-ajax browsers
 
244
    can load the file bug form, and it is also invoked directly via an XHR
 
245
    request to provide an HTML snippet for Javascript enabled browsers.
 
246
    """
 
247
 
 
248
    schema = IBug
 
249
 
 
250
    @property
 
251
    def field_names(self):
 
252
        """Return the list of field names to display."""
 
253
        return ['security_related']
 
254
 
 
255
    def setUpFields(self):
 
256
        """Set up the form fields. See `LaunchpadFormView`."""
 
257
        super(FileBugReportingGuidelines, self).setUpFields()
 
258
 
 
259
        security_related_field = Bool(
 
260
            __name__='security_related',
 
261
            title=_("This bug is a security vulnerability"),
 
262
            required=False, default=False)
 
263
 
 
264
        self.form_fields = self.form_fields.omit('security_related')
 
265
        self.form_fields += formlib.form.Fields(security_related_field)
 
266
 
 
267
    @property
 
268
    def bug_reporting_guidelines(self):
 
269
        """Guidelines for filing bugs in the current context.
 
270
 
 
271
        Returns a list of dicts, with each dict containing values for
 
272
        "preamble" and "content".
 
273
        """
 
274
 
 
275
        def target_name(target):
 
276
            # IProjectGroup can be considered the target of a bug during
 
277
            # the bug filing process, but does not extend IBugTarget
 
278
            # and ultimately cannot actually be the target of a
 
279
            # bug. Hence this function to determine a suitable
 
280
            # name/title to display. Hurrumph.
 
281
            if IBugTarget.providedBy(target):
 
282
                return target.bugtargetdisplayname
 
283
            else:
 
284
                return target.displayname
 
285
 
 
286
        guidelines = []
 
287
        bugtarget = self.context
 
288
        if bugtarget is not None:
 
289
            content = bugtarget.bug_reporting_guidelines
 
290
            if content is not None and len(content) > 0:
 
291
                guidelines.append({
 
292
                        "source": target_name(bugtarget),
 
293
                        "content": content,
 
294
                        })
 
295
            # Distribution source packages are shown with both their
 
296
            # own reporting guidelines and those of their
 
297
            # distribution.
 
298
            if IDistributionSourcePackage.providedBy(bugtarget):
 
299
                distribution = bugtarget.distribution
 
300
                content = distribution.bug_reporting_guidelines
 
301
                if content is not None and len(content) > 0:
 
302
                    guidelines.append({
 
303
                            "source": target_name(distribution),
 
304
                            "content": content,
 
305
                            })
 
306
        return guidelines
 
307
 
 
308
    def getMainContext(self):
 
309
        if IDistributionSourcePackage.providedBy(self.context):
 
310
            return self.context.distribution
 
311
        else:
 
312
            return self.context
 
313
 
 
314
 
 
315
class FileBugViewBase(FileBugReportingGuidelines, LaunchpadFormView):
238
316
    """Base class for views related to filing a bug."""
239
317
 
240
318
    implements(IBrowserPublisher)
307
385
        elif not IProduct.providedBy(context):
308
386
            raise AssertionError('Unknown context: %r' % context)
309
387
 
310
 
        if IHasBugSupervisor.providedBy(context):
311
 
            if self.user.inTeam(context.bug_supervisor):
312
 
                field_names.extend(
313
 
                    ['assignee', 'importance', 'milestone', 'status'])
 
388
        # If the context is a project group we want to render the optional
 
389
        # fields since they will initially be hidden and later exposed if the
 
390
        # selected project supports them.
 
391
        include_extra_fields = IProjectGroup.providedBy(context)
 
392
        if not include_extra_fields and IHasBugSupervisor.providedBy(context):
 
393
            include_extra_fields = self.user.inTeam(context.bug_supervisor)
 
394
 
 
395
        if include_extra_fields:
 
396
            field_names.extend(
 
397
                ['assignee', 'importance', 'milestone', 'status'])
314
398
 
315
399
        return field_names
316
400
 
438
522
        self.form_fields = self.form_fields.omit('subscribe_to_existing_bug')
439
523
        self.form_fields += formlib.form.Fields(subscribe_field)
440
524
 
441
 
        security_related_field = Bool(
442
 
            __name__='security_related',
443
 
            title=_("This bug is a security vulnerability"),
444
 
            required=False, default=False)
445
 
 
446
 
        self.form_fields = self.form_fields.omit('security_related')
447
 
        self.form_fields += formlib.form.Fields(security_related_field)
448
 
 
449
525
    def contextUsesMalone(self):
450
526
        """Does the context use Malone as its official bugtracker?"""
451
527
        if IProjectGroup.providedBy(self.context):
457
533
            bug_tracking_usage = self.getMainContext().bug_tracking_usage
458
534
            return bug_tracking_usage == ServiceUsage.LAUNCHPAD
459
535
 
460
 
    def getMainContext(self):
461
 
        if IDistributionSourcePackage.providedBy(self.context):
462
 
            return self.context.distribution
463
 
        else:
464
 
            return self.context
465
 
 
466
 
    def getSecurityContext(self):
467
 
        """Return the context used for security bugs."""
468
 
        return self.getMainContext()
469
 
 
470
 
    @property
471
 
    def can_decide_security_contact(self):
472
 
        """Will we be able to discern a security contact for this?"""
473
 
        return (self.getSecurityContext() is not None)
474
 
 
475
536
    def shouldSelectPackageName(self):
476
537
        """Should the radio button to select a package be selected?"""
477
538
        return (
556
617
 
557
618
        if extra_data.private:
558
619
            params.private = extra_data.private
559
 
        if 'status' in data:
560
 
            params.status = data['status']
561
 
        if 'assignee' in data:
562
 
            params.assignee = data['assignee']
563
 
        if 'status' in data:
564
 
            params.status = data['status']
565
 
        if 'importance' in data:
566
 
            params.importance = data['importance']
567
 
        if 'milestone' in data:
568
 
            params.milestone = data['milestone']
 
620
 
 
621
        # Apply any extra options given by a bug supervisor.
 
622
        if IHasBugSupervisor.providedBy(context):
 
623
            if self.user.inTeam(context.bug_supervisor):
 
624
                if 'assignee' in data:
 
625
                    params.assignee = data['assignee']
 
626
                if 'status' in data:
 
627
                    params.status = data['status']
 
628
                if 'importance' in data:
 
629
                    params.importance = data['importance']
 
630
                if 'milestone' in data:
 
631
                    params.milestone = data['milestone']
569
632
 
570
633
        self.added_bug = bug = context.createBug(params)
571
634
 
572
 
        # Apply any extra options given by a bug supervisor.
573
635
        for comment in extra_data.comments:
574
636
            bug.newMessage(self.user, bug.followup_subject(), comment)
575
637
            notifications.append(
809
871
        """
810
872
        return self.context
811
873
 
812
 
    @property
813
 
    def bug_reporting_guidelines(self):
814
 
        """Guidelines for filing bugs in the current context.
815
 
 
816
 
        Returns a list of dicts, with each dict containing values for
817
 
        "preamble" and "content".
818
 
        """
819
 
 
820
 
        def target_name(target):
821
 
            # IProjectGroup can be considered the target of a bug during
822
 
            # the bug filing process, but does not extend IBugTarget
823
 
            # and ultimately cannot actually be the target of a
824
 
            # bug. Hence this function to determine a suitable
825
 
            # name/title to display. Hurrumph.
826
 
            if IBugTarget.providedBy(target):
827
 
                return target.bugtargetdisplayname
828
 
            else:
829
 
                return target.title
830
 
 
831
 
        guidelines = []
832
 
        context = self.bugtarget
833
 
        if context is not None:
834
 
            content = context.bug_reporting_guidelines
835
 
            if content is not None and len(content) > 0:
836
 
                guidelines.append({
837
 
                        "source": target_name(context),
838
 
                        "content": content,
839
 
                        })
840
 
            # Distribution source packages are shown with both their
841
 
            # own reporting guidelines and those of their
842
 
            # distribution.
843
 
            if IDistributionSourcePackage.providedBy(context):
844
 
                distribution = context.distribution
845
 
                content = distribution.bug_reporting_guidelines
846
 
                if content is not None and len(content) > 0:
847
 
                    guidelines.append({
848
 
                            "source": target_name(distribution),
849
 
                            "content": content,
850
 
                            })
851
 
        return guidelines
852
 
 
853
874
    default_bug_reported_acknowledgement = "Thank you for your bug report."
854
875
 
855
876
    def getAcknowledgementMessage(self, context):
1019
1040
 
1020
1041
    _SEARCH_FOR_DUPES = ViewPageTemplateFile(
1021
1042
        "../templates/bugtarget-filebug-search.pt")
 
1043
    _PROJECTGROUP_SEARCH_FOR_DUPES = ViewPageTemplateFile(
 
1044
        "../templates/projectgroup-filebug-search.pt")
1022
1045
    _FILEBUG_FORM = ViewPageTemplateFile(
1023
1046
        "../templates/bugtarget-filebug-submit-bug.pt")
1024
1047
 
1042
1065
                config.malone.ubuntu_bug_filing_url)
1043
1066
 
1044
1067
    @safe_action
 
1068
    @action("Continue", name="projectgroupsearch", validator="validate_search")
 
1069
    def projectgroup_search_action(self, action, data):
 
1070
        """Search for similar bug reports."""
 
1071
        # Don't give focus to any widget, to ensure that the browser
 
1072
        # won't scroll past the "possible duplicates" list.
 
1073
        self.initial_focus_widget = None
 
1074
        return self._PROJECTGROUP_SEARCH_FOR_DUPES()
 
1075
 
 
1076
    @safe_action
1045
1077
    @action("Continue", name="search", validator="validate_search")
1046
1078
    def search_action(self, action, data):
1047
1079
        """Search for similar bug reports."""
1154
1186
            url = urlappend(url, self.extra_data_token)
1155
1187
        return url
1156
1188
 
1157
 
    def _getSelectedProduct(self):
1158
 
        """Return the product that's selected."""
1159
 
        assert self.widgets['product'].hasValidInput(), (
1160
 
            "This method should be called only when we know which"
1161
 
            " product the user selected.")
1162
 
        return self.widgets['product'].getInputValue()
1163
 
 
1164
 
    def getSecurityContext(self):
1165
 
        """See FileBugViewBase."""
1166
 
        return self._getSelectedProduct()
1167
 
 
1168
1189
 
1169
1190
class BugTargetBugListingView:
1170
1191
    """Helper methods for rendering bug listings."""