~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

__metaclass__ = type

__all__ = [
    'BranchPortletSubscribersContent',
    'BranchSubscriptionAddOtherView',
    'BranchSubscriptionAddView',
    'BranchSubscriptionEditOwnView',
    'BranchSubscriptionEditView',
    'BranchSubscriptionPrimaryContext',
    ]

from lazr.restful.utils import smartquote
from zope.interface import implements

from lp.app.browser.launchpadform import (
    action,
    LaunchpadEditFormView,
    LaunchpadFormView,
    )
from lp.code.enums import BranchSubscriptionNotificationLevel
from lp.code.interfaces.branchsubscription import IBranchSubscription
from lp.services.webapp import (
    canonical_url,
    LaunchpadView,
    )
from lp.services.webapp.authorization import (
    check_permission,
    precache_permission_for_objects,
    )
from lp.services.webapp.interfaces import IPrimaryContext
from lp.services.webapp.menu import structured


class BranchSubscriptionPrimaryContext:
    """The primary context is the subscription is that of the branch."""

    implements(IPrimaryContext)

    def __init__(self, branch_subscription):
        self.context = IPrimaryContext(branch_subscription.branch).context


class BranchPortletSubscribersContent(LaunchpadView):
    """View for the contents for the subscribers portlet.

    This view is strictly for use with ajax.
    """

    def subscriptions(self):
        """Return a decorated list of branch subscriptions."""

        # Cache permissions so private subscribers can be rendered.
        # The security adaptor will do the job also but we don't want or need
        # the expense of running several complex SQL queries.
        if self.user is not None:
            subscribers = [
                subscription.person
                for subscription in self.context.subscriptions]
            precache_permission_for_objects(
                self.request, "launchpad.LimitedView", subscribers)

        visible_subscriptions = [
            subscription for subscription in self.context.subscriptions
            if check_permission('launchpad.LimitedView', subscription.person)]
        return sorted(
            visible_subscriptions,
            key=lambda subscription: subscription.person.displayname)


class _BranchSubscriptionView(LaunchpadFormView):

    """Contains the common functionality of the Add and Edit views."""

    schema = IBranchSubscription
    field_names = ['notification_level', 'max_diff_lines', 'review_level']

    LEVELS_REQUIRING_LINES_SPECIFICATION = (
        BranchSubscriptionNotificationLevel.DIFFSONLY,
        BranchSubscriptionNotificationLevel.FULL)

    @property
    def user_is_subscribed(self):
        # Since it is technically possible to get to this page when
        # the user is not subscribed by hacking the URL, we should
        # handle the case nicely.
        return self.context.getSubscription(self.user) is not None

    @property
    def next_url(self):
        return canonical_url(self.context)

    cancel_url = next_url

    def add_notification_message(self, initial,
                                 notification_level, max_diff_lines,
                                 review_level):
        if notification_level in self.LEVELS_REQUIRING_LINES_SPECIFICATION:
            lines_message = '<li>%s</li>' % max_diff_lines.description
        else:
            lines_message = ''

        format_str = '%%s<ul><li>%%s</li>%s<li>%%s</li></ul>' % lines_message
        message = structured(format_str, initial,
                             notification_level.description,
                             review_level.description)
        self.request.response.addNotification(message)

    def optional_max_diff_lines(self, notification_level, max_diff_lines):
        if notification_level in self.LEVELS_REQUIRING_LINES_SPECIFICATION:
            return max_diff_lines
        else:
            return None


class BranchSubscriptionAddView(_BranchSubscriptionView):

    subscribing_self = True

    page_title = label = "Subscribe to branch"

    @action("Subscribe")
    def subscribe(self, action, data):
        # To catch the stale post problem, check that the user is not
        # subscribed before continuing.
        if self.context.hasSubscription(self.user):
            self.request.response.addNotification(
                'You are already subscribed to this branch.')
        else:
            notification_level = data['notification_level']
            max_diff_lines = self.optional_max_diff_lines(
                notification_level, data['max_diff_lines'])
            review_level = data['review_level']

            self.context.subscribe(
                self.user, notification_level, max_diff_lines, review_level,
                self.user)

            self.add_notification_message(
                'You have subscribed to this branch with: ',
                notification_level, max_diff_lines, review_level)


