~launchpad-pqm/launchpad/devel

14642.1.8 by Curtis Hovey
Updated copyright.
1
# Copyright 2010-2012 Canonical Ltd.  This software is licensed under the
8687.15.15 by Karl Fogel
Add the copyright header block to files under lib/lp/bugs/.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
2071 by Canonical.com Patch Queue Manager
[trivial] add more __all__ statements, other minor tidyings-up
4
"""Bug attachment views."""
5
6
__metaclass__ = type
7
__all__ = [
10104.4.2 by Abel Deuring
implemented reviewer's comments
8
    'BugAttachmentContentCheck',
11235.6.1 by Abel Deuring
navigation class added that allows to access Librarian files of bug attachments vir ProxiedLibraryFileAlias; some lint fixes.
9
    'BugAttachmentFileNavigation',
2628 by Canonical.com Patch Queue Manager
[trivial] converted a bunch of browser:traverse into navigation
10
    'BugAttachmentSetNavigation',
3854.1.2 by Bjorn Tillenius
make the bug attachment edit view into a LaunchpadFormView.
11
    'BugAttachmentEditView',
6887.5.14 by Gavin Panella
Use urldata to define the browser URL for a bug attachment.
12
    'BugAttachmentURL',
3854.1.2 by Bjorn Tillenius
make the bug attachment edit view into a LaunchpadFormView.
13
    ]
2238 by Canonical.com Patch Queue Manager
[r=jamesh] initial support for bug attachments. still some tweaks needed before the implementation fully matches the spec.
14
13931.2.1 by Steve Kowalik
Chip away at canonical.lazr a little more.
15
from lazr.restful.utils import smartquote
11538.4.1 by Abel Deuring
more efficent change of the content type of bug attachments
16
from zope.component import (
17
    getMultiAdapter,
18
    getUtility,
19
    )
10104.4.1 by Abel Deuring
Fix for bug 172501: reject non-code patch attachements
20
from zope.contenttype import guess_content_type
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
21
from zope.interface import implements
2238 by Canonical.com Patch Queue Manager
[r=jamesh] initial support for bug attachments. still some tweaks needed before the implementation fully matches the spec.
22
11929.9.1 by Tim Penhey
Move launchpadform into lp.app.browser.
23
from lp.app.browser.launchpadform import (
24
    action,
14642.1.3 by Curtis Hovey
Removed custom_widget from webapp glob.
25
    custom_widget,
11929.9.1 by Tim Penhey
Move launchpadform into lp.app.browser.
26
    LaunchpadFormView,
27
    )
12293.1.10 by Curtis Hovey
Formatted imports.
28
from lp.app.widgets.itemswidgets import LaunchpadBooleanRadioWidget
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
29
from lp.bugs.interfaces.bugattachment import (
30
    BugAttachmentType,
31
    IBugAttachment,
32
    IBugAttachmentEditForm,
33
    IBugAttachmentIsPatchConfirmationForm,
34
    IBugAttachmentSet,
35
    )
14578.2.1 by William Grant
Move librarian stuff from canonical.launchpad to lp.services.librarian. canonical.librarian remains untouched.
36
from lp.services.librarian.browser import (
37
    FileNavigationMixin,
38
    ProxiedLibraryFileAlias,
39
    )
40
from lp.services.librarian.interfaces import ILibraryFileAliasWithParent
14612.2.1 by William Grant
format-imports on lib/. So many imports.
41
from lp.services.webapp import (
42
    canonical_url,
43
    GetitemNavigation,
44
    Navigation,
45
    )
46
from lp.services.webapp.interfaces import (
47
    ICanonicalUrlData,
48
    ILaunchBag,
49
    )
50
from lp.services.webapp.menu import structured
11235.6.1 by Abel Deuring
navigation class added that allows to access Librarian files of bug attachments vir ProxiedLibraryFileAlias; some lint fixes.
51
2628 by Canonical.com Patch Queue Manager
[trivial] converted a bunch of browser:traverse into navigation
52
10104.4.2 by Abel Deuring
implemented reviewer's comments
53
class BugAttachmentContentCheck:
54
    """A mixin class that checks the consistency of patch flag and file type.
55
    """
