~launchpad-pqm/launchpad/devel

8687.15.15 by Karl Fogel
Add the copyright header block to files under lib/lp/bugs/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3607.4.9 by Bjorn Tillenius
use BugComment instead of BugMessage.
3
4
"""Bug comment browser view classes."""
5
6
__metaclass__ = type
7283.3.1 by Graham Binns
Changed comment_syncing_team.
7
__all__ = [
8
    'BugComment',
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
9
    'BugCommentBoxExpandedReplyView',
10
    'BugCommentBoxView',
11
    'BugCommentBreadcrumb',
7283.3.1 by Graham Binns
Changed comment_syncing_team.
12
    'BugCommentView',
8461.2.2 by Bjorn Tillenius
Provide a custom XHTML representation of a bug comment.
13
    'BugCommentXHTMLRepresentation',
7283.3.1 by Graham Binns
Changed comment_syncing_team.
14
    'build_comments_from_chunks',
13500.3.9 by j.c.sackett
Fixed formatting.
15
    'group_comments_with_activity',
16
    ]
3607.4.9 by Bjorn Tillenius
use BugComment instead of BugMessage.
17
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
18
from datetime import (
19
    datetime,
20
    timedelta,
21
    )
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
22
from itertools import (
23
    chain,
24
    groupby,
25
    )
26
from operator import itemgetter
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
27
28
from lazr.restful.interfaces import IWebServiceClientRequest
9232.2.70 by Stuart Bishop
Cache bug comments more aggressively
29
from pytz import utc
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
30
from zope.component import (
31
    adapts,
32
    getMultiAdapter,
33
    getUtility,
34
    )
35
from zope.interface import (
36
    implements,
37
    Interface,
38
    )
39
14612.2.1 by William Grant
format-imports on lib/. So many imports.
40
from lp.bugs.interfaces.bugmessage import IBugComment
14605.1.1 by Curtis Hovey
Moved canonical.config to lp.services.
41
from lp.services.config import config
14612.2.1 by William Grant
format-imports on lib/. So many imports.
42
from lp.services.features import getFeatureFlag
43
from lp.services.librarian.browser import ProxiedLibraryFileAlias
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
44
from lp.services.webapp import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
45
    canonical_url,
46
    LaunchpadView,
47
    )
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
48
from lp.services.webapp.breadcrumb import Breadcrumb
49
from lp.services.webapp.interfaces import ILaunchBag
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
50
51
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
52
COMMENT_ACTIVITY_GROUPING_WINDOW = timedelta(minutes=5)
53
54
12929.7.39 by j.c.sackett
Added spam controls to bug comments.
55
def build_comments_from_chunks(
14302.4.1 by Ian Booth
Allow users in project roles and comment owners to hide comments
56
        bugtask, truncate=False, slice_info=None, show_spam_controls=False,
57
        user=None):
12376.1.2 by Robert Collins
Basic implementation in place, tests not updated.
58
    """Build BugComments from MessageChunks.
12840.7.3 by j.c.sackett
Lint fixes.
59
12376.1.2 by Robert Collins
Basic implementation in place, tests not updated.
60
    :param truncate: Perform truncation of large messages.
61
    :param slice_info: If not None, an iterable of slices to retrieve.
62
    """
63
    chunks = bugtask.bug.getMessagesForView(slice_info=slice_info)
64
    # This would be better as part of indexed_messages eager loading.
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
65
    comments = {}
12376.1.2 by Robert Collins
Basic implementation in place, tests not updated.
66
    for bugmessage, message, chunk in chunks:
67
        bug_comment = comments.get(message.id)
3553.3.23 by Brad Bollenbach
small tweaks to kiko's patch
68
        if bug_comment is None:
12929.7.46 by j.c.sackett
Lint fixes.
69
            bug_comment = BugComment(
70
                bugmessage.index, message, bugtask, visible=message.visible,
14302.4.1 by Ian Booth
Allow users in project roles and comment owners to hide comments
71
                show_spam_controls=show_spam_controls, user=user)
12376.1.2 by Robert Collins
Basic implementation in place, tests not updated.
72
            comments[message.id] = bug_comment
