← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~sinzui/launchpad/mailing-list-simple-0 into lp:launchpad

 

Curtis Hovey has proposed merging lp:~sinzui/launchpad/mailing-list-simple-0 into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  #151290 IMailingList.__repr__ should include the mailing list's address as well
  https://bugs.launchpad.net/bugs/151290
  #669249 'MailingListManager' and mailing_list_experts can be removed
  https://bugs.launchpad.net/bugs/669249


Fix small issues with mailing lists.

    Launchpad bug:
        https://bugs.launchpad.net/bugs/669249
        https://bugs.launchpad.net/bugs/151290
    Pre-implementation: no one
    Test command: ./bin/test -vv -t mailinglist

Bug #669249 ['MailingListManager' and mailing_list_experts can be removed]
    The mailing_list_experts team is synonymous with registry administrator
    team. The 'MailingListManager' permission could be Moderate or Admin using
    the registry team.

Bug #151290 [IMailingList.__repr__ should include the mailing list's address]


--------------------------------------------------------------------

RULES

Bug #669249 ['MailingListManager' and mailing_list_experts can be removed]
    * Replace all uses of MailingListManager with Moderate.
    * Remove MailingListManager and its checker.

Bug #151290 [IMailingList.__repr__ should include the mailing list's address]
    Appart from the team and the mailing list status it should also contain
    the address of that mailing list.

QA

Bug #669249 ['MailingListManager' and mailing_list_experts can be removed]
    * As a list owner verify you can visit your teams 

Bug #151290 [IMailingList.__repr__ should include the mailing list's address]
    * Update the __repr__() method.


LINT

    lib/canonical/launchpad/permissions.zcml
    lib/canonical/launchpad/security.py
    lib/canonical/launchpad/doc/celebrities.txt
    lib/canonical/launchpad/interfaces/launchpad.py
    lib/canonical/launchpad/utilities/celebrities.py
    lib/lp/registry/browser/configure.zcml
    lib/lp/registry/browser/mailinglists.py
    lib/lp/registry/browser/person.py
    lib/lp/registry/browser/team.py
    lib/lp/registry/browser/tests/mailinglist-views.txt
    lib/lp/registry/doc/mailinglists.txt
    lib/lp/registry/model/mailinglist.py
    lib/lp/registry/stories/mailinglists/lifecycle.txt
    lib/lp/registry/tests/mailinglists_helper.py

^ There are are many line and white space issues reported in this files.
I will fix these before I land the branch. I think this will keep the diff
readable.



IMPLEMENTATION

Both these bugs were largely fixed using find and replace. 
    lib/canonical/launchpad/doc/celebrities.txt
    lib/canonical/launchpad/interfaces/launchpad.py
    lib/canonical/launchpad/utilities/celebrities.py
    lib/lp/registry/browser/person.py
    lib/lp/registry/browser/team.py
    lib/lp/registry/browser/tests/mailinglist-views.txt
    lib/lp/registry/doc/mailinglists.txt
    lib/lp/registry/model/mailinglist.py
    lib/lp/registry/stories/mailinglists/lifecycle.txt
    lib/lp/registry/tests/mailinglists_helper.py

I manually deleted some class and files after discovering that they are
not used.
    lib/canonical/launchpad/permissions.zcml
    lib/canonical/launchpad/security.py
    lib/lp/registry/browser/configure.zcml
    lib/lp/registry/browser/mailinglists.py
-- 
https://code.launchpad.net/~sinzui/launchpad/mailing-list-simple-0/+merge/43522
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~sinzui/launchpad/mailing-list-simple-0 into lp:launchpad.
=== modified file 'lib/canonical/launchpad/doc/celebrities.txt'
--- lib/canonical/launchpad/doc/celebrities.txt	2010-10-09 16:36:22 +0000
+++ lib/canonical/launchpad/doc/celebrities.txt	2010-12-13 15:01:21 +0000
@@ -118,21 +118,6 @@
     True
 
 
-== mailing_list_experts ==
-
-There is a 'Mailing List Experts' team that has administrative power
-over mailing lists.
-
-    >>> mailing_list_experts = personset.getByName('mailing-list-experts')
-    >>> celebs.mailing_list_experts == mailing_list_experts
-    True
-
-The Launchpad Administrators team is a member of that team.
-
-    >>> celebs.admin.inTeam(mailing_list_experts)
-    True
-
-
 == obsolete_junk ==
 
 The 'Obsolete Junk' project is used to hold undeletable objects like