56
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
57
    def guessContentType(self, filename, file_content):
10542.13.3 by Karl Fogel
Start on fix for bug #538219 ("debdiff does not look like a patch"):
58
        """Guess the content type a file with the given name and content."""
10104.4.2 by Abel Deuring
implemented reviewer's comments
59
        guessed_type, encoding = guess_content_type(
60
            name=filename, body=file_content)
10542.13.3 by Karl Fogel
Start on fix for bug #538219 ("debdiff does not look like a patch"):
61
        # Zope's guess_content_type() doesn't consider all the factors
62
        # we want considered.  So after we get its answer, we probe a
10542.13.5 by Karl Fogel
Finish bug #538219 ("debdiff does not look like a patch"):
63
        # little further.  But we still don't look at the encoding nor
64
        # the file content, because we'd like to avoid reimplementing
65
        # 'patch'.  See bug #538219 for more.
10542.13.3 by Karl Fogel
Start on fix for bug #538219 ("debdiff does not look like a patch"):
66
        if (guessed_type == 'text/plain'
10542.13.5 by Karl Fogel
Finish bug #538219 ("debdiff does not look like a patch"):
67
            and (filename.endswith('.diff')
68
                 or filename.endswith('.debdiff')
69
                 or filename.endswith('.patch'))):
10542.13.3 by Karl Fogel
Start on fix for bug #538219 ("debdiff does not look like a patch"):
70
            guessed_type = 'text/x-diff'
10104.4.2 by Abel Deuring
implemented reviewer's comments
71
        return guessed_type
72
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
73
    def attachmentTypeConsistentWithContentType(
10104.4.2 by Abel Deuring
implemented reviewer's comments
74
        self, patch_flag_set, filename, file_content):
75
        """Return True iff patch_flag is consistent with filename and content.
76
        """
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
77
        guessed_type = self.guessContentType(filename, file_content)
10104.4.2 by Abel Deuring
implemented reviewer's comments
78
        # An XOR of "is the patch flag selected?" with "is the
79
        # guessed type not a diff?" tells us if the type selected
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
80
        # by the user matches the guessed type.
10104.4.2 by Abel Deuring
implemented reviewer's comments
81
        return (patch_flag_set ^ (guessed_type != 'text/x-diff'))
82
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
83
    def nextUrlForInconsistentPatchFlags(self, attachment):
10104.4.2 by Abel Deuring
implemented reviewer's comments
84
        """The next_url value used for an inconistent patch flag."""
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
85
        return canonical_url(attachment) + '/+confirm-is-patch'
10104.4.2 by Abel Deuring
implemented reviewer's comments
86
87
2630 by Canonical.com Patch Queue Manager
[trivial] lots of tidying up. converting all database classes to use NotFoundError consistently, and to import it from launchpad.interfaces in preparation for the move to a new zope3. Also, introduced a NameNotAvailable error. removed browser:traverse rdirective. commented out shipit test that fails sometimes.
88
class BugAttachmentSetNavigation(GetitemNavigation):
2628 by Canonical.com Patch Queue Manager
[trivial] converted a bunch of browser:traverse into navigation
89
90
    usedfor = IBugAttachmentSet
91
92
12736.8.4 by William Grant
Add warning comment to BugAttachmentURL. It's not very canonical.
93
# Despite declaring compliance with ICanonicalUrlData, the LaunchBag
94
# dependency means this tends towards the "not canonical at all" end of
95
# the canonicalness scale. Beware.
6887.5.14 by Gavin Panella
Use urldata to define the browser URL for a bug attachment.
96
class BugAttachmentURL:
97
    """Bug URL creation rules."""
98
    implements(ICanonicalUrlData)
99
100
    rootsite = 'bugs'
101
102
    def __init__(self, context):
103
        self.context = context
104
105
    @property
106
    def inside(self):
6887.5.18 by Gavin Panella
Default to a bug context when no bugtask has been traversed.
107
        """Always relative to a traversed bugtask."""
6887.5.14 by Gavin Panella
Use urldata to define the browser URL for a bug attachment.
108
        bugtask = getUtility(ILaunchBag).bugtask
