Merge lp:~bac/launchpad/bug-421983 into lp:launchpad

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
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
To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

= 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.
* 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-spanish-team. Don't forget
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://launchpad.dev/~testing-spanish-team/+member/karl and set him to
expire within a few days. Then as an karl go to
https://launchpad.dev/+expiringmembership and do the renewal.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/registry/stories/mailinglists/subscriptions.txt
  lib/lp/registry/browser/tests/team-join-views.txt
  lib/canonical/launchpad/pagetitles.py
  lib/lp/registry/templates/team-join.pt
  lib/lp/registry/browser/person.py
  lib/lp/registry/templates/teammembership-self-renewal.pt
  lib/lp/registry/templates/team-leave.pt
  lib/lp/registry/templates/team-portlet-membership.pt

== 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
    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.interface' (No module named restful)

I'll find which of these are real and fix them.

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 &#8220;<span tal:replace="context/displayname" />&#8221;</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>