Merge lp:~barry/launchpad/413158-people into lp:launchpad

Proposed by Barry Warsaw
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
Reviewer Review Type Date Requested Status
Curtis Hovey (community) Approve
Review via email: mp+10323@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Barry Warsaw (barry) wrote :
Download full text (3.6 KiB)

 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
removed, as was a test that depended on it. I also moved
/people/+portlet-stats inline and removed that portlet.

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
BranchListingItemsMixin. In fact, there's no way the shadowed (i.e. earlier)
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_navigation.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:
  lib/lp/registry/browser/configure.zcml
  lib/canonical/launchpad/pagetests/basics/notfound-traversals.txt
  lib/lp/registry/browser/m...

Read more...

Revision history for this message
Curtis Hovey (sinzui) wrote :
Download full text (12.5 KiB)

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/launchpad/icing/style-3-0.css'
> --- lib/canonical/launchpad/icing/style-3-0.css 2009-08-17 11:22:39 +0000
> +++ lib/canonical/launchpad/icing/style-3-0.css 2009-08-17 17:50:02 +0000
> @@ -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/launchpad/webapp/batching.py'
> --- lib/canonical/launchpad/webapp/batching.py 2009-06-25 05:30:52 +0000
> +++ lib/canonical/launchpad/webapp/batching.py 2009-08-14 18:41:41 +0000
> @@ -62,6 +62,10 @@
> def max_batch_size(self):
> return config.launchpad.max_batch_size
>
> + @property
> + def multiple_pages(self):

can you add a docstring? Is has_multiple_pages a better name for what it
returns?

...

> === modified file 'lib/lp/code/browser/branchlisting.py'
> --- lib/lp/code/browser/branchlisting.py 2009-08-13 04:08:38 +0000
> +++ lib/lp/code/browser/branchlisting.py 2009-08-18 14:11:56 +0000
> @@ -331,56 +331,13 @@
> branches = list(self.currentBatch())
> # 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_permission_for_objects(request, 'launchpad.View', branches)
> return branches
>
> @cachedproperty
> - def branch_ids_with_bug_links(self):

I am not sure why this big chunk of code was removed. I this diff lying?

...

> === added file 'lib/lp/registry/browser/menu.py'
> --- lib/lp/registry/browser/menu.py 1970-01-01 00:00:00 +0000
> +++ lib/lp/registry/browser/menu.py 2009-08-18 13:54:51 +0000
> @@ -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__ = [
> + 'TopLevelMenuMixin',
> + ]
> +
> +
> +from canonical.launchpad.webapp.menu import Link
> +
> +
> +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...

review: Needs Fixing (code)
Revision history for this message
Barry Warsaw (barry) wrote :
Download full text (35.0 KiB)

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/launchpad/icing/style-3-0.css'
>> --- lib/canonical/launchpad/icing/style-3-0.css 2009-08-17 11:22:39 +0000
>> +++ lib/canonical/launchpad/icing/style-3-0.css 2009-08-17 17:50:02 +0000
>> @@ -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/launchpad/webapp/batching.py'
>> --- lib/canonical/launchpad/webapp/batching.py 2009-06-25 05:30:52 +0000
>> +++ lib/canonical/launchpad/webapp/batching.py 2009-08-14 18:41:41 +0000
>> @@ -62,6 +62,10 @@
>> def max_batch_size(self):
>> return config.launchpad.max_batch_size
>>
>> + @property
>> + def multiple_pages(self):
>
>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_pages' is a better name. So changed.

>> === modified file 'lib/lp/code/browser/branchlisting.py'
>> --- lib/lp/code/browser/branchlisting.py 2009-08-13 04:08:38 +0000
>> +++ lib/lp/code/browser/branchlisting.py 2009-08-18 14:11:56 +0000
>> @@ -331,56 +331,13 @@
>> branches = list(self.currentBatch())
>> # 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_permission_for_objects(request, 'launchpad.View', branches)
>> return branches
>>
>> @cachedproperty
>> - def branch_ids_with_bug_links(self):
>
>I am not...

Revision history for this message
Curtis Hovey (sinzui) wrote :
Download full text (8.2 KiB)

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/registry/browser/person.py'
> >> --- lib/lp/registry/browser/person.py 2009-08-03 15:21:35 +0000
> >> +++ lib/lp/registry/browser/person.py 2009-08-18 15:28:28 +0000
>
...

> implements(IRegistryCollectionNavigationMenu)
> >
> >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 PersonSetNavigationMenu(NavigationMenu):
> > """Navigation menu for PersonSet actions"""
> > usedfor = IPersonSet
> > links = [
> > register_team','adminpeoplemerge', 'adminteammerge', 'mergeaccounts']
> >
> >
> >class IRegistryCollectionNavigationMenu(Interface):
> > """Marker interface for the registry collection navigation menu."""
> >
> >
> >class RegistryCollectionNavigationMenu(NavigationMenu, TopLevelMenuMixin):
> > """Navigation menu for registry person search collection."""
> > usedfor = IRegistryCollectionNavigationMenu
> > facet = 'overview'
> > links = ['products', 'distributions', 'people', 'meetings']
> >
> >
> >class PeopleSearchView(LaunchpadView):
> > """Search for people and teams on the /people page."""
> > implements(IRegistryCollectionNavigationMenu)
> >
> >}}}
>
> 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 (RegistryCollectionNavigationMenu). All the views (that already exist) just need to add the implements.

