Hi Salgado, Here are the tests that you asked me to add. I will add another comment with a diff of all the boring test fixes needed for the changing names of links. -Edwin {{{ === modified file 'lib/lp/registry/browser/person.py' --- lib/lp/registry/browser/person.py 2009-08-31 12:01:46 +0000 +++ lib/lp/registry/browser/person.py 2009-09-01 00:11:08 +0000 @@ -3126,6 +3126,21 @@ """ @property + def can_show_subteam_portlet(self): + """Only show the subteam portlet if there is info to display. + + Either the team is a member of another team, or there are + invitations to join a team, and the owner needs to see the + link so that the invitation can be accepted. + """ + try: + return (self.context.super_teams.count() > 0 + or (self.context.open_membership_invitations + and check_permission('launchpad.Edit', self.context))) + except AttributeError, e: + raise AssertionError(e) + + @property def visibility(self): return self.context.visibility.title + ' Team' === modified file 'lib/lp/registry/stories/foaf/xx-team-home.txt' --- lib/lp/registry/stories/foaf/xx-team-home.txt 2009-08-29 03:14:48 +0000 +++ lib/lp/registry/stories/foaf/xx-team-home.txt 2009-09-01 00:48:09 +0000 @@ -51,11 +51,41 @@ English Set preferred languages +The polls portlet only shows a link to view all polls if current +polls exist. + + >>> print extract_text(find_tag_by_id(browser.contents, 'polls')) + Polls + A random poll that never closes... + Show polls + + >>> browser.open('http://launchpad.dev/~launchpad') + >>> print extract_text(find_tag_by_id(browser.contents, 'polls')) + Polls + No polls created. + +The subteam-of portlet is not shown if the team is not a subteam. + + >>> browser.open('http://launchpad.dev/~ubuntu-team') >>> print extract_text( ... find_tag_by_id(browser.contents, 'subteam-of')) Subteam of “Ubuntu Team” is a member of these teams: GuadaMen... + >>> browser.open('http://launchpad.dev/~launchpad') + >>> print find_tag_by_id(browser.contents, 'subteam-of') + None + +Unless the user is the owner of the team and there are invitations to +join a team. + + >>> admin_browser.open('http://launchpad.dev/~launchpad') + >>> print extract_text( + ... find_tag_by_id(admin_browser.contents, 'subteam-of')) + Subteam of... + itself is not a member of any other team. + Show received invitations + If the team does not have any recently approved or proposed members, the recent members sections are not displayed: === modified file 'lib/lp/registry/templates/person-macros.pt' --- lib/lp/registry/templates/person-macros.pt 2009-08-28 00:57:32 +0000 +++ lib/lp/registry/templates/person-macros.pt 2009-09-01 00:06:13 +0000 @@ -79,8 +79,8 @@
+ tal:condition="view/can_show_subteam_portlet" + >

Subteam of

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-navigation.txt' --- lib/lp/soyuz/stories/ppa/xx-ppa-navigation.txt 2009-08-31 12:01:46 +0000 +++ lib/lp/soyuz/stories/ppa/xx-ppa-navigation.txt 2009-08-31 23:43:49 +0000 @@ -184,3 +184,32 @@ >>> anon_browser.getLink('Previous').url 'http://launchpad.dev/%7Ecprov/+archive/ppa/+index?start=1&batch=1' + +== Maintained Packages link == + +The Maintained Packages link only appears if the person or team has +maintained packages to show. No-priv does not maintain packages. + + >>> anon_browser.open("http://launchpad.dev/~no-priv") + >>> print_tag_with_id(anon_browser.contents, 'ppas') + Personal package archives + PPA for No Privileges Person + >>> anon_browser.open( + ... "http://launchpad.dev/~no-priv/+maintained-packages") + >>> print extract_text( + ... find_tag_by_id(anon_browser.contents, 'packages')) + Maintained packages... + No Privileges Person does not maintain any packages. + +Mark has packages that he maintains. + + >>> anon_browser.open("http://launchpad.dev/~mark") + >>> print_tag_with_id(anon_browser.contents, 'ppas') + Personal package archives + PPA for Mark Shuttleworth + Maintained Packages + >>> anon_browser.getLink(url="+maintained-packages").click() + >>> print extract_text( + ... find_tag_by_id(anon_browser.contents, 'packages')) + Maintained packages... + alsa-utils ... 1.0.9a-4... === modified file 'lib/canonical/launchpad/webapp/tales.py' --- lib/canonical/launchpad/webapp/tales.py 2009-08-20 07:15:35 +0000 +++ lib/canonical/launchpad/webapp/tales.py 2009-08-29 03:14:48 +0000 @@ -14,6 +14,7 @@ import os.path import re import rfc822 +import sys import urllib from xml.sax.saxutils import unescape as xml_unescape from datetime import datetime, timedelta @@ -202,25 +203,36 @@ @property def navigation(self): """Navigation menu links list.""" - # NavigationMenus may be associated with a content object or one of - # its views. The context we need is the one from the TAL expression. - context = self._tales_context - if self._selectedfacetname is not None: - selectedfacetname = self._selectedfacetname - else: - # XXX sinzui 2008-05-09 bug=226917: We should be retrieving the - # facet name from the layer implemented by the request. - view = get_current_view(self._request) - selectedfacetname = get_facet(view) try: - menu = nearest_adapter( - context, INavigationMenu, name=selectedfacetname) - except NoCanonicalUrl: - menu = None - if menu is None or menu.disabled: - return {} - else: - return self._getMenuLinksAndAttributes(menu) + # NavigationMenus may be associated with a content object or one of + # its views. The context we need is the one from the TAL expression. + context = self._tales_context + if self._selectedfacetname is not None: + selectedfacetname = self._selectedfacetname + else: + # XXX sinzui 2008-05-09 bug=226917: We should be retrieving the + # facet name from the layer implemented by the request. + view = get_current_view(self._request) + selectedfacetname = get_facet(view) + try: + menu = nearest_adapter( + context, INavigationMenu, name=selectedfacetname) + except NoCanonicalUrl: + menu = None + if menu is None or menu.disabled: + return {} + else: + return self._getMenuLinksAndAttributes(menu) + except AttributeError, e: + # If this method gets an AttributeError, we rethrow it as a + # AssertionError. Otherwise, zope will hide the root cause + # of the error and just say that "navigation" can't be traversed. + new_exception = AssertionError( + 'AttributError in MenuAPI.navigation: %s' % e) + # We cannot use parens around the arguments to `raise`, + # since that will cause it to ignore the third argument, + # which is the original traceback. + raise new_exception, None, sys.exc_info()[2] class CountAPI: === modified file 'lib/lp/registry/browser/person.py' --- lib/lp/registry/browser/person.py 2009-08-28 00:57:32 +0000 +++ lib/lp/registry/browser/person.py 2009-08-29 03:14:48 +0000 @@ -849,6 +849,11 @@ class CommonMenuLinks: + @property + def person(self): + """Allow subclasses that use the view as the context.""" + return self.context + @enabled_with_permission('launchpad.Edit') def common_edithomepage(self): target = '+edithomepage' @@ -877,7 +882,7 @@ def maintained(self): target = '+maintained-packages' text = 'Maintained Packages' - enabled = bool(self.team.getLatestMaintainedPackages()) + enabled = bool(self.person.getLatestMaintainedPackages()) return Link(target, text, enabled=enabled, icon='info') def uploaded(self): @@ -1085,6 +1090,11 @@ facet = 'overview' links = ('summary', 'maintained', 'uploaded', 'ppa', 'projects') + @property + def person(self): + """Override CommonMenuLinks since the view is the context.""" + return self.context.context + class PersonEditNavigationMenu(NavigationMenu): """A sub-menu for different aspects of editing a Person's profile.""" @@ -1128,11 +1138,6 @@ has the view as its context object. """ - @property - def team(self): - """Allow subclasses that use the view as the context.""" - return self.context - def profile(self): target = '' text = 'Overview' @@ -1191,7 +1196,7 @@ target = '+add-my-teams' text = 'Add one of my teams' enabled = True - if self.team.subscriptionpolicy == TeamSubscriptionPolicy.RESTRICTED: + if self.person.subscriptionpolicy == TeamSubscriptionPolicy.RESTRICTED: # This is a restricted team; users can't join. enabled = False return Link(target, text, icon='add', enabled=enabled) @@ -1230,13 +1235,13 @@ text = 'Set contact address' summary = ( 'The address Launchpad uses to contact %s' % - self.team.displayname) + self.person.displayname) return Link(target, text, summary, icon='edit') @enabled_with_permission('launchpad.MailingListManager') def configure_mailing_list(self): target = '+mailinglist' - mailing_list = self.team.mailing_list + mailing_list = self.person.mailing_list if mailing_list is not None and mailing_list.is_usable: text = 'Configure mailing list' icon = 'edit' @@ -1264,9 +1269,9 @@ def leave(self): enabled = True - if not userIsActiveTeamMember(self.team): + if not userIsActiveTeamMember(self.person): enabled = False - if self.team.teamowner == self.user: + if self.person.teamowner == self.user: # The owner cannot leave his team. enabled = False target = '+leave' @@ -1276,9 +1281,9 @@ def join(self): enabled = True - if userIsActiveTeamMember(self.team): + if userIsActiveTeamMember(self.person): enabled = False - if self.team.subscriptionpolicy == TeamSubscriptionPolicy.RESTRICTED: + if self.person.subscriptionpolicy == TeamSubscriptionPolicy.RESTRICTED: # This is a restricted team; users can't join. enabled = False target = '+join' @@ -5531,8 +5536,8 @@ class TeamNavigationMenuBase(NavigationMenu, TeamMenuMixin): @property - def team(self): - """Override TeamOverviewMenu since the view is the context.""" + def person(self): + """Override CommonMenuLinks since the view is the context.""" return self.context.context }}}