~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.17 by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
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.
3
4
__metaclass__ = type
5
3847.2.118 by Mark Shuttleworth
Show team name as team membership page header
6
__all__ = [
9644.5.2 by Curtis Hovey
Added TeamMembershipBreadcrumb to provide breacrumbs for ~<team>/+member.
7
    'TeamMembershipBreadcrumb',
9436.2.1 by Barry Warsaw
Convert ~team/+invitations to UI 3.0.
8
    'TeamInvitationsView',
3847.2.118 by Mark Shuttleworth
Show team name as team membership page header
9
    'TeamMembershipEditView',
10
    ]
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
11
9436.2.1 by Barry Warsaw
Convert ~team/+invitations to UI 3.0.
12
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
13
from datetime import datetime
14
5534.2.6 by Mark Shuttleworth
De-flake
15
import pytz
5534.2.1 by Mark Shuttleworth
Initial use of datetime picker
16
from zope.app.form import CustomWidgetFactory
5534.2.9 by Mark Shuttleworth
Clean up use of datepicker for membership and sprint attendance.
17
from zope.app.form.interfaces import InputErrors
5534.2.1 by Mark Shuttleworth
Initial use of datetime picker
18
from zope.formlib import form
5534.2.2 by Mark Shuttleworth
Use date picker, and enable/disable it appropriately
19
from zope.schema import Date
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
20
14600.1.12 by Curtis Hovey
Move i18n to lp.
21
from lp import _
14612.2.1 by William Grant
format-imports on lib/. So many imports.
22
from lp.app.errors import UnexpectedFormData
23
from lp.app.widgets.date import DateWidget
24
from lp.registry.interfaces.teammembership import TeamMembershipStatus
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
25
from lp.services.webapp import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
26
    canonical_url,
27
    LaunchpadView,
28
    )
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
29
from lp.services.webapp.breadcrumb import Breadcrumb
5534.2.1 by Mark Shuttleworth
Initial use of datetime picker
30
3847.2.118 by Mark Shuttleworth
Show team name as team membership page header
31
9644.5.2 by Curtis Hovey
Added TeamMembershipBreadcrumb to provide breacrumbs for ~<team>/+member.
32
class TeamMembershipBreadcrumb(Breadcrumb):
33
    """Builds a breadcrumb for an `ITeamMembership`."""
9644.5.4 by Curtis Hovey
Quiet lint.
34
9644.5.2 by Curtis Hovey
Added TeamMembershipBreadcrumb to provide breacrumbs for ~<team>/+member.
35
    @property
36
    def text(self):
37
        return "%s's membership" % self.context.person.displayname
38
39
13303.11.10 by Aaron Bentley
Fix pages broken by assuming LaunchpadView.
40
class TeamMembershipEditView(LaunchpadView):
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
41
42
    def __init__(self, context, request):
13303.11.10 by Aaron Bentley
Fix pages broken by assuming LaunchpadView.
43
        super(TeamMembershipEditView, self).__init__(context, request)
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
44
        self.errormessage = ""
5534.2.1 by Mark Shuttleworth
Initial use of datetime picker
45
        self.prefix = 'membership'
46
        self.max_year = 2050
5534.2.2 by Mark Shuttleworth
Use date picker, and enable/disable it appropriately
47
        fields = form.Fields(Date(
5534.2.1 by Mark Shuttleworth
Initial use of datetime picker
48
            __name__='expirationdate', title=_('Expiration date')))
5534.2.3 by Mark Shuttleworth
Test fixes for new date picker on team membership
49
        expiration_field = fields['expirationdate']
50
        expiration_field.custom_widget = CustomWidgetFactory(DateWidget)
51
        expires = self.context.dateexpires
6767.8.5 by Maris Fogels
Removed the ITeamMembership StructuralHeaderPresentation.
52
        UTC = pytz.timezone('UTC')
5534.2.3 by Mark Shuttleworth
Test fixes for new date picker on team membership
53
        if self.isExpired():
54
            # For expired members, we will present the team's default
55
            # renewal date.
56
            expires = self.context.team.defaultrenewedexpirationdate
57
        if self.isDeactivated():
58
            # For members who were deactivated, we present by default
59
            # their original expiration date, or, if that has passed, or
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
60
            # never set, the team's default renewal date.
