~launchpad-pqm/launchpad/devel

12293.1.11 by Curtis Hovey
Updated copyright.
1
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
8687.15.11 by Karl Fogel
Add the copyright header block to files under lib/lp/answers/.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
2396 by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker
3
3691.398.18 by Francis J. Lacoste
Rename interfaces.
4
"""IQuestionTarget browser views."""
2396 by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker
5
6
__metaclass__ = type
7
8
__all__ = [
9565.2.1 by Gary Poster
fix vhost breadcrumbs so that we do not duplicate information
9
    'AnswersVHostBreadcrumb',
3791.1.2 by Francis J. Lacoste
Use a view to render the "Ask a question" link depending on context.
10
    'AskAQuestionButtonView',
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
11
    'ManageAnswerContactView',
12
    'SearchQuestionsView',
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
13
    'QuestionCollectionByLanguageView',
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
14
    'QuestionCollectionLatestQuestionsView',
15
    'QuestionCollectionMyQuestionsView',
16
    'QuestionCollectionNeedAttentionView',
3851.5.3 by Francis J. Lacoste
Add QuestionColletionOpenCountView.
17
    'QuestionCollectionOpenCountView',
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
18
    'QuestionCollectionAnswersMenu',
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
19
    'QuestionTargetFacetMixin',
20
    'QuestionTargetTraversalMixin',
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
21
    'QuestionTargetAnswersMenu',
3691.258.16 by Francis J. Lacoste
Add UserSupportLanguagesMixin.
22
    'UserSupportLanguagesMixin',
2396 by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker
23
    ]
24
3691.258.21 by Francis J. Lacoste
Only search tickets in the user preferred languages. Warn him when not all languages are displayed and add a All Languages checkbox.
25
from operator import attrgetter
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
26
from urllib import urlencode
27
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
28
from z3c.ptcompat import ViewPageTemplateFile
4004.4.17 by Curtis Hovey
Added NotRequiredRadioWidget to allow reconcile the behavior an presentation of the languages widget that is not required.
29
from zope.app.form.browser import DropdownWidget
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
30
from zope.component import (
11382.6.44 by Gavin Panella
Fix and reformat imports.
31
    getMultiAdapter,
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
32
    getUtility,
33
    queryMultiAdapter,
34
    )
4004.4.15 by Curtis Hovey
Replace All Languages checbox with radio buttons to toggle between all languages and preferred languages.
35
from zope.formlib import form
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
36
from zope.schema import (
37
    Bool,
38
    Choice,
39
    List,
40
    )
41
from zope.schema.vocabulary import (
42
    SimpleTerm,
43
    SimpleVocabulary,
44
    )
4294.2.1 by Curtis Hovey
Reformatted to standards.
45
3691.11.2 by Francis J. Lacoste
Compute the title of the tickettarget-tickets template in the view based
46
from canonical.launchpad import _
4417.3.4 by Curtis Hovey
Changes per review.
47
from canonical.launchpad.helpers import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
48
    browserLanguages,
49
    is_english_variant,
50
    preferred_or_request_languages,
51
    )
3691.110.23 by Francis J. Lacoste
Replaced +tickets view on ITicketTarget with a batched searchable listing.
52
from canonical.launchpad.webapp import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
53
    canonical_url,
54
    Link,
55
    stepthrough,
56
    stepto,
57
    urlappend,
58
    )
11227.3.26 by Curtis Hovey
Added can_configure_answers to QuestionSearchView.
59
from canonical.launchpad.webapp.authorization import check_permission
3691.110.23 by Francis J. Lacoste
Replaced +tickets view on ITicketTarget with a batched searchable listing.
60
from canonical.launchpad.webapp.batching import BatchNavigator
9209.4.5 by Guilherme Salgado
Update all existing breadcrumb adapters to use Breadcrumb rather than BreadcrumbBuilder. Also rename them all
61
from canonical.launchpad.webapp.breadcrumb import Breadcrumb
5594.1.21 by Maris Fogels
Fixed a call to addNotification() that contains internationalized markup.
62
from canonical.launchpad.webapp.menu import structured
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
63
from lp.answers.browser.faqcollection import FAQCollectionMenu
13130.1.12 by Curtis Hovey
Sorted imports.
64
from lp.answers.enums import QuestionStatus
10409.5.55 by Curtis Hovey
Removed glob imports from answers.
65
from lp.answers.interfaces.faqcollection import IFAQCollection
66
from lp.answers.interfaces.questioncollection import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
67
    IQuestionCollection,
68
    IQuestionSet,
69
    ISearchableByQuestionOwner,
70
    )
10409.5.55 by Curtis Hovey
Removed glob imports from answers.
71
from lp.answers.interfaces.questiontarget import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
72
    IQuestionTarget,
73
    ISearchQuestionsForm,
74
    )
11929.9.1 by Tim Penhey
Move launchpadform into lp.app.browser.
75
from lp.app.browser.launchpadform import (
76
    action,
77
    custom_widget,
78
    LaunchpadFormView,
79
    safe_action,
80
    )
11483.3.12 by j.c.sackett
Merged from devel.
81
from lp.app.enums import service_uses_launchpad
11382.6.44 by Gavin Panella
Fix and reformat imports.
82
from lp.app.errors import NotFoundError
13130.1.12 by Curtis Hovey
Sorted imports.
83
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
12293.1.10 by Curtis Hovey
Formatted imports.
84
from lp.app.widgets.itemswidgets import LabeledMultiCheckBoxWidget
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
85
from lp.registry.interfaces.distribution import IDistribution
11227.3.33 by Curtis Hovey
Merged devel.
86
from lp.registry.interfaces.product import IProduct
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
87
from lp.registry.interfaces.projectgroup import IProjectGroup
88
from lp.services.fields import PublicPersonChoice
11382.6.34 by Gavin Panella
Reformat imports in all files touched so far.
89
from lp.services.propertycache import cachedproperty
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
90
from lp.services.worlddata.interfaces.language import ILanguageSet
3024.1.59 by Christian Reis
Performance improvements to distro and product ticket pages; selectively cache in the view code, refactor the view code slightly, avoid extra work on __init__.py for non-form-posts, and add prejoins to optimize acquisition of data
91
3691.110.73 by Francis J. Lacoste
Move ISearchTicketsForm to interfaces/tickettarget_browser.py
92
3791.1.2 by Francis J. Lacoste
Use a view to render the "Ask a question" link depending on context.
93
class AskAQuestionButtonView:
4294.2.1 by Curtis Hovey
Reformatted to standards.
94
    """View that renders a button to ask a question on its context."""
3791.1.2 by Francis J. Lacoste
Use a view to render the "Ask a question" link depending on context.
95
96
    def __call__(self):
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
97
        # Check if the context has an +addquestion view available...
4294.2.1 by Curtis Hovey
Reformatted to standards.
98
        if queryMultiAdapter(
99
            (self.context, self.request), name='+addquestion'):
3851.3.8 by Francis J. Lacoste
Made AskAQuestionButtonView available on IProject.
100
            target = self.context
101
        else:
102
            # otherwise find an adapter to IQuestionTarget which will.
103
            target = IQuestionTarget(self.context)
104
3791.1.2 by Francis J. Lacoste
Use a view to render the "Ask a question" link depending on context.
105
        return """
9861.4.1 by Curtis Hovey
Replace the answers 1.0 ask a question button with an involvement menu.
106
            <div id="involvement" class="portlet involvement">
107
              <ul>
108
                <li style="border: none">
109
                  <a class="menu-link-ask_question sprite answers"
110
                    href="%s">Ask a question</a>
111
                </li>
112
              </ul>
113
            </div>
114
        """ % canonical_url(
115
            target, view_name='+addquestion', rootsite='answers')
3791.1.2 by Francis J. Lacoste
Use a view to render the "Ask a question" link depending on context.
116
117
3691.258.16 by Francis J. Lacoste
Add UserSupportLanguagesMixin.
118
class UserSupportLanguagesMixin:
119
    """Mixin for views that needs to get the set of user support languages."""
120
3691.258.21 by Francis J. Lacoste
Only search tickets in the user preferred languages. Warn him when not all languages are displayed and add a All Languages checkbox.
121
    @cachedproperty
3691.258.16 by Francis J. Lacoste
Add UserSupportLanguagesMixin.
122
    def user_support_languages(self):