12840.7.3 by j.c.sackett
Lint fixes.
73
            # This code path is currently only used from a BugTask view which
74
            # has already loaded all the bug watches. If we start lazy loading
12376.1.6 by Robert Collins
Test (and fix) Bug.getMessagesForView.
75
            # those, or not needing them we will need to batch lookup watches
76
            # here.
12376.1.2 by Robert Collins
Basic implementation in place, tests not updated.
77
            if bugmessage.bugwatchID is not None:
78
                bug_comment.bugwatch = bugmessage.bugwatch
79
                bug_comment.synchronized = (
80
                    bugmessage.remote_comment_id is not None)
3553.3.23 by Brad Bollenbach
small tweaks to kiko's patch
81
        bug_comment.chunks.append(chunk)
5796.3.4 by Bjorn Tillenius
move code.
82
83
    for comment in comments.values():
12376.1.2 by Robert Collins
Basic implementation in place, tests not updated.
84
        # Once we have all the chunks related to a comment populated,
5796.3.4 by Bjorn Tillenius
move code.
85
        # we get the text set up for display.
86
        comment.setupText(truncate=truncate)
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
87
    return comments
3607.4.9 by Bjorn Tillenius
use BugComment instead of BugMessage.
88
89
13955.1.1 by Graham Binns
Batched activity now no longer pulls in results that have already been shown.
90
def group_comments_with_activity(comments, activities):
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
91
    """Group comments and activity together for human consumption.
92
93
    Generates a stream of comment instances (with the activity grouped within)
94
    or `list`s of grouped activities.
95
12376.1.4 by Robert Collins
Fix known bugs.
96
    :param comments: An iterable of `BugComment` instances, which should be
97
        sorted by index already.
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
98
    :param activities: An iterable of `BugActivity` instances.
99
    """
100
    window = COMMENT_ACTIVITY_GROUPING_WINDOW
101
102
    comment_kind = "comment"
12376.1.4 by Robert Collins
Fix known bugs.
103
    if comments:
104
        max_index = comments[-1].index + 1
105
    else:
106
        max_index = 0
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
107
    comments = (
12840.7.3 by j.c.sackett
Lint fixes.
108
        (comment.datecreated, comment.index,
109
            comment.owner, comment_kind, comment)
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
110
        for comment in comments)
111
    activity_kind = "activity"
112
    activity = (
12840.7.3 by j.c.sackett
Lint fixes.
113
        (activity.datechanged, max_index,
114
            activity.person, activity_kind, activity)
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
115
        for activity in activities)
12376.1.4 by Robert Collins
Fix known bugs.
116
    # when an action and a comment happen at the same time, the action comes
117
    # second, when two events are tied the comment index is used to
118
    # disambiguate.
119
    events = sorted(chain(comments, activity), key=itemgetter(0, 1, 2))
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
120
121
    def gen_event_windows(events):
122
        """Generate event windows.
123
124
        Yields `(window_index, kind, event)` tuples, where `window_index` is
125
        an integer, and is incremented each time the windowing conditions are
126
        triggered.
127
12376.1.4 by Robert Collins
Fix known bugs.
128
        :param events: An iterable of `(date, ignored, actor, kind, event)`
129
            tuples in order.
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
130
        """
131
        window_comment, window_actor = None, None
132
        window_index, window_end = 0, None
12376.1.4 by Robert Collins
Fix known bugs.
133
        for date, _, actor, kind, event in events:
12075.3.14 by Gavin Panella
Move group_comments_with_activity() to lp.bugs.browser.bugcomment.
134
            window_ended = (
135
                # A window may contain only one comment.
136
                (window_comment is not None and kind is comment_kind) or
137
                # All events must have happened within a given timeframe.
138
                (window_end is None or date >= window_end) or
139
                # All events within the window must belong to the same actor.
140
                (window_actor is None or actor != window_actor))
141
            if window_ended:
142
                window_comment, window_actor = None, actor
143
                window_index, window_end = window_index + 1, date + window
144
            if kind is comment_kind:
145
                window_comment = event
146
            yield window_index, kind, event
147
148
    event_windows = gen_event_windows(events)
