~launchpad-pqm/launchpad/devel

« back to all changes in this revision

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

  • Committer: Curtis Hovey
  • Date: 2011-12-18 15:13:07 UTC
  • mto: This revision was merged to the branch mainline in revision 14547.
  • Revision ID: curtis.hovey@canonical.com-20111218151307-sdm2gzobt5tplbe0
Moved badges to lp.app.

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
from operator import attrgetter
56
56
import os.path
57
57
import re
 
58
import transaction
58
59
import urllib
59
60
import urlparse
60
61
 
78
79
from pytz import utc
79
80
from simplejson import dumps
80
81
from simplejson.encoder import JSONEncoderForHTML
81
 
import transaction
82
82
from z3c.pt.pagetemplate import ViewPageTemplateFile
83
83
from zope import (
84
84
    component,
107
107
    providedBy,
108
108
    )
109
109
from zope.schema import Choice
110
 
from zope.schema.interfaces import IContextSourceBinder
 
110
from zope.schema.interfaces import (
 
111
    IContextSourceBinder,
 
112
    )
111
113
from zope.schema.vocabulary import (
112
114
    getVocabularyRegistry,
113
115
    SimpleVocabulary,
120
122
from zope.traversing.browser import absoluteURL
121
123
from zope.traversing.interfaces import IPathAdapter
122
124
 
123
 
from lp import _
 
125
from canonical.config import config
 
126
from canonical.launchpad import (
 
127
    _,
 
128
    helpers,
 
129
    )
 
130
from canonical.launchpad.browser.feeds import (
 
131
    BugTargetLatestBugsFeedLink,
 
132
    FeedsMixin,
 
133
    )
 
134
from canonical.launchpad.interfaces.launchpad import IHasExternalBugTracker
 
135
from canonical.launchpad.mailnotification import get_unified_diff
 
136
from canonical.launchpad.searchbuilder import (
 
137
    all,
 
138
    any,
 
139
    NULL,
 
140
    )
 
141
from canonical.launchpad.webapp import (
 
142
    canonical_url,
 
143
    enabled_with_permission,
 
144
    GetitemNavigation,
 
145
    LaunchpadView,
 
146
    Link,
 
147
    Navigation,
 
148
    NavigationMenu,
 
149
    redirection,
 
150
    stepthrough,
 
151
    )
 
152
from canonical.launchpad.webapp.authorization import (
 
153
    check_permission,
 
154
    precache_permission_for_objects,
 
155
    )
 
156
from canonical.launchpad.webapp.batching import TableBatchNavigator
 
157
from canonical.launchpad.webapp.breadcrumb import Breadcrumb
 
158
from canonical.launchpad.webapp.interfaces import ILaunchBag
 
159
from canonical.launchpad.webapp.menu import structured
 
160
from canonical.lazr.interfaces import IObjectPrivacy
124
161
from lp.answers.interfaces.questiontarget import IQuestionTarget
125
162
from lp.app.browser.launchpad import iter_view_registrations
126
163
from lp.app.browser.launchpadform import (
215
252
    UNRESOLVED_BUGTASK_STATUSES,
216
253
    UserCannotEditBugTaskStatus,
217
254
    )
218
 
from lp.bugs.interfaces.bugtracker import (
219
 
    BugTrackerType,
220
 
    IHasExternalBugTracker,
221
 
    )
 
255
from lp.bugs.interfaces.bugtracker import BugTrackerType
222
256
from lp.bugs.interfaces.bugwatch import BugWatchActivityStatus
223
257
from lp.bugs.interfaces.cve import ICveSet
224
258
from lp.bugs.interfaces.malone import IMaloneApplication
244
278
from lp.registry.interfaces.sourcepackage import ISourcePackage
245
279
from lp.registry.model.personroles import PersonRoles
246
280
from lp.registry.vocabularies import MilestoneVocabulary
247
 
from lp.services.config import config
248
281
from lp.services.features import getFeatureFlag
249
 
from lp.services.feeds.browser import (
250
 
    BugTargetLatestBugsFeedLink,
251
 
    FeedsMixin,
252
 
    )
253
282
from lp.services.fields import PersonChoice
254
 
from lp.services.helpers import shortlist
255
 
from lp.services.mail.notification import get_unified_diff
256
 
from lp.services.privacy.interfaces import IObjectPrivacy
257
283
from lp.services.propertycache import (
258
284
    cachedproperty,
259
285
    get_property_cache,
260
286
    )
261
 
from lp.services.searchbuilder import (
262
 
    all,
263
 
    any,
264
 
    NULL,
265
 
    )
266
287
from lp.services.utils import obfuscate_structure
267
 
from lp.services.webapp import (
268
 
    canonical_url,
269
 
    enabled_with_permission,
270
 
    GetitemNavigation,
271
 
    LaunchpadView,
272
 
    Link,
273
 
    Navigation,
274
 
    NavigationMenu,
275
 
    redirection,
276
 
    stepthrough,
277
 
    )
278
 
from lp.services.webapp.authorization import (
279
 
    check_permission,
280
 
    precache_permission_for_objects,
281
 
    )
282
 
from lp.services.webapp.batching import TableBatchNavigator
283
 
from lp.services.webapp.breadcrumb import Breadcrumb
284
 
from lp.services.webapp.interfaces import ILaunchBag
285
 
from lp.services.webapp.menu import structured
286
 
 
287
288
 
288
289
vocabulary_registry = getVocabularyRegistry()
289
290
 
420
421
def get_sortorder_from_request(request):
421
422
    """Get the sortorder from the request.
422
423
 
423
 
    >>> from lp.services.webapp.servers import LaunchpadTestRequest
 
424
    >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
424
425
    >>> get_sortorder_from_request(LaunchpadTestRequest(form={}))
425
426
    ['-importance']
426
427
    >>> get_sortorder_from_request(
558
559
                # Security proxy this object on the way out.
559
560
                return getUtility(IBugTaskSet).get(bugtask.id)
560
561
 
561
 
        # If we've come this far, there's no task for the requested context.
562
 
        # If we are attempting to navigate past the non-existent bugtask,
563
 
        # we raise NotFound error. eg +delete or +edit etc.
564
 
        # Otherwise we are simply navigating to a non-existent task and so we
565
 
        # redirect to one that exists.
566
 
        travseral_stack = self.request.getTraversalStack()
567
 
        if len(travseral_stack) > 0:
568
 
            raise NotFoundError
 
562
        # If we've come this far, there's no task for the requested
 
563
        # context. Redirect to one that exists.
569
564
        return self.redirectSubTree(canonical_url(bug.default_bugtask))
570
565
 
571
566
 
1789
1784
    def next_url(self):
1790
1785
        """Return the next URL to call when this call completes."""
1791
1786
        if not self.request.is_ajax:
1792
 
            return self._next_url or self._return_url
 
1787
            return super(BugTaskDeletionView, self).next_url
1793
1788
        return None
1794
1789
 
1795
1790
    @action('Delete', name='delete_bugtask')
1800
1795
        success_message = ("This bug no longer affects %s."
1801
1796
                    % bugtask.bugtargetdisplayname)
1802
1797
        error_message = None
1803
 
        # We set the next_url here before the bugtask is deleted since later
1804
 
        # the bugtask will not be available if required to construct the url.
1805
 
        self._next_url = self._return_url
1806
1798
 
1807
1799
        try:
1808
1800
            bugtask.delete()
2239
2231
            self.bugtask.target,
2240
2232
            view_name="+bugs")
2241
2233
 
2242
 
        flattened = {
 
2234
        def build_tag_url(tag):
 
2235
            """Generate a url for the tag based on the current request ctx."""
 
2236
 
 
2237
        return {
2243
2238
            'age': age,
2244
2239
            'assignee': assignee,
2245
2240
            'bug_url': canonical_url(self.bugtask),
2260
2255
            'title': self.bug.title,
2261
2256
            }
2262
2257
 
2263
 
        # This is a total hack, but pystache will run both truth/false values
2264
 
        # for an empty list for some reason, and it "works" if it's just a
2265
 
        # flag like this. We need this value for the mustache template to be
2266
 
        # able to tell that there are no tags without looking at the list.
2267
 
        flattened['has_tags'] = True if len(flattened['tags']) else False
2268
 
        return flattened
2269
 
 
2270
2258
 
2271
2259
class BugListingBatchNavigator(TableBatchNavigator):
2272
2260
    """A specialised batch navigator to load smartly extra bug information."""
2502
2490
# All sort orders supported by BugTaskSet.search() and a title for
2503
2491
# them.
2504
2492
SORT_KEYS = [
2505
 
    ('importance', 'Importance', 'desc'),
2506
 
    ('status', 'Status', 'asc'),
2507
 
    ('id', 'Number', 'desc'),
2508
 
    ('title', 'Title', 'asc'),
2509
 
    ('targetname', 'Package/Project/Series name', 'asc'),
2510
 
    ('milestone_name', 'Milestone', 'asc'),
2511
 
    ('date_last_updated', 'Date last updated', 'desc'),
2512
 
    ('assignee', 'Assignee', 'asc'),
2513
 
    ('reporter', 'Reporter', 'asc'),
2514
 
    ('datecreated', 'Age', 'desc'),
2515
 
    ('tag', 'Tags', 'asc'),
2516
 
    ('heat', 'Heat', 'desc'),
2517
 
    ('date_closed', 'Date closed', 'desc'),
2518
 
    ('dateassigned', 'Date when the bug task was assigned', 'desc'),
2519
 
    ('number_of_duplicates', 'Number of duplicates', 'desc'),
2520
 
    ('latest_patch_uploaded', 'Date latest patch uploaded', 'desc'),
2521
 
    ('message_count', 'Number of comments', 'desc'),
2522
 
    ('milestone', 'Milestone ID', 'desc'),
2523
 
    ('specification', 'Linked blueprint', 'asc'),
2524
 
    ('task', 'Bug task ID', 'desc'),
2525
 
    ('users_affected_count', 'Number of affected users', 'desc'),
 
2493
    ('importance', 'Importance'),
 
2494
    ('status', 'Status'),
 
2495
    ('id', 'Bug number'),
 
2496
    ('title', 'Bug title'),
 
2497
    ('targetname', 'Package/Project/Series name'),
 
2498
    ('milestone_name', 'Milestone'),
 
2499
    ('date_last_updated', 'Date bug last updated'),
 
2500
    ('assignee', 'Assignee'),
 
2501
    ('reporter', 'Reporter'),
 
2502
    ('datecreated', 'Bug age'),
 
2503
    ('tag', 'Bug Tags'),
 
2504
    ('heat', 'Bug heat'),
 
2505
    ('date_closed', 'Date bug closed'),
 
2506
    ('dateassigned', 'Date when the bug task was assigned'),
 
2507
    ('number_of_duplicates', 'Number of duplicates'),
 
2508
    ('latest_patch_uploaded', 'Date latest patch uploaded'),
 
2509
    ('message_count', 'Number of comments'),
 
2510
    ('milestone', 'Milestone ID'),
 
2511
    ('specification', 'Linked blueprint'),
 
2512
    ('task', 'Bug task ID'),
 
2513
    ('users_affected_count', 'Number of affected users'),
2526
2514
    ]
2527
2515
 
2528
2516
 
2555
2543
    custom_widget('structural_subscriber', PersonPickerWidget)
2556
2544
    custom_widget('subscriber', PersonPickerWidget)
2557
2545
 
2558
 
    _batch_navigator = None
2559
 
 
2560
2546
    @cachedproperty
2561
2547
    def bug_tracking_usage(self):
2562
2548
        """Whether the context tracks bugs in Launchpad.
3021
3007
            the search criteria taken from the request. Params in
3022
3008
            `extra_params` take precedence over request params.
3023
3009
        """
3024
 
        if self._batch_navigator is None:
3025
 
            unbatchedTasks = self.searchUnbatched(
3026
 
                searchtext, context, extra_params)
3027
 
            self._batch_navigator = self._getBatchNavigator(unbatchedTasks)
3028
 
        return self._batch_navigator
 
3010
        unbatchedTasks = self.searchUnbatched(
 
3011
            searchtext, context, extra_params)
 
3012
        return self._getBatchNavigator(unbatchedTasks)
3029
3013
 
3030
3014
    def searchUnbatched(self, searchtext=None, context=None,
3031
3015
                        extra_params=None, prejoins=[]):
3071
3055
                dict(
3072
3056
                    value=term.token, title=term.title or term.token,
3073
3057
                    checked=term.value in default_values))
3074
 
        return shortlist(widget_values, longest_expected=12)
 
3058
        return helpers.shortlist(widget_values, longest_expected=12)
3075
3059
 
3076
3060
    def getStatusWidgetValues(self):
3077
3061
        """Return data used to render the status checkboxes."""
3576
3560
 
3577
3561
        # If we have made it to here then the logged in user can see the
3578
3562
        # bug, hence they can see any assignees.
3579
 
        # The security adaptor will do the job also but we don't want or need
3580
 
        # the expense of running several complex SQL queries.
3581
3563
        authorised_people = [task.assignee for task in self.bugtasks
3582
3564
                             if task.assignee is not None]
3583
3565
        precache_permission_for_objects(
4233
4215
        """Return the heading to search all Bugs."""
4234
4216
        return "Search all bug reports"
4235
4217
 
4236
 
    def search_macro_title(self):
4237
 
        return u'Search all bugs'
4238
 
 
4239
4218
    @property
4240
4219
    def label(self):
4241
4220
        return self.getSearchPageHeading()