=== modified file 'lib/canonical/launchpad/interfaces/launchpad.py'
--- lib/canonical/launchpad/interfaces/launchpad.py	2010-11-23 18:36:21 +0000
+++ lib/canonical/launchpad/interfaces/launchpad.py	2010-12-13 15:01:21 +0000
@@ -122,7 +122,6 @@
     launchpad_beta_testers = Attribute("The Launchpad Beta Testers team.")
     launchpad_developers = Attribute("The Launchpad development team.")
     lp_translations = Attribute("The Launchpad Translations product.")
-    mailing_list_experts = Attribute("The Mailing List Experts team.")
     obsolete_junk = Attribute("The Obsolete Junk project.")
     ppa_key_guard = Attribute("The PPA signing keys owner.")
     registry_experts = Attribute("The Registry Administrators team.")

=== modified file 'lib/canonical/launchpad/permissions.zcml'
--- lib/canonical/launchpad/permissions.zcml	2010-10-05 23:26:59 +0000
+++ lib/canonical/launchpad/permissions.zcml	2010-12-13 15:01:21 +0000
@@ -76,11 +76,6 @@
     title="The role of someone who created or otherwise owns an object."
     access_level="write" />
 
-  <permission
-    id="launchpad.MailingListManager"
-    title="The role of someone who can manage and configure a mailing list."
-    access_level="write" />
-
   <!-- This permission only exists to please apidoc.launchpad.dev and
        shouldn't be used anywhere else. -->
   <permission

=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py	2010-12-07 14:15:37 +0000
+++ lib/canonical/launchpad/security.py	2010-12-13 15:01:21 +0000
@@ -114,7 +114,6 @@
 from lp.registry.interfaces.gpg import IGPGKey
 from lp.registry.interfaces.irc import IIrcID
 from lp.registry.interfaces.location import IPersonLocation
-from lp.registry.interfaces.mailinglist import IMailingListSet
 from lp.registry.interfaces.milestone import (
     IMilestone,
     IProjectGroupMilestone,
@@ -2384,38 +2383,6 @@
         return False
 
 
-class MailingListApprovalByExperts(AuthorizationBase):
-    permission = 'launchpad.Admin'
-    usedfor = IMailingListSet
-
-    def checkAuthenticated(self, user):
-        return user.in_mailing_list_experts
-
-
-class ConfigureTeamMailingList(AuthorizationBase):
-    permission = 'launchpad.MailingListManager'
-    usedfor = ITeam
-
-    def checkAuthenticated(self, user):
-        """Check to see if the user can manage a mailing list.
-
-        A team's owner or administrator, the Launchpad administrators, and
-        Launchpad mailing list experts can all manage a team's mailing list
-        through its +mailinglist page.
-
-        :param user: The user whose permission is being checked.
-        :type user: `IPerson`
-        :return: True if the user can manage a mailing list, otherwise False.
-        :rtype: boolean
-        """
-        # The team owner, the Launchpad mailing list experts and the Launchpad
-        # administrators can all view a team's +mailinglist page.
-        team = ITeam(self.obj)
-        is_team_owner = (
-            team is not None and team in user.person.getAdministratedTeams())
-        return is_team_owner or user.in_admin or user.in_mailing_list_experts
-
-
 class ViewEmailAddress(AuthorizationBase):
     permission = 'launchpad.View'
     usedfor = IEmailAddress

=== modified file 'lib/canonical/launchpad/utilities/celebrities.py'
--- lib/canonical/launchpad/utilities/celebrities.py	2010-11-08 12:52:43 +0000
+++ lib/canonical/launchpad/utilities/celebrities.py	2010-12-13 15:01:21 +0000
@@ -146,7 +146,6 @@
         'launchpad-beta-testers')
     launchpad_developers = PersonCelebrityDescriptor('launchpad')
     lp_translations = CelebrityDescriptor(IProductSet, 'rosetta')
-    mailing_list_experts = PersonCelebrityDescriptor('mailing-list-experts')
     obsolete_junk = CelebrityDescriptor(IProductSet, 'obsolete-junk')
     ppa_key_guard = PersonCelebrityDescriptor('ppa-key-guard')
     registry_experts = PersonCelebrityDescriptor('registry')

=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml	2010-12-09 15:20:21 +0000
+++ lib/lp/registry/browser/configure.zcml	2010-12-13 15:01:21 +0000
@@ -1145,7 +1145,7 @@
             template="../templates/team-contactaddress.pt"/>
         <browser:page
             for="lp.registry.interfaces.person.ITeam"
