Merge lp:~wgrant/launchpad/unify-person-questions into lp:launchpad
- unify-person-questions
- Merge into devel
Proposed by
William Grant
Status: | Merged |
---|---|
Merged at revision: | 18208 |
Proposed branch: | lp:~wgrant/launchpad/unify-person-questions |
Merge into: | lp:launchpad |
Diff against target: |
676 lines (+288/-267) 3 files modified
lib/lp/answers/browser/configure.zcml (+10/-10) lib/lp/answers/browser/person.py (+278/-0) lib/lp/registry/browser/person.py (+0/-257) |
To merge this branch: | bzr merge lp:~wgrant/launchpad/unify-person-questions |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+306685@code.launchpad.net |
Commit message
Move Person questions views from lp.registry to lp.answers.
Description of the change
Move Person questions views from lp.registry to lp.answers.
To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/answers/browser/configure.zcml' |
2 | --- lib/lp/answers/browser/configure.zcml 2014-04-24 02:53:05 +0000 |
3 | +++ lib/lp/answers/browser/configure.zcml 2016-09-24 06:34:14 +0000 |
4 | @@ -385,14 +385,14 @@ |
5 | /> |
6 | <browser:page |
7 | for="lp.registry.interfaces.person.IPerson" |
8 | - class="lp.registry.browser.person.PersonLatestQuestionsView" |
9 | + class="lp.answers.browser.person.PersonLatestQuestionsView" |
10 | name="+portlet-latestquestions" |
11 | permission="zope.Public" |
12 | template="../templates/questiontarget-portlet-latestquestions.pt" |
13 | /> |
14 | <browser:page |
15 | for="lp.registry.interfaces.person.IPerson" |
16 | - class="lp.registry.browser.person.PersonSearchQuestionsView" |
17 | + class="lp.answers.browser.person.PersonSearchQuestionsView" |
18 | name="+questions" |
19 | permission="zope.Public" |
20 | /> |
21 | @@ -404,7 +404,7 @@ |
22 | /> |
23 | <browser:page |
24 | for="lp.registry.interfaces.person.IPerson" |
25 | - class="lp.registry.browser.person.SearchAnsweredQuestionsView" |
26 | + class="lp.answers.browser.person.SearchAnsweredQuestionsView" |
27 | name="+answeredquestions" |
28 | permission="zope.Public" |
29 | /> |
30 | @@ -416,7 +416,7 @@ |
31 | /> |
32 | <browser:page |
33 | for="lp.registry.interfaces.person.IPerson" |
34 | - class="lp.registry.browser.person.SearchAssignedQuestionsView" |
35 | + class="lp.answers.browser.person.SearchAssignedQuestionsView" |
36 | name="+assignedquestions" |
37 | permission="zope.Public" |
38 | /> |
39 | @@ -428,7 +428,7 @@ |
40 | /> |
41 | <browser:page |
42 | for="lp.registry.interfaces.person.IPerson" |
43 | - class="lp.registry.browser.person.SearchCommentedQuestionsView" |
44 | + class="lp.answers.browser.person.SearchCommentedQuestionsView" |
45 | name="+commentedquestions" |
46 | permission="zope.Public" |
47 | /> |
48 | @@ -440,7 +440,7 @@ |
49 | /> |
50 | <browser:page |
51 | for="lp.registry.interfaces.person.IPerson" |
52 | - class="lp.registry.browser.person.SearchCreatedQuestionsView" |
53 | + class="lp.answers.browser.person.SearchCreatedQuestionsView" |
54 | name="+createdquestions" |
55 | permission="zope.Public" |
56 | /> |
57 | @@ -452,7 +452,7 @@ |
58 | /> |
59 | <browser:page |
60 | for="lp.registry.interfaces.person.IPerson" |
61 | - class="lp.registry.browser.person.SearchNeedAttentionQuestionsView" |
62 | + class="lp.answers.browser.person.SearchNeedAttentionQuestionsView" |
63 | name="+needattentionquestions" |
64 | permission="zope.Public" |
65 | /> |
66 | @@ -464,7 +464,7 @@ |
67 | /> |
68 | <browser:page |
69 | for="lp.registry.interfaces.person.IPerson" |
70 | - class="lp.registry.browser.person.SearchSubscribedQuestionsView" |
71 | + class="lp.answers.browser.person.SearchSubscribedQuestionsView" |
72 | name="+subscribedquestions" |
73 | permission="zope.Public" |
74 | /> |
75 | @@ -476,13 +476,13 @@ |
76 | /> |
77 | <browser:page |
78 | for="lp.registry.interfaces.person.IPerson" |
79 | - class="lp.registry.browser.person.PersonAnswerContactForView" |
80 | + class="lp.answers.browser.person.PersonAnswerContactForView" |
81 | name="+answer-contact-for" |
82 | permission="zope.Public" |
83 | template="../templates/person-answer-contact-for.pt" |
84 | /> |
85 | <browser:menus |
86 | - module="lp.registry.browser.person" |
87 | + module="lp.answers.browser.person" |
88 | classes="PersonAnswersMenu" |
89 | /> |
90 | <browser:page |
91 | |
92 | === added file 'lib/lp/answers/browser/person.py' |
93 | --- lib/lp/answers/browser/person.py 1970-01-01 00:00:00 +0000 |
94 | +++ lib/lp/answers/browser/person.py 2016-09-24 06:34:14 +0000 |
95 | @@ -0,0 +1,278 @@ |
96 | +# Copyright 2009-2014 Canonical Ltd. This software is licensed under the |
97 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
98 | + |
99 | +"""Person-related answer listing classes.""" |
100 | + |
101 | +__metaclass__ = type |
102 | +__all__ = [ |
103 | + 'PersonAnswerContactForView', |
104 | + 'PersonAnswersMenu', |
105 | + 'PersonLatestQuestionsView', |
106 | + 'PersonSearchQuestionsView', |
107 | + 'SearchAnsweredQuestionsView', |
108 | + 'SearchAssignedQuestionsView', |
109 | + 'SearchCommentedQuestionsView', |
110 | + 'SearchCreatedQuestionsView', |
111 | + 'SearchNeedAttentionQuestionsView', |
112 | + 'SearchSubscribedQuestionsView', |
113 | + ] |
114 | + |
115 | + |
116 | +from operator import attrgetter |
117 | + |
118 | +from lp import _ |
119 | +from lp.answers.browser.questiontarget import SearchQuestionsView |
120 | +from lp.answers.enums import QuestionParticipation |
121 | +from lp.answers.interfaces.questionsperson import IQuestionsPerson |
122 | +from lp.app.browser.launchpadform import LaunchpadFormView |
123 | +from lp.registry.interfaces.person import IPerson |
124 | +from lp.services.propertycache import cachedproperty |
125 | +from lp.services.webapp import ( |
126 | + Link, |
127 | + NavigationMenu, |
128 | + ) |
129 | +from lp.services.webapp.publisher import LaunchpadView |
130 | + |
131 | + |
132 | +class PersonLatestQuestionsView(LaunchpadFormView): |
133 | + """View used by the porlet displaying the latest questions made by |
134 | + a person. |
135 | + """ |
136 | + |
137 | + @cachedproperty |
138 | + def getLatestQuestions(self, quantity=5): |
139 | + """Return <quantity> latest questions created for this target. """ |
140 | + return IQuestionsPerson(self.context).searchQuestions( |
141 | + participation=QuestionParticipation.OWNER)[:quantity] |
142 | + |
143 | + |
144 | +class PersonSearchQuestionsView(SearchQuestionsView): |
145 | + """View to search and display questions that involve an `IPerson`.""" |
146 | + |
147 | + display_target_column = True |
148 | + |
149 | + @property |
150 | + def template(self): |
151 | + # Persons always show the default template. |
152 | + return self.default_template |
153 | + |
154 | + @property |
155 | + def pageheading(self): |
156 | + """See `SearchQuestionsView`.""" |
157 | + return _('Questions involving $name', |
158 | + mapping=dict(name=self.context.displayname)) |
159 | + |
160 | + @property |
161 | + def empty_listing_message(self): |
162 | + """See `SearchQuestionsView`.""" |
163 | + return _('No questions involving $name found with the ' |
164 | + 'requested statuses.', |
165 | + mapping=dict(name=self.context.displayname)) |
166 | + |
167 | + |
168 | +class SearchAnsweredQuestionsView(PersonSearchQuestionsView): |
169 | + """View used to search and display questions answered by an IPerson.""" |
170 | + |
171 | + def getDefaultFilter(self): |
172 | + """See `SearchQuestionsView`.""" |
173 | + return dict(participation=QuestionParticipation.ANSWERER) |
174 | + |
175 | + @property |
176 | + def pageheading(self): |
177 | + """See `SearchQuestionsView`.""" |
178 | + return _('Questions answered by $name', |
179 | + mapping=dict(name=self.context.displayname)) |
180 | + |
181 | + @property |
182 | + def empty_listing_message(self): |
183 | + """See `SearchQuestionsView`.""" |
184 | + return _('No questions answered by $name found with the ' |
185 | + 'requested statuses.', |
186 | + mapping=dict(name=self.context.displayname)) |
187 | + |
188 | + |
189 | +class SearchAssignedQuestionsView(PersonSearchQuestionsView): |
190 | + """View used to search and display questions assigned to an IPerson.""" |
191 | + |
192 | + def getDefaultFilter(self): |
193 | + """See `SearchQuestionsView`.""" |
194 | + return dict(participation=QuestionParticipation.ASSIGNEE) |
195 | + |
196 | + @property |
197 | + def pageheading(self): |
198 | + """See `SearchQuestionsView`.""" |
199 | + return _('Questions assigned to $name', |
200 | + mapping=dict(name=self.context.displayname)) |
201 | + |
202 | + @property |
203 | + def empty_listing_message(self): |
204 | + """See `SearchQuestionsView`.""" |
205 | + return _('No questions assigned to $name found with the ' |
206 | + 'requested statuses.', |
207 | + mapping=dict(name=self.context.displayname)) |
208 | + |
209 | + |
210 | +class SearchCommentedQuestionsView(PersonSearchQuestionsView): |
211 | + """View used to search and show questions commented on by an IPerson.""" |
212 | + |
213 | + def getDefaultFilter(self): |
214 | + """See `SearchQuestionsView`.""" |
215 | + return dict(participation=QuestionParticipation.COMMENTER) |
216 | + |
217 | + @property |
218 | + def pageheading(self): |
219 | + """See `SearchQuestionsView`.""" |
220 | + return _('Questions commented on by $name ', |
221 | + mapping=dict(name=self.context.displayname)) |
222 | + |
223 | + @property |
224 | + def empty_listing_message(self): |
225 | + """See `SearchQuestionsView`.""" |
226 | + return _('No questions commented on by $name found with the ' |
227 | + 'requested statuses.', |
228 | + mapping=dict(name=self.context.displayname)) |
229 | + |
230 | + |
231 | +class SearchCreatedQuestionsView(PersonSearchQuestionsView): |
232 | + """View used to search and display questions created by an IPerson.""" |
233 | + |
234 | + def getDefaultFilter(self): |
235 | + """See `SearchQuestionsView`.""" |
236 | + return dict(participation=QuestionParticipation.OWNER) |
237 | + |
238 | + @property |
239 | + def pageheading(self): |
240 | + """See `SearchQuestionsView`.""" |
241 | + return _('Questions asked by $name', |
242 | + mapping=dict(name=self.context.displayname)) |
243 | + |
244 | + @property |
245 | + def empty_listing_message(self): |
246 | + """See `SearchQuestionsView`.""" |
247 | + return _('No questions asked by $name found with the ' |
248 | + 'requested statuses.', |
249 | + mapping=dict(name=self.context.displayname)) |
250 | + |
251 | + |
252 | +class SearchNeedAttentionQuestionsView(PersonSearchQuestionsView): |
253 | + """View used to search and show questions needing an IPerson attention.""" |
254 | + |
255 | + def getDefaultFilter(self): |
256 | + """See `SearchQuestionsView`.""" |
257 | + return dict(needs_attention=True) |
258 | + |
259 | + @property |
260 | + def pageheading(self): |
261 | + """See `SearchQuestionsView`.""" |
262 | + return _("Questions needing $name's attention", |
263 | + mapping=dict(name=self.context.displayname)) |
264 | + |
265 | + @property |
266 | + def empty_listing_message(self): |
267 | + """See `SearchQuestionsView`.""" |
268 | + return _("No questions need $name's attention.", |
269 | + mapping=dict(name=self.context.displayname)) |
270 | + |
271 | + |
272 | +class SearchSubscribedQuestionsView(PersonSearchQuestionsView): |
273 | + """View used to search and show questions subscribed to by an IPerson.""" |
274 | + |
275 | + def getDefaultFilter(self): |
276 | + """See `SearchQuestionsView`.""" |
277 | + return dict(participation=QuestionParticipation.SUBSCRIBER) |
278 | + |
279 | + @property |
280 | + def pageheading(self): |
281 | + """See `SearchQuestionsView`.""" |
282 | + return _('Questions $name is subscribed to', |
283 | + mapping=dict(name=self.context.displayname)) |
284 | + |
285 | + @property |
286 | + def empty_listing_message(self): |
287 | + """See `SearchQuestionsView`.""" |
288 | + return _('No questions subscribed to by $name found with the ' |
289 | + 'requested statuses.', |
290 | + mapping=dict(name=self.context.displayname)) |
291 | + |
292 | + |
293 | +class PersonAnswerContactForView(LaunchpadView): |
294 | + """View used to show all the IQuestionTargets that an IPerson is an answer |
295 | + contact for. |
296 | + """ |
297 | + |
298 | + @property |
299 | + def label(self): |
300 | + return 'Projects for which %s is an answer contact' % ( |
301 | + self.context.displayname) |
302 | + |
303 | + page_title = label |
304 | + |
305 | + @cachedproperty |
306 | + def direct_question_targets(self): |
307 | + """List of targets that the IPerson is a direct answer contact. |
308 | + |
309 | + Return a list of IQuestionTargets sorted alphabetically by title. |
310 | + """ |
311 | + return sorted( |
312 | + IQuestionsPerson(self.context).getDirectAnswerQuestionTargets(), |
313 | + key=attrgetter('title')) |
314 | + |
315 | + @cachedproperty |
316 | + def team_question_targets(self): |
317 | + """List of IQuestionTargets for the context's team membership. |
318 | + |
319 | + Sorted alphabetically by title. |
320 | + """ |
321 | + return sorted( |
322 | + IQuestionsPerson(self.context).getTeamAnswerQuestionTargets(), |
323 | + key=attrgetter('title')) |
324 | + |
325 | + def showRemoveYourselfLink(self): |
326 | + """The link is shown when the page is in the user's own profile.""" |
327 | + return self.user == self.context |
328 | + |
329 | + |
330 | +class PersonAnswersMenu(NavigationMenu): |
331 | + |
332 | + usedfor = IPerson |
333 | + facet = 'answers' |
334 | + links = ['answered', 'assigned', 'created', 'commented', 'need_attention', |
335 | + 'subscribed', 'answer_contact_for'] |
336 | + |
337 | + def answer_contact_for(self): |
338 | + summary = "Projects for which %s is an answer contact" % ( |
339 | + self.context.displayname) |
340 | + return Link( |
341 | + '+answer-contact-for', 'Answer contact for', summary, icon='edit') |
342 | + |
343 | + def answered(self): |
344 | + summary = 'Questions answered by %s' % self.context.displayname |
345 | + return Link( |
346 | + '+answeredquestions', 'Answered', summary, icon='question') |
347 | + |
348 | + def assigned(self): |
349 | + summary = 'Questions assigned to %s' % self.context.displayname |
350 | + return Link( |
351 | + '+assignedquestions', 'Assigned', summary, icon='question') |
352 | + |
353 | + def created(self): |
354 | + summary = 'Questions asked by %s' % self.context.displayname |
355 | + return Link('+createdquestions', 'Asked', summary, icon='question') |
356 | + |
357 | + def commented(self): |
358 | + summary = 'Questions commented on by %s' % ( |
359 | + self.context.displayname) |
360 | + return Link( |
361 | + '+commentedquestions', 'Commented', summary, icon='question') |
362 | + |
363 | + def need_attention(self): |
364 | + summary = 'Questions needing %s attention' % ( |
365 | + self.context.displayname) |
366 | + return Link('+needattentionquestions', 'Need attention', summary, |
367 | + icon='question') |
368 | + |
369 | + def subscribed(self): |
370 | + text = 'Subscribed' |
371 | + summary = 'Questions subscribed to by %s' % ( |
372 | + self.context.displayname) |
373 | + return Link('+subscribedquestions', text, summary, icon='question') |
374 | |
375 | === modified file 'lib/lp/registry/browser/person.py' |
376 | --- lib/lp/registry/browser/person.py 2016-07-28 00:26:13 +0000 |
377 | +++ lib/lp/registry/browser/person.py 2016-09-24 06:34:14 +0000 |
378 | @@ -11,8 +11,6 @@ |
379 | 'PeopleSearchView', |
380 | 'PersonAccountAdministerView', |
381 | 'PersonAdministerView', |
382 | - 'PersonAnswerContactForView', |
383 | - 'PersonAnswersMenu', |
384 | 'PersonBrandingView', |
385 | 'PersonBreadcrumb', |
386 | 'PersonCodeOfConductEditView', |
387 | @@ -29,7 +27,6 @@ |
388 | 'PersonIndexView', |
389 | 'PersonKarmaView', |
390 | 'PersonLanguagesView', |
391 | - 'PersonLatestQuestionsView', |
392 | 'PersonNavigation', |
393 | 'PersonOAuthTokensView', |
394 | 'PersonOverviewMenu', |
395 | @@ -38,7 +35,6 @@ |
396 | 'PersonRdfView', |
397 | 'PersonRelatedSoftwareView', |
398 | 'PersonRenameFormMixin', |
399 | - 'PersonSearchQuestionsView', |
400 | 'PersonSetActionNavigationMenu', |
401 | 'PersonSetContextMenu', |
402 | 'PersonSetNavigation', |
403 | @@ -47,12 +43,6 @@ |
404 | 'PPANavigationMenuMixIn', |
405 | 'RedirectToEditLanguagesView', |
406 | 'RestrictedMembershipsPersonView', |
407 | - 'SearchAnsweredQuestionsView', |
408 | - 'SearchAssignedQuestionsView', |
409 | - 'SearchCommentedQuestionsView', |
410 | - 'SearchCreatedQuestionsView', |
411 | - 'SearchNeedAttentionQuestionsView', |
412 | - 'SearchSubscribedQuestionsView', |
413 | 'archive_to_person', |
414 | ] |
415 | |
416 | @@ -109,9 +99,6 @@ |
417 | from zope.security.proxy import removeSecurityProxy |
418 | |
419 | from lp import _ |
420 | -from lp.answers.browser.questiontarget import SearchQuestionsView |
421 | -from lp.answers.enums import QuestionParticipation |
422 | -from lp.answers.interfaces.questionsperson import IQuestionsPerson |
423 | from lp.app.browser.launchpadform import ( |
424 | action, |
425 | custom_widget, |
426 | @@ -3320,250 +3307,6 @@ |
427 | self.next_url = self.action_url |
428 | |
429 | |
430 | -class PersonLatestQuestionsView(LaunchpadFormView): |
431 | - """View used by the porlet displaying the latest questions made by |
432 | - a person. |
433 | - """ |
434 | - |
435 | - @cachedproperty |
436 | - def getLatestQuestions(self, quantity=5): |
437 | - """Return <quantity> latest questions created for this target. """ |
438 | - return IQuestionsPerson(self.context).searchQuestions( |
439 | - participation=QuestionParticipation.OWNER)[:quantity] |
440 | - |
441 | - |
442 | -class PersonSearchQuestionsView(SearchQuestionsView): |
443 | - """View to search and display questions that involve an `IPerson`.""" |
444 | - |
445 | - display_target_column = True |
446 | - |
447 | - @property |
448 | - def template(self): |
449 | - # Persons always show the default template. |
450 | - return self.default_template |
451 | - |
452 | - @property |
453 | - def pageheading(self): |
454 | - """See `SearchQuestionsView`.""" |
455 | - return _('Questions involving $name', |
456 | - mapping=dict(name=self.context.displayname)) |
457 | - |
458 | - @property |
459 | - def empty_listing_message(self): |
460 | - """See `SearchQuestionsView`.""" |
461 | - return _('No questions involving $name found with the ' |
462 | - 'requested statuses.', |
463 | - mapping=dict(name=self.context.displayname)) |
464 | - |
465 | - |
466 | -class SearchAnsweredQuestionsView(PersonSearchQuestionsView): |
467 | - """View used to search and display questions answered by an IPerson.""" |
468 | - |
469 | - def getDefaultFilter(self): |
470 | - """See `SearchQuestionsView`.""" |
471 | - return dict(participation=QuestionParticipation.ANSWERER) |
472 | - |
473 | - @property |
474 | - def pageheading(self): |
475 | - """See `SearchQuestionsView`.""" |
476 | - return _('Questions answered by $name', |
477 | - mapping=dict(name=self.context.displayname)) |
478 | - |
479 | - @property |
480 | - def empty_listing_message(self): |
481 | - """See `SearchQuestionsView`.""" |
482 | - return _('No questions answered by $name found with the ' |
483 | - 'requested statuses.', |
484 | - mapping=dict(name=self.context.displayname)) |
485 | - |
486 | - |
487 | -class SearchAssignedQuestionsView(PersonSearchQuestionsView): |
488 | - """View used to search and display questions assigned to an IPerson.""" |
489 | - |
490 | - def getDefaultFilter(self): |
491 | - """See `SearchQuestionsView`.""" |
492 | - return dict(participation=QuestionParticipation.ASSIGNEE) |
493 | - |
494 | - @property |
495 | - def pageheading(self): |
496 | - """See `SearchQuestionsView`.""" |
497 | - return _('Questions assigned to $name', |
498 | - mapping=dict(name=self.context.displayname)) |
499 | - |
500 | - @property |
501 | - def empty_listing_message(self): |
502 | - """See `SearchQuestionsView`.""" |
503 | - return _('No questions assigned to $name found with the ' |
504 | - 'requested statuses.', |
505 | - mapping=dict(name=self.context.displayname)) |
506 | - |
507 | - |
508 | -class SearchCommentedQuestionsView(PersonSearchQuestionsView): |
509 | - """View used to search and show questions commented on by an IPerson.""" |
510 | - |
511 | - def getDefaultFilter(self): |
512 | - """See `SearchQuestionsView`.""" |
513 | - return dict(participation=QuestionParticipation.COMMENTER) |
514 | - |
515 | - @property |
516 | - def pageheading(self): |
517 | - """See `SearchQuestionsView`.""" |
518 | - return _('Questions commented on by $name ', |
519 | - mapping=dict(name=self.context.displayname)) |
520 | - |
521 | - @property |
522 | - def empty_listing_message(self): |
523 | - """See `SearchQuestionsView`.""" |
524 | - return _('No questions commented on by $name found with the ' |
525 | - 'requested statuses.', |
526 | - mapping=dict(name=self.context.displayname)) |
527 | - |
528 | - |
529 | -class SearchCreatedQuestionsView(PersonSearchQuestionsView): |
530 | - """View used to search and display questions created by an IPerson.""" |
531 | - |
532 | - def getDefaultFilter(self): |
533 | - """See `SearchQuestionsView`.""" |
534 | - return dict(participation=QuestionParticipation.OWNER) |
535 | - |
536 | - @property |
537 | - def pageheading(self): |
538 | - """See `SearchQuestionsView`.""" |
539 | - return _('Questions asked by $name', |
540 | - mapping=dict(name=self.context.displayname)) |
541 | - |
542 | - @property |
543 | - def empty_listing_message(self): |
544 | - """See `SearchQuestionsView`.""" |
545 | - return _('No questions asked by $name found with the ' |
546 | - 'requested statuses.', |
547 | - mapping=dict(name=self.context.displayname)) |
548 | - |
549 | - |
550 | -class SearchNeedAttentionQuestionsView(PersonSearchQuestionsView): |
551 | - """View used to search and show questions needing an IPerson attention.""" |
552 | - |
553 | - def getDefaultFilter(self): |
554 | - """See `SearchQuestionsView`.""" |
555 | - return dict(needs_attention=True) |
556 | - |
557 | - @property |
558 | - def pageheading(self): |
559 | - """See `SearchQuestionsView`.""" |
560 | - return _("Questions needing $name's attention", |
561 | - mapping=dict(name=self.context.displayname)) |
562 | - |
563 | - @property |
564 | - def empty_listing_message(self): |
565 | - """See `SearchQuestionsView`.""" |
566 | - return _("No questions need $name's attention.", |
567 | - mapping=dict(name=self.context.displayname)) |
568 | - |
569 | - |
570 | -class SearchSubscribedQuestionsView(PersonSearchQuestionsView): |
571 | - """View used to search and show questions subscribed to by an IPerson.""" |
572 | - |
573 | - def getDefaultFilter(self): |
574 | - """See `SearchQuestionsView`.""" |
575 | - return dict(participation=QuestionParticipation.SUBSCRIBER) |
576 | - |
577 | - @property |
578 | - def pageheading(self): |
579 | - """See `SearchQuestionsView`.""" |
580 | - return _('Questions $name is subscribed to', |
581 | - mapping=dict(name=self.context.displayname)) |
582 | - |
583 | - @property |
584 | - def empty_listing_message(self): |
585 | - """See `SearchQuestionsView`.""" |
586 | - return _('No questions subscribed to by $name found with the ' |
587 | - 'requested statuses.', |
588 | - mapping=dict(name=self.context.displayname)) |
589 | - |
590 | - |
591 | -class PersonAnswerContactForView(LaunchpadView): |
592 | - """View used to show all the IQuestionTargets that an IPerson is an answer |
593 | - contact for. |
594 | - """ |
595 | - |
596 | - @property |
597 | - def label(self): |
598 | - return 'Projects for which %s is an answer contact' % ( |
599 | - self.context.displayname) |
600 | - |
601 | - page_title = label |
602 | - |
603 | - @cachedproperty |
604 | - def direct_question_targets(self): |
605 | - """List of targets that the IPerson is a direct answer contact. |
606 | - |
607 | - Return a list of IQuestionTargets sorted alphabetically by title. |
608 | - """ |
609 | - return sorted( |
610 | - IQuestionsPerson(self.context).getDirectAnswerQuestionTargets(), |
611 | - key=attrgetter('title')) |
612 | - |
613 | - @cachedproperty |
614 | - def team_question_targets(self): |
615 | - """List of IQuestionTargets for the context's team membership. |
616 | - |
617 | - Sorted alphabetically by title. |
618 | - """ |
619 | - return sorted( |
620 | - IQuestionsPerson(self.context).getTeamAnswerQuestionTargets(), |
621 | - key=attrgetter('title')) |
622 | - |
623 | - def showRemoveYourselfLink(self): |
624 | - """The link is shown when the page is in the user's own profile.""" |
625 | - return self.user == self.context |
626 | - |
627 | - |
628 | -class PersonAnswersMenu(NavigationMenu): |
629 | - |
630 | - usedfor = IPerson |
631 | - facet = 'answers' |
632 | - links = ['answered', 'assigned', 'created', 'commented', 'need_attention', |
633 | - 'subscribed', 'answer_contact_for'] |
634 | - |
635 | - def answer_contact_for(self): |
636 | - summary = "Projects for which %s is an answer contact" % ( |
637 | - self.context.displayname) |
638 | - return Link( |
639 | - '+answer-contact-for', 'Answer contact for', summary, icon='edit') |
640 | - |
641 | - def answered(self): |
642 | - summary = 'Questions answered by %s' % self.context.displayname |
643 | - return Link( |
644 | - '+answeredquestions', 'Answered', summary, icon='question') |
645 | - |
646 | - def assigned(self): |
647 | - summary = 'Questions assigned to %s' % self.context.displayname |
648 | - return Link( |
649 | - '+assignedquestions', 'Assigned', summary, icon='question') |
650 | - |
651 | - def created(self): |
652 | - summary = 'Questions asked by %s' % self.context.displayname |
653 | - return Link('+createdquestions', 'Asked', summary, icon='question') |
654 | - |
655 | - def commented(self): |
656 | - summary = 'Questions commented on by %s' % ( |
657 | - self.context.displayname) |
658 | - return Link( |
659 | - '+commentedquestions', 'Commented', summary, icon='question') |
660 | - |
661 | - def need_attention(self): |
662 | - summary = 'Questions needing %s attention' % ( |
663 | - self.context.displayname) |
664 | - return Link('+needattentionquestions', 'Need attention', summary, |
665 | - icon='question') |
666 | - |
667 | - def subscribed(self): |
668 | - text = 'Subscribed' |
669 | - summary = 'Questions subscribed to by %s' % ( |
670 | - self.context.displayname) |
671 | - return Link('+subscribedquestions', text, summary, icon='question') |
672 | - |
673 | - |
674 | class BaseWithStats: |
675 | """An ISourcePackageRelease or a ISourcePackagePublishingHistory, |
676 | with extra stats added. |