149
    event_windows_grouper = groupby(event_windows, itemgetter(0))
150
    for window_index, window_group in event_windows_grouper:
151
        window_group = [
152
            (kind, event) for (index, kind, event) in window_group]
153
        for kind, event in window_group:
154
            if kind is comment_kind:
155
                window_comment = event
156
                window_comment.activity.extend(
157
                    event for (kind, event) in window_group
158
                    if kind is activity_kind)
159
                yield window_comment
160
                # There's only one comment per window.
161
                break
162
        else:
163
            yield [event for (kind, event) in window_group]
164
165
3607.4.9 by Bjorn Tillenius
use BugComment instead of BugMessage.
166
class BugComment:
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
167
    """Data structure that holds all data pertaining to a bug comment.
3607.4.9 by Bjorn Tillenius
use BugComment instead of BugMessage.
168
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
169
    It keeps track of which index it has in the bug comment list and
3607.4.9 by Bjorn Tillenius
use BugComment instead of BugMessage.
170
    also provides functionality to truncate the comment.
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
171
172
    Note that although this class is called BugComment it really takes
173
    as an argument a bugtask. The reason for this is to allow
174
    canonical_url()s of BugComments to take you to the correct
175
    (task-specific) location.
3607.4.9 by Bjorn Tillenius
use BugComment instead of BugMessage.
176
    """
177
    implements(IBugComment)
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
178
12929.7.39 by j.c.sackett
Added spam controls to bug comments.
179
    def __init__(
180
            self, index, message, bugtask, activity=None,
14302.4.1 by Ian Booth
Allow users in project roles and comment owners to hide comments
181
            visible=True, show_spam_controls=False, user=None):
12929.7.39 by j.c.sackett
Added spam controls to bug comments.
182
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
183
        self.index = index
3607.4.11 by Bjorn Tillenius
define a canonical url for IBugComment and use it.
184
        self.bugtask = bugtask
5796.3.1 by Bjorn Tillenius
quick hack
185
        self.bugwatch = None
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
186
187
        self.title = message.title
3847.2.47 by Mark Shuttleworth
Add tests for new bug comment behaviour, and extra bug sample data.
188
        self.display_title = False
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
189
        self.datecreated = message.datecreated
190
        self.owner = message.owner
5127.1.2 by Christian Reis
Fix for bug #139327, comments are missing in +text view. Also adds fields for privacy, security and dates created.
191
        self.rfc822msgid = message.rfc822msgid
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
192
193
        self.chunks = []
194
        self.bugattachments = []
10017.2.2 by Abel Deuring
added property BugComment.patches; removed attachment that are patches from BugComment.bugattachments; separate display of regular attachments and patches on bug pages
195
        self.patches = []
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
196
8128.8.2 by Graham Binns
Added activity param to BugComment.
197
        if activity is None:
198
            activity = []
199
200
        self.activity = activity
201
6293.1.1 by Tom Berger
reply to remore bug comments ui
202
        self.synchronized = False
12376.1.2 by Robert Collins
Basic implementation in place, tests not updated.
203
        self.visible = visible
14302.4.3 by Ian Booth
Add feature flag
204
        # We use a feature flag to control users deleting their own comments.
205
        user_owns_comment = False
206
        flag = 'disclosure.users_hide_own_bug_comments.enabled'
207
        if bool(getFeatureFlag(flag)):
208
            user_owns_comment = user is not None and user == self.owner
14302.4.1 by Ian Booth
Allow users in project roles and comment owners to hide comments
209
        self.show_spam_controls = show_spam_controls or user_owns_comment
5292.4.8 by Graham Binns
Added +comments page for bug watches and associated tests.
210
5292.4.1 by Graham Binns
Comments are now filtered properly. Imported comments say that they are.
211
    @property
8137.17.24 by Barry Warsaw
thread merge
212
    def show_for_admin(self):
213
        """Show hidden comments for Launchpad admins.
214
215
        This is used in templates to add a class to hidden
216
        comments to enable display for admins, so the admin
12840.7.3 by j.c.sackett
Lint fixes.
217
        can see the comment even after it is hidden. Since comments
218
        aren't published unless the user is registry or admin, this
219
        can just check if the comment is visible.
8137.17.24 by Barry Warsaw
thread merge
220
        """
