52
52
'PersonSpecWorkloadTableView',
53
53
'PersonSpecWorkloadView',
55
'PersonStructuralSubscriptionsView',
55
56
'PersonSubscribedBugTaskSearchListingView',
56
57
'PersonSubscriptionsView',
221
219
from lp.answers.interfaces.questioncollection import IQuestionSet
222
220
from lp.answers.interfaces.questionenums import QuestionParticipation
223
221
from lp.answers.interfaces.questionsperson import IQuestionsPerson
222
from lp.app.browser.launchpadform import (
225
LaunchpadEditFormView,
224
228
from lp.app.browser.stringformatter import FormattersAPI
225
229
from lp.app.browser.tales import (
226
230
DateTimeFormatterAPI,
543
552
"""Traverse to this person's recipes."""
544
553
return self.context.getRecipe(name)
555
@stepthrough('+merge-queues')
556
def traverse_merge_queue(self, name):
557
"""Traverse to this person's merge queues."""
558
return self.context.getMergeQueue(name)
547
561
class TeamNavigation(PersonNavigation):
990
1004
enabled = bool(self.person.getOwnedOrDrivenPillars())
991
1005
return Link(target, text, enabled=enabled, icon='info')
1007
def subscriptions(self):
1008
target = '+subscriptions'
1009
text = 'Direct subscriptions'
1010
return Link(target, text, icon='info')
1012
def structural_subscriptions(self):
1013
target = '+structural-subscriptions'
1014
text = 'Structural subscriptions'
1015
return Link(target, text, icon='info')
994
1018
class PersonMenuMixin(CommonMenuLinks):
1024
1048
usedfor = IPerson
1025
1049
facet = 'overview'
1026
links = ['edit', 'branding', 'common_edithomepage',
1027
'editemailaddresses', 'editlanguages', 'editwikinames',
1028
'editircnicknames', 'editjabberids',
1029
'editsshkeys', 'editpgpkeys', 'editlocation', 'memberships',
1030
'codesofconduct', 'karma', 'administer', 'administer_account',
1031
'projects', 'activate_ppa', 'maintained',
1032
'view_ppa_subscriptions', 'ppa', 'oauth_tokens',
1033
'related_software_summary', 'view_recipes']
1053
'common_edithomepage',
1054
'editemailaddresses',
1066
'administer_account',
1070
'view_ppa_subscriptions',
1073
'related_software_summary',
1076
'structural_subscriptions',
1035
1079
def related_software_summary(self):
1036
1080
target = '+related-software'
1310
1354
self.person.displayname)
1311
1355
return Link(target, text, summary, icon='edit')
1313
@enabled_with_permission('launchpad.MailingListManager')
1357
@enabled_with_permission('launchpad.Moderate')
1314
1358
def configure_mailing_list(self):
1315
1359
target = '+mailinglist'
1316
1360
mailing_list = self.person.mailing_list
1371
1415
usedfor = ITeam
1372
1416
facet = 'overview'
1373
links = ['edit', 'branding', 'common_edithomepage', 'members', 'mugshots',
1374
'add_member', 'proposed_members',
1375
'memberships', 'received_invitations',
1376
'editemail', 'configure_mailing_list', 'moderate_mailing_list',
1377
'editlanguages', 'map', 'polls',
1378
'add_poll', 'join', 'leave', 'add_my_teams',
1379
'reassign', 'projects', 'activate_ppa', 'maintained', 'ppa',
1380
'related_software_summary', 'view_recipes']
1420
'common_edithomepage',
1426
'received_invitations',
1428
'configure_mailing_list',
1429
'moderate_mailing_list',
1442
'related_software_summary',
1445
'structural_subscriptions',
1383
1449
class TeamOverviewNavigationMenu(NavigationMenu, TeamMenuMixin):
1711
1777
# Only the IPerson can be traversed to, so it provides the IAccount.
1712
1778
# It also means that permissions are checked on IAccount, not IPerson.
1713
1779
self.person = self.context
1714
from canonical.launchpad.interfaces import IMasterObject
1780
from canonical.launchpad.interfaces.lpstorm import IMasterObject
1715
1781
self.context = IMasterObject(self.context.account)
1716
1782
# Set fields to be displayed.
1717
1783
self.field_names = ['status', 'status_comment']
2074
2140
def getMilestoneWidgetValues(self):
2075
2141
"""Return data used to render the milestone checkboxes."""
2076
milestones = getUtility(IBugTaskSet).getAssignedMilestonesFromSearch(
2077
self.searchUnbatched())
2143
(Milestone, Join(Milestone, BugTask.milestone == Milestone.id))]
2146
for bugtask in self.searchUnbatched(prejoins=prejoins)]
2147
milestones = sorted(milestones, key=milestone_sort_key, reverse=True)
2079
2149
dict(title=milestone.title, value=milestone.id, checked=False)
2080
2150
for milestone in milestones]
2090
2160
page_title = 'Related bugs'
2092
2162
def searchUnbatched(self, searchtext=None, context=None,
2163
extra_params=None, prejoins=[]):
2094
2164
"""Return the open bugs related to a person.
2096
2166
:param extra_params: A dict that provides search params added to
2122
2192
return context.searchTasks(
2123
2193
assignee_params, subscriber_params, owner_params,
2194
commenter_params, prejoins=prejoins)
2126
2196
def getSearchPageHeading(self):
2127
2197
return "Bugs related to %s" % self.context.displayname
2162
2232
extra_params['assignee'] = context
2164
2234
sup = super(PersonAssignedBugTaskSearchListingView, self)
2165
return sup.searchUnbatched(searchtext, context, extra_params)
2235
return sup.searchUnbatched(
2236
searchtext, context, extra_params, prejoins)
2167
2238
def shouldShowAssigneeWidget(self):
2168
2239
"""Should the assignee widget be shown on the advanced search page?"""
2207
2278
page_title = 'Commented bugs'
2209
2280
def searchUnbatched(self, searchtext=None, context=None,
2281
extra_params=None, prejoins=[]):
2211
2282
"""Return the open bugs commented on by a person."""
2212
2283
if context is None:
2213
2284
context = self.context
2219
2290
extra_params['bug_commenter'] = context
2221
2292
sup = super(PersonCommentedBugTaskSearchListingView, self)
2222
return sup.searchUnbatched(searchtext, context, extra_params)
2293
return sup.searchUnbatched(
2294
searchtext, context, extra_params, prejoins)
2224
2296
def getSearchPageHeading(self):
2225
2297
"""The header for the search page."""
2267
2339
extra_params['bug_reporter'] = context
2269
2341
sup = super(PersonReportedBugTaskSearchListingView, self)
2270
return sup.searchUnbatched(searchtext, context, extra_params)
2342
return sup.searchUnbatched(
2343
searchtext, context, extra_params, prejoins)
2272
2345
def getSearchPageHeading(self):
2273
2346
"""The header for the search page."""
2308
2381
page_title = 'Subscribed bugs'
2310
2383
def searchUnbatched(self, searchtext=None, context=None,
2384
extra_params=None, prejoins=[]):
2312
2385
"""Return the bugs subscribed to by a person."""
2313
2386
if context is None:
2314
2387
context = self.context
2320
2393
extra_params['subscriber'] = context
2322
2395
sup = super(PersonSubscribedBugTaskSearchListingView, self)
2323
return sup.searchUnbatched(searchtext, context, extra_params)
2396
return sup.searchUnbatched(
2397
searchtext, context, extra_params, prejoins)
2325
2399
def getSearchPageHeading(self):
2326
2400
"""The header for the search page."""
2344
2418
return self.getSearchPageHeading()
2347
class PersonSubscriptionsView(BugTaskSearchListingView):
2421
class PersonSubscriptionsView(LaunchpadView):
2348
2422
"""All the subscriptions for a person."""
2350
2424
page_title = 'Subscriptions'
2352
2426
def subscribedBugTasks(self):
2353
"""Return a BatchNavigator for distinct bug tasks to which the
2354
person is subscribed."""
2428
Return a BatchNavigator for distinct bug tasks to which the person is
2355
2431
bug_tasks = self.context.searchTasks(None, user=self.user,
2356
2432
order_by='-date_last_updated',
2357
2433
status=(BugTaskStatus.NEW,
2364
2440
bug_subscriber=self.context)
2366
2442
sub_bug_tasks = []
2445
# XXX: GavinPanella 2010-10-08 bug=656904: This materializes the
2446
# entire result set. It would probably be more efficient implemented
2447
# with a pre_iter_hook on a DecoratedResultSet.
2369
2448
for task in bug_tasks:
2449
# We order the bugtasks by date_last_updated but we always display
2450
# the default task for the bug. This is to avoid ordering issues
2451
# in tests and also prevents user confusion (because nothing is
2452
# more confusing than your subscription targets changing seemingly
2370
2454
if task.bug not in sub_bugs:
2371
# We order the bugtasks by date_last_updated but we
2372
# always display the default task for the bug. This is
2373
# to avoid ordering issues in tests and also prevents
2374
# user confusion (because nothing is more confusing than
2375
# your subscription targets changing seemingly at
2455
# XXX: GavinPanella 2010-10-08 bug=656904: default_bugtask
2456
# causes a query to be executed. It would be more efficient to
2457
# get the default bugtask in bulk, in a pre_iter_hook on a
2458
# DecoratedResultSet perhaps.
2377
2459
sub_bug_tasks.append(task.bug.default_bugtask)
2378
sub_bugs.append(task.bug)
2460
sub_bugs.add(task.bug)
2379
2462
return BatchNavigator(sub_bug_tasks, self.request)
2381
2464
def canUnsubscribeFromBugTasks(self):
2382
viewed_person = self.context
2383
if self.user is None:
2385
elif viewed_person == self.user:
2387
elif (viewed_person.is_team and
2388
self.user.inTeam(viewed_person)):
2465
"""Can the current user unsubscribe from the bug tasks shown?"""
2466
return (self.user is not None and
2467
self.user.inTeam(self.context))
2391
def getSubscriptionsPageHeading(self):
2392
2471
"""The header for the subscriptions page."""
2393
2472
return "Subscriptions for %s" % self.context.displayname
2475
class PersonStructuralSubscriptionsView(LaunchpadView):
2476
"""All the structural subscriptions for a person."""
2478
page_title = 'Structural subscriptions'
2480
def canUnsubscribeFromBugTasks(self):
2481
"""Can the current user modify subscriptions for the context?"""
2482
return (self.user is not None and
2483
self.user.inTeam(self.context))
2396
2486
def label(self):
2397
return self.getSubscriptionsPageHeading()
2487
"""The header for the structural subscriptions page."""
2488
return "Structural subscriptions for %s" % self.context.displayname
2400
2491
class PersonVouchersView(LaunchpadFormView):
3408
3499
'center_lng': self.context.longitude}
3410
3501
<script type="text/javascript">
3411
YUI().use('node', 'lp.app.mapping', function(Y) {
3502
LPS.use('node', 'lp.app.mapping', function(Y) {
3412
3503
function renderMap() {
3413
3504
Y.lp.app.mapping.renderPersonMapSmall(
3414
3505
%(center_lat)s, %(center_lng)s);
3476
3567
return 'portlet'
3477
3568
return 'portlet private'
3571
def add_member_step_title(self):
3572
"""A string for setup_add_member_handler with escaped quotes."""
3573
vocabulary_registry = getVocabularyRegistry()
3574
vocabulary = vocabulary_registry.get(self.context, 'ValidTeamMember')
3575
return vocabulary.step_title.replace("'", "\\'").replace('"', '\\"')
3480
3578
class PersonCodeOfConductEditView(LaunchpadView):
3481
3579
"""View for the ~person/+codesofconduct pages."""