class BranchSubscriptionEditOwnView(_BranchSubscriptionView):

    @property
    def label(self):
        return "Edit subscription to branch"

    @property
    def page_title(self):
        return smartquote(
            'Edit subscription to branch "%s"' % self.context.displayname)

    @property
    def initial_values(self):
        subscription = self.context.getSubscription(self.user)
        if subscription is None:
            # This is the case of URL hacking or stale page.
            return {}
        else:
            return {'notification_level': subscription.notification_level,
                    'max_diff_lines': subscription.max_diff_lines,
                    'review_level': subscription.review_level}

    @action("Change")
    def change_details(self, action, data):
        # Be proactive in the checking to catch the stale post problem.
        if self.context.hasSubscription(self.user):
            subscription = self.context.getSubscription(self.user)
            subscription.notification_level = data['notification_level']
            subscription.max_diff_lines = self.optional_max_diff_lines(
                subscription.notification_level,
                data['max_diff_lines'])
            subscription.review_level = data['review_level']

            self.add_notification_message(
                'Subscription updated to: ',
                subscription.notification_level,
                subscription.max_diff_lines,
                subscription.review_level)
        else:
            self.request.response.addNotification(
                'You are not subscribed to this branch.')

    @action("Unsubscribe")
    def unsubscribe(self, action, data):
        # Be proactive in the checking to catch the stale post problem.
        if self.context.hasSubscription(self.user):
            self.context.unsubscribe(self.user, self.user)
            self.request.response.addNotification(
                "You have unsubscribed from this branch.")
        else:
            self.request.response.addNotification(
                'You are not subscribed to this branch.')


class BranchSubscriptionAddOtherView(_BranchSubscriptionView):
    """View used to subscribe someone other than the current user."""

    field_names = [
        'person', 'notification_level', 'max_diff_lines', 'review_level']
    for_input = True

    # Since we are subscribing other people, the current user
    # is never considered subscribed.
    user_is_subscribed = False
    subscribing_self = False

    page_title = label = "Subscribe to branch"

    @action("Subscribe", name="subscribe_action")
    def subscribe_action(self, action, data):
        """Subscribe the specified user to the branch.

        The user must be a member of a team in order to subscribe that team to
        the branch.  Launchpad Admins are special and they can subscribe any
        team.
        """
        notification_level = data['notification_level']
        max_diff_lines = self.optional_max_diff_lines(
            notification_level, data['max_diff_lines'])
        review_level = data['review_level']
        person = data['person']
        subscription = self.context.getSubscription(person)
        if subscription is None:
            self.context.subscribe(
                person, notification_level, max_diff_lines, review_level,
                self.user)

            self.add_notification_message(
                '%s has been subscribed to this branch with: '
                % person.displayname, notification_level, max_diff_lines,
                review_level)
        else:
            self.add_notification_message(
                '%s was already subscribed to this branch with: '
                % person.displayname,
                subscription.notification_level, subscription.max_diff_lines,
                review_level)


class BranchSubscriptionEditView(LaunchpadEditFormView):
    """The view for editing branch subscriptions.

    Used when traversed to the branch subscription itself rather than
    through the branch action item to edit the user's own subscription.
    This is the only current way to edit a team branch subscription.
    """
    schema = IBranchSubscription
    field_names = ['notification_level', 'max_diff_lines', 'review_level']

    @property
    def page_title(self):
        return smartquote(
            'Edit subscription to branch "%s"' % self.branch.displayname)

    @property
    def label(self):
        return "Edit subscription to branch for %s" % self.person.displayname

    def initialize(self):
        self.branch = self.context.branch
        self.person = self.context.person
        LaunchpadEditFormView.initialize(self)

    @action("Change", name="change")
    def change_action(self, action, data):
        """Update the branch subscription."""
        self.updateContextFromData(data)

    @action("Unsubscribe", name="unsubscribe")
    def unsubscribe_action(self, action, data):
        """Unsubscribe the team from the branch."""
        self.branch.unsubscribe(self.person, self.user)
        self.request.response.addNotification(
            "%s has been unsubscribed from this branch."
            % self.person.displayname)

    @property
    def next_url(self):
        return canonical_url(self.branch)

    cancel_url = next_url