61
            if expires is None or expires < datetime.now(UTC):
5534.2.3 by Mark Shuttleworth
Test fixes for new date picker on team membership
62
                expires = self.context.team.defaultrenewedexpirationdate
63
        if expires is not None:
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
64
            # We get a datetime from the database, but we want to use a
5534.2.3 by Mark Shuttleworth
Test fixes for new date picker on team membership
65
            # datepicker so we must feed it a plain date without time.
66
            expires = expires.date()
67
        data = {'expirationdate': expires}
5534.2.1 by Mark Shuttleworth
Initial use of datetime picker
68
        self.widgets = form.setUpWidgets(
69
            fields, self.prefix, context, request, ignore_request=False,
70
            data=data)
5534.2.3 by Mark Shuttleworth
Test fixes for new date picker on team membership
71
        self.expiration_widget = self.widgets['expirationdate']
72
        # Set the acceptable date range for expiration.
73
        self.expiration_widget.from_date = datetime.now(UTC).date()
5534.2.8 by Mark Shuttleworth
Allow a date or datetime picker to be disabled initially.
74
        # Disable the date widget if there is no current or required
75
        # expiration
76
        if not expires:
77
            self.expiration_widget.disabled = True
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
78
9436.2.5 by Barry Warsaw
Convert ~team/+member/person to UI 3.0. This is imperfect because of the lack
79
    @property
80
    def label(self):
81
        # This reproduces the logic of the old H1's in the pre-3.0 UI view.
82
        if self.isActive():
83
            prefix = 'Active'
84
        elif self.isInactive():
85
            prefix = 'Inactive'
86
        elif self.isProposed():
87
            prefix = 'Proposed'
88
        elif self.isDeclined():
89
            prefix = 'Declined'
90
        elif self.isInvited() or self.isInvitationDeclined():
91
            prefix = 'Invited'
92
        else:
93
            raise AssertionError('status unknown')
9436.2.8 by Barry Warsaw
Reviewer comments.
94
        return '%s member %s' % (prefix, self.context.person.displayname)
9436.2.5 by Barry Warsaw
Convert ~team/+member/person to UI 3.0. This is imperfect because of the lack
95
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
96
    # Boolean helpers
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
97
    def isActive(self):
98
        return self.context.status in [TeamMembershipStatus.APPROVED,
99
                                       TeamMembershipStatus.ADMIN]
100
101
    def isInactive(self):
102
        return self.context.status in [TeamMembershipStatus.EXPIRED,
103
                                       TeamMembershipStatus.DEACTIVATED]
104
105
    def isAdmin(self):
106
        return self.context.status == TeamMembershipStatus.ADMIN
107
108
    def isProposed(self):
109
        return self.context.status == TeamMembershipStatus.PROPOSED
110
111
    def isDeclined(self):
112
        return self.context.status == TeamMembershipStatus.DECLINED
113
114
    def isExpired(self):
115
        return self.context.status == TeamMembershipStatus.EXPIRED
116
117
    def isDeactivated(self):
118
        return self.context.status == TeamMembershipStatus.DEACTIVATED
119
9436.2.5 by Barry Warsaw
Convert ~team/+member/person to UI 3.0. This is imperfect because of the lack
120
    def isInvited(self):
121
        return self.context.status == TeamMembershipStatus.INVITED
122
123
    def isInvitationDeclined(self):
124
        return self.context.status == TeamMembershipStatus.INVITATION_DECLINED
125
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
126
    def adminIsSelected(self):
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
127
        """Whether the admin radiobutton should be selected."""
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
128
        request_admin = self.request.get('admin')
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
129
        if request_admin == 'yes':
130
            return 'checked'
131
        if self.isAdmin():
132
            return 'checked'
7849.16.64 by Sidnei da Silva
- Fix a couple problems found by Francis during review
133
        return None
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
134
135
    def adminIsNotSelected(self):
136
        """Whether the not-admin radiobutton should be selected."""
137
        if self.adminIsSelected() != 'checked':
138
            return 'checked'
7849.16.64 by Sidnei da Silva
- Fix a couple problems found by Francis during review
139
        return None
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
140
141
    def expiresIsSelected(self):
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
142
        """Whether the expiration date radiobutton should be selected."""
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
143
        request_expires = self.request.get('expires')
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
144
        if request_expires == 'date':