109
        if bugtask is None:
6887.5.18 by Gavin Panella
Default to a bug context when no bugtask has been traversed.
110
            return self.context.bug
6887.5.14 by Gavin Panella
Use urldata to define the browser URL for a bug attachment.
111
        else:
112
            return bugtask
113
114
    @property
115
    def path(self):
116
        """Return the path component of the URL."""
117
        return u"+attachment/%d" % self.context.id
118
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
119
10104.4.2 by Abel Deuring
implemented reviewer's comments
120
class BugAttachmentEditView(LaunchpadFormView, BugAttachmentContentCheck):
3854.1.4 by Bjorn Tillenius
add docstrings.
121
    """Edit a bug attachment."""
3854.1.2 by Bjorn Tillenius
make the bug attachment edit view into a LaunchpadFormView.
122
123
    schema = IBugAttachmentEditForm
124
    field_names = ['title', 'patch', 'contenttype']
125
126
    def __init__(self, context, request):
127
        LaunchpadFormView.__init__(self, context, request)
6887.5.18 by Gavin Panella
Default to a bug context when no bugtask has been traversed.
128
        self.next_url = self.cancel_url = (
129
            canonical_url(ICanonicalUrlData(context).inside))
3854.1.2 by Bjorn Tillenius
make the bug attachment edit view into a LaunchpadFormView.
130
131
    @property
132
    def initial_values(self):
133
        attachment = self.context
134
        return dict(
135
            title=attachment.title,
136
            patch=attachment.type == BugAttachmentType.PATCH,
137
            contenttype=attachment.libraryfile.mimetype)
138
139
    @action('Change', name='change')
140
    def change_action(self, action, data):
141
        if data['patch']:
142
            new_type = BugAttachmentType.PATCH
143
        else:
3854.1.8 by Bjorn Tillenius
review comments.
144
            new_type = BugAttachmentType.UNSPECIFIED
3854.1.2 by Bjorn Tillenius
make the bug attachment edit view into a LaunchpadFormView.
145
        if new_type != self.context.type:
10104.4.1 by Abel Deuring
Fix for bug 172501: reject non-code patch attachements
146
            filename = self.context.libraryfile.filename
147
            file_content = self.context.libraryfile.read()
148
            # We expect that users set data['patch'] to True only for
149
            # real patch data, indicated by guessed_content_type ==
150
            # 'text/x-diff'. If there are inconsistencies, we don't
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
151
            # set the value automatically. Instead, we lead the user to
152
            # another form where we ask him if he is sure about his
153
            # choice of the patch flag.
10104.4.1 by Abel Deuring
Fix for bug 172501: reject non-code patch attachements
154
            new_type_consistent_with_guessed_type = (
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
155
                self.attachmentTypeConsistentWithContentType(
10104.4.2 by Abel Deuring
implemented reviewer's comments
156
                    new_type == BugAttachmentType.PATCH, filename,
157
                    file_content))
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
158
            if new_type_consistent_with_guessed_type:
10104.4.1 by Abel Deuring
Fix for bug 172501: reject non-code patch attachements
159
                self.context.type = new_type
160
            else:
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
161
                self.next_url = self.nextUrlForInconsistentPatchFlags(
10104.4.2 by Abel Deuring
implemented reviewer's comments
162
                    self.context)
3854.1.2 by Bjorn Tillenius
make the bug attachment edit view into a LaunchpadFormView.
163
164
        if data['title'] != self.context.title:
165
            self.context.title = data['title']
166
167
        if self.context.libraryfile.mimetype != data['contenttype']:
11538.4.1 by Abel Deuring
more efficent change of the content type of bug attachments
168
            lfa_with_parent = getMultiAdapter(
169
                (self.context.libraryfile, self.context),
170
                ILibraryFileAliasWithParent)
171
            lfa_with_parent.mimetype = data['contenttype']
3854.1.2 by Bjorn Tillenius
make the bug attachment edit view into a LaunchpadFormView.
172
6036.2.1 by Abel Deuring
Fix for bugs 182282 and 182289
173
    @action('Delete Attachment', name='delete')