123
        """The set of user support languages.
124
4450.6.5 by Curtis Hovey
Save review changes before I run amok with getAnswerContactsForLanguage.
125
        This set includes the user's preferred languages, or request
4450.6.9 by Curtis Hovey
Commiting without tests proven. The mechanize cannot locate ClientCookie. I want this work saved before I destroy this branch.
126
        languages, or GeoIP languages, according to the implementation of
127
        preferred_or_request_languages(), which specifies:
128
129
        - When the user does not have preferred languages, the languages
130
          will be inferred from the request Accept-Language header.
131
132
        - As a last resort, the code falls back on GeoIP rules to determine
133
          the user's languages.
134
135
        English is added to the list instead when an English variant is
136
        returned.
3691.258.16 by Francis J. Lacoste
Add UserSupportLanguagesMixin.
137
        """
7117.5.2 by Jeroen Vermeulen
Made English a celebrity.
138
        english = getUtility(ILaunchpadCelebrities).english
4450.6.2 by Curtis Hovey
Revised sampledata and tests for translator with an English variant as answer contact scenario.
139
        languages = set()
4450.6.5 by Curtis Hovey
Save review changes before I run amok with getAnswerContactsForLanguage.
140
        for language in preferred_or_request_languages(self.request):
4450.6.2 by Curtis Hovey
Revised sampledata and tests for translator with an English variant as answer contact scenario.
141
            if is_english_variant(language):
142
                languages.add(english)
143
            else:
144
                languages.add(language)
4261.1.10 by Curtis Hovey
Replaced the Language radio button set with individual language
145
        languages = list(languages)
3691.258.16 by Francis J. Lacoste
Add UserSupportLanguagesMixin.
146
        return languages
147
148
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
149
class QuestionCollectionLatestQuestionsView:
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
150
    """View used to display the latest questions on a question target."""
3691.188.12 by Francis J. Lacoste
Factor getLatestTickets method into its own view class
151
9517.4.7 by Paul Hummer
Fixed tests
152
    @property
153
    def page_title(self):
154
        return 'Latest questions for %s' % (self.context.displayname)
155
1716.5.48 by kiko
Fix for bug #37347: Display team description even if no homepage is provided. Fix the template to DTRT. Also only display the latest tickets portlet if, well, the guy reported tickets
156
    @cachedproperty
3691.398.20 by Francis J. Lacoste
Rename all methods.
157
    def getLatestQuestions(self, quantity=5):
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
158
        """Return <quantity> latest questions created for this target. This
159
        is used by the +portlet-latestquestions view.
2396 by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker
160
        """
7675.113.2 by Guilherme Salgado
Make sure the context in browser/questiontarget.py is adapted into an IQuestionCollection before calling methods/properties of that interface.
161
        question_collection = IQuestionCollection(self.context)
162
        return question_collection.searchQuestions()[:quantity]
3691.258.21 by Francis J. Lacoste
Only search tickets in the user preferred languages. Warn him when not all languages are displayed and add a All Languages checkbox.
163
164
3851.5.3 by Francis J. Lacoste
Add QuestionColletionOpenCountView.
165
class QuestionCollectionOpenCountView:
3851.5.9 by Francis J. Lacoste
Add comment explaining rationale behing QuestionCollectionOpenCountView.
166
    """View used to render the number of open questions.
3859.4.4 by Francis J. Lacoste
Merge RF, resolving 18 conflicts.
167
168
    This view is used to render the number of open questions on
13014.2.11 by Curtis Hovey
Removed SP vestigial code.
169
    each IDistributionSourcePackage on the person-packages-templates.pt.
3851.5.9 by Francis J. Lacoste
Add comment explaining rationale behing QuestionCollectionOpenCountView.
170
    It is simpler to define generic view and an adapter (since
171
    SourcePackageRelease does not provide IQuestionCollection), than
172
    to write a specific view for that template.
173
    """
3851.5.3 by Francis J. Lacoste
Add QuestionColletionOpenCountView.
174
175
    def __call__(self):
3851.5.7 by Francis J. Lacoste
Adapt to IQuestionCollection instead of IQuestionTarget.
176
        questiontarget = IQuestionCollection(self.context)
3851.5.3 by Francis J. Lacoste
Add QuestionColletionOpenCountView.
177
        open_questions = questiontarget.searchQuestions(
178
            status=[QuestionStatus.OPEN, QuestionStatus.NEEDSINFO])
179
        return unicode(open_questions.count())
180
4004.4.17 by Curtis Hovey
Added NotRequiredRadioWidget to allow reconcile the behavior an presentation of the languages widget that is not required.
181
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
182
class SearchQuestionsView(UserSupportLanguagesMixin, LaunchpadFormView):
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
183
    """View that can filter the target's question in a batched listing.
3691.110.23 by Francis J. Lacoste
Replaced +tickets view on ITicketTarget with a batched searchable listing.
184
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
185
    This view provides a search form to filter the displayed questions.
3691.110.23 by Francis J. Lacoste
Replaced +tickets view on ITicketTarget with a batched searchable listing.
186
    """
4004.4.19 by Curtis Hovey
Removed unused imports.
187
3691.398.18 by Francis J. Lacoste
Rename interfaces.
188
    schema = ISearchQuestionsForm
3691.110.63 by Francis J. Lacoste
Convert SearchTicketsView to use LaunchpadFormView
189
4261.1.16 by Curtis Hovey
Revised ui language and minor code concerns for review.
190
    custom_widget('language', LabeledMultiCheckBoxWidget,
4261.1.10 by Curtis Hovey
Replaced the Language radio button set with individual language
191
                  orientation='horizontal')
4004.4.15 by Curtis Hovey
Replace All Languages checbox with radio buttons to toggle between all languages and preferred languages.
192
    custom_widget('sort', DropdownWidget, cssClass='inlined-widget')
3691.110.63 by Francis J. Lacoste
Convert SearchTicketsView to use LaunchpadFormView
193
    custom_widget('status', LabeledMultiCheckBoxWidget,
194
                  orientation='horizontal')
4004.4.19 by Curtis Hovey
Removed unused imports.
195
11227.3.38 by Curtis Hovey
Rename selected_template to template.
196
    default_template = ViewPageTemplateFile(
197
        '../templates/question-listing.pt')
11227.3.9 by Curtis Hovey
redefine the views __call__ and use selected_template property so that
198
    unknown_template = ViewPageTemplateFile('../templates/unknown-support.pt')
199
11227.3.4 by Curtis Hovey
Updated QuestionSearchView to use a different template when involvement.offical_answers
200
    @property
11227.3.38 by Curtis Hovey
Rename selected_template to template.
201
    def template(self):
11227.3.9 by Curtis Hovey
redefine the views __call__ and use selected_template property so that
202
        """The template to render the presentation.
203
204
        Subclasses can redefine this property to choose their own template.
205
        """
11227.3.29 by Curtis Hovey
QuestionSets always use the question-listing template.
206
        if IQuestionSet.providedBy(self.context):
11227.3.38 by Curtis Hovey
Rename selected_template to template.
207
            return self.default_template
11227.3.4 by Curtis Hovey
Updated QuestionSearchView to use a different template when involvement.offical_answers
208
        involvement = getMultiAdapter(
209
            (self.context, self.request), name='+get-involved')
11483.3.2 by j.c.sackett
Update callsites for official_answers
210
        if service_uses_launchpad(involvement.answers_usage):
11227.3.8 by Curtis Hovey
No need to mention and test person with +questions because it has its views have
211
            # Primary contexts that officially use answers have a
212
            # search and listing presentation.
11227.3.38 by Curtis Hovey
Rename selected_template to template.
213
            return self.default_template
11227.3.4 by Curtis Hovey
Updated QuestionSearchView to use a different template when involvement.offical_answers
214
        else:
215
            # Primary context that do not officially use answers have an
216
            # an explanation about about the current state.
11227.3.9 by Curtis Hovey
redefine the views __call__ and use selected_template property so that
217
            return self.unknown_template
218
11227.3.18 by Curtis Hovey
Redefine render() not __call__ to ensure redirect work. Added
219
    def render(self):
11227.3.9 by Curtis Hovey
redefine the views __call__ and use selected_template property so that
220
        """See `LaunchpadView`."""
11227.3.38 by Curtis Hovey
Rename selected_template to template.
221
        return self.template()
4004.4.19 by Curtis Hovey
Removed unused imports.
222
3691.402.4 by Francis J. Lacoste
Enable +tickets view on IProject.
223
    @property
9517.4.7 by Paul Hummer
Fixed tests
224
    def page_title(self):
225
        """Heading to display above the search results."""
226
227
        if IQuestionSet.providedBy(self.context):
228
            return _(
229
                'Questions matching "${search_text}"',
230
                mapping=dict(search_text=self.search_text))
231
232
        replacements = dict(
233
            context=self.context.displayname,
234
            search_text=self.search_text)