12840.7.3 by j.c.sackett
Lint fixes.
221
        return not self.visible
8137.17.24 by Barry Warsaw
thread merge
222
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
223
    def setupText(self, truncate=False):
4787.1.5 by Graham Binns
Kiko's review changes.
224
        """Set the text for display and truncate it if necessary.
225
226
        Note that this method must be called before either isIdenticalTo() or
227
        isEmpty() are called, since to do otherwise would mean that they could
228
        return false positives and negatives respectively.
229
        """
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
230
        comment_limit = config.malone.max_comment_size
231
6025.2.1 by Abel Deuring
fix for bug 163991: Attachments for initial comment are not displayed
232
        bits = [unicode(chunk.content)
6025.2.3 by Abel Deuring
implemented another reviewer remark
233
                for chunk in self.chunks
234
                if chunk.content is not None and len(chunk.content) > 0]
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
235
        text = self.text_contents = '\n\n'.join(bits)
236
237
        if truncate and comment_limit and len(text) > comment_limit:
3691.62.33 by kiko
Fix some of our test bustage. As part of this, don't deduct 3 from max_comment_size, because it breaks for max_comment_sizes under 3. Reorganize the bugcomment.txt test somewhat to account for comment omission.
238
            # Note here that we truncate at comment_limit, and not
239
            # comment_limit - 3; while it would be nice to account for
240
            # the ellipsis, this breaks down when the comment limit is
241
            # less than 3 (which can happen in a testcase) and it makes
242
            # counting the strings harder.
243
            self.text_for_display = "%s..." % text[:comment_limit]
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
244
            self.was_truncated = True
3607.4.9 by Bjorn Tillenius
use BugComment instead of BugMessage.
245
        else:
246
            self.text_for_display = text
3504.1.51 by kiko
r=bradb Refactor fetching of bug comments to issue O(1) queries. Essentially inverts the way we build the bug comments, fetching all the chunks first, grouping them by message. Should be a major performance boost for the bug page.
247
            self.was_truncated = False
3607.4.9 by Bjorn Tillenius
use BugComment instead of BugMessage.
248
3691.134.10 by kiko
Fix for bug 60574, Comments/Audit trail does not show multiple attachments. Check comment titles and attachment lists to ensure that we don't omit too much, and properly test the feature.
249
    def isIdenticalTo(self, other):
4787.1.5 by Graham Binns
Kiko's review changes.
250
        """Compare this BugComment to another and return True if they are
251
        identical.
252
        """
3869.1.3 by Diogo Matsubara
Fix bug 88531 (Comment shown in activity log but missing from bug page)
253
        if self.owner != other.owner:
254
            return False
3691.134.10 by kiko
Fix for bug 60574, Comments/Audit trail does not show multiple attachments. Check comment titles and attachment lists to ensure that we don't omit too much, and properly test the feature.
255
        if self.text_for_display != other.text_for_display:
256
            return False
257
        if self.title != other.title:
258
            return False
10017.2.2 by Abel Deuring
added property BugComment.patches; removed attachment that are patches from BugComment.bugattachments; separate display of regular attachments and patches on bug pages
259
        if (self.bugattachments or self.patches or other.bugattachments or
260
            other.patches):
3691.134.10 by kiko
Fix for bug 60574, Comments/Audit trail does not show multiple attachments. Check comment titles and attachment lists to ensure that we don't omit too much, and properly test the feature.
261
            # We shouldn't collapse comments which have attachments;
262
            # there's really no possible identity in that case.
263
            return False
264
        return True
265
4787.1.1 by Graham Binns
Empty comments will no longer show up on the index page of a bug task. (So if a comment with an attachment but no body has its attachment removed, the comment will disappear, too.)
266
    def isEmpty(self):
267
        """Return True if text_for_display is empty."""