3854.1.3 by Bjorn Tillenius
add ui for deleting a bug attachment.
174
    def delete_action(self, action, data):
11235.8.4 by Abel Deuring
change the bug attachment download URL shown in a otification when an attachment is deleted.
175
        libraryfile_url = ProxiedLibraryFileAlias(
176
            self.context.libraryfile, self.context).http_url
5594.1.9 by Maris Fogels
Fixed all of the calls to addInfoNotification() that contain HTML so that they use the structured() class. Also fixed a number of broken uses of the _() translation function.
177
        self.request.response.addInfoNotification(structured(
7675.415.9 by Abel Deuring
implemented reviewer's comments
178
            'Attachment "<a href="%(url)s">%(name)s</a>" has been deleted.',
11235.8.4 by Abel Deuring
change the bug attachment download URL shown in a otification when an attachment is deleted.
179
            url=libraryfile_url, name=self.context.title))
7947.5.7 by Graham Binns
Merged changes from attachments branch.
180
        self.context.removeFromBug(user=self.user)
3854.1.3 by Bjorn Tillenius
add ui for deleting a bug attachment.
181
9270.1.2 by Abel Deuring
changed page title and form label
182
    @property
183
    def label(self):
14433.2.29 by Curtis Hovey
Make labels look like other page headings.
184
        return smartquote('Edit attachment "%s"') % self.context.title
9270.1.2 by Abel Deuring
changed page title and form label
185
14433.2.28 by Curtis Hovey
Removed the redundant 'bug #nnnnnn -' from page_titles and breadcrumbs.
186
    page_title = 'Edit attachment'
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
187
188
189
class BugAttachmentPatchConfirmationView(LaunchpadFormView):
190
    """Confirmation of the "patch" flag setting.
191
192
    If the user sets the "patch" flag to a value that is inconsistent
193
    with the result of a call of guess_content_type() for this
194
    attachment, we show this view to ask the user if he is sure
195
    about his selection.
196
    """
197
198
    schema = IBugAttachmentIsPatchConfirmationForm
199
200
    custom_widget('patch', LaunchpadBooleanRadioWidget)
201
202
    def __init__(self, context, request):
203
        LaunchpadFormView.__init__(self, context, request)
204
        self.next_url = self.cancel_url = (
205
            canonical_url(ICanonicalUrlData(context).inside))
206
207
    def initialize(self):
208
        super(BugAttachmentPatchConfirmationView, self).initialize()
209
        self.widgets['patch'].setRenderedValue(self.is_patch)
210
211
    @property
212
    def label(self):
14433.2.29 by Curtis Hovey
Make labels look like other page headings.
213
        return smartquote('Confirm attachment type of "%s"') % (
214
            self.context.title)
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
215
14433.2.28 by Curtis Hovey
Removed the redundant 'bug #nnnnnn -' from page_titles and breadcrumbs.
216
    page_title = 'Confirm attachment type'
10104.4.4 by Abel Deuring
new view +confirm-is-patch shown when a bugattachment is added or when the patch flag of an existing attachment is changed and when LP treats the patch flag setting by the user as possibly wrong.
217
218
    @action('Change', name='change')
219
    def change_action(self, action, data):
220
        current_patch_setting = self.context.type == BugAttachmentType.PATCH
221
        if data['patch'] != current_patch_setting:
222
            if data['patch']:
223
                self.context.type = BugAttachmentType.PATCH
224
                #xxxxxxxxxx adjust content type!
225
                # xxx use mixin, together with BugAttachmnetEditView
226
            else:
227
                self.context.type = BugAttachmentType.UNSPECIFIED
10104.4.1 by Abel Deuring
Fix for bug 172501: reject non-code patch attachements
228
229
    @property
230
    def is_patch(self):
231
        """True if this attachment contains a patch, else False."""
232
        return self.context.type == BugAttachmentType.PATCH
11235.6.1 by Abel Deuring
navigation class added that allows to access Librarian files of bug attachments vir ProxiedLibraryFileAlias; some lint fixes.
233
234
235
class BugAttachmentFileNavigation(Navigation, FileNavigationMixin):
236
    """Traversal to +files/${filename}."""
237
238
    usedfor = IBugAttachment