145
            return 'checked'
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
146
        if self.isExpired():
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
147
            # Never checked when expired, because there's another
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
148
            # radiobutton in that situation.
7849.16.64 by Sidnei da Silva
- Fix a couple problems found by Francis during review
149
            return None
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
150
        if self.membershipExpires():
151
            return 'checked'
7849.16.64 by Sidnei da Silva
- Fix a couple problems found by Francis during review
152
        return None
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
153
154
    def neverExpiresIsSelected(self):
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
155
        """Whether the never-expires radiobutton should be selected."""
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
156
        request_expires = self.request.get('expires')
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
157
        if request_expires == 'never':
158
            return 'checked'
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
159
        if self.isExpired():
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
160
            # Never checked when expired, because there's another
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
161
            # radiobutton in that situation.
7849.16.64 by Sidnei da Silva
- Fix a couple problems found by Francis during review
162
            return None
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
163
        if not self.membershipExpires():
164
            return 'checked'
7849.16.64 by Sidnei da Silva
- Fix a couple problems found by Francis during review
165
        return None
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
166
167
    def canChangeExpirationDate(self):
168
        """Return True if the logged in user can change the expiration date of
3691.9.81 by Guilherme Salgado
Move the logic to prevent team admins from changing the expiration date of their own memberships from browser code to database code
169
        this membership.
4785.3.6 by Jeroen Vermeulen
Removed whitespace from ends of lines.
170
3691.9.81 by Guilherme Salgado
Move the logic to prevent team admins from changing the expiration date of their own memberships from browser code to database code
171
        Team administrators can't change the expiration date of their own
172
        membership.
173
        """
174
        return self.context.canChangeExpirationDate(self.user)
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
175
176
    def membershipExpires(self):
177
        """Return True if this membership is scheduled to expire one day."""
178
        if self.context.dateexpires is None:
179
            return False
180
        else:
181
            return True
182
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
183
    #
184
    # Form post handlers and helpers
185
    #
186
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
187
    def processForm(self):
1716.1.200 by Christian Reis
Deal with issues arising from Salgado's review and break pagetests intentionally for SteveA. Should be ready to roll once the pagetests are done
188
        if self.request.method != 'POST':
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
189
            return
190
191
        if self.request.form.get('editactive'):
192
            self.processActiveMember()
193
        elif self.request.form.get('editproposed'):
194
            self.processProposedMember()
195
        elif self.request.form.get('editinactive'):
196
            self.processInactiveMember()
197
198
    def processActiveMember(self):
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
199
        # This method checks the current status to ensure that we don't
200
        # crash because of users reposting a form.
3859.1.2 by Guilherme Salgado
Fix https://beta.launchpad.net/launchpad/+bug/88398 and do a bunch of other cleanups on teammembership code.
201
        form = self.request.form
202
        context = self.context
203
        if form.get('deactivate'):
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
204
            if self.context.status == TeamMembershipStatus.DEACTIVATED:
205
                # This branch and redirect is necessary because
206
                # TeamMembership.setStatus() does not allow us to set an
207
                # already-deactivated account to deactivated, causing
208
                # double form posts to crash there. We instead manually
209
                # ensure that the double-post is harmless.
3859.1.2 by Guilherme Salgado
Fix https://beta.launchpad.net/launchpad/+bug/88398 and do a bunch of other cleanups on teammembership code.
210
                self.request.response.redirect(
211
                    '%s/+members' % canonical_url(context.team))
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
212
                return
213
            new_status = TeamMembershipStatus.DEACTIVATED
3859.1.2 by Guilherme Salgado
Fix https://beta.launchpad.net/launchpad/+bug/88398 and do a bunch of other cleanups on teammembership code.
214
        elif form.get('change'):
3859.1.3 by Guilherme Salgado
Join some interdependent teammembership tests together and clean them up, making them a lot simpler and easier to maintain
215
            if (form.get('admin') == "no" and
3859.1.2 by Guilherme Salgado
Fix https://beta.launchpad.net/launchpad/+bug/88398 and do a bunch of other cleanups on teammembership code.
216
                context.status == TeamMembershipStatus.ADMIN):
217
                new_status = TeamMembershipStatus.APPROVED
