Merge lp:~sinzui/launchpad/team-membership-breadcrumbs into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~sinzui/launchpad/team-membership-breadcrumbs
Merge into: lp:launchpad
Diff against target: 241 lines
8 files modified
lib/lp/registry/browser/configure.zcml (+5/-0)
lib/lp/registry/browser/person.py (+4/-6)
lib/lp/registry/browser/teammembership.py (+10/-10)
lib/lp/registry/doc/teammembership-views.txt (+65/-0)
lib/lp/registry/stories/foaf/xx-team-membership.txt (+4/-4)
lib/lp/registry/stories/teammembership/xx-add-member.txt (+1/-2)
lib/lp/registry/stories/teammembership/xx-renew-subscription.txt (+2/-2)
lib/lp/registry/templates/teammembership-index.pt (+1/-1)
To merge this branch: bzr merge lp:~sinzui/launchpad/team-membership-breadcrumbs
Reviewer Review Type Date Requested Status
Paul Hummer (community) code Approve
Review via email: mp+13028@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

This is my branch to add breadcrumbs to team membership pages (+member and
+invitation). I am tempted to move TeamInvitationView from browser.person
to browser.teammembership because that is what it adapts. I decided not to
do it since it makes the diff harder to read.

    lp:~sinzui/launchpad/team-membership-breadcrumbs
    Diff size: 174
    Launchpad bug: https://bugs.launchpad.net/bugs/429663
    Test command: ./bin/test -vvt "teammembership-views"
    Pre-implementation: barry, salgado
    Target release: 3.1.10

= Add breadcrumbs to team membership pages =

After conversion to UI 3.0, ~team/+invitation/team has no breadcrumbs.
TeamMembership needs a Breadcrumb adapter. We expect to see:

    * +invitation/<team> (accept/decline invitation)
     “Launchpad Foundations” team >> “Registry Team” team invitation

Team membership is subordinate to team, so we expect to see the team
breadcrumbs first. What follows should be the member team:

    * +member/<member> (Edit membership)
      “Registry Team” team >> Members of “Registry Team” >> Guilherme Salgado

== Rules ==

    * Create a Breadcrumb adapter for ITeamMembership
      * It usesto TeamMembership.person.displayname.
      * It is appropriate for +member
    * Redefine TeamInvitationView.page_title to make a better breadcrumb
      since the membership is not accepted.

== QA ==

On Staging

    * Visit a team you are the owner of.
    * Choose the members link, then edit a member.
    * Verify that the page has breadcrumbs

    * Invite another team to be be an owner of the first team.
    * Visit the other team and choose the Invitation link
    * Choose the invitation
    * Verify that the page has breadcrumbs
    * Decline the invitation

== Lint ==

Linting changed files:
  lib/lp/registry/browser/configure.zcml
  lib/lp/registry/browser/person.py
  lib/lp/registry/browser/teammembership.py
  lib/lp/registry/doc/teammembership-views.txt

== Test ==

    * lib/lp/registry/doc/teammembership-views.txt
      * Added a test for the breadcrumb.
      * Added a test TeamMembershipEditView which did not have any
        verification for its label
      * Added a test to verify the TeamInvitationView page_title.

== Implementation ==

    * lib/lp/registry/browser/configure.zcml
      * Registered TeamMembershipBreadcrumb
    * lib/lp/registry/browser/person.py
      * Redefined TeamInvitationView page_title to make a breadcrumb that
        appropriate for a TeamMembership instance that was not accepted or
        declined.
    * lib/lp/registry/browser/teammembership.py
      * Added TeamMembershipBreadcrumb
      * Removed the page_title because the TeamMembershipBreadcrumb provides
        the text.

