120
122
from zope.traversing.browser import absoluteURL
121
123
from zope.traversing.interfaces import IPathAdapter
125
from canonical.config import config
126
from canonical.launchpad import (
130
from canonical.launchpad.browser.feeds import (
131
BugTargetLatestBugsFeedLink,
134
from canonical.launchpad.interfaces.launchpad import IHasExternalBugTracker
135
from canonical.launchpad.mailnotification import get_unified_diff
136
from canonical.launchpad.searchbuilder import (
141
from canonical.launchpad.webapp import (
143
enabled_with_permission,
152
from canonical.launchpad.webapp.authorization import (
154
precache_permission_for_objects,
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,
218
from lp.bugs.interfaces.bugtracker import (
220
IHasExternalBugTracker,
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,
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 (
259
285
get_property_cache,
261
from lp.services.searchbuilder import (
266
287
from lp.services.utils import obfuscate_structure
267
from lp.services.webapp import (
269
enabled_with_permission,
278
from lp.services.webapp.authorization import (
280
precache_permission_for_objects,
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
288
289
vocabulary_registry = getVocabularyRegistry()
558
559
# Security proxy this object on the way out.
559
560
return getUtility(IBugTaskSet).get(bugtask.id)
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:
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))
2075
2067
The bugtarget may be an `IDistribution`, `IDistroSeries`, `IProduct`,
2076
2068
or `IProductSeries`.
2070
days_old = config.malone.days_before_expiration
2078
2072
if target_has_expirable_bugs_listing(self.context):
2079
2073
return getUtility(IBugTaskSet).findExpirableBugTasks(
2080
0, user=self.user, target=self.context).count()
2074
days_old, user=self.user, target=self.context).count()
2255
2244
'reporter': self.bug.owner.displayname,
2256
2245
'status': self.status.title,
2257
2246
'status_class': 'status' + self.status.name,
2258
'tags': [{'url': base_tag_url + tag, 'tag': tag}
2259
for tag in self.bug.tags],
2247
'tags': ' '.join(self.bug.tags),
2260
2248
'title': self.bug.title,
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
2271
2252
class BugListingBatchNavigator(TableBatchNavigator):
2272
2253
"""A specialised batch navigator to load smartly extra bug information."""
2499
2480
return Link('+nominations', 'Review nominations', icon='bug')
2502
# All sort orders supported by BugTaskSet.search() and a title for
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'),
2529
2483
class BugTaskSearchListingView(LaunchpadFormView, FeedsMixin, BugsInfoMixin):
2530
2484
"""View that renders a list of bugs for a given set of search criteria."""
3021
2972
the search criteria taken from the request. Params in
3022
2973
`extra_params` take precedence over request params.
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
2975
unbatchedTasks = self.searchUnbatched(
2976
searchtext, context, extra_params)
2977
return self._getBatchNavigator(unbatchedTasks)
3030
2979
def searchUnbatched(self, searchtext=None, context=None,
3031
2980
extra_params=None, prejoins=[]):
3072
3021
value=term.token, title=term.title or term.token,
3073
3022
checked=term.value in default_values))
3074
return shortlist(widget_values, longest_expected=12)
3023
return helpers.shortlist(widget_values, longest_expected=12)
3076
3025
def getStatusWidgetValues(self):
3077
3026
"""Return data used to render the status checkboxes."""
3160
3109
def structural_subscriber_label(self):
3161
3110
if IDistribution.providedBy(self.context):
3162
return 'Package or series subscriber'
3111
return 'Package, or series subscriber'
3163
3112
elif IDistroSeries.providedBy(self.context):
3164
3113
return 'Package subscriber'
3165
3114
elif IProduct.providedBy(self.context):
3577
3526
# If we have made it to here then the logged in user can see the
3578
3527
# 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
3528
authorised_people = [task.assignee for task in self.bugtasks
3582
3529
if task.assignee is not None]
3583
3530
precache_permission_for_objects(
4386
4329
return ['id', 'summary', 'date_last_updated', 'heat']
4388
4332
def search(self):
4389
4333
"""Return an `ITableBatchNavigator` for the expirable bugtasks."""
4334
days_old = config.malone.days_before_expiration
4390
4335
bugtaskset = getUtility(IBugTaskSet)
4391
4336
bugtasks = bugtaskset.findExpirableBugTasks(
4392
user=self.user, target=self.context, min_days_old=0)
4337
days_old, user=self.user, target=self.context)
4393
4338
return BugListingBatchNavigator(
4394
4339
bugtasks, self.request, columns_to_show=self.columns_to_show,
4395
4340
size=config.malone.buglist_batch_size)