-            permission="launchpad.MailingListManager"
+            permission="launchpad.Moderate"
             class="lp.registry.browser.team.TeamMailingListConfigurationView"
             name="+mailinglist"
             template="../templates/team-mailinglist.pt"/>
@@ -1376,15 +1376,6 @@
             MilestoneOverviewMenu
             MilestoneOverviewNavigationMenu"
         module="lp.registry.browser.milestone"/>
-    <browser:defaultView
-        for="lp.registry.interfaces.mailinglist.IMailingListSet"
-        name="+review"/>
-    <browser:page
-        for="lp.registry.interfaces.mailinglist.IMailingListSet"
-        class="canonical.launchpad.browser.MailingListsReviewView"
-        name="+review"
-        template="../templates/mailinglists-review.pt"
-        permission="launchpad.Admin"/>
     <browser:page
         name="+moderation"
         for="lp.registry.interfaces.mailinglist.IMessageApproval"

=== modified file 'lib/lp/registry/browser/mailinglists.py'
--- lib/lp/registry/browser/mailinglists.py	2010-11-23 23:22:27 +0000
+++ lib/lp/registry/browser/mailinglists.py	2010-12-13 15:01:21 +0000
@@ -6,117 +6,25 @@
 __metaclass__ = type
 __all__ = [
     'HeldMessageView',
-    'MailingListsReviewView',
     'enabled_with_active_mailing_list',
     ]
 
 
 from cgi import escape
-from operator import itemgetter
 from textwrap import TextWrapper
 from urllib import quote
 
 from zope.component import getUtility
-from zope.interface import Interface
-from zope.security.proxy import removeSecurityProxy
 
-from canonical.launchpad import _
 from canonical.launchpad.webapp import (
     canonical_url,
     LaunchpadView,
     )
-from canonical.launchpad.webapp.menu import structured
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
-from lp.app.browser.tales import PersonFormatterAPI
-from lp.app.errors import UnexpectedFormData
 from lp.registry.interfaces.mailinglist import (
     IHeldMessageDetails,
     IMailingListSet,
-    MailingListStatus,
     )
 from lp.registry.interfaces.person import ITeam
-from lp.services.propertycache import cachedproperty
-
-
-class ReviewForm(Interface):
-    """An empty marker schema for the review form."""
-
-
-class MailingListsReviewView(LaunchpadFormView):
-    """Present review page for mailing list creation requests."""
-
-    schema = ReviewForm
-    page_title = 'Pending mailing lists requests'
-
-    @cachedproperty
-    def registered_lists(self):
-        """Return a list of mailing list information dictionaries.
-
-        The view needs a sorted list of dictionaries of information pertaining
-        to each mailing list pending approval.  The dictionaries have three
-        keys:
-
-        * the mailing list's team
-        * the mailing list's team's name
-        * the registrant of the mailing list request
-
-        The dictionaries are sorted by team name.
-
-        :return: Information about all the pending mailing list requests.
-        :rtype: list of dictionaries
-        """
-        list_info = []
-        for mailing_list in self.context.registered_lists:
-            naked_team = removeSecurityProxy(mailing_list.team)
-            list_info.append(dict(
-                # Use PersonFormatterAPI so that private team names aren't
-                # redacted, which doesn't help a mailing list expert much.
-                # This just ensures that the team name is not redacted, even
-                # though the MLE may (still) not have permission to visit the
-                # team page via the link.  That's a different issue and not
-                # one important enough to fix for now.
-                team_link=PersonFormatterAPI(naked_team).link(None),
-                name=naked_team.name,
-                registrant=mailing_list.registrant,
-                ))
-        return sorted(list_info, key=itemgetter('name'))
-
-    @action(_('Submit'), name='submit')
-    def submit_action(self, action, data):
-        """Process the mailing list review form."""
-        for mailing_list in self.context.registered_lists:
-            naked_team = removeSecurityProxy(mailing_list.team)
-            # Find out which disposition the administrator chose for this
-            # mailing list.  If there is no data in the form for this mailing
-            # list, just treat it as having been deferred.
-            action = self.request.form_ng.getOne('field.%s' % naked_team.name)
-            # This essentially acts like a switch statement or if/elifs.  It
-            # looks the action up in a map of allowed actions, watching out
-            # for bogus input.
-            try:
-                status = dict(
-                    approve=MailingListStatus.APPROVED,
-                    decline=MailingListStatus.DECLINED,
-                    hold=None,
-                    )[action]
-            except KeyError:
-                raise UnexpectedFormData(
-                    'Invalid review action for mailing list %s: %s' %
-                    (naked_team.displayname, action))
-            if status is not None:
-                mailing_list.review(self.user, status)
-                self.request.response.addInfoNotification(
-                    structured(
-                        '<a href="%s">%s</a> mailing list was %s',
-                            canonical_url(naked_team),
-                            naked_team.displayname,
-                            status.title.lower()))
-        # Redirect to prevent double posts (and not require
-        # flush_database_updates() :)
-        self.next_url = canonical_url(self.context)
 
 
 class HeldMessageView(LaunchpadView):