235
        # Check if the set of selected status has a special title.
236
        status_set_title = self.status_title_map.get(
237
            frozenset(self.status_filter))
238
        if status_set_title:
239
            replacements['status'] = status_set_title
240
            if self.search_text:
241
                return _('${status} questions matching "${search_text}" '
242
                         'for ${context}', mapping=replacements)
243
            else:
244
                return _('${status} questions for ${context}',
245
                         mapping=replacements)
246
        else:
247
            if self.search_text:
248
                return _('Questions matching "${search_text}" for '
249
                         '${context}', mapping=replacements)
250
            else:
251
                return _('Questions for ${context}',
252
                         mapping=replacements)
253
254
    label = page_title
255
256
    @property
3691.402.11 by Francis J. Lacoste
Rename displayTargetColumn and displaySourcePackageColumn to display_target_column and display_sourcepackage_column.
257
    def display_target_column(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
258
        """Return True when the context has question targets to display."""
10326.1.1 by Henning Eggers
Mechanically renamed IProject* to IProjectGroup*.
259
        return IProjectGroup.providedBy(self.context)
3691.188.16 by Francis J. Lacoste
Make person support requests searchable. Add a report listing only the support requests on which the person commented. Add an optional In column showing the target of the request in the search results.
260
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
261
    # Will contain the parameters used by searchResults
3691.110.23 by Francis J. Lacoste
Replaced +tickets view on ITicketTarget with a batched searchable listing.
262
    search_params = None
4294.2.1 by Curtis Hovey
Reformatted to standards.
263
4004.4.15 by Curtis Hovey
Replace All Languages checbox with radio buttons to toggle between all languages and preferred languages.
264
    def setUpFields(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
265
        """See `LaunchpadFormView`."""
4004.4.15 by Curtis Hovey
Replace All Languages checbox with radio buttons to toggle between all languages and preferred languages.
266
        LaunchpadFormView.setUpFields(self)
4261.1.16 by Curtis Hovey
Revised ui language and minor code concerns for review.
267
        if self.show_language_control:
268
            self.form_fields = self.createLanguageField() + self.form_fields
3691.110.23 by Francis J. Lacoste
Replaced +tickets view on ITicketTarget with a batched searchable listing.
269
3691.258.3 by Francis J. Lacoste
Merge tt-add-localized-requests.
270
    def setUpWidgets(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
271
        """See `LaunchpadFormView`."""
3691.258.3 by Francis J. Lacoste
Merge tt-add-localized-requests.
272
        LaunchpadFormView.setUpWidgets(self)
273
        # Make sure that the default filter is displayed
274
        # correctly in the widgets when not overriden by the user
275
        for name, value in self.getDefaultFilter().items():
276
            widget = self.widgets.get(name)
277
            if widget and not widget.hasValidInput():
278
                widget.setRenderedValue(value)
279
4261.1.16 by Curtis Hovey
Revised ui language and minor code concerns for review.
280
    def createLanguageField(self):
4004.4.19 by Curtis Hovey
Removed unused imports.
281
        """Create a field to choose a set of languages.
4004.4.15 by Curtis Hovey
Replace All Languages checbox with radio buttons to toggle between all languages and preferred languages.
282
4261.1.9 by Curtis Hovey
Merge from RF.
283
        Create a specialized vocabulary based on the user's preferred
284
        languages. If the user is anonymous, the languages submited in the
285
        browser's request will be used.
4261.1.10 by Curtis Hovey
Replaced the Language radio button set with individual language
286
        """
4261.1.14 by Curtis Hovey
Revised language control and rules for display.
287
        languages = set(self.user_support_languages)
288
        languages.intersection_update(self.context_question_languages)
4261.1.10 by Curtis Hovey
Replaced the Language radio button set with individual language
289
        terms = []
6061.15.4 by Curtis Hovey
Fixed the multilabelcheckbox to remove the double label. Fixed the sort order
290
        for lang in sorted(languages, key=attrgetter('code')):
4261.1.10 by Curtis Hovey
Replaced the Language radio button set with individual language
291
            terms.append(SimpleTerm(lang, lang.code, lang.displayname))
4004.4.15 by Curtis Hovey
Replace All Languages checbox with radio buttons to toggle between all languages and preferred languages.
292
        return form.Fields(
4261.1.16 by Curtis Hovey
Revised ui language and minor code concerns for review.
293
            List(__name__='language',
294
                 title=_('Languages filter'),
295
                 value_type=Choice(vocabulary=SimpleVocabulary(terms)),
296
                 required=False,
297
                 default=self.user_support_languages,
298
                 description=_(
299
                     'The languages to filter the search results by.')),
300
            render_context=self.render_context)
4261.1.1 by Curtis Hovey
Added basic functionality for preferred languages only searching.
301
4261.1.14 by Curtis Hovey
Revised language control and rules for display.
302
    def validate(self, data):
303
        """Validate hook.
304
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
305
        This validation method checks that a valid status is submitted.
4261.1.14 by Curtis Hovey
Revised language control and rules for display.
306
        """
4311.2.9 by Francis J. Lacoste
No status will be reported as [].
307
        if not data.get('status', []):
4261.1.14 by Curtis Hovey
Revised language control and rules for display.
308
            self.setFieldError(
309
                'status', _('You must choose at least one status.'))
310
4261.1.2 by Curtis Hovey
cleaned up the language list logic.
311
    @cachedproperty
3691.197.106 by Francis J. Lacoste
Merge RV, resolving 4 conflicts andupdating support request search views for status changes.
312
    def status_title_map(self):
313
        """Return a dictionary mapping set of statuses to their title.
314
315
        This is used to compute dynamically the page heading and empty
316
        listing messages.
317
        """
318
        mapping = {}
319
        # All set of only one statuses maps to the status title.
3691.398.17 by Francis J. Lacoste
Rename dbschemas.
320
        for status in QuestionStatus.items:
3691.197.106 by Francis J. Lacoste
Merge RV, resolving 4 conflicts andupdating support request search views for status changes.
321
            mapping[frozenset([status])] = status.title
322
4004.4.8 by Curtis Hovey
General Unsupported Questions features are complete. Interpol rocks\!
323
        mapping[frozenset(
324
            [QuestionStatus.ANSWERED, QuestionStatus.SOLVED])] = _('Answered')
3691.197.106 by Francis J. Lacoste
Merge RV, resolving 4 conflicts andupdating support request search views for status changes.
325
326
        return mapping
327
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
328
    @property
4508.3.24 by Graham Binns
Fixed a bunch of errors in answer-tracker pagetests that I'd missed because I'm a big hairy dope. Oh, and an OOPsmaker, too, whilst I was at it.
329
    def context_is_project(self):
330
        """Return True when the context is a project."""
10326.1.1 by Henning Eggers
Mechanically renamed IProject* to IProjectGroup*.
331
        return IProjectGroup.providedBy(self.context)
4508.3.24 by Graham Binns
Fixed a bunch of errors in answer-tracker pagetests that I'd missed because I'm a big hairy dope. Oh, and an OOPsmaker, too, whilst I was at it.
332
333
    @property
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
334
    def unspoken_languages(self):
335
        """Return a formatted string of unspoken question languages.
4476.4.5 by Curtis Hovey
Revisions per review.
336
337
        The string summarizes the questions that are in languages that
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
338
        no answer contact speaks. The string takes the form of an
339
        inline list with links to see the questions for each language:
4476.4.10 by Curtis Hovey
Changes per review. Added a redirect for +unsupported. Added a rule to return an empty string for non QuestionTargets.
340
        '_Hungarian_ (2), _Romanian_ (1)'. An empty string is returned
4476.4.5 by Curtis Hovey
Revisions per review.
341
        when all questions are in a language spoken by the answer contacts.
4785.3.6 by Jeroen Vermeulen
Removed whitespace from ends of lines.
342
4476.4.10 by Curtis Hovey
Changes per review. Added a redirect for +unsupported. Added a rule to return an empty string for non QuestionTargets.
343
        This method is relevant to QuestionTargets. Subclasses of this
344
        View that want to list unspoken languages for other objects must
345
        provide their own implementation.
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
346
        """
4476.4.10 by Curtis Hovey
Changes per review. Added a redirect for +unsupported. Added a rule to return an empty string for non QuestionTargets.
347
        if not IQuestionTarget.providedBy(self.context):
348
            return ''
4476.4.5 by Curtis Hovey
Revisions per review.
349
        language_counts = {}
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
350
        questions = self.context.searchQuestions(
351
            unsupported=self.context, status=[QuestionStatus.OPEN])
352
        for question in questions:
353
            lang = question.language
4476.4.10 by Curtis Hovey
Changes per review. Added a redirect for +unsupported. Added a rule to return an empty string for non QuestionTargets.
354
            language_counts[lang] = language_counts.get(lang, 0) + 1
4476.4.5 by Curtis Hovey
Revisions per review.
355
        if len(language_counts) == 0:
356
            return ''
357
        url = canonical_url(self.context, rootsite='answers')
4476.4.10 by Curtis Hovey
Changes per review. Added a redirect for +unsupported. Added a rule to return an empty string for non QuestionTargets.
358
        format = (u'%s in <a href="' + url + u'/+by-language'
359
                  u'?field.language=%s&field.status=Open">%s</a>')
360
        links = [format % (language_counts[key], key.code, key.englishname)
4476.4.5 by Curtis Hovey
Revisions per review.
361
                 for key in language_counts]
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
362
        return u', '.join(links)
363
364
    @property
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
365
    def empty_listing_message(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
366
        """Message shown when there is no questions matching the filter."""
3691.188.28 by Francis J. Lacoste
PEP-8 and some other code clarifications
367
        replacements = dict(
368
            context=self.context.displayname,
369
            search_text=self.search_text)
3691.197.106 by Francis J. Lacoste
Merge RV, resolving 4 conflicts andupdating support request search views for status changes.
370
        # Check if the set of selected status has a special title.
371
        status_set_title = self.status_title_map.get(
372
            frozenset(self.status_filter))
373
        if status_set_title:
374
            replacements['status'] = status_set_title.lower()
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
375
            if self.search_text:
3691.398.8 by Francis J. Lacoste
Merge RF, resolving 6 conflicts.
376
                return _('There are no ${status} questions matching '
3691.188.28 by Francis J. Lacoste
PEP-8 and some other code clarifications
377
                         '"${search_text}" for ${context}.',
378
                         mapping=replacements)
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
379
            else:
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
380
                return _('There are no ${status} questions for '
3691.188.28 by Francis J. Lacoste
PEP-8 and some other code clarifications
381
                         '${context}.', mapping=replacements)
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
382
        else:
383
            if self.search_text:
3691.398.8 by Francis J. Lacoste
Merge RF, resolving 6 conflicts.
384
                return _('There are no questions matching "${search_text}" '
385
                         'for ${context} with the requested statuses.',
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
386
                          mapping=replacements)
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
387
            else:
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
388
                return _('There are no questions for ${context} with '
3691.188.28 by Francis J. Lacoste
PEP-8 and some other code clarifications
389
                         'the requested statuses.', mapping=replacements)
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
390
391
    def getDefaultFilter(self):
3691.188.28 by Francis J. Lacoste
PEP-8 and some other code clarifications
392
        """Hook for subclass to provide a default search filter."""
4261.1.16 by Curtis Hovey
Revised ui language and minor code concerns for review.
393
        return dict(language=self.user_support_languages)
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
394
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
395
    @property
396
    def search_text(self):
3691.188.22 by Francis J. Lacoste
Style
397
        """Search text used by the filter."""
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
398
        if self.search_params:
3691.69.8 by Francis J. Lacoste
Fix bug 72866.
399
            return self.search_params.get('search_text')
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
400
        else:
401
            return self.getDefaultFilter().get('search_text')
402
403
    @property
404
    def status_filter(self):
3691.188.22 by Francis J. Lacoste
Style
405
        """Set of statuses to filter the search with."""
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
406
        if self.search_params:
3691.69.9 by Francis J. Lacoste
Style and add a comment requested by salgado.
407
            return set(self.search_params.get('status', []))
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
408
        else:
3691.69.9 by Francis J. Lacoste
Style and add a comment requested by salgado.
409
            return set(self.getDefaultFilter().get('status', []))
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
410
3691.258.29 by Francis J. Lacoste
Transform ticket_languages property into getTicketLanguages() method.
411
    @cachedproperty
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
412
    def context_question_languages(self):
413
        """Return the set of ILanguages used by this context's questions."""
7675.113.2 by Guilherme Salgado
Make sure the context in browser/questiontarget.py is adapted into an IQuestionCollection before calling methods/properties of that interface.
414
        question_collection = IQuestionCollection(self.context)
415
        return question_collection.getQuestionLanguages()
3691.258.29 by Francis J. Lacoste
Transform ticket_languages property into getTicketLanguages() method.
416
3691.258.21 by Francis J. Lacoste
Only search tickets in the user preferred languages. Warn him when not all languages are displayed and add a All Languages checkbox.
417
    @property
4261.1.16 by Curtis Hovey
Revised ui language and minor code concerns for review.
418
    def show_language_control(self):
4476.4.5 by Curtis Hovey
Revisions per review.
419
        """Whether to render Language control, or not at all.
4261.1.14 by Curtis Hovey
Revised language control and rules for display.
420
421
        When the QuestionTarget has questions in only one language,
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
422
        and that language is among the user's languages, we do not render
4261.1.14 by Curtis Hovey
Revised language control and rules for display.
423
        the language control because there are no choices to be made.
4261.1.10 by Curtis Hovey
Replaced the Language radio button set with individual language
424
        """
4261.1.14 by Curtis Hovey
Revised language control and rules for display.
425
        languages = list(self.context_question_languages)
426
        if len(languages) == 0:
427
            return False
428
        elif (len(languages) == 1
429
              and languages[0] in self.user_support_languages):
430
            return False
431
        else:
432
            return True
3691.258.21 by Francis J. Lacoste
Only search tickets in the user preferred languages. Warn him when not all languages are displayed and add a All Languages checkbox.
433
4319.7.5 by Francis J. Lacoste
Display link to matching FAQs when searching for questions.
434
    @cachedproperty
435
    def matching_faqs_count(self):
436
        """Return the FAQs matching the same keywords."""
437
        if not self.search_text:
438
            return 0
439
        try:
440
            faq_collection = IFAQCollection(self.context)
441
        except TypeError:
442
            # The context is not adaptable to IFAQCollection.
443
            return 0
444
        return faq_collection.searchFAQs(search_text=self.search_text).count()
445
446
    @property
447
    def matching_faqs_url(self):
448
        """Return the URL to use to display the list of matching FAQs."""
449
        assert self.matching_faqs_count > 0, (
450
            "can't call matching_faqs_url when matching_faqs_count == 0")
451
        collection = IFAQCollection(self.context)
452
        return canonical_url(collection) + '/+faqs?' + urlencode({
12952.4.4 by Henning Eggers
Fix the bug.
453
            'field.search_text': self.search_text.encode('utf-8'),
4319.7.5 by Francis J. Lacoste
Display link to matching FAQs when searching for questions.
454
            'field.actions.search': 'Search',
455
            })
456
4177.4.7 by James Henstridge
mark some form actions as safe, and fix tests that incorrectly used GET requests
457
    @safe_action
3691.110.63 by Francis J. Lacoste
Convert SearchTicketsView to use LaunchpadFormView
458
    @action(_('Search'))
3691.110.70 by Francis J. Lacoste
* Renamed action methods to use camelCase.
459
    def search_action(self, action, data):
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
460
        """Action executed when the user clicked the search button.
461
462
        Saves the user submitted search parameters in an instance
463
        attribute.
464
        """
3691.188.28 by Francis J. Lacoste
PEP-8 and some other code clarifications
465
        self.search_params = dict(self.getDefaultFilter())
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
466
        self.search_params.update(**data)
12952.4.3 by Henning Eggers
Added test for non ascii FAQ.
467
        search_text = self.search_params.get('search_text', None)
468
        if search_text is not None:
12952.4.8 by Henning Eggers
Small optimization.
469
            self.search_params['search_text'] = search_text.strip()
3691.110.23 by Francis J. Lacoste
Replaced +tickets view on ITicketTarget with a batched searchable listing.
470
471
    def searchResults(self):
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
472
        """Return the questions corresponding to the search."""
3691.110.23 by Francis J. Lacoste
Replaced +tickets view on ITicketTarget with a batched searchable listing.
473
        if self.search_params is None:
3691.69.9 by Francis J. Lacoste
Style and add a comment requested by salgado.
474
            # Search button wasn't clicked, use the default filter.
475
            # Copy it so that it doesn't get mutated accidently.
3691.69.8 by Francis J. Lacoste
Fix bug 72866.
476
            self.search_params = dict(self.getDefaultFilter())
4294.2.1 by Curtis Hovey
Reformatted to standards.
477
3691.258.21 by Francis J. Lacoste
Only search tickets in the user preferred languages. Warn him when not all languages are displayed and add a All Languages checkbox.
478
        # The search parameters used is defined by the union of the fields
3691.398.18 by Francis J. Lacoste
Rename interfaces.
479
        # present in ISearchQuestionsForm (search_text, status, sort) and the
3691.188.34 by Francis J. Lacoste
Add a comment about the use of search_params.
480
        # ones defined in getDefaultFilter() which varies based on the
481
        # concrete view class.
7675.113.2 by Guilherme Salgado
Make sure the context in browser/questiontarget.py is adapted into an IQuestionCollection before calling methods/properties of that interface.
482
        question_collection = IQuestionCollection(self.context)
483
        return BatchNavigator(question_collection.searchQuestions(
484
            **self.search_params), self.request)
3691.110.23 by Francis J. Lacoste
Replaced +tickets view on ITicketTarget with a batched searchable listing.
485
3691.402.11 by Francis J. Lacoste
Rename displayTargetColumn and displaySourcePackageColumn to display_target_column and display_sourcepackage_column.
486
    @property
487
    def display_sourcepackage_column(self):
3691.110.31 by Francis J. Lacoste
Added a source package column with link to its support page. Put the appropriate class on ticket status.
488
        """We display the source package column only on distribution."""
489
        return IDistribution.providedBy(self.context)
490
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
491
    def formatSourcePackageName(self, question):
492
        """Format the source package name related to question.
3691.110.31 by Francis J. Lacoste
Added a source package column with link to its support page. Put the appropriate class on ticket status.
493
494
        Return an URL to the support page of the source package related
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
495
        to question or mdash if there is no related source package.
3691.110.31 by Francis J. Lacoste
Added a source package column with link to its support page. Put the appropriate class on ticket status.
496
        """
5220.5.1 by Curtis Hovey
Switch to compare context and question by id because SQLObject can
497
        # XXX sinzui 2007-11-27 bug=164435:
498
        # SQLObject can refetch the question, so we are comparing ids.
499
        assert self.context.id == question.distribution.id, (
5099.5.1 by Curtis Hovey
Added a message to formatSourcePackageName context assertion for better debuging.
500
            "The question.distribution (%s) must be equal to the context (%s)"
501
            % (question.distribution, self.context))
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
502
        if not question.sourcepackagename:
3691.110.31 by Francis J. Lacoste
Added a source package column with link to its support page. Put the appropriate class on ticket status.
503
            return "&mdash;"
504
        else:
505
            sourcepackage = self.context.getSourcePackage(
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
506
                question.sourcepackagename)
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
507
            return '<a href="%s">%s</a>' % (
508
                canonical_url(sourcepackage, rootsite='answers'),
509
                question.sourcepackagename.name)
4004.4.19 by Curtis Hovey
Removed unused imports.
510
11227.3.14 by Curtis Hovey
Added ubuntu_packages property to SearchQuestionsView.
511
    @property
11227.3.26 by Curtis Hovey
Added can_configure_answers to QuestionSearchView.
512
    def can_configure_answers(self):
513
        """Can the user configure answers for the `IQuestionTarget`."""
514
        target = self.context
515
        if IProduct.providedBy(target) or IDistribution.providedBy(target):
516
            return check_permission('launchpad.Edit', self.context)
517
        else:
518
            return False
3691.110.23 by Francis J. Lacoste
Replaced +tickets view on ITicketTarget with a batched searchable listing.
519
520
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
521
class QuestionCollectionMyQuestionsView(SearchQuestionsView):
3847.2.16 by Mark Shuttleworth
Fix tests for answer tracker menu capitalisation
522
    """SearchQuestionsView specialization for the 'My questions' report.
3691.188.28 by Francis J. Lacoste
PEP-8 and some other code clarifications
523
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
524
    It displays and searches the questions made by the logged
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
525
    in user in a questiontarget context.
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
526
    """
527
4319.7.7 by Francis J. Lacoste
Typos.
528
    # No point showing a matching FAQs link on this report.
4319.7.6 by Francis J. Lacoste
Rename SearchFAQsBaseView to SearchFAQsView.
529
    matching_faqs_count = 0
4319.7.9 by Francis J. Lacoste
Use ApplicationMenu instead of ContextMenu. Style.
530
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
531
    @property
9517.4.1 by Paul Hummer
Question and FAQ listings ported to 3.0
532
    def page_title(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
533
        """See `SearchQuestionsView`."""
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
534
        if self.search_text:
3691.398.8 by Francis J. Lacoste
Merge RF, resolving 6 conflicts.
535
            return _('Questions you asked matching "${search_text}" for '
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
536
                     '${context}', mapping=dict(
537
                        context=self.context.displayname,
538
                        search_text=self.search_text))
539
        else:
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
540
            return _('Questions you asked about ${context}',
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
541
                     mapping={'context': self.context.displayname})
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
542
9517.4.1 by Paul Hummer
Question and FAQ listings ported to 3.0
543
    label = page_title
544
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
545
    @property
546
    def empty_listing_message(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
547
        """See `SearchQuestionsView`."""
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
548
        if self.search_text:
3691.398.8 by Francis J. Lacoste
Merge RF, resolving 6 conflicts.
549
            return _("You didn't ask any questions matching "
3691.188.28 by Francis J. Lacoste
PEP-8 and some other code clarifications
550
                     '"${search_text}" for ${context}.', mapping=dict(
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
551
                        context=self.context.displayname,
552
                        search_text=self.search_text))
553
        else:
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
554
            return _("You didn't ask any questions about ${context}.",
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
555
                     mapping={'context': self.context.displayname})
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
556
557
    def getDefaultFilter(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
558
        """See `SearchQuestionsView`."""
6499.1.1 by Curtis Hovey
Fixed QuestionCollectionMyQuestionsView to show all a user's questions by default.
559
        return dict(owner=self.user, status=set(QuestionStatus.items))
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
560
561
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
562
class QuestionCollectionNeedAttentionView(SearchQuestionsView):
3847.2.16 by Mark Shuttleworth
Fix tests for answer tracker menu capitalisation
563
    """SearchQuestionsView specialization for the 'Need attention' report.
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
564
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
565
    It displays and searches the questions needing attention from the
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
566
    logged in user in a questiontarget context.
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
567
    """
4319.7.9 by Francis J. Lacoste
Use ApplicationMenu instead of ContextMenu. Style.
568
4319.7.7 by Francis J. Lacoste
Typos.
569
    # No point showing a matching FAQs link on this report.
4319.7.6 by Francis J. Lacoste
Rename SearchFAQsBaseView to SearchFAQsView.
570
    matching_faqs_count = 0
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
571
572
    @property
9517.4.1 by Paul Hummer
Question and FAQ listings ported to 3.0
573
    def page_title(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
574
        """See `SearchQuestionsView`."""
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
575
        if self.search_text:
3691.398.8 by Francis J. Lacoste
Merge RF, resolving 6 conflicts.
576
            return _('Questions matching "${search_text}" needing your '
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
577
                     'attention for ${context}', mapping=dict(
578
                        context=self.context.displayname,
579
                        search_text=self.search_text))
580
        else:
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
581
            return _('Questions needing your attention for ${context}',
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
582
                     mapping={'context': self.context.displayname})
583
9517.4.1 by Paul Hummer
Question and FAQ listings ported to 3.0
584
    label = page_title
585
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
586
    @property
587
    def empty_listing_message(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
588
        """See `SearchQuestionsView`."""
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
589
        if self.search_text:
3691.398.8 by Francis J. Lacoste
Merge RF, resolving 6 conflicts.
590
            return _('No questions matching "${search_text}" need your '
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
591
                     'attention for ${context}.', mapping=dict(
592
                        context=self.context.displayname,
593
                        search_text=self.search_text))
594
        else:
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
595
            return _("No questions need your attention for ${context}.",
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
596
                     mapping={'context': self.context.displayname})
597
598
    def getDefaultFilter(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
599
        """See `SearchQuestionsView`."""
4261.1.10 by Curtis Hovey
Replaced the Language radio button set with individual language
600
        return dict(needs_attention_from=self.user,
4261.1.16 by Curtis Hovey
Revised ui language and minor code concerns for review.
601
                    language=self.user_support_languages)
3691.235.11 by Francis J. Lacoste
Add Needs Attention report for ITicketTarget.
602
603
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
604
class QuestionCollectionByLanguageView(SearchQuestionsView):
605
    """Search for questions in a specific language.
4294.2.1 by Curtis Hovey
Reformatted to standards.
606
4476.4.5 by Curtis Hovey
Revisions per review.
607
     This view displays questions that are asked in the specified language
608
     for the QuestionTarget context.
4004.4.5 by Curtis Hovey
Completed a narrow and brittle implementation of Unsupported Questions. The interface and implementation need refactoring and tests are broken. All haill British Sea Power.
609
     """
4319.7.9 by Francis J. Lacoste
Use ApplicationMenu instead of ContextMenu. Style.
610
4476.4.6 by Curtis Hovey
Factored out hide_language_control.
611
    custom_widget('language', LabeledMultiCheckBoxWidget, visible=False)
612
4319.7.7 by Francis J. Lacoste
Typos.
613
    # No point showing a matching FAQs link on this report.
4319.7.6 by Francis J. Lacoste
Rename SearchFAQsBaseView to SearchFAQsView.
614
    matching_faqs_count = 0
4294.2.1 by Curtis Hovey
Reformatted to standards.
615
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
616
    def __init__(self, context, request):
617
        """Initialize the view, and check that a language was submitted.
4476.4.5 by Curtis Hovey
Revisions per review.
618
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
619
        When the language is missing, the View redirects to the project's
620
        Answer facet.
621
        """
622
        SearchQuestionsView.__init__(self, context, request)
4476.4.11 by Curtis Hovey
Revisions per review.
623
        # Language is intrinsic to this view; it manages the language
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
624
        # field without the help of formlib.
625
        lang_code = request.get('field.language', '')
626
        try:
627
            self.language = getUtility(ILanguageSet)[lang_code]
628
        except NotFoundError:
629
            self.request.response.redirect(
630
                canonical_url(self.context, rootsite='answers'))
631
4004.4.5 by Curtis Hovey
Completed a narrow and brittle implementation of Unsupported Questions. The interface and implementation need refactoring and tests are broken. All haill British Sea Power.
632
    @property
9517.4.1 by Paul Hummer
Question and FAQ listings ported to 3.0
633
    def page_title(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
634
        """See `SearchQuestionsView`."""
4476.4.11 by Curtis Hovey
Revisions per review.
635
        mapping = dict(context=self.context.displayname,
636
                       search_text=self.search_text,
637
                       language=self.language.englishname)
4004.4.8 by Curtis Hovey
General Unsupported Questions features are complete. Interpol rocks\!
638
        if self.search_text:
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
639
            return _('${language} questions matching "${search_text}" '
4476.4.11 by Curtis Hovey
Revisions per review.
640
                     'in ${context}',
641
                     mapping=mapping)
4004.4.8 by Curtis Hovey
General Unsupported Questions features are complete. Interpol rocks\!
642
        else:
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
643
            return _('${language} questions in ${context}',
4476.4.11 by Curtis Hovey
Revisions per review.
644
                      mapping=mapping)
4294.2.1 by Curtis Hovey
Reformatted to standards.
645
9517.4.1 by Paul Hummer
Question and FAQ listings ported to 3.0
646
    label = page_title
647
4004.4.5 by Curtis Hovey
Completed a narrow and brittle implementation of Unsupported Questions. The interface and implementation need refactoring and tests are broken. All haill British Sea Power.
648
    @property
649
    def empty_listing_message(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
650
        """See `SearchQuestionsView`."""
4476.4.11 by Curtis Hovey
Revisions per review.
651
        mapping = dict(context=self.context.displayname,
652
                       search_text=self.search_text,
653
                       language=self.language.englishname)
4004.4.8 by Curtis Hovey
General Unsupported Questions features are complete. Interpol rocks\!
654
        if self.search_text:
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
655
            return _('No ${language} questions matching "${search_text}" '
4476.4.11 by Curtis Hovey
Revisions per review.
656
                     'in ${context} for the selected status.',
657
                     mapping=mapping)
4004.4.8 by Curtis Hovey
General Unsupported Questions features are complete. Interpol rocks\!
658
        else:
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
659
            return _('No ${language} questions in ${context} for the '
4476.4.11 by Curtis Hovey
Revisions per review.
660
                     'selected status.',
661
                     mapping=mapping)
4294.2.1 by Curtis Hovey
Reformatted to standards.
662
4261.1.14 by Curtis Hovey
Revised language control and rules for display.
663
    @property
4261.1.16 by Curtis Hovey
Revised ui language and minor code concerns for review.
664
    def show_language_control(self):
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
665
        """See `SearchQuestionsView`.
4476.4.5 by Curtis Hovey
Revisions per review.
666
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
667
        This view does not permit the user to select a language.
668
        """
669
        return True
670
4004.4.5 by Curtis Hovey
Completed a narrow and brittle implementation of Unsupported Questions. The interface and implementation need refactoring and tests are broken. All haill British Sea Power.
671
    def getDefaultFilter(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
672
        """See `SearchQuestionsView`."""
4476.4.1 by Curtis Hovey
Most features for questions in a language search are implemented. Test coverage is lacking. Unsupported view code is removed.
673
        return dict(language=self.language)
4004.4.5 by Curtis Hovey
Completed a narrow and brittle implementation of Unsupported Questions. The interface and implementation need refactoring and tests are broken. All haill British Sea Power.
674
675
4215.2.15 by Curtis Hovey
Added English to Lp. Rvised tests; removing old rules and added new rule. Translation tests probably fail. This branch needs some cleaning too.
676
class ManageAnswerContactView(UserSupportLanguagesMixin, LaunchpadFormView):
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
677
    """View class for managing answer contacts."""
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
678
4182.1.4 by Francis J. Lacoste
Converted ManageAnswerContactView to use LaunchpadFormView.
679
    label = _("Manage answer contacts")
680
9098.5.11 by Paul Hummer
Ported the answer contact page to 3.0
681
    @property
682
    def page_title(self):
9098.5.13 by Paul Hummer
Fixed the tests
683
        return 'Answer contact for %s' % self.context.title
9098.5.11 by Paul Hummer
Ported the answer contact page to 3.0
684
9517.4.1 by Paul Hummer
Question and FAQ listings ported to 3.0
685
    label = page_title
4182.1.4 by Francis J. Lacoste
Converted ManageAnswerContactView to use LaunchpadFormView.
686
    custom_widget('answer_contact_teams', LabeledMultiCheckBoxWidget)
687
688
    def setUpFields(self):
4436.2.4 by Curtis Hovey
Added RST links to parent classes.
689
        """See `LaunchpadFormView`."""
4182.1.4 by Francis J. Lacoste
Converted ManageAnswerContactView to use LaunchpadFormView.
690
        self.form_fields = form.Fields(
691
            self._createUserAnswerContactField(),
692
            self._createTeamAnswerContactsField())
693
694
    def _createUserAnswerContactField(self):
695
        """Create the want_to_be_answer_contact field."""
696
        return Bool(
697
                __name__='want_to_be_answer_contact',
698
                title=_("I want to be an answer contact for $context",
699
                        mapping=dict(context=self.context.displayname)),
700
                required=False)
701
702
    def _createTeamAnswerContactsField(self):
703
        """Create a list of teams the user is an administrator of."""
704
        sort_key = attrgetter('displayname')
705
        terms = []
5126.3.20 by Edwin Grubbs
expanded RestrictedMembershipsPersonView
706
        for team in sorted(self.administrated_teams, key=sort_key):
4182.1.4 by Francis J. Lacoste
Converted ManageAnswerContactView to use LaunchpadFormView.
707
            terms.append(SimpleTerm(team, team.name, team.displayname))
708
5485.1.11 by Edwin Grubbs
Using PublicPersonChoice almost everywhere.
709
        public_person_choice = PublicPersonChoice(
710
            vocabulary=SimpleVocabulary(terms))
4182.1.4 by Francis J. Lacoste
Converted ManageAnswerContactView to use LaunchpadFormView.
711
        return form.FormField(
712
            List(
713
                __name__='answer_contact_teams',
4182.1.6 by Francis J. Lacoste
Only allow team administrators to select teams as answer contacts.
714
                title=_("Let the following teams be an answer contact for "
715
                        "$context",
716
                        mapping=dict(context=self.context.displayname)),
5485.1.11 by Edwin Grubbs
Using PublicPersonChoice almost everywhere.
717
                value_type=public_person_choice,
7076.5.2 by Gary Poster
initial cut at changing files to take into account new behavior. four tests do not pass.
718
                required=False))
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
719
720
    @property
721
    def initial_values(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
722
        """Return a dictionary of the default values for the form_fields."""
3691.188.13 by Francis J. Lacoste
Replace use of LaunchBag by self.user
723
        user = self.user
3691.398.20 by Francis J. Lacoste
Rename all methods.
724
        answer_contacts = self.context.direct_answer_contacts
725
        answer_contact_teams = set(
5126.3.20 by Edwin Grubbs
expanded RestrictedMembershipsPersonView
726
            answer_contacts).intersection(self.administrated_teams)
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
727
        return {
3691.398.20 by Francis J. Lacoste
Rename all methods.
728
            'want_to_be_answer_contact': user in answer_contacts,
11235.5.4 by Curtis Hovey
hush lint.
729
            'answer_contact_teams': list(answer_contact_teams),
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
730
            }
3691.188.13 by Francis J. Lacoste
Replace use of LaunchBag by self.user
731
4182.1.4 by Francis J. Lacoste
Converted ManageAnswerContactView to use LaunchpadFormView.
732
    @action(_('Continue'), name='update')
733
    def update_action(self, action, data):
734
        """Update the answer contact registration."""
735
        want_to_be_answer_contact = data['want_to_be_answer_contact']
736
        answer_contact_teams = data.get('answer_contact_teams', [])
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
737
        response = self.request.response
3691.98.14 by Francis J. Lacoste
Remove XXX related to fix of bug 54987.
738
        replacements = {'context': self.context.displayname}
3691.398.20 by Francis J. Lacoste
Rename all methods.
739
        if want_to_be_answer_contact:
4417.3.1 by Curtis Hovey
Added view rules to manage +editlanguage for teams.
740
            self._updatePreferredLanguages(self.user)
12959.4.13 by Curtis Hovey
Updated callsite and interface to require and pass the subscribed_by arg.
741
            if self.context.addAnswerContact(self.user, self.user):
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
742
                response.addNotification(
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
743
                    _('You have been added as an answer contact for '
3691.98.14 by Francis J. Lacoste
Remove XXX related to fix of bug 54987.
744
                      '$context.', mapping=replacements))
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
745
        else:
12959.4.13 by Curtis Hovey
Updated callsite and interface to require and pass the subscribed_by arg.
746
            if self.context.removeAnswerContact(self.user, self.user):
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
747
                response.addNotification(
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
748
                    _('You have been removed as an answer contact for '
3691.98.14 by Francis J. Lacoste
Remove XXX related to fix of bug 54987.
749
                      '$context.', mapping=replacements))
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
750
5126.3.20 by Edwin Grubbs
expanded RestrictedMembershipsPersonView
751
        for team in self.administrated_teams:
3691.98.14 by Francis J. Lacoste
Remove XXX related to fix of bug 54987.
752
            replacements['teamname'] = team.displayname
3691.398.20 by Francis J. Lacoste
Rename all methods.
753
            if team in answer_contact_teams:
4417.3.1 by Curtis Hovey
Added view rules to manage +editlanguage for teams.
754
                self._updatePreferredLanguages(team)
12959.4.13 by Curtis Hovey
Updated callsite and interface to require and pass the subscribed_by arg.
755
                if self.context.addAnswerContact(team, self.user):
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
756
                    response.addNotification(
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
757
                        _('$teamname has been added as an answer contact '
3691.98.14 by Francis J. Lacoste
Remove XXX related to fix of bug 54987.
758
                          'for $context.', mapping=replacements))
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
759
            else:
12959.4.13 by Curtis Hovey
Updated callsite and interface to require and pass the subscribed_by arg.
760
                if self.context.removeAnswerContact(team, self.user):
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
761
                    response.addNotification(
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
762
                        _('$teamname has been removed as an answer contact '
3691.98.14 by Francis J. Lacoste
Remove XXX related to fix of bug 54987.
763
                          'for $context.', mapping=replacements))
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
764
4182.1.4 by Francis J. Lacoste
Converted ManageAnswerContactView to use LaunchpadFormView.
765
        self.next_url = canonical_url(self.context, rootsite='answers')
3258.7.5 by Bjorn Tillenius
add UI for support contacts. no pagetests yet though.
766
5126.3.20 by Edwin Grubbs
expanded RestrictedMembershipsPersonView
767
    @property
768
    def administrated_teams(self):
7675.110.3 by Curtis Hovey
Ran the migration script to move registry code to lp.registry.
769
        from lp.registry.browser.person import (
5126.3.20 by Edwin Grubbs
expanded RestrictedMembershipsPersonView
770
            RestrictedMembershipsPersonView)
5126.3.23 by Edwin Grubbs
Removed duplicate user attribute in views.
771
        restricted_view = RestrictedMembershipsPersonView(self.user,
772
                                                          self.request)
5126.3.20 by Edwin Grubbs
expanded RestrictedMembershipsPersonView
773
        return restricted_view.administrated_teams
774
4417.3.1 by Curtis Hovey
Added view rules to manage +editlanguage for teams.
775
    def _updatePreferredLanguages(self, person_or_team):
776
        """Check or update the Person's preferred languages as needed.
777
4417.3.3 by Curtis Hovey
Changes pre review.
778
        Answer contacts must tell Launchpad in which languages they provide
779
        help. If the Person has not already set his preferred languages, they
780
        are set to his browser languages. In the case of a team without
781
        languages, only English is added to the preferred languages. When
4450.6.6 by Curtis Hovey
Added a test for en in getAnswerContactsForLanguage() to do a LIKE match for english variants. Restored _updatePreferredLanguages to it current logic.
782
        languages are added, a notification is added to the response.
4417.3.1 by Curtis Hovey
Added view rules to manage +editlanguage for teams.
783
        """
7354.1.3 by Guilherme Salgado
Second aproach works.
784
        if len(person_or_team.languages) > 0:
4450.6.6 by Curtis Hovey
Added a test for en in getAnswerContactsForLanguage() to do a LIKE match for english variants. Restored _updatePreferredLanguages to it current logic.
785
            return
4417.3.1 by Curtis Hovey
Added view rules to manage +editlanguage for teams.
786
787
        response = self.request.response
7117.5.2 by Jeroen Vermeulen
Made English a celebrity.
788
        english = getUtility(ILaunchpadCelebrities).english
4417.3.1 by Curtis Hovey
Added view rules to manage +editlanguage for teams.
789
        if person_or_team.isTeam():
7117.5.2 by Jeroen Vermeulen
Made English a celebrity.
790
            person_or_team.addLanguage(english)
11235.5.4 by Curtis Hovey
hush lint.
791
            team_mapping = {'name': person_or_team.name,
792
                            'displayname': person_or_team.displayname}
5594.1.21 by Maris Fogels
Fixed a call to addNotification() that contains internationalized markup.
793
            msgid = _("English was added to ${displayname}'s "
794
                      '<a href="/~${name}/+editlanguages">preferred '
5594.1.27 by Maris Fogels
Pushed translations of structured markup down into structured()'s constructor.
795
                      'languages</a>.',
796
                      mapping=team_mapping)
797
            response.addNotification(structured(msgid))
4417.3.1 by Curtis Hovey
Added view rules to manage +editlanguage for teams.
798
        else:
4450.6.6 by Curtis Hovey
Added a test for en in getAnswerContactsForLanguage() to do a LIKE match for english variants. Restored _updatePreferredLanguages to it current logic.
799
            if len(browserLanguages(self.request)) > 0:
4417.3.4 by Curtis Hovey
Changes per review.
800
                languages = browserLanguages(self.request)
801
            else:
7117.5.2 by Jeroen Vermeulen
Made English a celebrity.
802
                languages = [english]
4417.3.4 by Curtis Hovey
Changes per review.
803
            for language in languages:
4417.3.1 by Curtis Hovey
Added view rules to manage +editlanguage for teams.
804
                person_or_team.addLanguage(language)
4417.3.4 by Curtis Hovey
Changes per review.
805
            language_str = ', '.join([lang.displayname for lang in languages])
5594.1.21 by Maris Fogels
Fixed a call to addNotification() that contains internationalized markup.
806
            msgid = _('<a href="/people/+me/+editlanguages">Your preferred '
807
                      'languages</a> were updated to include your browser '
808
                      'languages: $languages.',
11235.5.4 by Curtis Hovey
hush lint.
809
                      mapping={'languages': language_str})
5594.1.27 by Maris Fogels
Pushed translations of structured markup down into structured()'s constructor.
810
            response.addNotification(structured(msgid))
3691.188.2 by Francis J. Lacoste
Refactor all TicketTarget menus into a TicketTargetSupportMenu
811
4417.3.5 by Curtis Hovey
Changes per review. Added two tests to check that +editlanguages works as we know it, not necessarilly as it should.
812
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
813
class QuestionTargetFacetMixin:
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
814
    """Mixin for questiontarget facet definition."""
3691.188.5 by Francis J. Lacoste
Refactor common tickettarget support facet link and traversal into common mixins
815
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
816
    def answers(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
817
        """Return the link for Answers."""
3691.188.5 by Francis J. Lacoste
Refactor common tickettarget support facet link and traversal into common mixins
818
        summary = (
3691.398.2 by Francis J. Lacoste
Rename support tracker in browser code.
819
            'Questions for %s' % self.context.displayname)
4013.1.4 by kiko
Fix facet target links to take advantage of the defaultviews registered (and fix a defaultview for distributionsourcepackage, which was missing).
820
        return Link('', 'Answers', summary)
3691.188.5 by Francis J. Lacoste
Refactor common tickettarget support facet link and traversal into common mixins
821
822
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
823
class QuestionTargetTraversalMixin:
3691.398.18 by Francis J. Lacoste
Rename interfaces.
824
    """Navigation mixin for IQuestionTarget."""
3691.188.5 by Francis J. Lacoste
Refactor common tickettarget support facet link and traversal into common mixins
825
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
826
    @stepthrough('+question')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
827
    def traverse_question(self, name):
4294.2.1 by Curtis Hovey
Reformatted to standards.
828
        """Return the question."""
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
829
        # questions should be ints
3691.188.5 by Francis J. Lacoste
Refactor common tickettarget support facet link and traversal into common mixins
830
        try:
3691.398.20 by Francis J. Lacoste
Rename all methods.
831
            question_id = int(name)
3691.188.5 by Francis J. Lacoste
Refactor common tickettarget support facet link and traversal into common mixins
832
        except ValueError:
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
833
            raise NotFoundError(name)
4311.1.3 by Francis J. Lacoste
Redirect to proper target.
834
        question = self.context.getQuestion(question_id)
835
        if question is not None:
836
            return question
4353.1.1 by Curtis Hovey
Removed trailing whitespace.
837
4311.1.6 by Francis J. Lacoste
Small cleanup suggested by reviewer.
838
        # Try to find the question in another context, since it may have
839
        # been retargeted.
4311.1.3 by Francis J. Lacoste
Redirect to proper target.
840
        question = getUtility(IQuestionSet).get(question_id)
841
        if question is None:
842
            raise NotFoundError(name)
843
        return self.redirectSubTree(canonical_url(question))
3691.188.5 by Francis J. Lacoste
Refactor common tickettarget support facet link and traversal into common mixins
844
3859.4.4 by Francis J. Lacoste
Merge RF, resolving 18 conflicts.
845
    @stepto('+ticket')
846
    def redirect_ticket(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
847
        """Use RedirectionNavigation to redirect to +question.
848
849
        It will take care of the remaining steps and query URL.
850
        """
3859.4.4 by Francis J. Lacoste
Merge RF, resolving 18 conflicts.
851
        target = urlappend(
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
852
            canonical_url(self.context, rootsite='answers'), '+question')
3859.4.4 by Francis J. Lacoste
Merge RF, resolving 18 conflicts.
853
        return self.redirectSubTree(target)
3691.188.5 by Francis J. Lacoste
Refactor common tickettarget support facet link and traversal into common mixins
854
855
4319.7.1 by Francis J. Lacoste
Add basic FAQs listing.
856
class QuestionCollectionAnswersMenu(FAQCollectionMenu):
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
857
    """Base menu definition for QuestionCollection searchable by owner."""
11235.5.4 by Curtis Hovey
hush lint.
858
    # XXX flacoste 2007-07-08 bug=125851:
859
    # This menu shouldn't "extend" FAQCollectionMenu.
860
    # architecture. But this is needed because of limitations in the current
861
    # menu  Menu should be built by merging all menus applying to the context
862
    # object (-based on the interfaces it provides).
3691.398.18 by Francis J. Lacoste
Rename interfaces.
863
    usedfor = ISearchableByQuestionOwner
3691.398.22 by Francis J. Lacoste
Rename support facet to answers.
864
    facet = 'answers'
4319.7.1 by Francis J. Lacoste
Add basic FAQs listing.
865
    links = FAQCollectionMenu.links + [
866
        'open', 'answered', 'myrequests', 'need_attention']
3691.188.7 by Francis J. Lacoste
Add an All link. Relabeled since support requests are _for_ instead of _in_ a context.
867
3691.345.6 by Francis J. Lacoste
Make recent update first sort order default for Open report.
868
    def makeSearchLink(self, statuses, sort='by relevancy'):
4294.2.1 by Curtis Hovey
Reformatted to standards.
869
        """Return the search parameters for a search link."""
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
870
        return "+questions?" + urlencode(
3691.188.28 by Francis J. Lacoste
PEP-8 and some other code clarifications
871
            {'field.status': statuses,
3691.345.6 by Francis J. Lacoste
Make recent update first sort order default for Open report.
872
             'field.sort': sort,
3691.188.28 by Francis J. Lacoste
PEP-8 and some other code clarifications
873
             'field.search_text': '',
874
             'field.actions.search': 'Search',
3691.188.8 by Francis J. Lacoste
Implement Open and Answered report by passing the correct status in the URL. Compute title and empty list message dynamically based on the search filter.
875
             'field.status': statuses}, doseq=True)
3691.188.7 by Francis J. Lacoste
Add an All link. Relabeled since support requests are _for_ instead of _in_ a context.
876
877
    def open(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
878
        """Return a Link that opens a question."""
3691.345.10 by Francis J. Lacoste
Renamed "recent update first" to "recently updated first".
879
        url = self.makeSearchLink('Open', sort='recently updated first')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
880
        return Link(url, 'Open', icon='question')
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
881
882
    def answered(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
883
        """Return a Link to display questions that are open."""
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
884
        text = 'Answered'
3691.197.106 by Francis J. Lacoste
Merge RV, resolving 4 conflicts andupdating support request search views for status changes.
885
        return Link(
4294.2.1 by Curtis Hovey
Reformatted to standards.
886
            self.makeSearchLink(['Answered', 'Solved']),
887
            text, icon='question')
3691.188.4 by Francis J. Lacoste
Refactor SearchTicketsView to make it easy to make specialised searchable report. Add Open, Answered and My Requests reports on TicketTarget
888
889
    def myrequests(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
890
        """Return a Link to display the user's questions."""
3847.2.16 by Mark Shuttleworth
Fix tests for answer tracker menu capitalisation
891
        text = 'My questions'
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
892
        return Link('+myquestions', text, icon='question')
3691.188.2 by Francis J. Lacoste
Refactor all TicketTarget menus into a TicketTargetSupportMenu
893
3691.235.13 by Francis J. Lacoste
Renamed Needs Attention to Need Attention.
894
    def need_attention(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
895
        """Return a Link to display questions that need attention."""
3847.2.16 by Mark Shuttleworth
Fix tests for answer tracker menu capitalisation
896
        text = 'Need attention'
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
897
        return Link('+need-attention', text, icon='question')
4004.4.6 by Curtis Hovey
Remove the dead implimentation parts. More code is needed to pass the tests.
898
899
900
class QuestionTargetAnswersMenu(QuestionCollectionAnswersMenu):
901
    """Base menu definition for QuestionTargets."""
902
903
    usedfor = IQuestionTarget
904
    facet = 'answers'
4476.4.5 by Curtis Hovey
Revisions per review.
905
    links = QuestionCollectionAnswersMenu.links + ['new', 'answer_contact']
4294.2.1 by Curtis Hovey
Reformatted to standards.
906
3691.188.2 by Francis J. Lacoste
Refactor all TicketTarget menus into a TicketTargetSupportMenu
907
    def new(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
908
        """Return a link to ask a question."""
3857.1.15 by Matthew Paul Thomas
Tweaks wording of menu items etc.
909
        text = 'Ask a question'
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
910
        return Link('+addquestion', text, icon='add')
3691.188.2 by Francis J. Lacoste
Refactor all TicketTarget menus into a TicketTargetSupportMenu
911
3691.398.20 by Francis J. Lacoste
Rename all methods.
912
    def answer_contact(self):
4294.2.1 by Curtis Hovey
Reformatted to standards.
913
        """Return a link to the manage answer contact view."""
3857.1.15 by Matthew Paul Thomas
Tweaks wording of menu items etc.
914
        text = 'Set answer contact'
3859.4.3 by Francis J. Lacoste
Rename URLs of the Answer Tracker containging ticket or support-contact.
915
        return Link('+answer-contact', text, icon='edit')
9087.4.16 by Guilherme Salgado
Add VHost-specific breadcrumbs for IQuestionTarget,IProject,IPerson
916
917
9565.2.1 by Gary Poster
fix vhost breadcrumbs so that we do not duplicate information
918
class AnswersVHostBreadcrumb(Breadcrumb):
919
    rootsite = 'answers'
920
    text = 'Questions'