Merge lp:~barry/launchpad/413158-people into lp:launchpad
- 413158-people
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Curtis Hovey | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~barry/launchpad/413158-people | ||||
Merge into: | lp:launchpad | ||||
Diff against target: | None lines | ||||
To merge this branch: | bzr merge lp:~barry/launchpad/413158-people | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Curtis Hovey (community) | Approve | ||
Review via email: mp+10323@code.launchpad.net |
Commit message
Description of the change
Barry Warsaw (barry) wrote : | # |
Curtis Hovey (sinzui) wrote : | # |
Hi Barry.
This is very good. I know you had concerns about the menus, but I think they
are close to solving the issues in this branch and in the other top-level
collections. I think a subtle renaming of the marker interface will help you
see what I see. I have a code fragment at the bottom of how I would name and
restructure the menus so that you have something ready for your next branches.
...
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -590,3 +590,10 @@
> word-wrap: break-word;
> }
>
> +/* ==== Listing tables ==== */
> +
> +table.listing thead {
> + color: #9f2b33;
This colour is the bug colour. We need something neutral #333; maybe.
> + font-size: 1.125em;
This font-size is not supported by YUI. We must specify all sizes
using the percentages from the table in this file. try 116% or 123.1%.
...
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -62,6 +62,10 @@
> def max_batch_
> return config.
>
> + @property
> + def multiple_
can you add a docstring? Is has_multiple_pages a better name for what it
returns?
...
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -331,56 +331,13 @@
> branches = list(self.
> # XXX: TimPenhey 2009-04-08 bug=324546
> # Until there is an API to do this nicely, shove the launchpad.view
> - # permission into the request cache directly. This is done because the
> - # security proxy sometimes causes extra requests to be made.
> + # permission into the request cache directly. This is done because
> + # the security proxy sometimes causes extra requests to be made.
> request = self.view.request
> precache_
> return branches
>
> @cachedproperty
> - def branch_
I am not sure why this big chunk of code was removed. I this diff lying?
...
> === added file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -0,0 +1,40 @@
> +# Copyright 2009 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +"""Shared menus."""
> +
> +__metaclass__ = type
> +__all__ = [
> + 'TopLevelMenuMi
> + ]
> +
> +
> +from canonical.
> +
> +
> +class TopLevelMenuMixin:
> + """Menu shared by top level collection objects."""
I think I would prefer this in __init__.py where other shared code
is? Since these collections are in the root of the mainsit...
Barry Warsaw (barry) wrote : | # |
On Aug 18, 2009, at 07:11 PM, Curtis Hovey wrote:
>This is very good. I know you had concerns about the menus, but I think they
>are close to solving the issues in this branch and in the other top-level
>collections. I think a subtle renaming of the marker interface will help you
>see what I see. I have a code fragment at the bottom of how I would name and
>restructure the menus so that you have something ready for your next branches.
Yep, it's been a frustrating branch; thanks for talking me off the ledge. Thanks for the review and it's definitely encouraging to see how close this branch puts me to completing the other top level collections.
>...
>> === modified file 'lib/canonical/
>> --- lib/canonical/
>> +++ lib/canonical/
>> @@ -590,3 +590,10 @@
>> word-wrap: break-word;
>> }
>>
>> +/* ==== Listing tables ==== */
>> +
>> +table.listing thead {
>> + color: #9f2b33;
>
>This colour is the bug colour. We need something neutral #333; maybe.
I never made the correlation between the heading color and the app color. I thought it was a general heading cue. If not, then I think the default color setting is just fine. You provided the hint for bolding the th in a comment on the bug and that works better, so I'll keep that and remove the color setting.
>> + font-size: 1.125em;
>
>This font-size is not supported by YUI. We must specify all sizes
>using the percentages from the table in this file. try 116% or 123.1%.
116% looks good to me.
>> === modified file 'lib/canonical/
>> --- lib/canonical/
>> +++ lib/canonical/
>> @@ -62,6 +62,10 @@
>> def max_batch_
>> return config.
>>
>> + @property
>> + def multiple_
>
>can you add a docstring? Is has_multiple_pages a better name for what it
>returns?
Docstring added. 'multiple_pages' was the original name for the property before refactoring, but I agree that 'has_multiple_
>> === modified file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>> @@ -331,56 +331,13 @@
>> branches = list(self.
>> # XXX: TimPenhey 2009-04-08 bug=324546
>> # Until there is an API to do this nicely, shove the launchpad.view
>> - # permission into the request cache directly. This is done because the
>> - # security proxy sometimes causes extra requests to be made.
>> + # permission into the request cache directly. This is done because
>> + # the security proxy sometimes causes extra requests to be made.
>> request = self.view.request
>> precache_
>> return branches
>>
>> @cachedproperty
>> - def branch_
>
>I am not...
Curtis Hovey (sinzui) wrote : | # |
Hi Barry.
This looks good. I see have a small concern about one menu and I think
some of the links need to be handled differently.
On Wed, 2009-08-19 at 00:03 +0000, Barry Warsaw wrote:
...
>> + def meetings(self):
> >> + return Link('/sprints/', 'View meetings', icon='info')
> >
> >Seeing this really make me think we need to separate sprints from
> >blueprints. I am certain this link belongs to the mainsite (which
> >is registry)
>
> mainsite seems reasonable. Is there something you want me to change here?
>
No. This a remark. I recently realised how these were implement and I was offended. I think this is a great feature that is hard to find and use. Tim did not know this existed until I showed him yesterday.
...
>> === modified file 'lib/lp/
> >> --- lib/lp/
> >> +++ lib/lp/
>
...
> implements(
> >
> >So you have one marker interface, and one implementation below that
> >is used in 5 collections. Your other branches will be much smaller.
> >I think this is what you intend to do placed the like definitions in
> >a shared place.
>
> Well, now I have my justification for menu.py! :)
>
I think you are correct.
...
>These look like the menus we want to build.
> >
> >{{{
> >
> >class PersonSetNaviga
> > """Navigation menu for PersonSet actions"""
> > usedfor = IPersonSet
> > links = [
> > register_
> >
> >
> >class IRegistryCollec
> > """Marker interface for the registry collection navigation menu."""
> >
> >
> >class RegistryCollect
> > """Navigation menu for registry person search collection."""
> > usedfor = IRegistryCollec
> > facet = 'overview'
> > links = ['products', 'distributions', 'people', 'meetings']
> >
> >
> >class PeopleSearchVie
> > """Search for people and teams on the /people page."""
> > implements(
> >
> >}}}
>
> Nice. This works but with a small ugliness. Because `usedfor` in the action
> menu must correspond to the context object's interface, it can't be completely
> generalized. It will force each top level collection view to create a
> subclass containing only the `usedfor` attribute, set to the right interface.
> Icky, but close enough for jazz.
>
No. I do not see that. We created the one shared menu (RegistryCollec
I am not sure of the value of RegistryCollect
Doing this does feel like a hack because it would be better to register both
> menus on the view instead of one on the view and one on the context object.
> But it's a hack that will live another day be...
Barry Warsaw (barry) wrote : | # |
On Aug 19, 2009, at 07:30 PM, Curtis Hovey wrote:
>This looks good. I see have a small concern about one menu and I think
>some of the links need to be handled differently.
Okay. Hopefully we're close!
>>These look like the menus we want to build.
>> >
>> >{{{
>> >
>> >class PersonSetNaviga
>> > """Navigation menu for PersonSet actions"""
>> > usedfor = IPersonSet
>> > links = [
>> > register_
>> >
>> >
>> >class IRegistryCollec
>> > """Marker interface for the registry collection navigation menu."""
>> >
>> >
>> >class RegistryCollect
>> > """Navigation menu for registry person search collection."""
>> > usedfor = IRegistryCollec
>> > facet = 'overview'
>> > links = ['products', 'distributions', 'people', 'meetings']
>> >
>> >
>> >class PeopleSearchVie
>> > """Search for people and teams on the /people page."""
>> > implements(
>> >
>> >}}}
>>
>> Nice. This works but with a small ugliness. Because `usedfor` in the action
>> menu must correspond to the context object's interface, it can't be completely
>> generalized. It will force each top level collection view to create a
>> subclass containing only the `usedfor` attribute, set to the right interface.
>> Icky, but close enough for jazz.
>>
>No. I do not see that. We created the one shared menu (RegistryCollec
>
>I am not sure of the value of RegistryCollect
I'm not so sure about that. Certainly, the admin_merge_* links pertain only to IPersonSet, but that's easily controlled by setting the 'links' attribute. In fact, that's exactly the refactoring I've done in my /projects branch, so I don't want to do it here. But I do think the register_projects and register_teams link make sense on all the top level collections, and I don't think we need to filter that further for the context. Also, create_account when anonymous makes sense on all the collections, so I think that justifies RegistryCollect
>
>Doing this does feel like a hack because it would be better to register both
>> menus on the view instead of one on the view and one on the context object.
>> But it's a hack that will live another day because I'm not going to worry
>> about it any more. :)
>>
>Creating a base that has only one user is a waste, remove it.
It won't be a waste when you see the /projects branch.
>> === modified file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>
>...
>
>> @@ -27,14 +34,72 @@
>> def meetings(self):
>> return Link('/sprints/', 'View meetings', icon='info')
>>
>> +...
Curtis Hovey (sinzui) wrote : | # |
Hi barry.
Your changes look great. I accept you arguments to keep the base menu.
review approve
Preview Diff
1 | === modified file 'lib/canonical/launchpad/doc/batch_navigation.txt' |
2 | --- lib/canonical/launchpad/doc/batch_navigation.txt 2009-03-24 12:43:49 +0000 |
3 | +++ lib/canonical/launchpad/doc/batch_navigation.txt 2009-08-14 18:41:41 +0000 |
4 | @@ -1,4 +1,6 @@ |
5 | -= Batch Navigation = |
6 | +================ |
7 | +Batch Navigation |
8 | +================ |
9 | |
10 | Most of the test for this behavior is in the lazr.batchnavigation package. |
11 | |
12 | @@ -44,10 +46,12 @@ |
13 | >>> reindeer = ['Dasher', 'Dancer', 'Prancer', 'Vixen', 'Comet', |
14 | ... 'Cupid', 'Donner', 'Blitzen', 'Rudolph'] |
15 | |
16 | -== Performance with SQLObject == |
17 | - |
18 | -This section demonstrates that batching generates sensible SQL queries when used |
19 | -with SQLObject, i.e. that it puts appropriate LIMIT clauses on queries. |
20 | + |
21 | +Performance with SQLObject |
22 | +========================== |
23 | + |
24 | +This section demonstrates that batching generates sensible SQL queries when |
25 | +used with SQLObject, i.e. that it puts appropriate LIMIT clauses on queries. |
26 | |
27 | Imports: |
28 | |
29 | @@ -58,8 +62,7 @@ |
30 | Prepare a query, and create a batch of the results: |
31 | |
32 | >>> select_results = EmailAddress.select(orderBy='id') |
33 | - >>> batch_nav = BatchNavigator(select_results, build_request(), |
34 | - ... size=10) |
35 | + >>> batch_nav = BatchNavigator(select_results, build_request(), size=10) |
36 | >>> email_batch = batch_nav.currentBatch() |
37 | >>> batch_items = list(email_batch) |
38 | |
39 | @@ -85,7 +88,17 @@ |
40 | True |
41 | |
42 | |
43 | -== Maximum batch size == |
44 | +Multiple pages |
45 | +============== |
46 | + |
47 | +The batch navigator tells us whether multiple pages will be used. |
48 | + |
49 | + >>> batch_nav.multiple_pages |
50 | + True |
51 | + |
52 | + |
53 | +Maximum batch size |
54 | +================== |
55 | |
56 | Since the batch size is exposed in the URL, it's possible for users to |
57 | tweak the batch parameter to retrieve more results. Since that may |
58 | @@ -108,7 +121,8 @@ |
59 | >>> ignored = config.pop('max-batch-size') |
60 | |
61 | |
62 | -== Batch views == |
63 | +Batch views |
64 | +=========== |
65 | |
66 | A view is often used with a BatchNavigator to determine when to |
67 | display the current batch. |
68 | |
69 | === modified file 'lib/canonical/launchpad/icing/style-3-0.css' |
70 | --- lib/canonical/launchpad/icing/style-3-0.css 2009-08-17 11:22:39 +0000 |
71 | +++ lib/canonical/launchpad/icing/style-3-0.css 2009-08-17 17:50:02 +0000 |
72 | @@ -590,3 +590,10 @@ |
73 | word-wrap: break-word; |
74 | } |
75 | |
76 | +/* ==== Listing tables ==== */ |
77 | + |
78 | +table.listing thead { |
79 | + color: #9f2b33; |
80 | + font-size: 1.125em; |
81 | + font-weight: bold; |
82 | +} |
83 | |
84 | === modified file 'lib/canonical/launchpad/pagetests/basics/notfound-traversals.txt' |
85 | --- lib/canonical/launchpad/pagetests/basics/notfound-traversals.txt 2009-08-13 19:20:04 +0000 |
86 | +++ lib/canonical/launchpad/pagetests/basics/notfound-traversals.txt 2009-08-17 17:50:02 +0000 |
87 | @@ -368,7 +368,6 @@ |
88 | >>> check("/people/") |
89 | >>> check("/people/+requestmerge", auth=True) |
90 | >>> check_redirect("/people/+mergerequest-sent", auth=True) |
91 | ->>> check("/people/+portlet-about") |
92 | |
93 | This is for a team: |
94 | |
95 | |
96 | === modified file 'lib/canonical/launchpad/webapp/batching.py' |
97 | --- lib/canonical/launchpad/webapp/batching.py 2009-06-25 05:30:52 +0000 |
98 | +++ lib/canonical/launchpad/webapp/batching.py 2009-08-14 18:41:41 +0000 |
99 | @@ -62,6 +62,10 @@ |
100 | def max_batch_size(self): |
101 | return config.launchpad.max_batch_size |
102 | |
103 | + @property |
104 | + def multiple_pages(self): |
105 | + return self.batch.total() > self.batch.size |
106 | + |
107 | |
108 | class TableBatchNavigator(BatchNavigator): |
109 | """See canonical.launchpad.interfaces.ITableBatchNavigator.""" |
110 | |
111 | === modified file 'lib/lp/code/browser/branchlisting.py' |
112 | --- lib/lp/code/browser/branchlisting.py 2009-08-13 04:08:38 +0000 |
113 | +++ lib/lp/code/browser/branchlisting.py 2009-08-18 14:11:56 +0000 |
114 | @@ -331,56 +331,13 @@ |
115 | branches = list(self.currentBatch()) |
116 | # XXX: TimPenhey 2009-04-08 bug=324546 |
117 | # Until there is an API to do this nicely, shove the launchpad.view |
118 | - # permission into the request cache directly. This is done because the |
119 | - # security proxy sometimes causes extra requests to be made. |
120 | + # permission into the request cache directly. This is done because |
121 | + # the security proxy sometimes causes extra requests to be made. |
122 | request = self.view.request |
123 | precache_permission_for_objects(request, 'launchpad.View', branches) |
124 | return branches |
125 | |
126 | @cachedproperty |
127 | - def branch_ids_with_bug_links(self): |
128 | - """Return a set of branch ids that should show bug badges.""" |
129 | - bug_branches = getUtility(IBugBranchSet).getBugBranchesForBranches( |
130 | - self._branches_for_current_batch, self.view.user) |
131 | - return set(bug_branch.branch.id for bug_branch in bug_branches) |
132 | - |
133 | - @cachedproperty |
134 | - def branch_ids_with_spec_links(self): |
135 | - """Return a set of branch ids that should show blueprint badges.""" |
136 | - spec_branches = getUtility( |
137 | - ISpecificationBranchSet).getSpecificationBranchesForBranches( |
138 | - self._branches_for_current_batch, self.view.user) |
139 | - return set(spec_branch.branch.id for spec_branch in spec_branches) |
140 | - |
141 | - @cachedproperty |
142 | - def branch_ids_with_merge_proposals(self): |
143 | - """Return a set of branches that should show merge proposal badges. |
144 | - |
145 | - Branches have merge proposals badges if they've been proposed for |
146 | - merging into another branch (source branches). |
147 | - """ |
148 | - branches = self.view._getCollection().visibleByUser(self.view.user) |
149 | - proposals = branches.getMergeProposals( |
150 | - for_branch=self._branch_for_current_batch) |
151 | - return set(proposal.source_branch.id for proposal in proposals) |
152 | - |
153 | - @cachedproperty |
154 | - def tip_revisions(self): |
155 | - """Return a set of branch ids that should show blueprint badges.""" |
156 | - revisions = getUtility(IRevisionSet).getTipRevisionsForBranches( |
157 | - self._branches_for_current_batch) |
158 | - if revisions is None: |
159 | - revision_map = {} |
160 | - else: |
161 | - # Key the revisions by revision id. |
162 | - revision_map = dict((revision.revision_id, revision) |
163 | - for revision in revisions) |
164 | - |
165 | - # Return a dict keyed on branch id. |
166 | - return dict((branch.id, revision_map.get(branch.last_scanned_id)) |
167 | - for branch in self._branches_for_current_batch) |
168 | - |
169 | - @cachedproperty |
170 | def product_series_map(self): |
171 | """Return a map of branch id to a list of product series. |
172 | |
173 | @@ -526,10 +483,6 @@ |
174 | """Return a list of BranchListingItems.""" |
175 | return self.decoratedBranches(self.visible_branches_for_view) |
176 | |
177 | - @cachedproperty |
178 | - def multiple_pages(self): |
179 | - return self.batch.total() > self.batch.size |
180 | - |
181 | @property |
182 | def table_class(self): |
183 | # XXX: MichaelHudson 2007-10-18 bug=153894: This means there are two |
184 | |
185 | === modified file 'lib/lp/registry/browser/configure.zcml' |
186 | --- lib/lp/registry/browser/configure.zcml 2009-08-14 00:52:28 +0000 |
187 | +++ lib/lp/registry/browser/configure.zcml 2009-08-18 13:54:51 +0000 |
188 | @@ -794,18 +794,20 @@ |
189 | <browser:menus |
190 | module="lp.registry.browser.person" |
191 | classes=" |
192 | + PeopleSearchNavigationMenu |
193 | + PersonBugsMenu |
194 | + PersonEditNavigationMenu |
195 | PersonFacets |
196 | PersonOverviewMenu |
197 | - TeamOverviewMenu |
198 | - PersonBugsMenu |
199 | + PersonOverviewNavigationMenu |
200 | + PersonRelatedSoftwareNavigationMenu |
201 | + PersonSetContextMenu |
202 | PersonSpecsMenu |
203 | TeamBugsMenu |
204 | + TeamOverviewMenu |
205 | + TeamOverviewNavigationMenu |
206 | TeamSpecsMenu |
207 | - PersonSetContextMenu |
208 | - PersonOverviewNavigationMenu |
209 | - PersonEditNavigationMenu |
210 | - PersonRelatedSoftwareNavigationMenu |
211 | - TeamOverviewNavigationMenu"/> |
212 | + "/> |
213 | <browser:url |
214 | for="lp.registry.interfaces.person.IPerson" |
215 | path_expression="string:~${name}" |
216 | @@ -1242,13 +1244,10 @@ |
217 | <browser:pages |
218 | for="lp.registry.interfaces.person.IPersonSet" |
219 | permission="zope.Public" |
220 | - class="lp.registry.browser.person.FOAFSearchView"> |
221 | + class="lp.registry.browser.person.PeopleSearchView"> |
222 | <browser:page |
223 | name="+index" |
224 | template="../templates/people-index.pt"/> |
225 | - <browser:page |
226 | - name="+portlet-stats" |
227 | - template="../templates/people-portlet-stats.pt"/> |
228 | </browser:pages> |
229 | <browser:page |
230 | name="+requestmerge" |
231 | @@ -1284,13 +1283,6 @@ |
232 | name="+mergerequest-sent" |
233 | template="../templates/people-mergerequest-sent.pt"/> |
234 | </browser:pages> |
235 | - <browser:pages |
236 | - for="lp.registry.interfaces.person.IPersonSet" |
237 | - permission="zope.Public"> |
238 | - <browser:page |
239 | - name="+portlet-about" |
240 | - template="../templates/people-portlet-about.pt"/> |
241 | - </browser:pages> |
242 | </facet> |
243 | <browser:navigation |
244 | module="lp.registry.browser.milestone" |
245 | |
246 | === added file 'lib/lp/registry/browser/menu.py' |
247 | --- lib/lp/registry/browser/menu.py 1970-01-01 00:00:00 +0000 |
248 | +++ lib/lp/registry/browser/menu.py 2009-08-18 13:54:51 +0000 |
249 | @@ -0,0 +1,40 @@ |
250 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
251 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
252 | + |
253 | +"""Shared menus.""" |
254 | + |
255 | +__metaclass__ = type |
256 | +__all__ = [ |
257 | + 'TopLevelMenuMixin', |
258 | + ] |
259 | + |
260 | + |
261 | +from canonical.launchpad.webapp.menu import Link |
262 | + |
263 | + |
264 | +class TopLevelMenuMixin: |
265 | + """Menu shared by top level collection objects.""" |
266 | + |
267 | + def products(self): |
268 | + return Link('/projects/', 'View projects', icon='info') |
269 | + |
270 | + def distributions(self): |
271 | + return Link('/distros/', 'View distributions', icon='info') |
272 | + |
273 | + def people(self): |
274 | + return Link('/people/', 'View people', icon='info') |
275 | + |
276 | + def meetings(self): |
277 | + return Link('/sprints/', 'View meetings', icon='info') |
278 | + |
279 | + def register_project(self): |
280 | + text = 'Register a project' |
281 | + return Link('/projects/+new', text, icon='add') |
282 | + |
283 | + def register_team(self): |
284 | + text = 'Register a team' |
285 | + return Link('/people/+newteam', text, icon='add') |
286 | + |
287 | + def create_account(self): |
288 | + text = 'Create an account' |
289 | + return Link('/people/+login', text, icon='add') |
290 | |
291 | === modified file 'lib/lp/registry/browser/person.py' |
292 | --- lib/lp/registry/browser/person.py 2009-08-03 15:21:35 +0000 |
293 | +++ lib/lp/registry/browser/person.py 2009-08-18 15:28:28 +0000 |
294 | @@ -10,11 +10,12 @@ |
295 | __all__ = [ |
296 | 'BeginTeamClaimView', |
297 | 'BugSubscriberPackageBugsSearchListingView', |
298 | - 'FOAFSearchView', |
299 | 'EmailToPersonView', |
300 | + 'PeopleSearchMenu' |
301 | + 'PeopleSearchView', |
302 | 'PersonAccountAdministerView', |
303 | + 'PersonAddView', |
304 | 'PersonAdministerView', |
305 | - 'PersonAddView', |
306 | 'PersonAnswerContactForView', |
307 | 'PersonAnswersMenu', |
308 | 'PersonAssignedBugTaskSearchListingView', |
309 | @@ -30,10 +31,10 @@ |
310 | 'PersonEditHomePageView', |
311 | 'PersonEditIRCNicknamesView', |
312 | 'PersonEditJabberIDsView', |
313 | + 'PersonEditLocationView', |
314 | 'PersonEditSSHKeysView', |
315 | 'PersonEditView', |
316 | 'PersonEditWikiNamesView', |
317 | - 'PersonEditLocationView', |
318 | 'PersonFacets', |
319 | 'PersonGPGView', |
320 | 'PersonIndexView', |
321 | @@ -42,8 +43,8 @@ |
322 | 'PersonNavigation', |
323 | 'PersonOAuthTokensView', |
324 | 'PersonOverviewMenu', |
325 | + 'PersonRdfContentsView', |
326 | 'PersonRdfView', |
327 | - 'PersonRdfContentsView', |
328 | 'PersonRelatedBugTaskSearchListingView', |
329 | 'PersonRelatedSoftwareView', |
330 | 'PersonReportedBugTaskSearchListingView', |
331 | @@ -51,9 +52,9 @@ |
332 | 'PersonSetContextMenu', |
333 | 'PersonSetNavigation', |
334 | 'PersonSpecFeedbackView', |
335 | + 'PersonSpecWorkloadTableView', |
336 | + 'PersonSpecWorkloadView', |
337 | 'PersonSpecsMenu', |
338 | - 'PersonSpecWorkloadView', |
339 | - 'PersonSpecWorkloadTableView', |
340 | 'PersonSubscribedBugTaskSearchListingView', |
341 | 'PersonView', |
342 | 'PersonVouchersView', |
343 | @@ -66,14 +67,14 @@ |
344 | 'SearchNeedAttentionQuestionsView', |
345 | 'SearchSubscribedQuestionsView', |
346 | 'TeamAddMyTeamsView', |
347 | + 'TeamBreadcrumbBuilder', |
348 | 'TeamEditLocationView', |
349 | 'TeamJoinView', |
350 | - 'TeamBreadcrumbBuilder', |
351 | 'TeamLeaveView', |
352 | + 'TeamMembershipView', |
353 | + 'TeamMugshotView', |
354 | 'TeamNavigation', |
355 | 'TeamOverviewMenu', |
356 | - 'TeamMembershipView', |
357 | - 'TeamMugshotView', |
358 | 'TeamReassignmentView', |
359 | 'TeamSpecsMenu', |
360 | 'archive_to_person', |
361 | @@ -198,6 +199,7 @@ |
362 | from canonical.launchpad.browser.branding import BrandingChangeView |
363 | from lp.registry.browser.mailinglists import ( |
364 | enabled_with_active_mailing_list) |
365 | +from lp.registry.browser.menu import TopLevelMenuMixin |
366 | from lp.answers.browser.questiontarget import SearchQuestionsView |
367 | |
368 | from canonical.launchpad.fields import LocationField |
369 | @@ -627,29 +629,14 @@ |
370 | canonical_url(me, request=self.request), status=303) |
371 | |
372 | |
373 | -class PersonSetContextMenu(ContextMenu): |
374 | +class PersonSetContextMenu(ContextMenu, TopLevelMenuMixin): |
375 | |
376 | usedfor = IPersonSet |
377 | |
378 | - links = ['products', 'distributions', 'people', 'meetings', 'newteam', |
379 | + links = ['products', 'distributions', 'people', 'meetings', |
380 | + 'register_team', |
381 | 'adminpeoplemerge', 'adminteammerge', 'mergeaccounts'] |
382 | |
383 | - def products(self): |
384 | - return Link('/projects/', 'View projects') |
385 | - |
386 | - def distributions(self): |
387 | - return Link('/distros/', 'View distributions') |
388 | - |
389 | - def people(self): |
390 | - return Link('/people/', 'View people') |
391 | - |
392 | - def meetings(self): |
393 | - return Link('/sprints/', 'View meetings') |
394 | - |
395 | - def newteam(self): |
396 | - text = 'Register a team' |
397 | - return Link('+newteam', text, icon='add') |
398 | - |
399 | def mergeaccounts(self): |
400 | text = 'Merge accounts' |
401 | return Link('+requestmerge', text, icon='edit') |
402 | @@ -1348,35 +1335,64 @@ |
403 | return self.proposed_memberships or self.invited_memberships |
404 | |
405 | |
406 | -class FOAFSearchView: |
407 | +class IPeopleSearchNavigationMenu(Interface): |
408 | + """Marker interface for people search navigation menu.""" |
409 | + |
410 | + |
411 | +class PeopleSearchNavigationMenu(NavigationMenu, TopLevelMenuMixin): |
412 | + """Navigation menu for people search.""" |
413 | + |
414 | + usedfor = IPeopleSearchNavigationMenu |
415 | + facet = 'overview' |
416 | + |
417 | + links = ['products', 'distributions', 'people', 'meetings', |
418 | + 'register_team', 'register_project'] |
419 | + |
420 | + def initialize(self): |
421 | + """See `MenuBase`.""" |
422 | + if self.context.user is None: |
423 | + # Make a copy of the links so as not to permanently mutate the |
424 | + # class attribute. |
425 | + self.links = PeopleSearchNavigationMenu.links[:] |
426 | + self.links.append('create_account') |
427 | + |
428 | + |
429 | +class PeopleSearchView(LaunchpadView): |
430 | + """Search for people and teams on the /people page.""" |
431 | + |
432 | + implements(IPeopleSearchNavigationMenu) |
433 | |
434 | def __init__(self, context, request): |
435 | - self.context = context |
436 | - self.request = request |
437 | + super(PeopleSearchView, self).__init__(context, request) |
438 | self.results = [] |
439 | |
440 | - def teamsCount(self): |
441 | - return getUtility(IPersonSet).teamsCount() |
442 | + @property |
443 | + def number_of_people(self): |
444 | + return self.context.peopleCount() |
445 | |
446 | - def peopleCount(self): |
447 | - return getUtility(IPersonSet).peopleCount() |
448 | + @property |
449 | + def number_of_teams(self): |
450 | + return self.context.teamsCount() |
451 | |
452 | def searchPeopleBatchNavigator(self): |
453 | name = self.request.get("name") |
454 | - |
455 | if not name: |
456 | return None |
457 | - |
458 | searchfor = self.request.get("searchfor") |
459 | if searchfor == "peopleonly": |
460 | - results = getUtility(IPersonSet).findPerson(name) |
461 | + results = self.context.findPerson(name) |
462 | elif searchfor == "teamsonly": |
463 | - results = getUtility(IPersonSet).findTeam(name) |
464 | + results = self.context.findTeam(name) |
465 | else: |
466 | - results = getUtility(IPersonSet).find(name) |
467 | - |
468 | + results = self.context.find(name) |
469 | return BatchNavigator(results, self.request) |
470 | |
471 | + @property |
472 | + def is_admin(self): |
473 | + """Is the logged in user a Launchpad administrator?""" |
474 | + return (self.user is not None and |
475 | + self.user.inTeam(getUtility(ILaunchpadCelebrities).admin)) |
476 | + |
477 | |
478 | class PersonAddView(LaunchpadFormView): |
479 | """The page where users can create new Launchpad profiles.""" |
480 | |
481 | === modified file 'lib/lp/registry/browser/product.py' |
482 | --- lib/lp/registry/browser/product.py 2009-08-12 13:20:10 +0000 |
483 | +++ lib/lp/registry/browser/product.py 2009-08-18 13:54:51 +0000 |
484 | @@ -82,6 +82,7 @@ |
485 | from lp.bugs.browser.bugtask import ( |
486 | BugTargetTraversalMixin, get_buglisting_search_filter_url) |
487 | from lp.registry.browser.distribution import UsesLaunchpadMixin |
488 | +from lp.registry.browser.menu import TopLevelMenuMixin |
489 | from lp.answers.browser.faqtarget import FAQTargetNavigationMixin |
490 | from canonical.launchpad.browser.feeds import FeedsMixin |
491 | from lp.registry.browser.productseries import get_series_branch_error |
492 | @@ -546,40 +547,17 @@ |
493 | enable_only = ['overview'] |
494 | |
495 | |
496 | -class ProductSetContextMenu(ContextMenu): |
497 | +class ProductSetContextMenu(ContextMenu, TopLevelMenuMixin): |
498 | |
499 | usedfor = IProductSet |
500 | |
501 | links = ['products', 'distributions', 'people', 'meetings', |
502 | - 'all', 'register', 'register_team', 'review_licenses'] |
503 | - |
504 | - def register(self): |
505 | - text = 'Register a project' |
506 | - # We link to the guided form, though people who know the URL can |
507 | - # just jump to +new directly. That might be considered a |
508 | - # feature! |
509 | - return Link('+new', text, icon='add') |
510 | - |
511 | - def register_team(self): |
512 | - text = 'Register a team' |
513 | - return Link('/people/+newteam', text, icon='add') |
514 | + 'all', 'register_project', 'register_team', 'review_licenses'] |
515 | |
516 | def all(self): |
517 | text = 'List all projects' |
518 | return Link('+all', text, icon='list') |
519 | |
520 | - def products(self): |
521 | - return Link('/projects/', 'View projects') |
522 | - |
523 | - def distributions(self): |
524 | - return Link('/distros/', 'View distributions') |
525 | - |
526 | - def people(self): |
527 | - return Link('/people/', 'View people') |
528 | - |
529 | - def meetings(self): |
530 | - return Link('/sprints/', 'View meetings') |
531 | - |
532 | @enabled_with_permission('launchpad.ProjectReview') |
533 | def review_licenses(self): |
534 | return Link('+review-licenses', 'Review projects') |
535 | |
536 | === added file 'lib/lp/registry/browser/tests/people-views.txt' |
537 | --- lib/lp/registry/browser/tests/people-views.txt 1970-01-01 00:00:00 +0000 |
538 | +++ lib/lp/registry/browser/tests/people-views.txt 2009-08-14 17:31:10 +0000 |
539 | @@ -0,0 +1,70 @@ |
540 | +============================== |
541 | +Searching for people and teams |
542 | +============================== |
543 | + |
544 | +The view behind the /people page provides searching for people and teams. The |
545 | +view knows how many people and teams are currently registered in Launchpad. |
546 | + |
547 | + >>> from lp.registry.interfaces.person import IPersonSet |
548 | + >>> login('foo.bar@canonical.com') |
549 | + >>> person_set = getUtility(IPersonSet) |
550 | + |
551 | + >>> view = create_initialized_view(person_set, '+index') |
552 | + >>> view.number_of_people |
553 | + 48 |
554 | + >>> view.number_of_teams |
555 | + 17 |
556 | + |
557 | +The view also knows whether the logged in user, such as Foo Bar is a Launchpad |
558 | +administrator or not. |
559 | + |
560 | + >>> view.is_admin |
561 | + True |
562 | + |
563 | +But the anonymous user is not an administrator. |
564 | + |
565 | + >>> logout() |
566 | + >>> view = create_initialized_view(person_set, '+index') |
567 | + >>> view.is_admin |
568 | + False |
569 | + |
570 | +Sample Person is also not an administrator. |
571 | + |
572 | + >>> login('test@canonical.com') |
573 | + >>> view = create_initialized_view(person_set, '+index') |
574 | + >>> view.is_admin |
575 | + False |
576 | + |
577 | + |
578 | +Batch navigator |
579 | +=============== |
580 | + |
581 | +The view returns a batch navigator for searching through sets of teams, |
582 | +people, or both. By default, both are searched for the given name. There is |
583 | +one person and one team matching the 'test' string. |
584 | + |
585 | + >>> from zope.security.proxy import removeSecurityProxy |
586 | + >>> def print_batch(batch): |
587 | + ... for thing in batch.currentBatch(): |
588 | + ... naked_thing = removeSecurityProxy(thing) |
589 | + ... print naked_thing |
590 | + |
591 | + >>> form = dict(name='test') |
592 | + >>> view = create_initialized_view(person_set, '+index', form=form) |
593 | + >>> print_batch(view.searchPeopleBatchNavigator()) |
594 | + <Person at ... name12 (Sample Person)> |
595 | + <Person at ... testing-spanish-team (testing Spanish team)> |
596 | + |
597 | +Searching for just people returns Sample Person. |
598 | + |
599 | + >>> form['searchfor'] = 'peopleonly' |
600 | + >>> view = create_initialized_view(person_set, '+index', form=form) |
601 | + >>> print_batch(view.searchPeopleBatchNavigator()) |
602 | + <Person at ... name12 (Sample Person)> |
603 | + |
604 | +Searching for just teams returns the testing Spanish team. |
605 | + |
606 | + >>> form['searchfor'] = 'teamsonly' |
607 | + >>> view = create_initialized_view(person_set, '+index', form=form) |
608 | + >>> print_batch(view.searchPeopleBatchNavigator()) |
609 | + <Person at ... testing-spanish-team (testing Spanish team)> |
610 | |
611 | === modified file 'lib/lp/registry/templates/people-index.pt' |
612 | --- lib/lp/registry/templates/people-index.pt 2009-07-17 17:59:07 +0000 |
613 | +++ lib/lp/registry/templates/people-index.pt 2009-08-18 14:16:16 +0000 |
614 | @@ -6,39 +6,36 @@ |
615 | xml:lang="en" |
616 | lang="en" |
617 | dir="ltr" |
618 | - metal:use-macro="view/macro:page/pillarindex" |
619 | + metal:use-macro="view/macro:page/main_side" |
620 | i18n:domain="launchpad" |
621 | > |
622 | <body> |
623 | <h1 metal:fill-slot="heading">People and teams</h1> |
624 | |
625 | - <metal:leftportlets fill-slot="portlets_one"> |
626 | - <div tal:replace="structure context/@@+portlet-about" /> |
627 | - </metal:leftportlets> |
628 | - |
629 | - <metal:rightportlets fill-slot="portlets_two"> |
630 | - <div tal:replace="structure context/@@+portlet-stats" /> |
631 | - </metal:rightportlets> |
632 | - |
633 | - <h1 metal:fill-slot="application-heading">People and teams</h1> |
634 | + <tal:side metal:fill-slot="side"> |
635 | + <tal:menu replace="structure view/@@+global-actions" /> |
636 | + </tal:side> |
637 | |
638 | <div metal:fill-slot="main"> |
639 | - <tal:block define="bn view/searchPeopleBatchNavigator"> |
640 | - <div align="center"> |
641 | + There are currently <span tal:replace="view/number_of_people" /> people |
642 | + and <span tal:replace="view/number_of_teams" /> teams registered in |
643 | + Launchpad. |
644 | + <tal:block define="batch view/searchPeopleBatchNavigator"> |
645 | + <div> |
646 | <form class="central" name="search" action="." method="GET"> |
647 | <table style="margin-bottom: 1em;"> |
648 | <tr> |
649 | <td> |
650 | - <input type="text" name="name" size="35" |
651 | + <input type="text" name="name" size="50" |
652 | tal:attributes="value request/name|nothing" |
653 | /> |
654 | <input |
655 | - tal:condition="not: bn" |
656 | + tal:condition="not: batch" |
657 | type="submit" |
658 | value="Search" |
659 | /> |
660 | <input |
661 | - tal:condition="bn" |
662 | + tal:condition="batch" |
663 | type="submit" |
664 | value="Search Again" |
665 | /> |
666 | @@ -110,57 +107,57 @@ |
667 | // --></script> |
668 | </div> |
669 | |
670 | - <tal:block condition="not: bn"> |
671 | - <p id="application-summary"> |
672 | - Launchpad may create <b>duplicate profiles</b> for you |
673 | - as it automatically reviews translation files, mailing lists and |
674 | - other forums. If you find you have more than one profile here, |
675 | - perhaps for multiple email addresses, you can |
676 | - <a href="+requestmerge">merge the duplicate accounts</a>. |
677 | - </p> |
678 | + <tal:block condition="not: batch"> |
679 | + <div id="application-summary"> |
680 | + <p>Launchpad creates |
681 | + <a href="https://help.launchpad.net/YourAccount">profiles</a> for |
682 | + people based on Launchpad usage, as well as information collected |
683 | + from public sources such as bug trackers, mailing lists, public |
684 | + key servers, and published application translations. You can |
685 | + <a href="+me">manage your account</a> in Launchpad, and you can |
686 | + <a href="+newteam">create teams of people</a> for organizing |
687 | + around common interests, discussions or permissions.</p> |
688 | + |
689 | + <p>Launchpad may create |
690 | + <a href="https://help.launchpad.net/YourAccount/Merging">duplicate |
691 | + profiles</a> for you as it automatically collects information from |
692 | + public sources. If you find you have more than one profile on |
693 | + Launchpad, you can <a href="+requestmerge">request a merge</a> of |
694 | + the duplicate profiles.</p> |
695 | + |
696 | + <p tal:condition="view/is_admin">As a Launchpad administrator, you |
697 | + can also service |
698 | + <a href="+adminpeoplemerge">person merge requests</a> and |
699 | + <a href="+adminteammerge">team merge requests</a>. |
700 | + </p> |
701 | + </div> |
702 | </tal:block> |
703 | - <tal:block condition="bn"> |
704 | - <tal:block |
705 | - define="prev_url bn/prevBatchURL; |
706 | - next_url bn/nextBatchURL;" |
707 | - > |
708 | - <table class="listing"> |
709 | - <thead class="results"> |
710 | - <tr tal:condition="python:prev_url or next_url"> |
711 | - <td colspan="3"> |
712 | - <div style="float: right; text-align: right;"> |
713 | - <tal:block condition="prev_url"> |
714 | - <a tal:attributes="href prev_url">Previous</a> |
715 | - </tal:block> |
716 | - <tal:block repeat="page_link bn/batchPageURLs"> |
717 | - <a tal:attributes="href python:page_link.values()[0]" |
718 | - tal:content="python:page_link.keys()[0]"></a> |
719 | - </tal:block> |
720 | - <tal:block condition="next_url" > |
721 | - <a tal:attributes="href next_url">Next</a> |
722 | - </tal:block> |
723 | - </div> |
724 | - </td> |
725 | - </tr> |
726 | - <tr> |
727 | - <th colspan="2"> |
728 | - Name |
729 | - </th> |
730 | - <th> |
731 | - Karma |
732 | - </th> |
733 | - </tr> |
734 | - </thead> |
735 | - <tbody> |
736 | - <tr tal:repeat="person bn/currentBatch"> |
737 | - <td><a tal:content="person/displayname" |
738 | - tal:attributes="href person/fmt:url"/> |
739 | - </td> |
740 | - <td tal:content="person/name">foobar</td> |
741 | - <td class="amount" tal:content="person/karma">34</td> |
742 | - </tr> |
743 | - </tbody> |
744 | - </table> |
745 | + <tal:block condition="batch"> |
746 | + <tal:block condition="batch/multiple_pages"> |
747 | + <tal:navigation |
748 | + replace="structure batch/@@+navigation-links-upper"/> |
749 | + </tal:block> |
750 | + <table class="listing"> |
751 | + <thead> |
752 | + <tr> |
753 | + <th>Name</th> |
754 | + <th>Launchpad ID</th> |
755 | + <th>Karma</th> |
756 | + </tr> |
757 | + </thead> |
758 | + <tbody> |
759 | + <tr tal:repeat="person batch/currentBatch"> |
760 | + <td><a tal:content="person/displayname" |
761 | + tal:attributes="href person/fmt:url"/> |
762 | + </td> |
763 | + <td tal:content="person/name">foobar</td> |
764 | + <td class="amount" tal:content="person/karma">34</td> |
765 | + </tr> |
766 | + </tbody> |
767 | + </table> |
768 | + <tal:block condition="batch/multiple_pages"> |
769 | + <tal:navigation |
770 | + replace="structure batch/@@+navigation-links-upper"/> |
771 | </tal:block> |
772 | </tal:block> |
773 | </tal:block> |
774 | |
775 | === removed file 'lib/lp/registry/templates/people-portlet-about.pt' |
776 | --- lib/lp/registry/templates/people-portlet-about.pt 2009-07-17 17:59:07 +0000 |
777 | +++ lib/lp/registry/templates/people-portlet-about.pt 1970-01-01 00:00:00 +0000 |
778 | @@ -1,22 +0,0 @@ |
779 | -<tal:root |
780 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
781 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
782 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
783 | - omit-tag=""> |
784 | -<div class="portlet" id="portlet-about"> |
785 | - |
786 | - <h2>About these profiles</h2> |
787 | - |
788 | - <div class="portletBody portletContent"> |
789 | - |
790 | - <img alt="" src="/@@/info" /> Launchpad profiles are based on actual |
791 | - usage of Launchpad, as well as information available from public sources |
792 | - such as bug trackers, mailing lists, public key servers and published |
793 | - application translations. You can manage your account in the Launchpad, |
794 | - and you can create teams of people who can be maintainers, translators, |
795 | - members, clubs... almost any kind of grouping you need. |
796 | - |
797 | - </div> |
798 | - |
799 | -</div> |
800 | -</tal:root> |
801 | |
802 | === removed file 'lib/lp/registry/templates/people-portlet-stats.pt' |
803 | --- lib/lp/registry/templates/people-portlet-stats.pt 2009-07-17 17:59:07 +0000 |
804 | +++ lib/lp/registry/templates/people-portlet-stats.pt 1970-01-01 00:00:00 +0000 |
805 | @@ -1,19 +0,0 @@ |
806 | -<tal:root |
807 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
808 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
809 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
810 | - omit-tag=""> |
811 | -<div class="portlet" id="portlet-stats"> |
812 | - |
813 | - <h2>Statistics</h2> |
814 | - |
815 | - <div class="portletBody portletContent"> |
816 | - |
817 | - There are currently <span tal:replace="view/peopleCount" /> people |
818 | - and <span tal:replace="view/teamsCount" /> teams registered in |
819 | - Launchpad. |
820 | - |
821 | - </div> |
822 | - |
823 | -</div> |
824 | -</tal:root> |
reviewer sinzui
= Summary =
This branch fixes bug 413158, updating the /people top level page to UI 3.0.
== Proposed fix ==
Update the people-index.pt template and clean up the UI as necessary.
== Pre-implementation notes ==
Curtis and Martin have seen mockups of the page and made comments on the
original bug report. Many of these have been addressed but there are still
imperfection in the page. In the interest of time boxing, we'll deal with
those at a later date.
== Implementation details ==
There are still two known imperfections that I won't fix now. First, the
style-3.0.css isn't quite right for the <th> column headers. In style.css,
these headings are red and bold, but in style-3.0.css they are only red,
though they are also slightly larger. The problem is that the thead styles
are reset in style-3.0.css and although I copied the thead styles over, the
font-weight does not seem to work. I don't know why.
The other imperfection is that there is only one navigation menu, even though
it contains a few actions. This is a legitimate case for having two menus,
but I have been unable to get two menus working. The single menu approach
doesn't look horrible, so I consider this acceptable in the interest of time
boxing the work.
I have refactored the calculation of whether a batch navigation has multiple
pages into the base class, and added a test. Previously, this test was in the
branch listing page, but it's of general usefulness and belongs in the base
class.
The /people/ +portlet- about text has been moved inline so this portlet was +portlet- stats inline and removed that portlet.
removed, as was a test that depended on it. I also moved
/people/
I refactored the top-level object menu into a mixin. This menu will be shared
with the top-level projects and project-group pages when I update their UI
next.
I renamed FOAFSearchView to PeopleSearchView since we're not really doing FOAF.
lint complained about some duplicate methods in class emsMixin. In fact, there's no way the shadowed (i.e. earlier)
BranchListingIt
methods could possibly be called, so I removed them. I'm about to send the
branch through ec2 so we'll see if they broke anything, but I'd be quite
surprised if they did.
I did some other refactoring and cleanup I found along the way.
== Tests ==
bin/test -vv -t batch_navigatio n.txt -t people-views.txt
== Demo and Q/A ==
* Visit http:// launchpad. dev/people as an admin, a normal user, and not
logged in. As an admin you'll get a little extra text in the narrative,
and as an anonymous user you'll get a 'Create an account' action link.
This was approved by Martin.
* As a normal user, visit http:// launchpad. dev/people and search for 's' to
see the multi-page batch navigation.
* As a normal user, visit http:// launchpad. dev/people and search for 'test'
to see the single-page batch navigation.
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files: registry/ browser/ configure. zcml /launchpad/ pagetests/ basics/ notfound- traversals. txt registry/ browser/ m...
lib/lp/
lib/canonical
lib/lp/