6025.2.1 by Abel Deuring
fix for bug 163991: Attachments for initial comment are not displayed
268
4767.5.43 by Graham Binns
Fixed a formatting snafu.
269
        return (len(self.text_for_display) == 0 and
10017.2.2 by Abel Deuring
added property BugComment.patches; removed attachment that are patches from BugComment.bugattachments; separate display of regular attachments and patches on bug pages
270
            len(self.bugattachments) == 0 and len(self.patches) == 0)
4787.1.1 by Graham Binns
Empty comments will no longer show up on the index page of a bug task. (So if a comment with an attachment but no body has its attachment removed, the comment will disappear, too.)
271
6293.1.1 by Tom Berger
reply to remore bug comments ui
272
    @property
273
    def add_comment_url(self):
274
        return canonical_url(self.bugtask, view_name='+addcomment')
275
8128.8.6 by Graham Binns
Moved activity table into a macro and added styling; added show_footer property to BugComment.
276
    @property
277
    def show_footer(self):
278
        """Return True if the footer should be shown for this comment."""
12929.7.47 by j.c.sackett
Some quick fixes for tests.
279
        return bool(
12929.7.39 by j.c.sackett
Added spam controls to bug comments.
280
            len(self.activity) > 0 or
281
            self.bugwatch or
282
            self.show_spam_controls)
8128.8.6 by Graham Binns
Moved activity table into a macro and added styling; added show_footer property to BugComment.
283
9232.2.70 by Stuart Bishop
Cache bug comments more aggressively
284
    @property
285
    def rendered_cache_time(self):
286
        """The number of seconds we can cache the rendered comment for.
287
288
        Bug comments are cached with 'authenticated' visibility, so
289
        should contain no information hidden from some users. We use
290
        'authenticated' rather than 'public' as email addresses are
291
        obfuscated for unauthenticated users.
292
        """
293
        now = datetime.now(tz=utc)
12075.3.13 by Gavin Panella
Prevent caching of bug comments that are less than 5 minutes old.
294
295
        # The major factor in how long we can cache a bug comment is the
296
        # timestamp. For up to 5 minutes comments and activity can be grouped
297
        # together as related, so do not cache.
298
        if self.datecreated > now - COMMENT_ACTIVITY_GROUPING_WINDOW:
299
            # Don't return 0 because that indicates no time limit.
300
            return -1
301
302
        # The rendering of the timestamp changes every minute for the first
303
        # hour because we say '7 minutes ago'.
304
        elif self.datecreated > now - timedelta(hours=1):
9232.2.70 by Stuart Bishop
Cache bug comments more aggressively
305
            return 60
306
307
        # Don't cache for long if we are waiting for synchronization.
308
        elif self.bugwatch and not self.synchronized:
13500.5.2 by j.c.sackett
Lint fixes.
309
            return 5 * 60
9232.2.70 by Stuart Bishop
Cache bug comments more aggressively
310
311
        # For the rest of the first day, the rendering changes every
312
        # hour. '4 hours ago'. Expire in 15 minutes so the timestamp
313
        # is at most 15 minutes out of date.
314
        elif self.datecreated > now - timedelta(days=1):
13500.5.2 by j.c.sackett
Lint fixes.
315
            return 15 * 60
9232.2.70 by Stuart Bishop
Cache bug comments more aggressively
316
317
        # Otherwise, cache away. Lets cache for 6 hours. We don't want
318
        # to cache for too long as there are still things that can
319
        # become stale - eg. if a bug attachment has been deleted we
320
        # should stop rendering the link.
321
        else:
13500.5.2 by j.c.sackett
Lint fixes.
322
            return 6 * 60 * 60
9232.2.70 by Stuart Bishop
Cache bug comments more aggressively
323
3607.4.9 by Bjorn Tillenius
use BugComment instead of BugMessage.
324
325
class BugCommentView(LaunchpadView):
326
    """View for a single bug comment."""
327
328
    def __init__(self, context, request):
329
        # We use the current bug task as the context in order to get the
330
        # menu and portlets working.
331
        bugtask = getUtility(ILaunchBag).bugtask
332
        LaunchpadView.__init__(self, bugtask, request)
3607.4.12 by Bjorn Tillenius
s/message/comment/
333
        self.comment = context