Revision history for this message
Paul Hummer (rockstar) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/configure.zcml'
2--- lib/lp/registry/browser/configure.zcml 2009-10-05 22:14:18 +0000
3+++ lib/lp/registry/browser/configure.zcml 2009-10-09 00:13:13 +0000
4@@ -23,6 +23,11 @@
5 path_expression="string:+member/${person/name}"
6 rootsite="mainsite"
7 attribute_to_parent="team"/>
8+ <adapter
9+ provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
10+ for="lp.registry.interfaces.teammembership.ITeamMembership"
11+ factory="lp.registry.browser.teammembership.TeamMembershipBreadcrumb"
12+ permission="zope.Public"/>
13 <browser:defaultView
14 for="lp.registry.interfaces.teammembership.ITeamMembership"
15 name="+index"/>
16
17=== modified file 'lib/lp/registry/browser/person.py'
18--- lib/lp/registry/browser/person.py 2009-09-24 17:41:06 +0000
19+++ lib/lp/registry/browser/person.py 2009-10-09 00:13:13 +0000
20@@ -595,12 +595,10 @@
21 return "Make %s a member of %s" % (
22 self.context.person.displayname, self.context.team.displayname)
23
24- # XXX BarryWarsaw 2009-09-14 bug 429663. Because this view is subordinate
25- # to the ~person in its context, no +hierarchy adapter can be found. This
26- # means the page has no breadcrumbs and no proper page title. For
27- # expediency during the 3.0 conversion work, we'll just set the page title
28- # to its label and move on.
29- page_title = label
30+ @property
31+ def page_title(self):
32+ return smartquote(
33+ '"%s" team invitation') % self.context.team.displayname
34
35 def browserDefault(self, request):
36 return self, ()
37
38=== modified file 'lib/lp/registry/browser/teammembership.py'
39--- lib/lp/registry/browser/teammembership.py 2009-09-15 18:33:47 +0000
40+++ lib/lp/registry/browser/teammembership.py 2009-10-09 00:13:13 +0000
41@@ -4,12 +4,12 @@
42 __metaclass__ = type
43
44 __all__ = [
45+ 'TeamMembershipBreadcrumb',
46 'TeamInvitationsView',
47 'TeamMembershipEditView',
48 ]
49
50
51-import cgi
52 import pytz
53 from datetime import datetime
54
55@@ -22,14 +22,22 @@
56 from canonical.launchpad import _
57 from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
58 from canonical.launchpad.webapp import LaunchpadView, canonical_url
59+from canonical.launchpad.webapp.breadcrumb import Breadcrumb
60 from canonical.launchpad.webapp.interfaces import (
61 ILaunchBag, UnexpectedFormData)
62-from canonical.lazr.utils import smartquote
63 from canonical.widgets import DateWidget
64
65 from lp.registry.interfaces.teammembership import TeamMembershipStatus
66
67
68+class TeamMembershipBreadcrumb(Breadcrumb):
69+ """Builds a breadcrumb for an `ITeamMembership`."""
70+
71+ @property
72+ def text(self):
73+ return "%s's membership" % self.context.person.displayname
74+
75+
76 class TeamMembershipEditView:
77
78 def __init__(self, context, request):
79@@ -88,14 +96,6 @@
80 raise AssertionError('status unknown')
81 return '%s member %s' % (prefix, self.context.person.displayname)
82
83- # XXX BarryWarsaw 2009-09-14 bug 429729. This should really go away, but
84- # since there is no +hierarchy adapter for this view, there is no
85- # breadcrumbs, and thus no default reverse-breadcrumb page title.
86- @property
87- def page_title(self):
88- return smartquote("%s's membership status in %s") % (
89- self.context.person.displayname, self.context.team.displayname)
90-
91 # Boolean helpers
92 def userIsTeamOwnerOrLPAdmin(self):
93 return (self.user.inTeam(self.context.team.teamowner) or
94
95=== modified file 'lib/lp/registry/doc/teammembership-views.txt'
96--- lib/lp/registry/doc/teammembership-views.txt 2009-09-14 19:53:46 +0000
97+++ lib/lp/registry/doc/teammembership-views.txt 2009-10-09 00:13:13 +0000
98@@ -2,6 +2,47 @@
99 Team membership views
100 =====================
101
102+
103+Breadcrumbs
104+-----------
105+
106+Team membership breadcrumbs uses the member's displayname to create
107+"<displayname>'s membership".
108+
109+ >>> from canonical.lazr.testing.menus import make_fake_request
110+ >>> from lp.registry.interfaces.teammembership import ITeamMembershipSet
111+
112+ >>> team_owner = factory.makePerson(name='team-owner')
113+ >>> super_team = factory.makeTeam(name='us', owner=team_owner)
114+ >>> membership_set = getUtility(ITeamMembershipSet)
115+ >>> membership = membership_set.getByPersonAndTeam(
116+ ... team_owner, super_team)
117+ >>> view = create_view(membership, '+index')
118+
119+ >>> request = make_fake_request(
120+ ... 'http://launchpad.dev/~us/+member/team-owner',
121+ ... [super_team, membership, view])
122+ >>> hierarchy = create_initialized_view(
123+ ... membership, '+hierarchy', request=request)
124+ >>> hierarchy.items
125+ [<TeamBreadcrumb ... text='\u201cUs\u201d team'>,
126+ <TeamMembershipBreadcrumb ... text='Team-owner's membership'>]
127+
128+
129++member view
130+------------
131+
132+The TeamMembershipEditView provides a label that described the membership
133+state of the IPerson.
134+
135+ >>> view = create_view(membership, '+index')
136+ >>> print view.label
137+ Active member Team-owner
138+
139+
140++invitations view
141+-----------------
142+
143 When a team is invited to join another team, the TeamInvitationsView controls
144 the ~team/+invitations page.
145
146@@ -9,3 +50,27 @@
147 >>> view = create_initialized_view(team, '+invitations')
148 >>> print view.label
149 Invitations for Bassists
150+
151+
152++invitation/<team> view
153+-----------------------
154+
155+The TeamInvitationView allows a team admin to accept or decline an invitation
156+to join another team. The invitation is a TeamMembership instance. The view
157+is applied during the stepto traversal--it is not a named view in ZCML.
158+
159+ >>> from lp.registry.browser.person import TeamInvitationView
160+
161+ >>> login_person(team_owner)
162+ >>> super_team.addMember(team, team_owner)
163+ >>> membership = membership_set.getByPersonAndTeam(team, super_team)
164+ >>> login_person(team.teamowner)
165+ >>> view = TeamInvitationView(membership, request)
166+ >>> print view.label
167+ Make Bassists a member of Us
168+
169+The view provides page_title to create a breadcrumb that describes this
170+use the TeamMembership.
171+
172+ >>> print view.page_title.encode('ascii', 'backslashreplace')
173+ \u201cUs\u201d team invitation
174
175=== modified file 'lib/lp/registry/stories/foaf/xx-team-membership.txt'
176--- lib/lp/registry/stories/foaf/xx-team-membership.txt 2009-09-16 18:36:46 +0000
177+++ lib/lp/registry/stories/foaf/xx-team-membership.txt 2009-10-09 00:13:13 +0000
178@@ -12,8 +12,8 @@
179 >>> url = '/~ubuntu-team/+member/kamion'
180 >>> browser.getLink(url=url).click()
181
182- >>> "Colin Watson's membership status in Ubuntu Team" in browser.contents
183- True
184+ >>> print browser.title
185+ Colin Watson's membership : ...Ubuntu Team... team
186 >>> "Active member" in browser.contents
187 True
188 >>> browser.getControl(name='admin').value
189@@ -109,8 +109,8 @@
190 >>> url = '/~ubuntu-team/+member/jdub'
191 >>> jdub_browser.getLink(url=url).click()
192
193- >>> "Jeff Waugh's membership status in Ubuntu Team" in jdub_browser.contents
194- True
195+ >>> print jdub_browser.title
196+ Jeff Waugh's membership : ...Ubuntu Team... team
197 >>> "Active member" in jdub_browser.contents
198 True
199
200
201=== modified file 'lib/lp/registry/stories/teammembership/xx-add-member.txt'
202--- lib/lp/registry/stories/teammembership/xx-add-member.txt 2009-09-14 23:03:49 +0000
203+++ lib/lp/registry/stories/teammembership/xx-add-member.txt 2009-10-09 00:13:13 +0000
204@@ -99,8 +99,7 @@
205 >>> browser.open(
206 ... 'http://launchpad.dev/~landscape-developers/+member/launchpad')
207
208- >>> print extract_text(find_tag_by_id(browser.contents, 'maincontent'))
209- Invited member Launchpad Developers
210+ >>> print extract_text(find_tag_by_id(browser.contents, 'not-responded'))
211 Launchpad Developers (launchpad) has been invited to join this team, but
212 hasn't responded to the invitation yet.
213
214
215=== modified file 'lib/lp/registry/stories/teammembership/xx-renew-subscription.txt'
216--- lib/lp/registry/stories/teammembership/xx-renew-subscription.txt 2009-09-18 15:24:30 +0000
217+++ lib/lp/registry/stories/teammembership/xx-renew-subscription.txt 2009-10-09 00:13:13 +0000
218@@ -6,8 +6,8 @@
219
220 >>> browser = setupBrowser(auth='Basic mark@example.com:test')
221 >>> browser.open('http://launchpad.dev/~name18/+member/mark')
222- >>> browser.title
223- "Mark Shuttleworth's membership status in Ubuntu Gnome Team"
224+ >>> print browser.title
225+ Mark Shuttleworth's membership : ...Ubuntu Gnome Team... team
226 >>> content = find_main_content(browser.contents)
227 >>> print extract_text(content.p)
228 Mark Shuttleworth (mark) is an Expired Member of Ubuntu Gnome Team.
229
230=== modified file 'lib/lp/registry/templates/teammembership-index.pt'
231--- lib/lp/registry/templates/teammembership-index.pt 2009-09-16 18:36:46 +0000
232+++ lib/lp/registry/templates/teammembership-index.pt 2009-10-09 00:13:13 +0000
233@@ -289,7 +289,7 @@
234 </div>
235
236 <div tal:condition="view/isInvited">
237- <p>
238+ <p id="not-responded">
239 <metal:person use-macro="template/macros/person" />
240 has been invited to join this team, but hasn't responded to the
241 invitation yet.