I am not sure of the value of RegistryCollectionActionMenuBase. I do not think those links are usable between collections. Those links belong to the IPersonSet only. You might as well move those links to the one true implementing class PersonSetNavigationMenu

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...

Read more...

review: Needs Fixing (code)
Revision history for this message
Barry Warsaw (barry) wrote :
Download full text (12.1 KiB)

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 PersonSetNavigationMenu(NavigationMenu):
>> > """Navigation menu for PersonSet actions"""
>> > usedfor = IPersonSet
>> > links = [
>> > register_team','adminpeoplemerge', 'adminteammerge', 'mergeaccounts']
>> >
>> >
>> >class IRegistryCollectionNavigationMenu(Interface):
>> > """Marker interface for the registry collection navigation menu."""
>> >
>> >
>> >class RegistryCollectionNavigationMenu(NavigationMenu, TopLevelMenuMixin):
>> > """Navigation menu for registry person search collection."""
>> > usedfor = IRegistryCollectionNavigationMenu
>> > facet = 'overview'
>> > links = ['products', 'distributions', 'people', 'meetings']
>> >
>> >
>> >class PeopleSearchView(LaunchpadView):
>> > """Search for people and teams on the /people page."""
>> > implements(IRegistryCollectionNavigationMenu)
>> >
>> >}}}
>>
>> 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 (RegistryCollectionNavigationMenu). All the views (that already exist) just need to add the implements.
>
>I am not sure of the value of RegistryCollectionActionMenuBase. I do not think those links are usable between collections. Those links belong to the IPersonSet only. You might as well move those links to the one true implementing class PersonSetNavigationMenu

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 RegistryCollectionActionMenuBase.

>
>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/registry/browser/menu.py'
>> --- lib/lp/registry/browser/menu.py 2009-08-18 13:54:51 +0000
>> +++ lib/lp/registry/browser/menu.py 2009-08-18 23:27:23 +0000
>
>...
>
>> @@ -27,14 +34,72 @@
>> def meetings(self):
>> return Link('/sprints/', 'View meetings', icon='info')
>>
>> +...

Revision history for this message
Curtis Hovey (sinzui) wrote :

Hi barry.