3859.1.3 by Guilherme Salgado
Join some interdependent teammembership tests together and clean them up, making them a lot simpler and easier to maintain
218
            elif (form.get('admin') == "yes" and
14443.3.2 by William Grant
Let team admins promote others to admins.
219
                  context.status == TeamMembershipStatus.APPROVED):
3859.1.2 by Guilherme Salgado
Fix https://beta.launchpad.net/launchpad/+bug/88398 and do a bunch of other cleanups on teammembership code.
220
                new_status = TeamMembershipStatus.ADMIN
221
            else:
222
                # No status change will happen
223
                new_status = self.context.status
1716.1.200 by Christian Reis
Deal with issues arising from Salgado's review and break pagetests intentionally for SteveA. Should be ready to roll once the pagetests are done
224
        else:
3859.1.2 by Guilherme Salgado
Fix https://beta.launchpad.net/launchpad/+bug/88398 and do a bunch of other cleanups on teammembership code.
225
            raise UnexpectedFormData(
226
                "None of the expected actions were found.")
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
227
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
228
        if self._setMembershipData(new_status):
3859.1.2 by Guilherme Salgado
Fix https://beta.launchpad.net/launchpad/+bug/88398 and do a bunch of other cleanups on teammembership code.
229
            self.request.response.redirect(
230
                '%s/+members' % canonical_url(context.team))
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
231
232
    def processProposedMember(self):
3361.1.1 by Diogo Matsubara
Fix https://launchpad.net/products/launchpad/+bug/34202 (Approving a proposed team member twice will cause an OOPS)
233
        if self.context.status != TeamMembershipStatus.PROPOSED:
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
234
            # Catch a double-form-post.
3361.1.2 by Diogo Matsubara
reviewer's comments
235
            self.errormessage = _(
4785.3.6 by Jeroen Vermeulen
Removed whitespace from ends of lines.
236
                'The membership request for %s has already been processed.' %
3361.1.2 by Diogo Matsubara
reviewer's comments
237
                    self.context.person.displayname)
3361.1.1 by Diogo Matsubara
Fix https://launchpad.net/products/launchpad/+bug/34202 (Approving a proposed team member twice will cause an OOPS)
238
            return
239
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
240
        assert self.context.status == TeamMembershipStatus.PROPOSED
241
3859.1.2 by Guilherme Salgado
Fix https://beta.launchpad.net/launchpad/+bug/88398 and do a bunch of other cleanups on teammembership code.
242
        if self.request.form.get('decline'):
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
243
            status = TeamMembershipStatus.DECLINED
3859.1.2 by Guilherme Salgado
Fix https://beta.launchpad.net/launchpad/+bug/88398 and do a bunch of other cleanups on teammembership code.
244
        elif self.request.form.get('approve'):
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
245
            status = TeamMembershipStatus.APPROVED
3859.1.2 by Guilherme Salgado
Fix https://beta.launchpad.net/launchpad/+bug/88398 and do a bunch of other cleanups on teammembership code.
246
        else:
247
            raise UnexpectedFormData(
248
                "None of the expected actions were found.")
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
249
        if self._setMembershipData(status):
2908.3.2 by Guilherme Salgado
Some fixes Bjorn suggested
250
            self.request.response.redirect(
2963.1.1 by Dafydd Harries
fix redirects for approving new team members and renewing team subscriptions
251
                '%s/+members' % canonical_url(self.context.team))
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
252
253
    def processInactiveMember(self):
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
254
        if self.context.status not in (TeamMembershipStatus.EXPIRED,
255
                                       TeamMembershipStatus.DEACTIVATED):
256
            # Catch a double-form-post.
257
            self.errormessage = _(
4785.3.6 by Jeroen Vermeulen
Removed whitespace from ends of lines.
258
                'The membership request for %s has already been processed.' %
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
259
                    self.context.person.displayname)
260
            return
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
261
262
        if self._setMembershipData(TeamMembershipStatus.APPROVED):
2908.3.2 by Guilherme Salgado
Some fixes Bjorn suggested
263
            self.request.response.redirect(
2963.1.1 by Dafydd Harries
fix redirects for approving new team members and renewing team subscriptions
264
                '%s/+members' % canonical_url(self.context.team))
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
265
266
    def _setMembershipData(self, status):