8461.1.1 by Bjorn Tillenius
Move the rendering of a comment box into a view.
334
12929.7.47 by j.c.sackett
Some quick fixes for tests.
335
    @property
336
    def show_spam_controls(self):
337
        return self.comment.show_spam_controls
13500.5.2 by j.c.sackett
Lint fixes.
338
9232.1.1 by Deryck Hodge
Update bugcomment-index to 3.0 UI. Drop portlets, which
339
    def page_title(self):
340
        return 'Comment %d for bug %d' % (
341
            self.comment.index, self.context.bug.id)
342
13500.3.3 by j.c.sackett
Adds privacy ribbon to comments.
343
    @property
14412.3.2 by mbp at canonical
bugcomment page description is the text of the comment
344
    def page_description(self):
345
        return self.comment.text_contents
346
347
    @property
13500.3.3 by j.c.sackett
Adds privacy ribbon to comments.
348
    def privacy_notice_classes(self):
349
        if not self.context.bug.private:
350
            return 'hidden'
351
        else:
352
            return ''
353
8461.1.1 by Bjorn Tillenius
Move the rendering of a comment box into a view.
354
11235.6.3 by Abel Deuring
update those tests that are affected by the switch to ProxiedLibraryFileAlias
355
class BugCommentBoxViewMixin:
356
    """A class which provides proxied Librarian URLs for bug attachments."""
357
12929.7.49 by j.c.sackett
Better spam control check.
358
    @property
359
    def show_spam_controls(self):
360
        if hasattr(self.context, 'show_spam_controls'):
13500.5.2 by j.c.sackett
Lint fixes.
361
            return self.context.show_spam_controls
12929.7.49 by j.c.sackett
Better spam control check.
362
        elif (hasattr(self, 'comment') and
13500.5.2 by j.c.sackett
Lint fixes.
363
            hasattr(self.comment, 'show_spam_controls')):
364
            return self.comment.show_spam_controls
12929.7.49 by j.c.sackett
Better spam control check.
365
        else:
13500.5.2 by j.c.sackett
Lint fixes.
366
            return False
12929.7.47 by j.c.sackett
Some quick fixes for tests.
367
11235.6.3 by Abel Deuring
update those tests that are affected by the switch to ProxiedLibraryFileAlias
368
    def proxiedUrlOfLibraryFileAlias(self, attachment):
369
        """Return the proxied URL for the Librarian file of the attachment."""
370
        return ProxiedLibraryFileAlias(
371
            attachment.libraryfile, attachment).http_url
372
373
374
class BugCommentBoxView(LaunchpadView, BugCommentBoxViewMixin):
8461.1.1 by Bjorn Tillenius
Move the rendering of a comment box into a view.
375
    """Render a comment box with reply field collapsed."""
376
377
    expand_reply_box = False
378
379
11235.6.3 by Abel Deuring
update those tests that are affected by the switch to ProxiedLibraryFileAlias
380
class BugCommentBoxExpandedReplyView(LaunchpadView, BugCommentBoxViewMixin):
8461.1.1 by Bjorn Tillenius
Move the rendering of a comment box into a view.
381
    """Render a comment box with reply field expanded."""
382
383
    expand_reply_box = True
8461.2.2 by Bjorn Tillenius
Provide a custom XHTML representation of a bug comment.
384
385
386
class BugCommentXHTMLRepresentation:
387
    adapts(IBugComment, IWebServiceClientRequest)
388
    implements(Interface)
389
390
    def __init__(self, comment, request):
391
        self.comment = comment
392
        self.request = request
393
394
    def __call__(self):
395
        """Render `BugComment` as XHTML using the webservice."""
396
        comment_view = getMultiAdapter(
397
            (self.comment, self.request), name="+box")
398
        return comment_view()
399
10542.12.2 by Karl Fogel
Fix bug #78565: no direct link from bug comment page to corresponding bug.
400
401
class BugCommentBreadcrumb(Breadcrumb):
402
    """Breadcrumb for an `IBugComment`."""
403
404
    def __init__(self, context):
405
        super(BugCommentBreadcrumb, self).__init__(context)
406
407
    @property
408
    def text(self):
409
        return "Comment #%d" % self.context.index