=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py	2010-12-02 19:45:50 +0000
+++ lib/lp/registry/browser/person.py	2010-12-13 15:01:21 +0000
@@ -1354,7 +1354,7 @@
             self.person.displayname)
         return Link(target, text, summary, icon='edit')
 
-    @enabled_with_permission('launchpad.MailingListManager')
+    @enabled_with_permission('launchpad.Moderate')
     def configure_mailing_list(self):
         target = '+mailinglist'
         mailing_list = self.person.mailing_list

=== modified file 'lib/lp/registry/browser/team.py'
--- lib/lp/registry/browser/team.py	2010-12-03 16:33:03 +0000
+++ lib/lp/registry/browser/team.py	2010-12-13 15:01:21 +0000
@@ -745,7 +745,7 @@
         """
         is_moderator = check_permission('launchpad.Moderate', self.context)
         is_mailing_list_manager = check_permission(
-            'launchpad.MailingListManager', self.context)
+            'launchpad.Moderate', self.context)
         if is_moderator or is_mailing_list_manager:
             return self.getListInState(*PURGE_STATES) is not None
         else:

=== modified file 'lib/lp/registry/browser/tests/mailinglist-views.txt'
--- lib/lp/registry/browser/tests/mailinglist-views.txt	2010-11-01 20:39:17 +0000
+++ lib/lp/registry/browser/tests/mailinglist-views.txt	2010-12-13 15:01:21 +0000
@@ -27,7 +27,7 @@
 
     >>> login('test@xxxxxxxxxxxxx')
     >>> view = create_initialized_view(aardvarks, '+mailinglist')
-    >>> check_permission('launchpad.MailingListManager', view)
+    >>> check_permission('launchpad.Moderate', view)
     True
 
 No Privileges Person is added as a member of the Aardvarks.
@@ -39,7 +39,7 @@
 
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> view = create_initialized_view(aardvarks, '+mailinglist')
-    >>> check_permission('launchpad.MailingListManager', view)
+    >>> check_permission('launchpad.Moderate', view)
     False
 
 Sample Person trusts No Privileges Person so she makes no-priv a team
@@ -60,7 +60,7 @@
 
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> view = create_initialized_view(aardvarks, '+mailinglist')
-    >>> check_permission('launchpad.MailingListManager', view)
+    >>> check_permission('launchpad.Moderate', view)
     True
 
 
@@ -81,7 +81,7 @@
     >>> from canonical.launchpad.interfaces.launchpad import (
     ...     ILaunchpadCelebrities)
     >>> celebrities = getUtility(ILaunchpadCelebrities)
-    >>> an_expert = list(celebrities.mailing_list_experts.allmembers)[0]
+    >>> an_expert = list(celebrities.registry_experts.allmembers)[0]
     >>> an_admin = list(celebrities.admin.allmembers)[0]
 
     >>> def create_view(principal, form=None):

=== modified file 'lib/lp/registry/doc/mailinglists.txt'
--- lib/lp/registry/doc/mailinglists.txt	2010-10-19 18:44:31 +0000
+++ lib/lp/registry/doc/mailinglists.txt	2010-12-13 15:01:21 +0000
@@ -51,13 +51,14 @@
     >>> verifyObject(IMailingList, list_one)
     True
     >>> list_one
-    <MailingList for team "team-one"; status=APPROVED at 0x...>
+    <MailingList for team "team-one"; status=APPROVED;
+     address=team-one@xxxxxxxxxxxxxxxxxxx at 0x...>
 
 You can always access the mailing list through its team, if the team has a
 mailing list.
 
     >>> team_one.mailing_list
-    <MailingList for team "team-one"; status=APPROVED at 0x...>
+    <MailingList for team "team-one"; status=APPROVED; ...>
     >>> print team_two.mailing_list
     None
 
@@ -89,7 +90,7 @@
     >>> login(ANONYMOUS)
     >>> list_two = list_set.new(team_two, anne)
     >>> list_two
-    <MailingList for team "team-two"; status=APPROVED at ...>
+    <MailingList for team "team-two"; status=APPROVED; ...>
     >>> print list_two.address
     team-two@xxxxxxxxxxxxxxxxxxx
 
@@ -129,8 +130,8 @@
 state.
 
     >>> sorted_lists(list_set.approved_lists)
-    [<MailingList for team "team-one"; status=APPROVED at ...>,
-     <MailingList for team "team-two"; status=APPROVED at ...>]
+    [<MailingList for team "team-one"; status=APPROVED; ...>,
+     <MailingList for team "team-two"; status=APPROVED; ...>]
 
     >>> list_one.startConstructing()
     >>> print list_one.status.name
@@ -139,7 +140,7 @@
 Once in the construction phase, a list is no longer in the approval state.
 
     >>> sorted_lists(list_set.approved_lists)
-    [<MailingList for team "team-two"; status=APPROVED at ...>]
+    [<MailingList for team "team-two"; status=APPROVED; ...>]
 
 Lists should never be constructed more than once.
 
@@ -163,7 +164,8 @@
 and associated with the team's mailing list, so that it can be used as the
 team's contact address.
 
-    >>> from canonical.launchpad.interfaces.emailaddress import IEmailAddressSet
+    >>> from canonical.launchpad.interfaces.emailaddress import (
+    ...     IEmailAddressSet)
     >>> email_set = getUtility(IEmailAddressSet)
     >>> print email_set.getByEmail(list_one.address)
     None
@@ -190,9 +192,9 @@
 You can then get the mailing list for a team, given the team name.
 
     >>> list_set.get(team_one.name)
-    <MailingList for team "team-one"; status=ACTIVE at ...>
+    <MailingList for team "team-one"; status=ACTIVE; ...>
     >>> list_set.get(team_two.name)
-    <MailingList for team "team-two"; status=FAILED at ...>
+    <MailingList for team "team-two"; status=FAILED; ...>
 
 This method will return None for missing teams or non-team people.
 
@@ -223,7 +225,7 @@
 and report the deactivation results.
 
     >>> sorted_lists(list_set.deactivated_lists)
-    [<MailingList for team "team-three"; status=DEACTIVATING at ...>]
+    [<MailingList for team "team-three"; status=DEACTIVATING; ...>]
     >>> list_three.transitionToStatus(MailingListStatus.INACTIVE)
     >>> print list_three.status.name
     INACTIVE
@@ -303,7 +305,7 @@
     ...     new_list_for_team)
     >>> thereminists_list = new_list_for_team(thereminists)
     >>> thereminists_list
-    <MailingList for team "thereminists"; status=ACTIVE at ...>
+    <MailingList for team "thereminists"; status=ACTIVE; ...>
 
 Welcome messages
 ================
@@ -324,7 +326,7 @@
     >>> print list_one.status.name
     MODIFIED
     >>> sorted_lists(list_set.modified_lists)
-    [<MailingList for team "team-one"; status=MODIFIED at ...>]
+    [<MailingList for team "team-one"; status=MODIFIED; ...>]
 
 Eventually, Mailman will get around to acting on this modification.  When it
 does so, the list's state transitions first to UPDATING so as to avoid

=== modified file 'lib/lp/registry/model/mailinglist.py'
--- lib/lp/registry/model/mailinglist.py	2010-10-26 03:51:12 +0000
+++ lib/lp/registry/model/mailinglist.py	2010-12-13 15:01:21 +0000
@@ -64,7 +64,9 @@
     sqlvalues,
     )
 from canonical.launchpad import _
-from canonical.launchpad.components.decoratedresultset import DecoratedResultSet
+from canonical.launchpad.components.decoratedresultset import (
+    DecoratedResultSet,
+    )
 from canonical.launchpad.database.account import Account
 from canonical.launchpad.database.emailaddress import EmailAddress
 from canonical.launchpad.database.message import Message
@@ -245,8 +247,8 @@
         return template.safe_substitute(team_name=self.team.name)
 
     def __repr__(self):
-        return '<MailingList for team "%s"; status=%s at %#x>' % (
-            self.team.name, self.status.name, id(self))
+        return '<MailingList for team "%s"; status=%s; address=%s at %#x>' % (
+            self.team.name, self.status.name, self.address, id(self))
 
     def startConstructing(self):
         """See `IMailingList`."""

=== modified file 'lib/lp/registry/stories/mailinglists/lifecycle.txt'
--- lib/lp/registry/stories/mailinglists/lifecycle.txt	2010-11-03 22:14:42 +0000
+++ lib/lp/registry/stories/mailinglists/lifecycle.txt	2010-12-13 15:01:21 +0000
@@ -338,7 +338,7 @@
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> person_set = getUtility(IPersonSet)
     >>> test = person_set.getByName('name12')
-    >>> experts = getUtility(ILaunchpadCelebrities).mailing_list_experts
+    >>> experts = getUtility(ILaunchpadCelebrities).registry_experts
     >>> ignored = experts.addMember(test, reviewer=experts.teamowner)
     >>> logout()
     >>> transaction.commit()

=== removed file 'lib/lp/registry/templates/mailinglists-review.pt'
--- lib/lp/registry/templates/mailinglists-review.pt	2009-08-16 17:21:54 +0000
+++ lib/lp/registry/templates/mailinglists-review.pt	1970-01-01 00:00:00 +0000
@@ -1,64 +0,0 @@
-<html
-  xmlns="http://www.w3.org/1999/xhtml";
-  xmlns:tal="http://xml.zope.org/namespaces/tal";
-  xmlns:metal="http://xml.zope.org/namespaces/metal";
-  xmlns:i18n="http://xml.zope.org/namespaces/i18n";
-  metal:use-macro="view/macro:page/main_only"
-  i18n:domain="launchpad"
-  >
-
-<body>
-  <div metal:fill-slot="main">
-    <h1 tal:content="view/page_title" />
-    <div tal:condition="view/registered_lists">
-      <metal:form use-macro="context/@@launchpad_form/form">
-        <p metal:fill-slot="extra_top">These team administrators have
-            requested mailing lists for their teams.  Your approval is
-            required before Launchpad will create the mailing list.  For each
-            mailing list request, you can choose whether to <em>Approve</em>
-            or <em>Decline</em> the request.  Or you may <em>Hold</em> the
-            request to defer your decision until later.
-        </p>
-        <table class="listing sortable" id="mailing-lists"
-               metal:fill-slot="widgets">
-          <thead><tr>
-            <th>Approve</th><th>Decline</th><th>Hold</th>
-            <th>Team</th><th>Requester</th>
-          </tr></thead>
-          <tr tal:repeat="mailing_list view/registered_lists"
-              style="text-align: center">
-              <tal:block tal:define=
-               "team_link mailing_list/team_link;
-                team_name mailing_list/name;
-                registrant mailing_list/registrant;
-                action_name string:field.${team_name}"
-                >
-                <td><input type="radio" value="approve"
-                     tal:attributes="name action_name;
-                                     id string:approve.${team_name}"/>
-                </td>
-                <td><input type="radio" value="decline"
-                     tal:attributes="name action_name;
-                                     id string:decline.${team_name}"/>
-                </td>
-                <td><input type="radio" value="hold" checked="checked"
-                     tal:attributes="name action_name;
-                                     id string:hold.${team_name}"/>
-                </td>
-                <td>
-                  <a tal:replace="structure team_link"/>
-                </td>
-                <td>
-                  <a tal:replace="structure registrant/fmt:link"/>
-                </td>
-                </tal:block>
-            </tr>
-        </table>
-      </metal:form>
-    </div>
-    <p tal:condition="not: view/registered_lists" id="status"
-        >There is nothing to do.</p>
-  </div>
-
-</body>
-</html>

=== modified file 'lib/lp/registry/tests/mailinglists_helper.py'
--- lib/lp/registry/tests/mailinglists_helper.py	2010-10-26 03:51:12 +0000
+++ lib/lp/registry/tests/mailinglists_helper.py	2010-12-13 15:01:21 +0000
@@ -181,7 +181,7 @@
     """
     # Any member of the mailing-list-experts team can review a list
     # registration.  It doesn't matter which one.
-    experts = getUtility(ILaunchpadCelebrities).mailing_list_experts
+    experts = getUtility(ILaunchpadCelebrities).registry_experts
     reviewer = list(experts.allmembers)[0]
     list_set = getUtility(IMailingListSet)
     team_list = list_set.new(team)