Merge lp:~bac/launchpad/bug-421983 into lp:launchpad
- bug-421983
- Merge into devel
Proposed by
Brad Crittenden
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Paul Hummer | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~bac/launchpad/bug-421983 | ||||
Merge into: | lp:launchpad | ||||
Diff against target: | None lines | ||||
To merge this branch: | bzr merge lp:~bac/launchpad/bug-421983 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Barry Warsaw (community) | ui | Approve | |
Paul Hummer (community) | ui | Approve | |
Celso Providelo (community) | code | Approve | |
Review via email: mp+11146@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote : | # |
Revision history for this message
Celso Providelo (cprov) wrote : | # |
Hi Bac,
The changes look very good, you can land it as long as you fix the lint issues.
You've done a fantastic job updating the views to the 21st century!
Thank you.
review:
Approve
(code)
Revision history for this message
Paul Hummer (rockstar) : | # |
review:
Approve
(ui)
Revision history for this message
Barry Warsaw (barry) wrote : | # |
Brad, the pages look good. I was not able to view the +expiringmembership page unfortunately, but the leave and join pages look fine, with one caveat. The headers and title are not correct, but there's nothing (good) you can do about that right now. After my branch for bug 417089 lands, you'll need to update the templates and views to conform them to the 3.0 UI standard. It should be an easy upgrade though, and a lot of pages will need updating, so I'll mark this ui=me under that condition.
review:
Approve
(ui)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/canonical/launchpad/pagetitles.py' |
2 | --- lib/canonical/launchpad/pagetitles.py 2009-09-02 12:37:58 +0000 |
3 | +++ lib/canonical/launchpad/pagetitles.py 2009-09-03 17:20:42 +0000 |
4 | @@ -1166,10 +1166,6 @@ |
5 | |
6 | team_invitations = ContextBrowsername("Invitations sent to %s") |
7 | |
8 | -team_join = ContextBrowsername('Join %s') |
9 | - |
10 | -team_leave = ContextBrowsername('Leave %s') |
11 | - |
12 | team_mailinglist = 'Configure mailing list' |
13 | |
14 | team_mailinglist_moderate = 'Moderate mailing list' |
15 | @@ -1193,11 +1189,6 @@ |
16 | return "Make %s a member of %s" % ( |
17 | context.person.displayname, context.team.displayname) |
18 | |
19 | -def teammembership_self_renewal(context, view): |
20 | - """Return the page title renew membership in a team.""" |
21 | - return "Renew membership of %s in %s" % ( |
22 | - context.person.displayname, context.team.displayname) |
23 | - |
24 | team_mentoringoffers = ContextTitle('Mentoring available for newcomers to %s') |
25 | |
26 | team_newpoll = ContextTitle('New poll for team %s') |
27 | |
28 | === modified file 'lib/lp/registry/browser/person.py' |
29 | --- lib/lp/registry/browser/person.py 2009-09-01 21:49:24 +0000 |
30 | +++ lib/lp/registry/browser/person.py 2009-09-03 17:20:42 +0000 |
31 | @@ -8,7 +8,6 @@ |
32 | __metaclass__ = type |
33 | |
34 | |
35 | - |
36 | __all__ = [ |
37 | 'BeginTeamClaimView', |
38 | 'BugSubscriberPackageBugsSearchListingView', |
39 | @@ -481,30 +480,37 @@ |
40 | template = ViewPageTemplateFile( |
41 | '../templates/teammembership-self-renewal.pt') |
42 | |
43 | + @property |
44 | + def page_title(self): |
45 | + return "Renew membership of %s in %s" % ( |
46 | + self.context.person.displayname, self.context.team.displayname) |
47 | + |
48 | def __init__(self, context, request): |
49 | # Only the member himself or admins of the member (in case it's a |
50 | # team) can see the page in which they renew memberships that are |
51 | # about to expire. |
52 | if not check_permission('launchpad.Edit', context.person): |
53 | raise Unauthorized( |
54 | - "Only the member himself can renew his memberships.") |
55 | + "You may not renew the membership for %s." % |
56 | + context.person.displayname) |
57 | LaunchpadFormView.__init__(self, context, request) |
58 | |
59 | def browserDefault(self, request): |
60 | return self, () |
61 | |
62 | - def getReasonForDeniedRenewal(self): |
63 | + @property |
64 | + def get_reason_for_denied_renewal(self): |
65 | """Return text describing why the membership can't be renewed.""" |
66 | context = self.context |
67 | ondemand = TeamMembershipRenewalPolicy.ONDEMAND |
68 | admin = TeamMembershipStatus.ADMIN |
69 | approved = TeamMembershipStatus.APPROVED |
70 | - date_limit = datetime.now(pytz.timezone('UTC')) - timedelta( |
71 | + date_limit = datetime.now(pytz.UTZ) - timedelta( |
72 | days=DAYS_BEFORE_EXPIRATION_WARNING_IS_SENT) |
73 | if context.status not in (admin, approved): |
74 | text = "it is not active." |
75 | elif context.team.renewal_policy != ondemand: |
76 | - text = ('<a href="%s">%s</a> is not a team which accepts its ' |
77 | + text = ('<a href="%s">%s</a> is not a team which allows its ' |
78 | 'members to renew their own memberships.' |
79 | % (canonical_url(context.team), |
80 | context.team.unique_displayname)) |
81 | @@ -531,6 +537,10 @@ |
82 | def next_url(self): |
83 | return canonical_url(self.context.person) |
84 | |
85 | + @property |
86 | + def cancel_url(self): |
87 | + return canonical_url(self.context) |
88 | + |
89 | @action(_("Renew"), name="renew") |
90 | def renew_action(self, action, data): |
91 | member = self.context.person |
92 | @@ -539,11 +549,6 @@ |
93 | _("Membership renewed until ${date}.", mapping=dict( |
94 | date=self.context.dateexpires.strftime('%Y-%m-%d')))) |
95 | |
96 | - @action(_("Let it Expire"), name="nothing") |
97 | - def do_nothing_action(self, action, data): |
98 | - # Redirect back and wait for the membership to expire automatically. |
99 | - pass |
100 | - |
101 | |
102 | class ITeamMembershipInvitationAcknowledgementForm(Interface): |
103 | """Schema for the form in which team admins acknowledge invitations. |
104 | @@ -896,7 +901,6 @@ |
105 | return Link(target, text) |
106 | |
107 | |
108 | - |
109 | class PersonOverviewMenu(ApplicationMenu, CommonMenuLinks): |
110 | |
111 | usedfor = IPerson |
112 | @@ -2508,7 +2512,67 @@ |
113 | rootsite='answers') |
114 | |
115 | |
116 | -class PersonView(LaunchpadView, FeedsMixin): |
117 | +class TeamJoinMixin: |
118 | + """Mixin class for views related to joining teams.""" |
119 | + |
120 | + @property |
121 | + def user_can_subscribe_to_list(self): |
122 | + """Can the user subscribe to this team's mailing list? |
123 | + |
124 | + A user can subscribe to the list if the team has an active |
125 | + mailing list, and if they do not already have a subscription. |
126 | + """ |
127 | + if self.team_has_mailing_list: |
128 | + # If we are already subscribed, then we can not subscribe again. |
129 | + return not self.user_is_subscribed_to_list |
130 | + else: |
131 | + return False |
132 | + |
133 | + @property |
134 | + def user_is_subscribed_to_list(self): |
135 | + """Is the user subscribed to the team's mailing list? |
136 | + |
137 | + Subscriptions hang around even if the list is deactivated, etc. |
138 | + |
139 | + It is an error to ask if the user is subscribed to a mailing list |
140 | + that doesn't exist. |
141 | + """ |
142 | + if self.user is None: |
143 | + return False |
144 | + |
145 | + mailing_list = self.context.mailing_list |
146 | + assert mailing_list is not None, "This team has no mailing list." |
147 | + has_subscription = bool(mailing_list.getSubscription(self.user)) |
148 | + return has_subscription |
149 | + |
150 | + @property |
151 | + def team_has_mailing_list(self): |
152 | + """Is the team mailing list available for subscription?""" |
153 | + mailing_list = self.context.mailing_list |
154 | + return mailing_list is not None and mailing_list.is_usable |
155 | + |
156 | + @property |
157 | + def user_is_active_member(self): |
158 | + """Return True if the user is an active member of this team.""" |
159 | + return userIsActiveTeamMember(self.context) |
160 | + |
161 | + @property |
162 | + def user_is_proposed_member(self): |
163 | + """Return True if the user is a proposed member of this team.""" |
164 | + if self.user is None: |
165 | + return False |
166 | + return self.user in self.context.proposedmembers |
167 | + |
168 | + @property |
169 | + def user_can_request_to_leave(self): |
170 | + """Return true if the user can request to leave this team. |
171 | + |
172 | + A given user can leave a team only if he's an active member. |
173 | + """ |
174 | + return self.user_is_active_member |
175 | + |
176 | + |
177 | +class PersonView(LaunchpadView, FeedsMixin, TeamJoinMixin): |
178 | """A View class used in almost all Person's pages.""" |
179 | |
180 | @cachedproperty |
181 | @@ -2619,42 +2683,6 @@ |
182 | raise AssertionError('Unknown subscription policy.') |
183 | return description |
184 | |
185 | - @property |
186 | - def user_can_subscribe_to_list(self): |
187 | - """Can the user subscribe to this team's mailing list? |
188 | - |
189 | - A user can subscribe to the list if the team has an active |
190 | - mailing list, and if they do not already have a subscription. |
191 | - """ |
192 | - if self.team_has_mailing_list: |
193 | - # If we are already subscribed, then we can not subscribe again. |
194 | - return not self.user_is_subscribed_to_list |
195 | - else: |
196 | - return False |
197 | - |
198 | - @property |
199 | - def user_is_subscribed_to_list(self): |
200 | - """Is the user subscribed to the team's mailing list? |
201 | - |
202 | - Subscriptions hang around even if the list is deactivated, etc. |
203 | - |
204 | - It is an error to ask if the user is subscribed to a mailing list |
205 | - that doesn't exist. |
206 | - """ |
207 | - if self.user is None: |
208 | - return False |
209 | - |
210 | - mailing_list = self.context.mailing_list |
211 | - assert mailing_list is not None, "This team has no mailing list." |
212 | - has_subscription = bool(mailing_list.getSubscription(self.user)) |
213 | - return has_subscription |
214 | - |
215 | - @property |
216 | - def team_has_mailing_list(self): |
217 | - """Is the team mailing list available for subscription?""" |
218 | - mailing_list = self.context.mailing_list |
219 | - return mailing_list is not None and mailing_list.is_usable |
220 | - |
221 | def getURLToAssignedBugsInProgress(self): |
222 | """Return an URL to a page which lists all bugs assigned to this |
223 | person that are In Progress. |
224 | @@ -2789,23 +2817,6 @@ |
225 | return False |
226 | return self.user.inTeam(self.context) |
227 | |
228 | - def userIsActiveMember(self): |
229 | - """Return True if the user is an active member of this team.""" |
230 | - return userIsActiveTeamMember(self.context) |
231 | - |
232 | - def userIsProposedMember(self): |
233 | - """Return True if the user is a proposed member of this team.""" |
234 | - if self.user is None: |
235 | - return False |
236 | - return self.user in self.context.proposedmembers |
237 | - |
238 | - def userCanRequestToLeave(self): |
239 | - """Return true if the user can request to leave this team. |
240 | - |
241 | - A given user can leave a team only if he's an active member. |
242 | - """ |
243 | - return self.userIsActiveMember() |
244 | - |
245 | @cachedproperty |
246 | def email_address_visibility(self): |
247 | """The EmailAddressVisibleState of this person or team. |
248 | @@ -3779,12 +3790,38 @@ |
249 | schema = IPerson |
250 | |
251 | |
252 | -class TeamJoinView(PersonView): |
253 | - |
254 | - def initialize(self): |
255 | - super(TeamJoinView, self).initialize() |
256 | - if self.request.method == "POST": |
257 | - self.processForm() |
258 | +class TeamJoinForm(Interface): |
259 | + """Schema for team join.""" |
260 | + mailinglist_subscribe = Bool( |
261 | + title=_("Subscribe me to this team's mailing list"), |
262 | + required=True, default=True) |
263 | + |
264 | + |
265 | +class TeamJoinView(LaunchpadFormView, TeamJoinMixin): |
266 | + """A view class for joining a team.""" |
267 | + schema = TeamJoinForm |
268 | + |
269 | + def setUpWidgets(self): |
270 | + super(TeamJoinView, self).setUpWidgets() |
271 | + if 'mailinglist_subscribe' in self.field_names: |
272 | + widget = self.widgets['mailinglist_subscribe'] |
273 | + widget.setRenderedValue(self.user_wants_list_subscriptions) |
274 | + |
275 | + @property |
276 | + def page_title(self): |
277 | + return "Join %s" % self.context.displayname |
278 | + |
279 | + @property |
280 | + def field_names(self): |
281 | + """See `LaunchpadFormView`. |
282 | + |
283 | + If the user can subscribe to the mailing list then include the |
284 | + mailinglist subscription checkbox otherwise remove it. |
285 | + """ |
286 | + if self.user_can_subscribe_to_list: |
287 | + return ['mailinglist_subscribe'] |
288 | + else: |
289 | + return [] |
290 | |
291 | @property |
292 | def join_allowed(self): |
293 | @@ -3816,7 +3853,7 @@ |
294 | """ |
295 | if not self.join_allowed: |
296 | return False |
297 | - return not (self.userIsActiveMember() or self.userIsProposedMember()) |
298 | + return not (self.user_is_active_member or self.user_is_proposed_member) |
299 | |
300 | @property |
301 | def user_wants_list_subscriptions(self): |
302 | @@ -3833,46 +3870,42 @@ |
303 | policy = self.context.subscriptionpolicy |
304 | return policy == TeamSubscriptionPolicy.MODERATED |
305 | |
306 | - def processForm(self): |
307 | - request = self.request |
308 | - user = self.user |
309 | - context = self.context |
310 | + @property |
311 | + def next_url(self): |
312 | + return canonical_url(self.context) |
313 | + |
314 | + @property |
315 | + def cancel_url(self): |
316 | + return canonical_url(self.context) |
317 | + |
318 | + @action(_("Join"), name="join") |
319 | + def action_save(self, action, data): |
320 | response = self.request.response |
321 | |
322 | - notification = None |
323 | - if 'join' in request.form and self.user_can_request_to_join: |
324 | + if self.user_can_request_to_join: |
325 | # Shut off mailing list auto-subscription - we want direct |
326 | # control over it. |
327 | - user.join(context, may_subscribe_to_list=False) |
328 | + self.user.join(self.context, may_subscribe_to_list=False) |
329 | |
330 | if self.team_is_moderated: |
331 | response.addInfoNotification( |
332 | _('Your request to join ${team} is awaiting ' |
333 | 'approval.', |
334 | - mapping={'team': context.displayname})) |
335 | + mapping={'team': self.context.displayname})) |
336 | else: |
337 | response.addInfoNotification( |
338 | _('You have successfully joined ${team}.', |
339 | - mapping={'team': context.displayname})) |
340 | - |
341 | - if 'mailinglist_subscribe' in request.form: |
342 | - self._subscribeToList() |
343 | - |
344 | - elif 'join' in request.form: |
345 | + mapping={'team': self.context.displayname})) |
346 | + if data.get('mailinglist_subscribe', False): |
347 | + self._subscribeToList(response) |
348 | + |
349 | + else: |
350 | response.addErrorNotification( |
351 | _('You cannot join ${team}.', |
352 | - mapping={'team': context.displayname})) |
353 | - elif 'goback' in request.form: |
354 | - # User clicked on the 'Go back' button, so we'll simply redirect. |
355 | - pass |
356 | - else: |
357 | - raise UnexpectedFormData( |
358 | - "Couldn't find any of the expected actions.") |
359 | - self.request.response.redirect(canonical_url(context)) |
360 | + mapping={'team': self.context.displayname})) |
361 | |
362 | - def _subscribeToList(self): |
363 | + def _subscribeToList(self, response): |
364 | """Subscribe the user to the team's mailing list.""" |
365 | - response = self.request.response |
366 | |
367 | if self.user_can_subscribe_to_list: |
368 | # 'user_can_subscribe_to_list' should have dealt with |
369 | @@ -3979,18 +4012,26 @@ |
370 | self.request.response.addInfoNotification("%s %s" % (team_names, msg)) |
371 | |
372 | |
373 | -class TeamLeaveView(PersonView): |
374 | - |
375 | - def processForm(self): |
376 | - if self.request.method != "POST" or not self.userCanRequestToLeave(): |
377 | - # Nothing to do |
378 | - return |
379 | - |
380 | - if self.request.form.get('leave'): |
381 | +class TeamLeaveView(LaunchpadFormView, TeamJoinMixin): |
382 | + schema = Interface |
383 | + |
384 | + @property |
385 | + def page_title(self): |
386 | + return "Leave %s" % self.context.displayname |
387 | + |
388 | + @property |
389 | + def cancel_url(self): |
390 | + return canonical_url(self.context) |
391 | + |
392 | + next_url = cancel_url |
393 | + |
394 | + @action(_("Leave"), name="leave") |
395 | + def action_save(self, action, data): |
396 | + response = self.request.response |
397 | + |
398 | + if self.user_can_request_to_leave: |
399 | self.user.leave(self.context) |
400 | |
401 | - self.request.response.redirect('./') |
402 | - |
403 | |
404 | class PersonEditEmailsView(LaunchpadFormView): |
405 | """A view for editing a person's email settings. |
406 | |
407 | === modified file 'lib/lp/registry/browser/tests/team-join-views.txt' |
408 | --- lib/lp/registry/browser/tests/team-join-views.txt 2009-04-17 10:32:16 +0000 |
409 | +++ lib/lp/registry/browser/tests/team-join-views.txt 2009-09-03 17:20:42 +0000 |
410 | @@ -11,7 +11,7 @@ |
411 | >>> from zope.component import getMultiAdapter |
412 | >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
413 | >>> def join_team(team): |
414 | - ... form = {'join': '1', 'mailinglist_subscribe': '1'} |
415 | + ... form = {'field.actions.join': '1', 'field.mailinglist_subscribe': u'on'} |
416 | ... request = LaunchpadTestRequest(method='POST', form=form) |
417 | ... view = getMultiAdapter((team, request), name='+join') |
418 | ... view.initialize() |
419 | @@ -32,9 +32,9 @@ |
420 | >>> from canonical.launchpad.interfaces import IMailingListSet |
421 | >>> subscribers = getUtility(IMailingListSet) |
422 | |
423 | -Posting a mailing list subscription request to a team that has no list |
424 | -results in an error notification. (This is probably the result of |
425 | -mangled form data.) |
426 | +Attempting to post a mailing list subscription request to a team that has no list |
427 | +is thwarted by the view because it strips the checkbox from the form. |
428 | +Therefore no error message is seen. |
429 | |
430 | >>> sample_person = personset.getByName('name12') |
431 | >>> login_person(sample_person) |
432 | @@ -43,7 +43,6 @@ |
433 | |
434 | >>> join_team(no_list_team) |
435 | You have successfully joined open-team-no-list. |
436 | - Mailing list subscription failed. |
437 | |
438 | Someone subscribing to a moderated team's list will be shown an |
439 | informative message regarding the delayed subscription. |
440 | |
441 | === modified file 'lib/lp/registry/stories/mailinglists/subscriptions.txt' |
442 | --- lib/lp/registry/stories/mailinglists/subscriptions.txt 2009-08-29 03:14:48 +0000 |
443 | +++ lib/lp/registry/stories/mailinglists/subscriptions.txt 2009-09-03 17:20:42 +0000 |
444 | @@ -228,8 +228,8 @@ |
445 | >>> browser.url |
446 | 'http://launchpad.dev/~rosetta-admins/+join' |
447 | |
448 | - >>> browser.getControl(name='mailinglist_subscribe').value = ['checked'] |
449 | - >>> browser.getControl(name='join').click() |
450 | + >>> browser.getControl(name='field.mailinglist_subscribe').value = ['checked'] |
451 | + >>> browser.getControl(name='field.actions.join').click() |
452 | >>> browser.url |
453 | 'http://launchpad.dev/~rosetta-admins' |
454 | |
455 | @@ -284,7 +284,7 @@ |
456 | list is not presented. |
457 | |
458 | >>> browser.open('http://launchpad.dev/~rosetta-admins/+leave') |
459 | - >>> browser.getControl(name='leave').click() |
460 | + >>> browser.getControl(name='field.actions.leave').click() |
461 | |
462 | >>> browser.open('http://launchpad.dev/~rosetta-admins') |
463 | >>> browser.getLink('Join the team').click() |
464 | @@ -561,8 +561,8 @@ |
465 | >>> browser.url |
466 | 'http://launchpad.dev/~rosetta-admins/+join' |
467 | |
468 | - >>> print browser.getControl(name='mailinglist_subscribe').value |
469 | - ['subscribe'] |
470 | + >>> print browser.getControl(name='field.mailinglist_subscribe').value |
471 | + True |
472 | |
473 | # Change James' setting |
474 | >>> browser.open('http://launchpad.dev/~jblack') |
475 | @@ -575,8 +575,8 @@ |
476 | |
477 | >>> browser.open('http://launchpad.dev/~rosetta-admins') |
478 | >>> browser.getLink('Join the team').click() |
479 | - >>> print browser.getControl(name='mailinglist_subscribe').value |
480 | - ['subscribe'] |
481 | + >>> print browser.getControl(name='field.mailinglist_subscribe').value |
482 | + True |
483 | |
484 | Users who have chosen to never be auto-subscribed to mailing |
485 | lists will not have the box checked. |
486 | @@ -592,8 +592,8 @@ |
487 | |
488 | >>> browser.open('http://launchpad.dev/~rosetta-admins') |
489 | >>> browser.getLink('Join the team').click() |
490 | - >>> print browser.getControl(name='mailinglist_subscribe').value |
491 | - [] |
492 | + >>> print browser.getControl(name='field.mailinglist_subscribe').value |
493 | + False |
494 | |
495 | # Restore James' setting. |
496 | >>> browser.open('http://launchpad.dev/~jblack') |
497 | |
498 | === modified file 'lib/lp/registry/templates/team-join.pt' |
499 | --- lib/lp/registry/templates/team-join.pt 2009-07-17 17:59:07 +0000 |
500 | +++ lib/lp/registry/templates/team-join.pt 2009-09-03 17:20:42 +0000 |
501 | @@ -1,74 +1,47 @@ |
502 | -<tal:root |
503 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
504 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
505 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
506 | - omit-tag=""> |
507 | - |
508 | -<html |
509 | +<team-join |
510 | xmlns="http://www.w3.org/1999/xhtml" |
511 | xmlns:tal="http://xml.zope.org/namespaces/tal" |
512 | xmlns:metal="http://xml.zope.org/namespaces/metal" |
513 | xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
514 | - xml:lang="en" |
515 | - lang="en" |
516 | - dir="ltr" |
517 | - metal:use-macro="context/@@main_template/master" |
518 | - i18n:domain="launchpad" |
519 | -> |
520 | - |
521 | -<body> |
522 | + metal:use-macro="view/macro:page/main_only" |
523 | + i18n:domain="launchpad"> |
524 | |
525 | <div metal:fill-slot="main"> |
526 | |
527 | - <tal:block tal:condition="view/user_can_request_to_join"> |
528 | - <h1>Joining <span tal:replace="context/displayname" /></h1> |
529 | - |
530 | - <h2>Are you sure you want to join this team?</h2> |
531 | + <h1>Joining <span tal:replace="context/displayname" /></h1> |
532 | + |
533 | + <tal:can-join tal:condition="view/user_can_request_to_join"> |
534 | + |
535 | + <p>Are you sure you want to join this team?</p> |
536 | |
537 | <p tal:condition="view/team_is_moderated"> |
538 | Since this is a moderated team, one of its administrators will have to |
539 | approve your membership before you actually become a member. |
540 | </p> |
541 | |
542 | - <form class="actions" name="join" action="" method="post"> |
543 | - <p tal:condition="view/user_can_subscribe_to_list"> |
544 | - <input type="checkbox" name="mailinglist_subscribe" |
545 | - value="subscribe" |
546 | - tal:attributes="checked view/user_wants_list_subscriptions"/> |
547 | - Subscribe me to this team's mailing list |
548 | - </p> |
549 | - <input type="submit" name="goback" value="Cancel" /> |
550 | - <input type="submit" name="join" value="Join" /> |
551 | - </form> |
552 | - </tal:block> |
553 | + <div metal:use-macro="context/@@launchpad_form/form" /> |
554 | + </tal:can-join> |
555 | |
556 | - <tal:block tal:condition="not: view/user_can_request_to_join"> |
557 | - <p tal:condition="view/join_allowed" class="warning message"> |
558 | - <tal:active-member condition="view/userIsActiveMember"> |
559 | + <tal:cannot-join tal:condition="not: view/user_can_request_to_join"> |
560 | + <p tal:condition="view/join_allowed" class="informational message"> |
561 | + <tal:active-member condition="view/user_is_active_member"> |
562 | You are an active member of this team already. |
563 | </tal:active-member> |
564 | - <tal:proposed-member condition="view/userIsProposedMember"> |
565 | + <tal:proposed-member condition="view/user_is_proposed_member"> |
566 | Your membership is awaiting approval from one of this team's |
567 | administrators. |
568 | </tal:proposed-member> |
569 | </p> |
570 | |
571 | - <p tal:condition="not: view/join_allowed" class="warning message"> |
572 | + <p tal:condition="not: view/join_allowed" class="informational message"> |
573 | <span tal:replace="context/displayname" /> is a restricted team. |
574 | Only a team administrator can add new members. |
575 | </p> |
576 | |
577 | - <form name="join" action="" method="post"> |
578 | - <table width="100%"> |
579 | - <tr><td> |
580 | - <input type="submit" name="goback" value="Back to team" /> |
581 | - </td></tr> |
582 | - </table> |
583 | - </form> |
584 | - </tal:block> |
585 | + <p> |
586 | + <a tal:attributes="href view/cancel_url">Back</a> |
587 | + </p> |
588 | + </tal:cannot-join> |
589 | |
590 | </div> |
591 | - |
592 | -</body> |
593 | -</html> |
594 | -</tal:root> |
595 | +</team-join> |
596 | |
597 | === modified file 'lib/lp/registry/templates/team-leave.pt' |
598 | --- lib/lp/registry/templates/team-leave.pt 2009-07-17 17:59:07 +0000 |
599 | +++ lib/lp/registry/templates/team-leave.pt 2009-09-03 17:20:42 +0000 |
600 | @@ -1,19 +1,9 @@ |
601 | -<tal:root |
602 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
603 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
604 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
605 | - omit-tag=""> |
606 | - |
607 | -<tal:do-this-first tal:content="view/processForm" /> |
608 | <html |
609 | xmlns="http://www.w3.org/1999/xhtml" |
610 | xmlns:tal="http://xml.zope.org/namespaces/tal" |
611 | xmlns:metal="http://xml.zope.org/namespaces/metal" |
612 | xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
613 | - xml:lang="en" |
614 | - lang="en" |
615 | - dir="ltr" |
616 | - metal:use-macro="context/@@main_template/master" |
617 | + metal:use-macro="view/macro:page/main_only" |
618 | i18n:domain="launchpad" |
619 | > |
620 | |
621 | @@ -22,29 +12,23 @@ |
622 | <div metal:fill-slot="main"> |
623 | |
624 | <h1>Leaving “<span tal:replace="context/displayname" />”</h1> |
625 | - <tal:block tal:condition="view/userCanRequestToLeave"> |
626 | - |
627 | - <p>Are you sure you want to leave this team?</p> |
628 | - |
629 | - <form class="actions" name="leave" action="" method="post"> |
630 | - <input type="submit" name="goback" value="Cancel" /> |
631 | - <input type="submit" name="leave" value="Leave" /> |
632 | - </form> |
633 | - </tal:block> |
634 | - |
635 | - <tal:block tal:condition="not: view/userCanRequestToLeave"> |
636 | - <p class="warning message"> |
637 | + |
638 | + <tal:can-leave tal:condition="view/user_can_request_to_leave"> |
639 | + <p>Are you sure you want to leave this team?</p> |
640 | + |
641 | + <div metal:use-macro="context/@@launchpad_form/form" /> |
642 | + </tal:can-leave> |
643 | + |
644 | + <tal:cannot-leave tal:condition="not: view/user_can_request_to_leave"> |
645 | + <p class="informational message"> |
646 | You are not an active member of this team. You don't need to leave it. |
647 | </p> |
648 | <p> |
649 | - <a tal:attributes="href context/fmt:url"> |
650 | - Back to <span tal:replace="context/displayname" /> |
651 | - </a> |
652 | + <a tal:attributes="href view/cancel_url">Back</a> |
653 | </p> |
654 | - </tal:block> |
655 | + </tal:cannot-leave> |
656 | |
657 | -</div> |
658 | +</div> |
659 | |
660 | </body> |
661 | </html> |
662 | -</tal:root> |
663 | |
664 | === modified file 'lib/lp/registry/templates/team-portlet-membership.pt' |
665 | --- lib/lp/registry/templates/team-portlet-membership.pt 2009-08-28 00:57:32 +0000 |
666 | +++ lib/lp/registry/templates/team-portlet-membership.pt 2009-09-03 17:20:42 +0000 |
667 | @@ -60,18 +60,18 @@ |
668 | <tal:user condition="not: context/teamowner/is_team"> |
669 | You are the owner of this team. |
670 | </tal:user> |
671 | - <tal:absentee-owner condition="not: view/userIsActiveMember"> |
672 | + <tal:absentee-owner condition="not: view/user_is_active_member"> |
673 | You are not currently an active member. |
674 | </tal:absentee-owner> |
675 | </tal:is-owner> |
676 | |
677 | <tal:not-owner condition="not: view/userIsOwner"> |
678 | - <tal:active-member tal:condition="view/userIsActiveMember"> |
679 | - <tal:can-leave condition="view/userCanRequestToLeave"> |
680 | + <tal:active-member tal:condition="view/user_is_active_member"> |
681 | + <tal:can-leave condition="view/user_can_request_to_leave"> |
682 | You are a member of this team. |
683 | </tal:can-leave> |
684 | </tal:active-member> |
685 | - <tal:not-active-member tal:condition="not: view/userIsActiveMember"> |
686 | + <tal:not-active-member tal:condition="not: view/user_is_active_member"> |
687 | <div condition="not: view/userIsParticipant" |
688 | style="margin-top: 1.5em"> |
689 | <a tal:define="link context/menu:overview/join" |
690 | |
691 | === modified file 'lib/lp/registry/templates/teammembership-self-renewal.pt' |
692 | --- lib/lp/registry/templates/teammembership-self-renewal.pt 2009-07-17 17:59:07 +0000 |
693 | +++ lib/lp/registry/templates/teammembership-self-renewal.pt 2009-09-03 17:20:42 +0000 |
694 | @@ -3,18 +3,11 @@ |
695 | xmlns:tal="http://xml.zope.org/namespaces/tal" |
696 | xmlns:metal="http://xml.zope.org/namespaces/metal" |
697 | xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
698 | - xml:lang="en" |
699 | - lang="en" |
700 | - dir="ltr" |
701 | - metal:use-macro="context/@@main_template/master" |
702 | + metal:use-macro="view/macro:page/main_only" |
703 | i18n:domain="launchpad" |
704 | > |
705 | <body> |
706 | |
707 | -<metal:portlets fill-slot="portlets"> |
708 | - <div tal:replace="structure context/person/@@+portlet-details" /> |
709 | -</metal:portlets> |
710 | - |
711 | <div metal:fill-slot="main"> |
712 | |
713 | <div tal:condition="context/canBeRenewedByMember"> |
714 | @@ -29,7 +22,7 @@ |
715 | you must renew it. |
716 | </tal:is-team> |
717 | <tal:not-team condition="not: context/person/isTeam"> |
718 | - if you want to remain a member of |
719 | + if you want to remain a member of |
720 | <span tal:replace="structure context/team/fmt:link" />, |
721 | you must renew it. |
722 | </tal:not-team> |
723 | @@ -39,7 +32,7 @@ |
724 | |
725 | <p tal:condition="not: context/canBeRenewedByMember"> |
726 | This membership cannot be renewed because |
727 | - <span tal:replace="structure view/getReasonForDeniedRenewal" /> |
728 | + <span tal:replace="structure view/get_reason_for_denied_renewal" /> |
729 | </p> |
730 | |
731 | </div> |
= Summary =
Bug 421983 is about converting the pages for joining, leaving, and self-renewal of
team membership to UI 3.0.
== Proposed fix ==
Ideally this would've been a very fast, mechanical change. Unfortunately the first
two pages were created before the Bronze Age using only rocks, goat intestines, and
spittle. I converted them to use LaunchpadFormView, refactored a bunch of stuff,
changed non-conforming names, and did general clean up. The conversion to LPFV
required changes to test code that referred to the actual form element names.
== Pre-implementation notes ==
None.
== Implementation details ==
As above.
== Tests ==
bin/test -vvm lp.registry
== Demo and Q/A ==
* Log in as karl. Join and leave ~testing- spanish- team. spanish- team. Don't forget /launchpad. dev/~testing- spanish- team/+member/ karl and set him to /launchpad. dev/+expiringme mbership and do the renewal.
* Try to join to a team you have already joined and to unsubscribe from a team you
haven't joined.
* As mark create and approve a mailing list for ~testing-
to start with 'make run_all'.
* Demoing the self-renewal is a little more involved. As mark, set the team renewal
policy to allow self-renewal and put in a renewal period. Then go to the (hidden, I
think) URL https:/
expire within a few days. Then as an karl go to
https:/
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files: registry/ stories/ mailinglists/ subscriptions. txt registry/ browser/ tests/team- join-views. txt /launchpad/ pagetitles. py registry/ templates/ team-join. pt registry/ browser/ person. py registry/ templates/ teammembership- self-renewal. pt registry/ templates/ team-leave. pt registry/ templates/ team-portlet- membership. pt
lib/lp/
lib/lp/
lib/canonical
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
== XmlLint notices ==
lib/lp/ registry/ templates/ team-portlet- membership. pt
131: parser error : Opening and ending tag mismatch: div line 7 and tal:root
</tal:root>
^
132: parser error : Premature end of data in tag root line 1
^
== Pylint notices ==
lib/lp/ registry/ browser/ person. py interface' (No module named restful)
1198: [C0301] Line too long (79/78)
1285: [C0301] Line too long (79/78)
3139: [W0311] Bad indentation. Found 10 spaces, expected 12
3143: [W0311] Bad indentation. Found 10 spaces, expected 12
3856: [C0301] Line too long (79/78)
119: [F0401] Unable to import 'lazr.delegates' (No module named delegates)
120: [F0401] Unable to import 'lazr.config' (No module named config)
121: [F0401] Unable to import 'lazr.restful.
I'll find which of these are real and fix them.