267
        """Set all data specified on the form, for this TeamMembership.
268
269
        Get all data from the form, together with the given status and set
270
        them for this TeamMembership object.
1716.1.200 by Christian Reis
Deal with issues arising from Salgado's review and break pagetests intentionally for SteveA. Should be ready to roll once the pagetests are done
271
272
        Returns True if we successfully set the data, False otherwise.
273
        Callsites should not commit the transaction if we return False.
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
274
        """
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
275
        if self.canChangeExpirationDate():
276
            if self.request.form.get('expires') == 'never':
277
                expires = None
278
            else:
279
                try:
280
                    expires = self._getExpirationDate()
281
                except ValueError, err:
282
                    self.errormessage = (
5534.2.4 by Mark Shuttleworth
Ensure date ranges are enforced by the DateWidget and use that for membership
283
                        'Invalid expiration: %s' % err)
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
284
                    return False
285
        else:
286
            expires = self.context.dateexpires
287
10054.14.13 by Jonathan Davies
browser/teammembership.py: Ensure that silent is never None.
288
        silent = self.request.form.get('silent', False)
289
3691.9.81 by Guilherme Salgado
Move the logic to prevent team admins from changing the expiration date of their own memberships from browser code to database code
290
        self.context.setExpirationDate(expires, self.user)
291
        self.context.setStatus(
10054.14.1 by Jonathan Davies
Added an option to a person's team membership so that changes to their
292
            status, self.user, self.request.form_ng.getOne('comment'),
10054.14.13 by Jonathan Davies
browser/teammembership.py: Ensure that silent is never None.
293
            silent)
1716.1.197 by Christian Reis
Seriously whack the team membership edit view, improving the way validation is done, preserving form values when submitting, and reducing the wordiness everywhere. De-indent some main page silliness. Simplify the template by using macros. Add a test that checks the form's behaviour
294
        return True
295
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
296
    def _getExpirationDate(self):
297
        """Return a datetime with the expiration date selected on the form.
298
299
        Raises ValueError if the date selected is invalid. The use of
300
        that exception is unusual but allows us to present a consistent
301
        API to the caller, who needs to check only for that specific
302
        exception.
303
        """
5534.2.9 by Mark Shuttleworth
Clean up use of datepicker for membership and sprint attendance.
304
        expires = None
5534.2.4 by Mark Shuttleworth
Ensure date ranges are enforced by the DateWidget and use that for membership
305
        try:
306
            expires = self.expiration_widget.getInputValue()
5534.2.9 by Mark Shuttleworth
Clean up use of datepicker for membership and sprint attendance.
307
        except InputErrors, value:
5534.2.14 by Mark Shuttleworth
Address review comments for [r=barry]
308
            # Handle conversion errors. We have to do this explicitly here
309
            # because we are not using the full form machinery which would
310
            # put the relevant error message into the field error. We are
311
            # mixing the zope3 widget stuff with a hand-crafted form
312
            # processor, so we need to trap this manually.
5534.2.9 by Mark Shuttleworth
Clean up use of datepicker for membership and sprint attendance.
313
            raise ValueError(value.doc())
5534.2.4 by Mark Shuttleworth
Ensure date ranges are enforced by the DateWidget and use that for membership
314
        if expires is None:
315
            return None
316
5534.2.2 by Mark Shuttleworth
Use date picker, and enable/disable it appropriately
317
        # We used a date picker, so we have a date. What we want is a
318
        # datetime in UTC
5534.2.4 by Mark Shuttleworth
Ensure date ranges are enforced by the DateWidget and use that for membership
319
        UTC = pytz.timezone('UTC')
5534.2.9 by Mark Shuttleworth
Clean up use of datepicker for membership and sprint attendance.
320
        expires = datetime(expires.year, expires.month, expires.day,
321
                           tzinfo=UTC)
322
        return expires
3691.200.1 by kiko
Fix for bug 30649: Shouldn't be possible to set an expiry date prior to today when editting a team membership. As a consequence fixes bug 57300:
323
9436.2.1 by Barry Warsaw
Convert ~team/+invitations to UI 3.0.
324
325
class TeamInvitationsView(LaunchpadView):
326
    """View for ~team/+invitations."""
327
328
    @property
329
    def label(self):
9436.2.8 by Barry Warsaw
Reviewer comments.
330
        return 'Invitations for ' + self.context.displayname