Your changes look great. I accept you arguments to keep the base menu.

  review approve

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/doc/batch_navigation.txt'
--- lib/canonical/launchpad/doc/batch_navigation.txt 2009-03-24 12:43:49 +0000
+++ lib/canonical/launchpad/doc/batch_navigation.txt 2009-08-14 18:41:41 +0000
@@ -1,4 +1,6 @@
1= Batch Navigation =1================
2Batch Navigation
3================
24
3Most of the test for this behavior is in the lazr.batchnavigation package.5Most of the test for this behavior is in the lazr.batchnavigation package.
46
@@ -44,10 +46,12 @@
44 >>> reindeer = ['Dasher', 'Dancer', 'Prancer', 'Vixen', 'Comet',46 >>> reindeer = ['Dasher', 'Dancer', 'Prancer', 'Vixen', 'Comet',
45 ... 'Cupid', 'Donner', 'Blitzen', 'Rudolph']47 ... 'Cupid', 'Donner', 'Blitzen', 'Rudolph']
4648
47== Performance with SQLObject ==49
4850Performance with SQLObject
49This section demonstrates that batching generates sensible SQL queries when used51==========================
50with SQLObject, i.e. that it puts appropriate LIMIT clauses on queries.52
53This section demonstrates that batching generates sensible SQL queries when
54used with SQLObject, i.e. that it puts appropriate LIMIT clauses on queries.
5155
52Imports:56Imports:
5357
@@ -58,8 +62,7 @@
58Prepare a query, and create a batch of the results:62Prepare a query, and create a batch of the results:
5963
60 >>> select_results = EmailAddress.select(orderBy='id')64 >>> select_results = EmailAddress.select(orderBy='id')
61 >>> batch_nav = BatchNavigator(select_results, build_request(),65 >>> batch_nav = BatchNavigator(select_results, build_request(), size=10)
62 ... size=10)
63 >>> email_batch = batch_nav.currentBatch()66 >>> email_batch = batch_nav.currentBatch()
64 >>> batch_items = list(email_batch)67 >>> batch_items = list(email_batch)
6568
@@ -85,7 +88,17 @@
85 True88 True
8689
8790
88== Maximum batch size ==91Multiple pages
92==============
93
94The batch navigator tells us whether multiple pages will be used.
95
96 >>> batch_nav.multiple_pages
97 True
98
99
100Maximum batch size
101==================
89102
90Since the batch size is exposed in the URL, it's possible for users to103Since the batch size is exposed in the URL, it's possible for users to
91tweak the batch parameter to retrieve more results. Since that may104tweak the batch parameter to retrieve more results. Since that may
@@ -108,7 +121,8 @@
108 >>> ignored = config.pop('max-batch-size')121 >>> ignored = config.pop('max-batch-size')
109122
110123
111== Batch views ==124Batch views
125===========
112126
113A view is often used with a BatchNavigator to determine when to127A view is often used with a BatchNavigator to determine when to
114display the current batch.128display the current batch.
115129
=== modified file 'lib/canonical/launchpad/icing/style-3-0.css'
--- lib/canonical/launchpad/icing/style-3-0.css 2009-08-17 11:22:39 +0000
+++ lib/canonical/launchpad/icing/style-3-0.css 2009-08-17 17:50:02 +0000
@@ -590,3 +590,10 @@
590 word-wrap: break-word;590 word-wrap: break-word;
591}591}
592592
593/* ==== Listing tables ==== */
594
595table.listing thead {
596 color: #9f2b33;
597 font-size: 1.125em;
598 font-weight: bold;
599}
593600
=== modified file 'lib/canonical/launchpad/pagetests/basics/notfound-traversals.txt'
--- lib/canonical/launchpad/pagetests/basics/notfound-traversals.txt 2009-08-13 19:20:04 +0000
+++ lib/canonical/launchpad/pagetests/basics/notfound-traversals.txt 2009-08-17 17:50:02 +0000
@@ -368,7 +368,6 @@
368>>> check("/people/")368>>> check("/people/")
369>>> check("/people/+requestmerge", auth=True)369>>> check("/people/+requestmerge", auth=True)
370>>> check_redirect("/people/+mergerequest-sent", auth=True)370>>> check_redirect("/people/+mergerequest-sent", auth=True)
371>>> check("/people/+portlet-about")
372371
373This is for a team:372This is for a team:
374373
375374
=== modified file 'lib/canonical/launchpad/webapp/batching.py'
--- lib/canonical/launchpad/webapp/batching.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/launchpad/webapp/batching.py 2009-08-14 18:41:41 +0000
@@ -62,6 +62,10 @@
62 def max_batch_size(self):62 def max_batch_size(self):
63 return config.launchpad.max_batch_size63 return config.launchpad.max_batch_size
6464
65 @property
66 def multiple_pages(self):
67 return self.batch.total() > self.batch.size
68
6569
66class TableBatchNavigator(BatchNavigator):70class TableBatchNavigator(BatchNavigator):
67 """See canonical.launchpad.interfaces.ITableBatchNavigator."""71 """See canonical.launchpad.interfaces.ITableBatchNavigator."""
6872
=== modified file 'lib/lp/code/browser/branchlisting.py'
--- lib/lp/code/browser/branchlisting.py 2009-08-13 04:08:38 +0000
+++ lib/lp/code/browser/branchlisting.py 2009-08-18 14:11:56 +0000
@@ -331,56 +331,13 @@
331 branches = list(self.currentBatch())331 branches = list(self.currentBatch())
332 # XXX: TimPenhey 2009-04-08 bug=324546332 # XXX: TimPenhey 2009-04-08 bug=324546
333 # Until there is an API to do this nicely, shove the launchpad.view333 # Until there is an API to do this nicely, shove the launchpad.view
334 # permission into the request cache directly. This is done because the334 # permission into the request cache directly. This is done because
335 # security proxy sometimes causes extra requests to be made.335 # the security proxy sometimes causes extra requests to be made.
336 request = self.view.request336 request = self.view.request
337 precache_permission_for_objects(request, 'launchpad.View', branches)337 precache_permission_for_objects(request, 'launchpad.View', branches)
338 return branches338 return branches
339339
340 @cachedproperty340 @cachedproperty
341 def branch_ids_with_bug_links(self):
342 """Return a set of branch ids that should show bug badges."""
343 bug_branches = getUtility(IBugBranchSet).getBugBranchesForBranches(
344 self._branches_for_current_batch, self.view.user)
345 return set(bug_branch.branch.id for bug_branch in bug_branches)
346
347 @cachedproperty
348 def branch_ids_with_spec_links(self):
349 """Return a set of branch ids that should show blueprint badges."""
350 spec_branches = getUtility(
351 ISpecificationBranchSet).getSpecificationBranchesForBranches(
352 self._branches_for_current_batch, self.view.user)
353 return set(spec_branch.branch.id for spec_branch in spec_branches)
354
355 @cachedproperty
356 def branch_ids_with_merge_proposals(self):
357 """Return a set of branches that should show merge proposal badges.
358
359 Branches have merge proposals badges if they've been proposed for
360 merging into another branch (source branches).
361 """
362 branches = self.view._getCollection().visibleByUser(self.view.user)
363 proposals = branches.getMergeProposals(
364 for_branch=self._branch_for_current_batch)
365 return set(proposal.source_branch.id for proposal in proposals)
366
367 @cachedproperty
368 def tip_revisions(self):
369 """Return a set of branch ids that should show blueprint badges."""
370 revisions = getUtility(IRevisionSet).getTipRevisionsForBranches(
371 self._branches_for_current_batch)
372 if revisions is None:
373 revision_map = {}
374 else:
375 # Key the revisions by revision id.
376 revision_map = dict((revision.revision_id, revision)
377 for revision in revisions)
378
379 # Return a dict keyed on branch id.
380 return dict((branch.id, revision_map.get(branch.last_scanned_id))
381 for branch in self._branches_for_current_batch)
382
383 @cachedproperty
384 def product_series_map(self):341 def product_series_map(self):
385 """Return a map of branch id to a list of product series.342 """Return a map of branch id to a list of product series.
386343
@@ -526,10 +483,6 @@
526 """Return a list of BranchListingItems."""483 """Return a list of BranchListingItems."""
527 return self.decoratedBranches(self.visible_branches_for_view)484 return self.decoratedBranches(self.visible_branches_for_view)
528485
529 @cachedproperty
530 def multiple_pages(self):
531 return self.batch.total() > self.batch.size
532
533 @property486 @property
534 def table_class(self):487 def table_class(self):
535 # XXX: MichaelHudson 2007-10-18 bug=153894: This means there are two488 # XXX: MichaelHudson 2007-10-18 bug=153894: This means there are two
536489
=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml 2009-08-14 00:52:28 +0000
+++ lib/lp/registry/browser/configure.zcml 2009-08-18 13:54:51 +0000
@@ -794,18 +794,20 @@
794 <browser:menus794 <browser:menus
795 module="lp.registry.browser.person"795 module="lp.registry.browser.person"
796 classes="796 classes="
797 PeopleSearchNavigationMenu
798 PersonBugsMenu
799 PersonEditNavigationMenu
797 PersonFacets800 PersonFacets
798 PersonOverviewMenu801 PersonOverviewMenu
799 TeamOverviewMenu802 PersonOverviewNavigationMenu
800 PersonBugsMenu803 PersonRelatedSoftwareNavigationMenu
804 PersonSetContextMenu
801 PersonSpecsMenu805 PersonSpecsMenu
802 TeamBugsMenu806 TeamBugsMenu
807 TeamOverviewMenu
808 TeamOverviewNavigationMenu
803 TeamSpecsMenu809 TeamSpecsMenu
804 PersonSetContextMenu810 "/>
805 PersonOverviewNavigationMenu
806 PersonEditNavigationMenu
807 PersonRelatedSoftwareNavigationMenu
808 TeamOverviewNavigationMenu"/>
809 <browser:url811 <browser:url
810 for="lp.registry.interfaces.person.IPerson"812 for="lp.registry.interfaces.person.IPerson"
811 path_expression="string:~${name}"813 path_expression="string:~${name}"
@@ -1242,13 +1244,10 @@
1242 <browser:pages1244 <browser:pages
1243 for="lp.registry.interfaces.person.IPersonSet"1245 for="lp.registry.interfaces.person.IPersonSet"
1244 permission="zope.Public"1246 permission="zope.Public"
1245 class="lp.registry.browser.person.FOAFSearchView">1247 class="lp.registry.browser.person.PeopleSearchView">
1246 <browser:page1248 <browser:page
1247 name="+index"1249 name="+index"
1248 template="../templates/people-index.pt"/>1250 template="../templates/people-index.pt"/>
1249 <browser:page
1250 name="+portlet-stats"
1251 template="../templates/people-portlet-stats.pt"/>
1252 </browser:pages>1251 </browser:pages>
1253 <browser:page1252 <browser:page
1254 name="+requestmerge"1253 name="+requestmerge"
@@ -1284,13 +1283,6 @@
1284 name="+mergerequest-sent"1283 name="+mergerequest-sent"
1285 template="../templates/people-mergerequest-sent.pt"/>1284 template="../templates/people-mergerequest-sent.pt"/>
1286 </browser:pages>1285 </browser:pages>
1287 <browser:pages
1288 for="lp.registry.interfaces.person.IPersonSet"
1289 permission="zope.Public">
1290 <browser:page
1291 name="+portlet-about"
1292 template="../templates/people-portlet-about.pt"/>
1293 </browser:pages>
1294 </facet>1286 </facet>
1295 <browser:navigation1287 <browser:navigation
1296 module="lp.registry.browser.milestone"1288 module="lp.registry.browser.milestone"
12971289
=== added file 'lib/lp/registry/browser/menu.py'
--- lib/lp/registry/browser/menu.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/browser/menu.py 2009-08-18 13:54:51 +0000
@@ -0,0 +1,40 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Shared menus."""
5
6__metaclass__ = type
7__all__ = [
8 'TopLevelMenuMixin',
9 ]
10
11
12from canonical.launchpad.webapp.menu import Link
13
14
15class TopLevelMenuMixin:
16 """Menu shared by top level collection objects."""
17
18 def products(self):
19 return Link('/projects/', 'View projects', icon='info')
20
21 def distributions(self):
22 return Link('/distros/', 'View distributions', icon='info')
23
24 def people(self):
25 return Link('/people/', 'View people', icon='info')
26
27 def meetings(self):
28 return Link('/sprints/', 'View meetings', icon='info')
29
30 def register_project(self):
31 text = 'Register a project'
32 return Link('/projects/+new', text, icon='add')
33
34 def register_team(self):
35 text = 'Register a team'
36 return Link('/people/+newteam', text, icon='add')
37
38 def create_account(self):
39 text = 'Create an account'
40 return Link('/people/+login', text, icon='add')
041
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py 2009-08-03 15:21:35 +0000
+++ lib/lp/registry/browser/person.py 2009-08-18 15:28:28 +0000
@@ -10,11 +10,12 @@
10__all__ = [10__all__ = [
11 'BeginTeamClaimView',11 'BeginTeamClaimView',
12 'BugSubscriberPackageBugsSearchListingView',12 'BugSubscriberPackageBugsSearchListingView',
13 'FOAFSearchView',
14 'EmailToPersonView',13 'EmailToPersonView',
14 'PeopleSearchMenu'
15 'PeopleSearchView',
15 'PersonAccountAdministerView',16 'PersonAccountAdministerView',
17 'PersonAddView',
16 'PersonAdministerView',18 'PersonAdministerView',
17 'PersonAddView',
18 'PersonAnswerContactForView',19 'PersonAnswerContactForView',
19 'PersonAnswersMenu',20 'PersonAnswersMenu',
20 'PersonAssignedBugTaskSearchListingView',21 'PersonAssignedBugTaskSearchListingView',
@@ -30,10 +31,10 @@
30 'PersonEditHomePageView',31 'PersonEditHomePageView',
31 'PersonEditIRCNicknamesView',32 'PersonEditIRCNicknamesView',
32 'PersonEditJabberIDsView',33 'PersonEditJabberIDsView',
34 'PersonEditLocationView',
33 'PersonEditSSHKeysView',35 'PersonEditSSHKeysView',
34 'PersonEditView',36 'PersonEditView',
35 'PersonEditWikiNamesView',37 'PersonEditWikiNamesView',
36 'PersonEditLocationView',
37 'PersonFacets',38 'PersonFacets',
38 'PersonGPGView',39 'PersonGPGView',
39 'PersonIndexView',40 'PersonIndexView',
@@ -42,8 +43,8 @@
42 'PersonNavigation',43 'PersonNavigation',
43 'PersonOAuthTokensView',44 'PersonOAuthTokensView',
44 'PersonOverviewMenu',45 'PersonOverviewMenu',
46 'PersonRdfContentsView',
45 'PersonRdfView',47 'PersonRdfView',
46 'PersonRdfContentsView',
47 'PersonRelatedBugTaskSearchListingView',48 'PersonRelatedBugTaskSearchListingView',
48 'PersonRelatedSoftwareView',49 'PersonRelatedSoftwareView',
49 'PersonReportedBugTaskSearchListingView',50 'PersonReportedBugTaskSearchListingView',
@@ -51,9 +52,9 @@
51 'PersonSetContextMenu',52 'PersonSetContextMenu',
52 'PersonSetNavigation',53 'PersonSetNavigation',
53 'PersonSpecFeedbackView',54 'PersonSpecFeedbackView',
55 'PersonSpecWorkloadTableView',
56 'PersonSpecWorkloadView',
54 'PersonSpecsMenu',57 'PersonSpecsMenu',
55 'PersonSpecWorkloadView',
56 'PersonSpecWorkloadTableView',
57 'PersonSubscribedBugTaskSearchListingView',58 'PersonSubscribedBugTaskSearchListingView',
58 'PersonView',59 'PersonView',
59 'PersonVouchersView',60 'PersonVouchersView',
@@ -66,14 +67,14 @@
66 'SearchNeedAttentionQuestionsView',67 'SearchNeedAttentionQuestionsView',
67 'SearchSubscribedQuestionsView',68 'SearchSubscribedQuestionsView',
68 'TeamAddMyTeamsView',69 'TeamAddMyTeamsView',
70 'TeamBreadcrumbBuilder',
69 'TeamEditLocationView',71 'TeamEditLocationView',
70 'TeamJoinView',72 'TeamJoinView',
71 'TeamBreadcrumbBuilder',
72 'TeamLeaveView',73 'TeamLeaveView',
74 'TeamMembershipView',
75 'TeamMugshotView',
73 'TeamNavigation',76 'TeamNavigation',
74 'TeamOverviewMenu',77 'TeamOverviewMenu',
75 'TeamMembershipView',
76 'TeamMugshotView',
77 'TeamReassignmentView',78 'TeamReassignmentView',
78 'TeamSpecsMenu',79 'TeamSpecsMenu',
79 'archive_to_person',80 'archive_to_person',
@@ -198,6 +199,7 @@
198from canonical.launchpad.browser.branding import BrandingChangeView199from canonical.launchpad.browser.branding import BrandingChangeView
199from lp.registry.browser.mailinglists import (200from lp.registry.browser.mailinglists import (
200 enabled_with_active_mailing_list)201 enabled_with_active_mailing_list)
202from lp.registry.browser.menu import TopLevelMenuMixin
201from lp.answers.browser.questiontarget import SearchQuestionsView203from lp.answers.browser.questiontarget import SearchQuestionsView
202204
203from canonical.launchpad.fields import LocationField205from canonical.launchpad.fields import LocationField
@@ -627,29 +629,14 @@
627 canonical_url(me, request=self.request), status=303)629 canonical_url(me, request=self.request), status=303)
628630
629631
630class PersonSetContextMenu(ContextMenu):632class PersonSetContextMenu(ContextMenu, TopLevelMenuMixin):
631633
632 usedfor = IPersonSet634 usedfor = IPersonSet
633635
634 links = ['products', 'distributions', 'people', 'meetings', 'newteam',636 links = ['products', 'distributions', 'people', 'meetings',
637 'register_team',
635 'adminpeoplemerge', 'adminteammerge', 'mergeaccounts']638 'adminpeoplemerge', 'adminteammerge', 'mergeaccounts']
636639
637 def products(self):
638 return Link('/projects/', 'View projects')
639
640 def distributions(self):
641 return Link('/distros/', 'View distributions')
642
643 def people(self):
644 return Link('/people/', 'View people')
645
646 def meetings(self):
647 return Link('/sprints/', 'View meetings')
648
649 def newteam(self):
650 text = 'Register a team'
651 return Link('+newteam', text, icon='add')
652
653 def mergeaccounts(self):640 def mergeaccounts(self):
654 text = 'Merge accounts'641 text = 'Merge accounts'
655 return Link('+requestmerge', text, icon='edit')642 return Link('+requestmerge', text, icon='edit')
@@ -1348,35 +1335,64 @@
1348 return self.proposed_memberships or self.invited_memberships1335 return self.proposed_memberships or self.invited_memberships
13491336
13501337
1351class FOAFSearchView:1338class IPeopleSearchNavigationMenu(Interface):
1339 """Marker interface for people search navigation menu."""
1340
1341
1342class PeopleSearchNavigationMenu(NavigationMenu, TopLevelMenuMixin):
1343 """Navigation menu for people search."""
1344
1345 usedfor = IPeopleSearchNavigationMenu
1346 facet = 'overview'
1347
1348 links = ['products', 'distributions', 'people', 'meetings',
1349 'register_team', 'register_project']
1350
1351 def initialize(self):
1352 """See `MenuBase`."""
1353 if self.context.user is None:
1354 # Make a copy of the links so as not to permanently mutate the
1355 # class attribute.
1356 self.links = PeopleSearchNavigationMenu.links[:]
1357 self.links.append('create_account')
1358
1359
1360class PeopleSearchView(LaunchpadView):
1361 """Search for people and teams on the /people page."""
1362
1363 implements(IPeopleSearchNavigationMenu)
13521364
1353 def __init__(self, context, request):1365 def __init__(self, context, request):
1354 self.context = context1366 super(PeopleSearchView, self).__init__(context, request)
1355 self.request = request
1356 self.results = []1367 self.results = []
13571368
1358 def teamsCount(self):1369 @property
1359 return getUtility(IPersonSet).teamsCount()1370 def number_of_people(self):
1371 return self.context.peopleCount()
13601372
1361 def peopleCount(self):1373 @property
1362 return getUtility(IPersonSet).peopleCount()1374 def number_of_teams(self):
1375 return self.context.teamsCount()
13631376
1364 def searchPeopleBatchNavigator(self):1377 def searchPeopleBatchNavigator(self):
1365 name = self.request.get("name")1378 name = self.request.get("name")
1366
1367 if not name:1379 if not name:
1368 return None1380 return None
1369
1370 searchfor = self.request.get("searchfor")1381 searchfor = self.request.get("searchfor")
1371 if searchfor == "peopleonly":1382 if searchfor == "peopleonly":
1372 results = getUtility(IPersonSet).findPerson(name)1383 results = self.context.findPerson(name)
1373 elif searchfor == "teamsonly":1384 elif searchfor == "teamsonly":
1374 results = getUtility(IPersonSet).findTeam(name)1385 results = self.context.findTeam(name)
1375 else:1386 else:
1376 results = getUtility(IPersonSet).find(name)1387 results = self.context.find(name)
1377
1378 return BatchNavigator(results, self.request)1388 return BatchNavigator(results, self.request)
13791389
1390 @property
1391 def is_admin(self):
1392 """Is the logged in user a Launchpad administrator?"""
1393 return (self.user is not None and
1394 self.user.inTeam(getUtility(ILaunchpadCelebrities).admin))
1395
13801396
1381class PersonAddView(LaunchpadFormView):1397class PersonAddView(LaunchpadFormView):
1382 """The page where users can create new Launchpad profiles."""1398 """The page where users can create new Launchpad profiles."""
13831399
=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py 2009-08-12 13:20:10 +0000
+++ lib/lp/registry/browser/product.py 2009-08-18 13:54:51 +0000
@@ -82,6 +82,7 @@
82from lp.bugs.browser.bugtask import (82from lp.bugs.browser.bugtask import (
83 BugTargetTraversalMixin, get_buglisting_search_filter_url)83 BugTargetTraversalMixin, get_buglisting_search_filter_url)
84from lp.registry.browser.distribution import UsesLaunchpadMixin84from lp.registry.browser.distribution import UsesLaunchpadMixin
85from lp.registry.browser.menu import TopLevelMenuMixin
85from lp.answers.browser.faqtarget import FAQTargetNavigationMixin86from lp.answers.browser.faqtarget import FAQTargetNavigationMixin
86from canonical.launchpad.browser.feeds import FeedsMixin87from canonical.launchpad.browser.feeds import FeedsMixin
87from lp.registry.browser.productseries import get_series_branch_error88from lp.registry.browser.productseries import get_series_branch_error
@@ -546,40 +547,17 @@
546 enable_only = ['overview']547 enable_only = ['overview']
547548
548549
549class ProductSetContextMenu(ContextMenu):550class ProductSetContextMenu(ContextMenu, TopLevelMenuMixin):
550551
551 usedfor = IProductSet552 usedfor = IProductSet
552553
553 links = ['products', 'distributions', 'people', 'meetings',554 links = ['products', 'distributions', 'people', 'meetings',
554 'all', 'register', 'register_team', 'review_licenses']555 'all', 'register_project', 'register_team', 'review_licenses']
555
556 def register(self):
557 text = 'Register a project'
558 # We link to the guided form, though people who know the URL can
559 # just jump to +new directly. That might be considered a
560 # feature!
561 return Link('+new', text, icon='add')
562
563 def register_team(self):
564 text = 'Register a team'
565 return Link('/people/+newteam', text, icon='add')
566556
567 def all(self):557 def all(self):
568 text = 'List all projects'558 text = 'List all projects'
569 return Link('+all', text, icon='list')559 return Link('+all', text, icon='list')
570560
571 def products(self):
572 return Link('/projects/', 'View projects')
573
574 def distributions(self):
575 return Link('/distros/', 'View distributions')
576
577 def people(self):
578 return Link('/people/', 'View people')
579
580 def meetings(self):
581 return Link('/sprints/', 'View meetings')
582
583 @enabled_with_permission('launchpad.ProjectReview')561 @enabled_with_permission('launchpad.ProjectReview')
584 def review_licenses(self):562 def review_licenses(self):
585 return Link('+review-licenses', 'Review projects')563 return Link('+review-licenses', 'Review projects')
586564
=== added file 'lib/lp/registry/browser/tests/people-views.txt'
--- lib/lp/registry/browser/tests/people-views.txt 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/browser/tests/people-views.txt 2009-08-14 17:31:10 +0000
@@ -0,0 +1,70 @@
1==============================
2Searching for people and teams
3==============================
4
5The view behind the /people page provides searching for people and teams. The
6view knows how many people and teams are currently registered in Launchpad.
7
8 >>> from lp.registry.interfaces.person import IPersonSet
9 >>> login('foo.bar@canonical.com')
10 >>> person_set = getUtility(IPersonSet)
11
12 >>> view = create_initialized_view(person_set, '+index')
13 >>> view.number_of_people
14 48
15 >>> view.number_of_teams
16 17
17
18The view also knows whether the logged in user, such as Foo Bar is a Launchpad
19administrator or not.
20
21 >>> view.is_admin
22 True
23
24But the anonymous user is not an administrator.
25
26 >>> logout()
27 >>> view = create_initialized_view(person_set, '+index')
28 >>> view.is_admin
29 False
30
31Sample Person is also not an administrator.
32
33 >>> login('test@canonical.com')
34 >>> view = create_initialized_view(person_set, '+index')
35 >>> view.is_admin
36 False
37
38
39Batch navigator
40===============
41
42The view returns a batch navigator for searching through sets of teams,
43people, or both. By default, both are searched for the given name. There is
44one person and one team matching the 'test' string.
45
46 >>> from zope.security.proxy import removeSecurityProxy
47 >>> def print_batch(batch):
48 ... for thing in batch.currentBatch():
49 ... naked_thing = removeSecurityProxy(thing)
50 ... print naked_thing
51
52 >>> form = dict(name='test')
53 >>> view = create_initialized_view(person_set, '+index', form=form)
54 >>> print_batch(view.searchPeopleBatchNavigator())
55 <Person at ... name12 (Sample Person)>
56 <Person at ... testing-spanish-team (testing Spanish team)>
57
58Searching for just people returns Sample Person.
59
60 >>> form['searchfor'] = 'peopleonly'
61 >>> view = create_initialized_view(person_set, '+index', form=form)
62 >>> print_batch(view.searchPeopleBatchNavigator())
63 <Person at ... name12 (Sample Person)>
64
65Searching for just teams returns the testing Spanish team.
66
67 >>> form['searchfor'] = 'teamsonly'
68 >>> view = create_initialized_view(person_set, '+index', form=form)
69 >>> print_batch(view.searchPeopleBatchNavigator())
70 <Person at ... testing-spanish-team (testing Spanish team)>
071
=== modified file 'lib/lp/registry/templates/people-index.pt'
--- lib/lp/registry/templates/people-index.pt 2009-07-17 17:59:07 +0000
+++ lib/lp/registry/templates/people-index.pt 2009-08-18 14:16:16 +0000
@@ -6,39 +6,36 @@
6 xml:lang="en"6 xml:lang="en"
7 lang="en"7 lang="en"
8 dir="ltr"8 dir="ltr"
9 metal:use-macro="view/macro:page/pillarindex"9 metal:use-macro="view/macro:page/main_side"
10 i18n:domain="launchpad"10 i18n:domain="launchpad"
11>11>
12 <body>12 <body>
13 <h1 metal:fill-slot="heading">People and teams</h1>13 <h1 metal:fill-slot="heading">People and teams</h1>
1414
15 <metal:leftportlets fill-slot="portlets_one">15 <tal:side metal:fill-slot="side">
16 <div tal:replace="structure context/@@+portlet-about" />16 <tal:menu replace="structure view/@@+global-actions" />
17 </metal:leftportlets>17 </tal:side>
18
19 <metal:rightportlets fill-slot="portlets_two">
20 <div tal:replace="structure context/@@+portlet-stats" />
21 </metal:rightportlets>
22
23 <h1 metal:fill-slot="application-heading">People and teams</h1>
2418
25 <div metal:fill-slot="main">19 <div metal:fill-slot="main">
26 <tal:block define="bn view/searchPeopleBatchNavigator">20 There are currently <span tal:replace="view/number_of_people" /> people
27 <div align="center">21 and <span tal:replace="view/number_of_teams" /> teams registered in
22 Launchpad.
23 <tal:block define="batch view/searchPeopleBatchNavigator">
24 <div>
28 <form class="central" name="search" action="." method="GET">25 <form class="central" name="search" action="." method="GET">
29 <table style="margin-bottom: 1em;">26 <table style="margin-bottom: 1em;">
30 <tr>27 <tr>
31 <td>28 <td>
32 <input type="text" name="name" size="35"29 <input type="text" name="name" size="50"
33 tal:attributes="value request/name|nothing"30 tal:attributes="value request/name|nothing"
34 />31 />
35 <input32 <input
36 tal:condition="not: bn"33 tal:condition="not: batch"
37 type="submit"34 type="submit"
38 value="Search"35 value="Search"
39 />36 />
40 <input37 <input
41 tal:condition="bn"38 tal:condition="batch"
42 type="submit"39 type="submit"
43 value="Search Again"40 value="Search Again"
44 />41 />
@@ -110,57 +107,57 @@
110 // --></script>107 // --></script>
111 </div>108 </div>
112109
113 <tal:block condition="not: bn">110 <tal:block condition="not: batch">
114 <p id="application-summary">111 <div id="application-summary">
115 Launchpad may create <b>duplicate profiles</b> for you112 <p>Launchpad creates
116 as it automatically reviews translation files, mailing lists and113 <a href="https://help.launchpad.net/YourAccount">profiles</a> for
117 other forums. If you find you have more than one profile here,114 people based on Launchpad usage, as well as information collected
118 perhaps for multiple email addresses, you can115 from public sources such as bug trackers, mailing lists, public
119 <a href="+requestmerge">merge the duplicate accounts</a>.116 key servers, and published application translations. You can
120 </p>117 <a href="+me">manage your account</a> in Launchpad, and you can
118 <a href="+newteam">create teams of people</a> for organizing
119 around common interests, discussions or permissions.</p>
120
121 <p>Launchpad may create
122 <a href="https://help.launchpad.net/YourAccount/Merging">duplicate
123 profiles</a> for you as it automatically collects information from
124 public sources. If you find you have more than one profile on
125 Launchpad, you can <a href="+requestmerge">request a merge</a> of
126 the duplicate profiles.</p>
127
128 <p tal:condition="view/is_admin">As a Launchpad administrator, you
129 can also service
130 <a href="+adminpeoplemerge">person merge requests</a> and
131 <a href="+adminteammerge">team merge requests</a>.
132 </p>
133 </div>
121 </tal:block>134 </tal:block>
122 <tal:block condition="bn">135 <tal:block condition="batch">
123 <tal:block136 <tal:block condition="batch/multiple_pages">
124 define="prev_url bn/prevBatchURL;137 <tal:navigation
125 next_url bn/nextBatchURL;"138 replace="structure batch/@@+navigation-links-upper"/>
126 >139 </tal:block>
127 <table class="listing">140 <table class="listing">
128 <thead class="results">141 <thead>
129 <tr tal:condition="python:prev_url or next_url">142 <tr>
130 <td colspan="3">143 <th>Name</th>
131 <div style="float: right; text-align: right;">144 <th>Launchpad ID</th>
132 <tal:block condition="prev_url">145 <th>Karma</th>
133 <a tal:attributes="href prev_url">Previous</a>146 </tr>
134 </tal:block>147 </thead>
135 <tal:block repeat="page_link bn/batchPageURLs">148 <tbody>
136 <a tal:attributes="href python:page_link.values()[0]"149 <tr tal:repeat="person batch/currentBatch">
137 tal:content="python:page_link.keys()[0]"></a>150 <td><a tal:content="person/displayname"
138 </tal:block>151 tal:attributes="href person/fmt:url"/>
139 <tal:block condition="next_url" >152 </td>
140 <a tal:attributes="href next_url">Next</a>153 <td tal:content="person/name">foobar</td>
141 </tal:block>154 <td class="amount" tal:content="person/karma">34</td>
142 </div>155 </tr>
143 </td>156 </tbody>
144 </tr>157 </table>
145 <tr>158 <tal:block condition="batch/multiple_pages">
146 <th colspan="2">159 <tal:navigation
147 Name160 replace="structure batch/@@+navigation-links-upper"/>
148 </th>
149 <th>
150 Karma
151 </th>
152 </tr>
153 </thead>
154 <tbody>
155 <tr tal:repeat="person bn/currentBatch">
156 <td><a tal:content="person/displayname"
157 tal:attributes="href person/fmt:url"/>
158 </td>
159 <td tal:content="person/name">foobar</td>
160 <td class="amount" tal:content="person/karma">34</td>
161 </tr>
162 </tbody>
163 </table>
164 </tal:block>161 </tal:block>
165 </tal:block>162 </tal:block>
166 </tal:block>163 </tal:block>
167164
=== removed file 'lib/lp/registry/templates/people-portlet-about.pt'
--- lib/lp/registry/templates/people-portlet-about.pt 2009-07-17 17:59:07 +0000
+++ lib/lp/registry/templates/people-portlet-about.pt 1970-01-01 00:00:00 +0000
@@ -1,22 +0,0 @@
1<tal:root
2 xmlns:tal="http://xml.zope.org/namespaces/tal"
3 xmlns:metal="http://xml.zope.org/namespaces/metal"
4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
5 omit-tag="">
6<div class="portlet" id="portlet-about">
7
8 <h2>About these profiles</h2>
9
10 <div class="portletBody portletContent">
11
12 <img alt="" src="/@@/info" /> Launchpad profiles are based on actual
13 usage of Launchpad, as well as information available from public sources
14 such as bug trackers, mailing lists, public key servers and published
15 application translations. You can manage your account in the Launchpad,
16 and you can create teams of people who can be maintainers, translators,
17 members, clubs... almost any kind of grouping you need.
18
19 </div>
20
21</div>
22</tal:root>
230
=== removed file 'lib/lp/registry/templates/people-portlet-stats.pt'
--- lib/lp/registry/templates/people-portlet-stats.pt 2009-07-17 17:59:07 +0000
+++ lib/lp/registry/templates/people-portlet-stats.pt 1970-01-01 00:00:00 +0000
@@ -1,19 +0,0 @@
1<tal:root
2 xmlns:tal="http://xml.zope.org/namespaces/tal"
3 xmlns:metal="http://xml.zope.org/namespaces/metal"
4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
5 omit-tag="">
6<div class="portlet" id="portlet-stats">
7
8 <h2>Statistics</h2>
9
10 <div class="portletBody portletContent">
11
12 There are currently <span tal:replace="view/peopleCount" /> people
13 and <span tal:replace="view/teamsCount" /> teams registered in
14 Launchpad.
15
16 </div>
17
18</div>
19</tal:root>