launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #33009
[Merge] ~alvarocs/launchpad:remove-mailinglist-links into launchpad:master
Alvaro Crespo Serrano has proposed merging ~alvarocs/launchpad:remove-mailinglist-links into launchpad:master.
Commit message:
Remove mailing list portlet with links from team page and contact address page
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~alvarocs/launchpad/+git/launchpad/+merge/493207
- Team overview ~team: remove the mailing-list portlet so links are no longer shown.
- Team contact address form: remove the "set up a mailing list" link. Update warning message.
- Stories: remove doctests that depend on the now hidden UI.
- Tests: update test to to expect no subscribe link.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~alvarocs/launchpad:remove-mailinglist-links into launchpad:master.
diff --git a/lib/lp/registry/browser/tests/test_mailinglists.py b/lib/lp/registry/browser/tests/test_mailinglists.py
index 9493022..8bc50da 100644
--- a/lib/lp/registry/browser/tests/test_mailinglists.py
+++ b/lib/lp/registry/browser/tests/test_mailinglists.py
@@ -80,7 +80,7 @@ class MailingListSubscriptionControlsTestCase(TestCaseWithFactory):
)
content = view.render()
link_tag = find_tag_by_id(content, "link-list-subscribe")
- self.assertNotEqual(None, link_tag)
+ self.assertIsNone(link_tag)
def test_subscribe_control_doesnt_render_for_non_member(self):
other_person = self.factory.makePerson()
diff --git a/lib/lp/registry/stories/mailinglists/hosted-email-address.rst b/lib/lp/registry/stories/mailinglists/hosted-email-address.rst
deleted file mode 100644
index 428067c..0000000
--- a/lib/lp/registry/stories/mailinglists/hosted-email-address.rst
+++ /dev/null
@@ -1,84 +0,0 @@
-Changing from a hosted ML to an external address
-================================================
-
-Once made active, hosted mailing lists have their posting address stored in
-the EmailAddress table. These are not deleted when a team's contact address
-is changed from a hosted ML to an external address, in order to ensure that
-the address is reserved for the team which owns the ML.
-
- >>> login("foo.bar@xxxxxxxxxxxxx")
- >>> team, mailing_list = factory.makeTeamAndMailingList(
- ... "aardvarks", "no-priv"
- ... )
- >>> transaction.commit()
-
- >>> from lp.services.identity.model.emailaddress import EmailAddressSet
- >>> email_set = EmailAddressSet()
- >>> email_set.getByEmail(mailing_list.address)
- <EmailAddress ...
-
-The team owner sets the contact address to the hosted mailing list.
-
- >>> from lp.testing.pages import strip_label
-
- >>> logout()
- >>> user_browser.open("http://launchpad.test/~aardvarks/+edit")
- >>> user_browser.getLink(url="+contactaddress").click()
- >>> user_browser.getControl("The Launchpad mailing list").selected = True
- >>> user_browser.getControl("Change").click()
- >>> user_browser.getLink(url="+contactaddress").click()
- >>> control = user_browser.getControl(name="field.contact_method")
- >>> [strip_label(label) for label in control.displayValue]
- ['The Launchpad mailing list for this team...]
-
-The team owner sets the contact address to an external email address, and
-Launchpad sends that address a confirmation message.
-
- >>> from lp.services.mail import stub
- >>> stub.test_emails = []
- >>> user_browser.getControl("Another email address").selected = True
- >>> user_browser.getControl(name="field.contact_address").value = (
- ... "bar@xxxxxxxxxxx"
- ... )
- >>> user_browser.getControl("Change").click()
- >>> print(user_browser.title)
- Aardvarks in Launchpad
- >>> print_feedback_messages(user_browser.contents)
- A confirmation message has been sent to...
- >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
- >>> stub.test_emails
- []
- >>> import email
- >>> msg = email.message_from_bytes(raw_msg)
- >>> print(msg["From"])
- Launchpad Email Validator <noreply@xxxxxxxxxxxxx>
- >>> print(msg["Subject"])
- Launchpad: Validate your team's contact email address
-
-When the confirmation token url is followed, the external email address is
-confirmed.
-
- >>> from lp.services.verification.tests.logintoken import (
- ... get_token_url_from_email,
- ... )
- >>> token_url = get_token_url_from_email(raw_msg)
- >>> user_browser.open(token_url)
- >>> print(user_browser.title)
- Confirm email address
- >>> user_browser.getControl("Continue").click()
- >>> print(user_browser.title)
- Aardvarks in Launchpad
- >>> print_feedback_messages(user_browser.contents)
- Email address successfully confirmed.
-
- >>> user_browser.getLink(url="+contactaddress").click()
- >>> control = user_browser.getControl(name="field.contact_method")
- >>> [strip_label(label) for label in control.displayValue]
- ['Another email address']
-
-The web UI will only display the team's contact address, but the
-EmailAddress object for that team's mailing list will still be in the
-database.
-
- >>> email_set.getByEmail(mailing_list.address)
- <EmailAddress ...
diff --git a/lib/lp/registry/stories/mailinglists/lifecycle.rst b/lib/lp/registry/stories/mailinglists/lifecycle.rst
deleted file mode 100644
index 10a092e..0000000
--- a/lib/lp/registry/stories/mailinglists/lifecycle.rst
+++ /dev/null
@@ -1,424 +0,0 @@
-======================
-Mailing list lifecycle
-======================
-
-Every team in Launchpad can have a mailing list, and every mailing list is
-associated with exactly one team.
-
-
-Hosted mailing list
-===================
-
-The owner of Landscape Developers cannot create a new mailing list.
-
- >>> from zope.security.management import newInteraction, endInteraction
- >>> from lp.testing.factory import LaunchpadObjectFactory
-
- >>> browser = setupBrowser(auth="Basic test@xxxxxxxxxxxxx:test")
- >>> browser.open(
- ... "http://launchpad.test/~landscape-developers/+mailinglist"
- ... )
- >>> from lp.services.helpers import backslashreplace
- >>> print(backslashreplace(browser.title))
- Mailing list configuration : \u201cLandscape Developers\u201d team
- >>> print(
- ... extract_text(find_tag_by_id(browser.contents, "no_mailing_list"))
- ... )
- Launchpad no longer supports the creation of new mailing lists.
- Read more about it here.
-
- >>> browser.open("http://launchpad.test/~landscape-developers")
- >>> print(browser.title)
- Landscape Developers in Launchpad
-
- # Create a new mailing list for testing purposes.
- >>> newInteraction()
- >>> factory = LaunchpadObjectFactory()
- >>> factory.makeTeamAndMailingList(
- ... "landscape-developers", "test"
- ... ) # doctest: +ELLIPSIS
- (<Person landscape-developers (Landscape Developers)>,
- <MailingList for team "landscape-developers"; status=ACTIVE;
- address=landscape-developers@xxxxxxxxxxxxxxxxxxxx at ...>)
- >>> endInteraction()
-
- >>> def mailing_list_status_message(contents):
- ... """Find out if a mailing list is in an unusual state."""
- ... tag = find_tag_by_id(contents, "mailing_list_status_message")
- ... if tag:
- ... return extract_text(tag.strong)
- ... else:
- ... return ""
- ...
-
-Mailman helper function.
-
- >>> from lp.registry.tests import mailinglists_helper
- >>> def act():
- ... login("foo.bar@xxxxxxxxxxxxx")
- ... mailinglists_helper.mailman.act()
- ... transaction.commit()
- ... logout()
- ...
-
-Once the team's mailing list is active, there is a link to its archive. This
-is true even if no messages have yet been posted to the mailing list (since
-the archiver will display an informative message to that effect).
-
- >>> browser.open("http://launchpad.test/~landscape-developers")
- >>> print(
- ... extract_link_from_tag(
- ... find_tag_by_id(browser.contents, "mailing-list-archive")
- ... )
- ... )
- http://lists.launchpad.test/landscape-developers
-
-The team's overview page also displays the posting address.
-
- >>> print(
- ... extract_text(
- ... find_tag_by_id(
- ... browser.contents, "mailing-list-posting-address"
- ... )
- ... )
- ... )
- landscape-developers@xxxxxxxxxxxxxxxxxxxx
-
-Now that the mailing list is active, it can be used as the team's contact
-address.
-
- >>> from lp.testing.pages import strip_label
-
- >>> browser.getLink(url="+mailinglist").click()
- >>> print(
- ... extract_text(
- ... find_tag_by_id(
- ... browser.contents, "mailing_list_not_contact_address"
- ... )
- ... )
- ... )
- The mailing list is not set as the team contact address. You can
- set it.
-
- >>> browser.getLink(url="+contactaddress").click()
- >>> browser.getControl("The Launchpad mailing list").selected = True
- >>> browser.getControl("Change").click()
-
- >>> browser.getLink(url="+contactaddress").click()
- >>> control = browser.getControl(name="field.contact_method")
- >>> [strip_label(label) for label in control.displayValue]
- ['The Launchpad mailing list for this team...]
-
-The mailing list's configuration screen is also now available.
-
- >>> print(browser.getLink(url="+mailinglist").url)
- http://launchpad.test/~landscape-developers/+mailinglist
-
-When the mailing list is not the team's contact address, the mailing
-list configuration screen displays a message to this effect.
-
- >>> browser.getControl("Each member individually").selected = True
- >>> browser.getControl("Change").click()
-
- >>> browser.getLink(url="+mailinglist").click()
- >>> print(
- ... extract_text(
- ... find_tag_by_id(
- ... browser.contents, "mailing_list_not_contact_address"
- ... )
- ... )
- ... )
- The mailing list is not set as the team contact address. You can
- set it.
-
-The message contains a link to the contact address screen.
-
- >>> browser.getLink("set it").click()
- >>> browser.getControl("The Launchpad mailing list").selected = True
- >>> browser.getControl("Change").click()
- >>> print(browser.title)
- Landscape Developers in Launchpad
-
-When the mailing list is the team's contact address, the message does
-not show up.
-
- >>> browser.getLink(url="+mailinglist").click()
- >>> find_tag_by_id(
- ... browser.contents, "mailing_list_not_contact_address"
- ... ) is None
- True
-
-The contact address is now set to the mailing list address.
-
- >>> browser.goBack()
- >>> browser.getLink(url="+contactaddress").click()
- >>> control = browser.getControl(name="field.contact_method")
- >>> [strip_label(label) for label in control.displayValue]
- ['The Launchpad mailing list for this team -
- landscape-developers@xxxxxxxxxxxxxxxxxxxx']
-
-
-Deactivating and reactivating lists
-===================================
-
-An active mailing list can be deactivated. If the deactivated mailing
-list was the team contact method, the contact method will be changed
-to 'each user individually'.
-
- >>> browser.open(
- ... "http://launchpad.test/~landscape-developers/+mailinglist"
- ... )
- >>> browser.getControl("Deactivate this Mailing List").click()
- >>> browser.getLink(url="+contactaddress").click()
- >>> control = browser.getControl(name="field.contact_method")
- >>> [strip_label(label) for label in control.displayValue]
- ['Each member individually']
-
- >>> act()
- >>> browser.open(
- ... "http://launchpad.test/~landscape-developers/+mailinglist"
- ... )
- >>> print(mailing_list_status_message(browser.contents))
- This team's mailing list has been deactivated.
-
-A deactivated mailing list still has a link to its archive, because archives
-are never deleted.
-
- >>> browser.open("http://launchpad.test/~landscape-developers")
- >>> print(
- ... extract_link_from_tag(
- ... find_tag_by_id(browser.contents, "mailing-list-archive")
- ... )
- ... )
- http://lists.launchpad.test/landscape-developers
-
-An inactive mailing list cannot be reactivated.
-
- >>> browser.getLink(url="+mailinglist").click()
- >>> print(
- ... extract_text(
- ... find_tag_by_id(browser.contents, "mailing_list_reactivate")
- ... )
- ... )
- Launchpad no longer supports the reactivation of mailing lists.
- Read more about it here.
-
-The archive link is only available for public mailing lists as shown above,
-and for private mailing lists for team members.
-
- >>> from lp.registry.interfaces.person import PersonVisibility
- >>> login("foo.bar@xxxxxxxxxxxxx")
- >>> bassists = mailinglists_helper.new_team("bassists")
- >>> bassists.visibility = PersonVisibility.PRIVATE
- >>> bassists_list = mailinglists_helper.new_list_for_team(bassists)
- >>> logout()
-
-The owner of the list can see archive link.
-
- >>> user_browser.open("http://launchpad.test/~bassists")
- >>> print(
- ... extract_link_from_tag(
- ... find_tag_by_id(user_browser.contents, "mailing-list-archive")
- ... )
- ... )
- http://lists.launchpad.test/bassists
-
-Anonymous users cannot see the link, because they cannot even see the
-private team.
-
- >>> anon_browser.open("http://launchpad.test/~bassists")
- Traceback (most recent call last):
- ...
- zope.publisher.interfaces.NotFound: Object: <...>, name: '~bassists'
-
-The same is true for normal users who are not team members.
-
- >>> browser.open("http://launchpad.test/~bassists")
- Traceback (most recent call last):
- ...
- zope.publisher.interfaces.NotFound: Object: <...>, name: '~bassists'
-
-Members who are not owners can see the link.
-
- >>> cprov_browser = setupBrowser(
- ... auth="Basic celso.providelo@xxxxxxxxxxxxx:test"
- ... )
- >>> cprov_browser.open("http://launchpad.test/~bassists")
- Traceback (most recent call last):
- ...
- zope.publisher.interfaces.NotFound: Object: <...>, name: '~bassists'
-
- >>> admin_browser.open("http://launchpad.test/~bassists/+addmember")
- >>> admin_browser.getControl("New member").value = "cprov"
- >>> admin_browser.getControl("Add Member").click()
-
- >>> cprov_browser.open("http://launchpad.test/~bassists")
- >>> print(
- ... extract_link_from_tag(
- ... find_tag_by_id(cprov_browser.contents, "mailing-list-archive")
- ... )
- ... )
- http://lists.launchpad.test/bassists
-
-Admins who are not members of the team can see the link too.
-
- >>> admin_browser.open("http://launchpad.test/~bassists")
- >>> print(
- ... extract_link_from_tag(
- ... find_tag_by_id(admin_browser.contents, "mailing-list-archive")
- ... )
- ... )
- http://lists.launchpad.test/bassists
-
-
-Purge permissions
-=================
-
-A mailing list may be 'purged' when it is in one of several safe states.
-By 'safe' we mean that there are no artifacts of the mailing list on the
-Mailman side that need to be preserved. This is not guaranteed by the
-code, except by the state of the mailing list, so if for example we want
-to delete the archives of an INACTIVE list, this must be done manually.
-
- # Create a team without a mailing list owned by no-priv so the owner of
- # the team has no additional privileges.
- >>> login("foo.bar@xxxxxxxxxxxxx")
- >>> team = mailinglists_helper.new_team("aardvarks")
- >>> logout()
-
- >>> from zope.component import getUtility
- >>> from lp.registry.interfaces.mailinglist import IMailingListSet
- >>> def print_list_state(team_name="aardvarks"):
- ... login("foo.bar@xxxxxxxxxxxxx")
- ... mailing_list = getUtility(IMailingListSet).get(team_name)
- ... print(mailing_list.status.name)
- ... logout()
- ...
-
-The team owner cannot create new mailing lists.
-
- >>> user_browser.open("http://launchpad.test/~aardvarks/+mailinglist")
- >>> print(
- ... extract_text(
- ... find_tag_by_id(user_browser.contents, "no_mailing_list")
- ... )
- ... )
- Launchpad no longer supports the creation of new mailing lists.
- Read more about it here.
-
- # Create a mailing list to test the deletion, purging, and reactivation
- # options.
- >>> newInteraction()
- >>> mailinglists_helper.new_list_for_team(team) # doctest: +ELLIPSIS
- <MailingList for team "aardvarks"; status=ACTIVE;
- address=aardvarks@xxxxxxxxxxxxxxxxxxxx at ...>
- >>> endInteraction()
-
-The team owner can purge or deactivate mailing lists.
-
- >>> user_browser.open("http://launchpad.test/~aardvarks/+mailinglist")
- >>> user_browser.getControl("Deactivate this Mailing List").click()
- >>> act()
- >>> print_list_state()
- INACTIVE
-
- >>> def purge_text(browser):
- ... tag = find_tag_by_id(browser.contents, "mailing_list_purge")
- ... if tag is None:
- ... return None
- ... return tag.p.contents[0].strip()
- ...
-
- >>> user_browser.getLink(url="+mailinglist").click()
- >>> print(purge_text(user_browser))
- You can purge this mailing list...
-
-The team owner cannot reactivate mailing lists.
-
- >>> print(
- ... extract_text(
- ... find_tag_by_id(
- ... user_browser.contents, "mailing_list_reactivate"
- ... )
- ... )
- ... )
- Launchpad no longer supports the reactivation of mailing lists.
- Read more about it here.
-
- >>> user_browser.getControl("Purge this Mailing List")
- <SubmitControl name='field.actions.purge_list' type='submit'>
-
-Mailing list experts can also purge mailing lists. Sample Person is
-trustworthy enough to become a mailing list expert, but not a Launchpad
-administrator. They're given mailing list expert authority so that they can
-purge mailing lists.
-
- >>> login("foo.bar@xxxxxxxxxxxxx")
- >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities
- >>> from lp.registry.interfaces.person import IPersonSet
- >>> person_set = getUtility(IPersonSet)
- >>> test = person_set.getByName("name12")
- >>> experts = getUtility(ILaunchpadCelebrities).registry_experts
- >>> ignored = experts.addMember(test, reviewer=experts.teamowner)
- >>> logout()
- >>> transaction.commit()
-
-Sample Person, who is now a mailing list expert but not a Launchpad
-administrator, can purge a list.
-
- >>> expert_browser = setupBrowser("Basic test@xxxxxxxxxxxxx:test")
- >>> expert_browser.open("http://launchpad.test/~aardvarks/+mailinglist")
- >>> print(purge_text(expert_browser))
- You can purge this mailing list...
-
-A constructing, modified, updating, or deactivating or mod-failed list cannot
-be purged.
-
- >>> from zope.security.proxy import removeSecurityProxy
- >>> from lp.registry.interfaces.mailinglist import MailingListStatus
-
- >>> def set_list_state(team_name, status):
- ... login("foo.bar@xxxxxxxxxxxxx")
- ... mailing_list = getUtility(IMailingListSet).get(team_name)
- ... naked_list = removeSecurityProxy(mailing_list)
- ... naked_list.status = status
- ... transaction.commit()
- ... logout()
- ...
-
- >>> def show_states(*states):
- ... url = "http://launchpad.test/~aardvarks/+mailinglist"
- ... for status in states:
- ... set_list_state("aardvarks", status)
- ... print_list_state()
- ... admin_browser.open(url)
- ... print(purge_text(admin_browser))
- ... expert_browser.open(url)
- ... print(purge_text(expert_browser))
- ...
-
-A purged list acts as if it doesn't even exist.
-
- >>> set_list_state("aardvarks", MailingListStatus.PURGED)
- >>> print_list_state()
- PURGED
- >>> admin_browser.open("http://launchpad.test/~aardvarks/+mailinglist")
- >>> print(
- ... extract_text(
- ... find_tag_by_id(admin_browser.contents, "no_mailing_list")
- ... )
- ... )
- Launchpad no longer supports the creation of new mailing lists.
- Read more about it here.
- >>> expert_browser.open("http://launchpad.test/~aardvarks/+mailinglist")
- >>> print(
- ... extract_text(
- ... find_tag_by_id(expert_browser.contents, "no_mailing_list")
- ... )
- ... )
- Launchpad no longer supports the creation of new mailing lists.
- Read more about it here.
-
-The team owner can see that an inactive list can be reactivated or purged.
-
- >>> set_list_state("aardvarks", MailingListStatus.INACTIVE)
diff --git a/lib/lp/registry/stories/mailinglists/subscriptions.rst b/lib/lp/registry/stories/mailinglists/subscriptions.rst
deleted file mode 100644
index cb8db26..0000000
--- a/lib/lp/registry/stories/mailinglists/subscriptions.rst
+++ /dev/null
@@ -1,665 +0,0 @@
-====================================
-Mailing list subscription management
-====================================
-
-Teams can have mailing lists associated with them, and a member of a
-team with a usable mailing list can subscribe to that list. A person
-can subscribe to a mailing list using any of their email addresses, or a
-dynamic subscription which uses whichever email address is their
-preferred address at a given time.
-
-Non-team members can request a subscription to a mailing list at the
-same time as they sign up for a new team. Their list membership is
-approved at the same time that they are approved for the team.
-
-
-Setup
-=====
-
-Carlos is a member of four teams: admins, rosetta-admins, testing-spanish-team
-and ubuntu-translators. He has two email addresses, one of which is his
-preferred email address. Both the admins and rosetta-admins teams are given
-mailing lists but only admins will actually use its mailing list as its
-contact address.
-
- >>> from zope.security.management import newInteraction, endInteraction
- >>> from lp.testing.factory import LaunchpadObjectFactory
-
- >>> newInteraction()
- >>> factory = LaunchpadObjectFactory()
- >>> factory.makeTeamAndMailingList("admins", "foo") # doctest: +ELLIPSIS
- (<Person admins (Launchpad Administrators)>,
- <MailingList for team "admins"; status=ACTIVE;
- address=admins@xxxxxxxxxxxxxxxxxxxx at ...>)
- >>> endInteraction()
-
- >>> newInteraction()
- >>> factory = LaunchpadObjectFactory()
- >>> factory.makeTeamAndMailingList(
- ... "rosetta-admins", "foo"
- ... ) # doctest: +ELLIPSIS
- (<Person rosetta-admins (Rosetta Administrators)>,
- <MailingList for team "rosetta-admins"; status=ACTIVE;
- address=rosetta-admins@xxxxxxxxxxxxxxxxxxxx at ...>)
- >>> endInteraction()
-
- >>> admin_browser.open("http://launchpad.test/~admins/+edit")
- >>> admin_browser.getLink(url="+contactaddress").click()
- >>> admin_browser.getControl("The Launchpad mailing list").selected = True
- >>> admin_browser.getControl("Change").click()
-
-Carlos requests a mailing list for testing-spanish-team but it will not
-actually be approved (new mailing lists cannot be created).
-
- >>> browser = setupBrowser(auth="Basic carlos@xxxxxxxxxxxxx:test")
- >>> browser.open(
- ... "http://launchpad.test/~testing-spanish-team/+mailinglist"
- ... )
- >>> print(
- ... extract_text(find_tag_by_id(browser.contents, "no_mailing_list"))
- ... )
- Launchpad no longer supports the creation of new mailing lists.
- Read more about it here.
-
-
-Subscribing
-===========
-
-Any team member can join the mailing list.
-
- >>> admin_browser.open("http://launchpad.test/~rosetta-admins/+addmember")
- >>> admin_browser.getControl("New member").value = "no-team-memberships"
- >>> admin_browser.getControl("Add Member").click()
-
- >>> from lp.testing.pages import setupBrowserFreshLogin
- >>> from zope.component import getUtility
- >>> from lp.registry.interfaces.person import IPersonSet
- >>> login(ANONYMOUS)
- >>> person = getUtility(IPersonSet).getByEmail(
- ... "no-team-memberships@xxxxxxxx"
- ... )
- >>> logout()
- >>> no_team_browser = setupBrowserFreshLogin(person)
-
- >>> no_team_browser.open(
- ... "http://launchpad.test/people/+me/+editmailinglists"
- ... )
- >>> rosetta_admins = no_team_browser.getControl(
- ... name="field.subscription.rosetta-admins"
- ... )
- >>> rosetta_admins.displayOptions
- ['Preferred address', "Don't subscribe", 'no-team-memberships@xxxxxxxx']
-
-
-Subscription management
-=======================
-
-To subscribe to a mailing list, Carlos uses his subscription management
-screen, which shows a subscription control for the mailing lists of every team
-he's a member of. Mailing lists show up in this list regardless of whether
-it's currently the team contact method.
-
- >>> login(ANONYMOUS)
- >>> carlos = getUtility(IPersonSet).getByName("carlos")
- >>> logout()
- >>> carlos_browser = setupBrowserFreshLogin(carlos)
- >>> carlos_browser.open("http://launchpad.test/~carlos")
- >>> carlos_browser.getLink(url="+editmailinglists").click()
-
- >>> from lp.services.helpers import backslashreplace
- >>> print(backslashreplace(carlos_browser.title))
- Change your mailing list subscriptions...
-
- >>> admins = carlos_browser.getControl(name="field.subscription.admins")
- >>> rosetta_admins = carlos_browser.getControl(
- ... name="field.subscription.rosetta-admins"
- ... )
-
- >>> admins.displayOptions
- ['Preferred address', "Don't subscribe",
- 'carlos@xxxxxxxxxxxxx', 'carlos@xxxxxxxx']
-
- >>> print(admins.value)
- ["Don't subscribe"]
- >>> print(rosetta_admins.value)
- ["Don't subscribe"]
-
-However, testing-spanish-team's list doesn't show up because its creation has
-not been completed (specifically, Mailman hasn't constructed it yet).
-
- >>> carlos_browser.getControl(
- ... name="field.subscription.testing-spanish-team"
- ... )
- Traceback (most recent call last):
- ...
- LookupError: name ...'field.subscription.testing-spanish-team'
- ...
-
-Carlos can subscribe to a list using his preferred email address. Such
-subscriptions will track changes to his preferred address without requiring
-him to update his subscription. So this is not the same as subscribing
-explicitly with whatever is his preferred email address.
-
- >>> admins = carlos_browser.getControl(name="field.subscription.admins")
- >>> admins.value = ["Preferred address"]
- >>> carlos_browser.getControl("Update Subscriptions").click()
-
- >>> print_feedback_messages(carlos_browser.contents)
- Subscriptions updated.
-
- >>> admins = carlos_browser.getControl(name="field.subscription.admins")
- >>> rosetta_admins = carlos_browser.getControl(
- ... name="field.subscription.rosetta-admins"
- ... )
- >>> print(admins.value)
- ['Preferred address']
- >>> print(rosetta_admins.value)
- ["Don't subscribe"]
-
-Carlos can subscribe to a list using any of his validated addresses
-explicitly.
-
- >>> admins.value = ["carlos@xxxxxxxxxxxxx"]
- >>> rosetta_admins.value = ["carlos@xxxxxxxx"]
- >>> carlos_browser.getControl("Update Subscriptions").click()
-
- >>> admins = carlos_browser.getControl(name="field.subscription.admins")
- >>> rosetta_admins = carlos_browser.getControl(
- ... name="field.subscription.rosetta-admins"
- ... )
- >>> print(admins.value)
- ['carlos@xxxxxxxxxxxxx']
- >>> print(rosetta_admins.value)
- ['carlos@xxxxxxxx']
-
-He can switch from one address to another, or from a specific address
-to the preferred address.
-
- >>> admins.value = ["Preferred address"]
- >>> rosetta_admins.value = ["carlos@xxxxxxxxxxxxx"]
- >>> carlos_browser.getControl("Update Subscriptions").click()
-
- >>> admins = carlos_browser.getControl(name="field.subscription.admins")
- >>> rosetta_admins = carlos_browser.getControl(
- ... name="field.subscription.rosetta-admins"
- ... )
- >>> print(admins.value)
- ['Preferred address']
- >>> print(rosetta_admins.value)
- ['carlos@xxxxxxxxxxxxx']
-
-Finally, he can unsubscribe from any mailing list by setting the subscription
-menu item to "Don't subscribe".
-
- >>> admins = carlos_browser.getControl(name="field.subscription.admins")
- >>> rosetta_admins = carlos_browser.getControl(
- ... name="field.subscription.rosetta-admins"
- ... )
- >>> admins.value = ["Don't subscribe"]
- >>> rosetta_admins.value = ["Don't subscribe"]
- >>> carlos_browser.getControl("Update Subscriptions").click()
-
- >>> admins = carlos_browser.getControl(name="field.subscription.admins")
- >>> rosetta_admins = carlos_browser.getControl(
- ... name="field.subscription.rosetta-admins"
- ... )
- >>> print(admins.value)
- ["Don't subscribe"]
- >>> print(rosetta_admins.value)
- ["Don't subscribe"]
-
-
-Subscription during team sign up
-================================
-
-Jdub is only a member of the ubuntu team. He can request to be placed
-on another team's mailing list at the same time that he requests
-membership on the team.
-
-First we need to confirm that the desired team has a list to subscribe
-to. We will use Carlos, as he is an administrator for the Rosetta
-Admins team, and he should know if the list is available.
-
- >>> carlos_browser.open("http://launchpad.test/~carlos")
- >>> carlos_browser.getLink(url="+editmailinglists").click()
- >>> print(backslashreplace(carlos_browser.title))
- Change your mailing list subscriptions...
-
- >>> rosetta_admins = carlos_browser.getControl(
- ... name="field.subscription.rosetta-admins"
- ... )
- >>> rosetta_admins.displayOptions
- ['Preferred address', "Don't subscribe",
- 'carlos@xxxxxxxxxxxxx', 'carlos@xxxxxxxx']
-
-Now Jdub can apply for team membership and mailing list access.
-
- >>> browser = setupBrowser(auth="Basic jeff.waugh@xxxxxxxxxxxxxxx:test")
- >>> browser.open("http://launchpad.test/~rosetta-admins")
- >>> browser.getLink("Join the team").click()
- >>> browser.url
- 'http://launchpad.test/~rosetta-admins/+join'
-
- >>> browser.getControl(name="field.mailinglist_subscribe").value = True
- >>> browser.getControl(name="field.actions.join").click()
- >>> browser.url
- 'http://launchpad.test/~rosetta-admins'
-
- >>> for tag in find_tags_by_class(browser.contents, "informational"):
- ... print(tag.decode_contents())
- ...
- Your request to join Rosetta Administrators is awaiting approval.
- Your mailing list subscription is awaiting approval.
-
-Jdub hasn't been approved for the team yet, so he is not subscribed to
-the list. The list does not show up on his Subscription Management
-screen.
-
- >>> login(ANONYMOUS)
- >>> jdub = getUtility(IPersonSet).getByName("jdub")
- >>> logout()
- >>> jdub_browser = setupBrowserFreshLogin(jdub)
- >>> jdub_browser.open("http://launchpad.test/~jdub")
- >>> jdub_browser.getLink(url="+editmailinglists").click()
- >>> print(jdub_browser.title)
- Change your mailing list subscriptions...
-
- >>> jdub_browser.getControl(name="field.subscription.rosetta-admins")
- Traceback (most recent call last):
- ...
- LookupError: name ...'field.subscription.rosetta-admins'
- ...
-
-Jdub will become a member of the team's mailing list as soon as he has
-been approved for the team.
-
- >>> admin_browser.open("http://launchpad.test/~rosetta-admins")
- >>> admin_browser.getLink("All members").click()
- >>> admin_browser.getLink(url="/~rosetta-admins/+member/jdub").click()
- >>> print(admin_browser.url)
- http://launchpad.test/~rosetta-admins/+member/jdub
- >>> admin_browser.getControl(name="approve").click()
-
-His mailing list subscription is now available to be managed.
-
- >>> jdub_browser.open("http://launchpad.test/~jdub")
- >>> jdub_browser.getLink(url="+editmailinglists").click()
- >>> print(jdub_browser.title)
- Change your mailing list subscriptions...
-
- >>> rosetta_team = jdub_browser.getControl(
- ... name="field.subscription.rosetta-admins"
- ... )
-
- >>> rosetta_team.displayOptions
- ['Preferred address', "Don't subscribe", 'jeff.waugh@xxxxxxxxxxxxxxx']
-
-Jdub's mailing list preferences are preserved when he leaves the team.
-When he requests to re-join, the option to re-subscribe to the mailing
-list is not presented.
-
- >>> browser.open("http://launchpad.test/~rosetta-admins/+leave")
- >>> browser.getControl(name="field.actions.leave").click()
-
- >>> browser.open("http://launchpad.test/~rosetta-admins")
- >>> browser.getLink("Join the team").click()
- >>> print(browser.url)
- http://launchpad.test/~rosetta-admins/+join
-
- >>> browser.getControl(name="mailinglist_subscribe")
- Traceback (most recent call last):
- ...
- LookupError: name ...'mailinglist_subscribe'
- ...
-
-Of course, the option to subscribe to the mailing list isn't present
-for teams that don't have mailing lists.
-
- >>> browser.open("http://launchpad.test/~testing-spanish-team")
- >>> browser.getLink("Join the team").click()
- >>> print(browser.url)
- http://launchpad.test/~testing-spanish-team/+join
-
- >>> browser.getControl(name="mailinglist_subscribe")
- Traceback (most recent call last):
- ...
- LookupError: name ...'mailinglist_subscribe'
- ...
-
-And the option is also missing from the sign-up pages of teams that
-have restricted membership. (Note that we can only see the join page
-if we visit the URL directly, as the link is not present on the Team
-Overview.)
-
- >>> browser.open("http://launchpad.test/~launchpad/+join")
- >>> print(browser.url)
- http://launchpad.test/~launchpad/+join
-
- >>> browser.getControl(name="mailinglist_subscribe")
- Traceback (most recent call last):
- ...
- LookupError: name ...'mailinglist_subscribe'
- ...
-
-
-Team page quick-links
-=====================
-
-Links to subscribe and unsubscribe from the mailing lists are also
-available from the team's Overview page.
-
-Carlos can see the subscribe link on the admin team's Overview
-page, because he is not subscribed to the team mailing list.
-
- >>> carlos_browser.open("http://launchpad.test/~admins")
- >>> carlos_browser.getLink("Subscribe to mailing list").click()
- >>> print(carlos_browser.url)
- http://launchpad.test/~carlos/+editmailinglists
-
-The unsubscribe link is visible for the rosetta admins team, which
-has an active mailing list.
-
- # Subscribe to the list using the normal technique.
- >>> carlos_browser.open("http://launchpad.test/~carlos")
- >>> carlos_browser.getLink(url="+editmailinglists").click()
- >>> rosetta_admins = carlos_browser.getControl(
- ... name="field.subscription.rosetta-admins"
- ... )
- >>> rosetta_admins.value = ["Preferred address"]
- >>> carlos_browser.getControl("Update Subscriptions").click()
- >>> print(rosetta_admins.value)
- ['Preferred address']
- >>> for tag in find_tags_by_class(
- ... carlos_browser.contents, "informational"
- ... ):
- ... print(tag.decode_contents())
- Subscriptions updated.
-
- >>> carlos_browser.open("http://launchpad.test/~rosetta-admins")
- >>> carlos_browser.getControl("Unsubscribe")
- <SubmitControl name='unsubscribe' type='submit'>
-
-Clicking the link will unsubscribe you from the list immediately.
-
- >>> carlos_browser.getControl("Unsubscribe").click()
- >>> print_feedback_messages(carlos_browser.contents)
- You have been unsubscribed from the team mailing list.
-
- >>> carlos_browser.open("http://launchpad.test/~rosetta-admins")
- >>> print(
- ... extract_text(
- ... find_tag_by_id(carlos_browser.contents, "mailing-lists")
- ... )
- ... )
- Mailing list...
- Subscribe to mailing list...
-
-The Ubuntu translators team, which does not have any lists configured,
-does not show either link.
-
- >>> carlos_browser.open("http://launchpad.test/~ubuntu-translators")
- >>> print(
- ... extract_text(
- ... find_portlet(carlos_browser.contents, "Mailing list")
- ... )
- ... )
- Mailing list
- Launchpad no longer supports the creation of new mailing lists.
- Read more about it here.
-
- >>> carlos_browser.getLink("Subscribe")
- Traceback (most recent call last):
- ...
- zope.testbrowser.browser.LinkNotFoundError
-
- >>> carlos_browser.getLink("Unsubscribe")
- Traceback (most recent call last):
- ...
- zope.testbrowser.browser.LinkNotFoundError
-
-
-Team page subscribers link
-==========================
-
-Team administrators can see a link to a page listing the team's
-mailing list subscribers, if there is an active mailing list. The
-rosetta admins team has such a list and carlos is the owner.
-
- >>> carlos_browser.open("http://launchpad.test/~rosetta-admins")
- >>> print(
- ... extract_text(
- ... find_portlet(carlos_browser.contents, "Mailing list")
- ... )
- ... )
- Mailing list
- rosetta-admins@xxxxxxxxxxxxxxxxxxxx
- Policy: You must be a team member to subscribe to the team mailing list.
- Subscribe to mailing list
- View public archive
- View subscribers...
-
-The mailing list for Rosetta Admins has no subscribers.
-(Jeff Waugh has asked to subscribe but he's not considered a subscriber
-because his membership on Rosetta Admins hasn't been approved)
-
- >>> carlos_browser.getLink("View subscribers").click()
- >>> print(carlos_browser.title)
- Mailing list subscribers for the Rosetta Administrators team...
-
- >>> print(
- ... extract_text(
- ... find_tag_by_id(carlos_browser.contents, "subscribers")
- ... )
- ... )
- Nobody has subscribed to this team's mailing list yet.
-
-If it had subscribers, though, they'd be shown on that page, in a batched
-list.
-
- # Forge two new subscribers to the team's mailing list.
- >>> from lp.services.config import config
- >>> config.push(
- ... "default-batch-size",
- ... """
- ... [launchpad]
- ... default_batch_size: 1
- ... """,
- ... )
- >>> login("foo.bar@xxxxxxxxxxxxx")
-
- >>> person_set = getUtility(IPersonSet)
- >>> jdub = person_set.getByName("jdub")
- >>> mark = person_set.getByName("mark")
- >>> salgado = person_set.getByName("salgado")
- >>> jordi = person_set.getByName("jordi")
- >>> rosetta_admins = person_set.getByName("rosetta-admins")
- >>> ignored = rosetta_admins.addMember(salgado, reviewer=mark)
- >>> rosetta_admins.mailing_list.subscribe(salgado)
- >>> ignored = rosetta_admins.addMember(jordi, reviewer=mark)
- >>> rosetta_admins.mailing_list.subscribe(jordi)
- >>> logout()
- >>> carlos_browser.reload()
- >>> print(
- ... extract_text(
- ... find_tag_by_id(carlos_browser.contents, "subscribers")
- ... )
- ... )
- The following people are subscribed...
- Guilherme Salgado
- 1 of 2 results...
-
- >>> carlos_browser.getLink("Next").click()
- >>> print(
- ... extract_text(
- ... find_tag_by_id(carlos_browser.contents, "subscribers")
- ... )
- ... )
- The following people are subscribed...
- Jordi Mallach
- 2 of 2 results...
-
- >>> config_data = config.pop("default-batch-size")
-
-If the team has no mailing list, then the archive and subscribers
-links are not present.
-
- >>> admin_browser.open("http://launchpad.test/~commercial-admins")
- >>> summary_content = extract_text(
- ... find_portlet(admin_browser.contents, "Mailing list")
- ... )
- >>> "Mailing list archive" in summary_content
- False
- >>> "Mailing list subscribers" in summary_content
- False
-
-
-Mailing list auto-subscription settings
-=======================================
-
-Launchpad may automatically subscribe a person to a team's mailing
-list based on a setting in the person's Email preferences page.
-
- >>> carlos_browser.open("http://launchpad.test/~carlos")
- >>> carlos_browser.getLink(url="+editmailinglists").click()
- >>> print(backslashreplace(carlos_browser.title))
- Change your mailing list subscriptions...
-
-Carlos's default setting, 'Ask me when I join a team', is still in place.
-
- >>> print_radio_button_field(
- ... carlos_browser.contents, "mailing_list_auto_subscribe_policy"
- ... )
- ( ) Never subscribe to mailing lists
- (*) Ask me when I join a team
- ( ) Always subscribe me to mailing lists
-
-Carlos can update the value at any time using his Email Preferences
-page.
-
- # A convenient helper for setting and submitting a new
- # auto-subscribe policy.
- >>> def set_autosubscribe_policy_and_submit(newvalue, current_browser):
- ... control = current_browser.getControl(
- ... name="field.mailing_list_auto_subscribe_policy"
- ... )
- ... control.value = [newvalue]
- ... current_browser.getControl("Update Policy").click()
- ... print_radio_button_field(
- ... current_browser.contents, "mailing_list_auto_subscribe_policy"
- ... )
- ...
-
- >>> original_value = carlos_browser.getControl(
- ... name="field.mailing_list_auto_subscribe_policy"
- ... ).value.pop()
-
- >>> set_autosubscribe_policy_and_submit("ALWAYS", carlos_browser)
- ( ) Never subscribe to mailing lists
- ( ) Ask me when I join a team
- (*) Always subscribe me to mailing lists
-
- # We only need to check this once.
- >>> print_feedback_messages(carlos_browser.contents)
- Your auto-subscribe policy has been updated.
-
- >>> set_autosubscribe_policy_and_submit("NEVER", carlos_browser)
- (*) Never subscribe to mailing lists
- ( ) Ask me when I join a team
- ( ) Always subscribe me to mailing lists
-
- >>> set_autosubscribe_policy_and_submit("ON_REGISTRATION", carlos_browser)
- ( ) Never subscribe to mailing lists
- (*) Ask me when I join a team
- ( ) Always subscribe me to mailing lists
-
-Updating the value twice has no adverse affect.
-
- # Restores the original value while performing the test.
- >>> assert original_value == "ON_REGISTRATION"
- >>> set_autosubscribe_policy_and_submit(original_value, carlos_browser)
- ( ) Never subscribe to mailing lists
- (*) Ask me when I join a team
- ( ) Always subscribe me to mailing lists
-
-Regardless of this setting, users will always receive a notification when a
-team they are a member of creates a new mailing list. This notification
-offers them to join the new mailing list. This page informs them of this
-behaviour.
-
- >>> print(
- ... extract_text(
- ... find_tag_by_id(carlos_browser.contents, "notification-info")
- ... )
- ... )
- When a team you are a member of creates a new mailing list, you will
- receive an email notification offering you the opportunity to join the new
- mailing list. Launchpad can also automatically subscribe you to a team's
- mailing list whenever you join a team.
-
-These settings also effect what a user sees when they go to join a new
-team. The 'Team Join' page has a checkbox that allows a user to sign
-up for the team mailing list at the same time as joining the
-team. Users who have chosen the 'On registration' or 'Always'
-subscription settings will see the box checked by default.
-
- >>> login(ANONYMOUS)
- >>> james = getUtility(IPersonSet).getByEmail(
- ... "james.blackwell@xxxxxxxxxxxxxxx"
- ... )
- >>> logout()
- >>> browser = setupBrowserFreshLogin(james)
- >>> browser.open("http://launchpad.test/~jblack")
- >>> browser.getLink(url="+editmailinglists").click()
- >>> print_radio_button_field(
- ... browser.contents, "mailing_list_auto_subscribe_policy"
- ... )
- ( ) Never subscribe to mailing lists
- (*) Ask me when I join a team
- ( ) Always subscribe me to mailing lists
-
- >>> browser.open("http://launchpad.test/~rosetta-admins")
- >>> browser.getLink("Join the team").click()
- >>> print(browser.url)
- http://launchpad.test/~rosetta-admins/+join
-
- >>> print(browser.getControl(name="field.mailinglist_subscribe").value)
- True
-
- # Change James' setting
- >>> browser.open("http://launchpad.test/~jblack")
- >>> browser.getLink(url="+editmailinglists").click()
- >>> set_autosubscribe_policy_and_submit("ALWAYS", browser)
- ( ) Never subscribe to mailing lists
- ( ) Ask me when I join a team
- (*) Always subscribe me to mailing lists
-
- >>> browser.open("http://launchpad.test/~rosetta-admins")
- >>> browser.getLink("Join the team").click()
- >>> print(browser.getControl(name="field.mailinglist_subscribe").value)
- True
-
-Users who have chosen to never be auto-subscribed to mailing
-lists will not have the box checked.
-
- # Change James' setting
- >>> browser.open("http://launchpad.test/~jblack")
- >>> browser.getLink(url="+editmailinglists").click()
- >>> set_autosubscribe_policy_and_submit("NEVER", browser)
- (*) Never subscribe to mailing lists
- ( ) Ask me when I join a team
- ( ) Always subscribe me to mailing lists
-
- >>> browser.open("http://launchpad.test/~rosetta-admins")
- >>> browser.getLink("Join the team").click()
- >>> print(
- ... bool(browser.getControl(name="field.mailinglist_subscribe").value)
- ... )
- False
-
- # Restore James' setting.
- >>> browser.open("http://launchpad.test/~jblack")
- >>> browser.getLink(url="+editmailinglists").click()
- >>> set_autosubscribe_policy_and_submit("ON_REGISTRATION", browser)
- ( ) Never subscribe to mailing lists
- (*) Ask me when I join a team
- ( ) Always subscribe me to mailing lists
diff --git a/lib/lp/registry/stories/mailinglists/welcome-message.rst b/lib/lp/registry/stories/mailinglists/welcome-message.rst
deleted file mode 100644
index f49663c..0000000
--- a/lib/lp/registry/stories/mailinglists/welcome-message.rst
+++ /dev/null
@@ -1,66 +0,0 @@
-Customizing the welcome message
-===============================
-
-A team mailing list can have a custom welcome message. The welcome message is
-customizable whether or not the mailing list is currently the team's contact
-address.
-
- >>> login("foo.bar@xxxxxxxxxxxxx")
- >>> team, mailing_list = factory.makeTeamAndMailingList(
- ... "aardvarks", "no-priv"
- ... )
- >>> logout()
- >>> transaction.commit()
-
- >>> user_browser.open("http://launchpad.test/~aardvarks")
- >>> user_browser.getLink("Configure mailing list").click()
- >>> welcome_message = user_browser.getControl("Welcome message")
- >>> print(welcome_message.value)
- >>> welcome_message.value = "Welcome to the Aardvarks."
- >>> user_browser.getControl("Save").click()
-
-Changes to the welcome message take effect as soon as Mailman can act on it.
-
- >>> user_browser.getLink("Configure mailing list").click()
- >>> welcome_message = user_browser.getControl("Welcome message")
- >>> print(welcome_message.value)
- Welcome to the Aardvarks.
-
- >>> from lp.registry.tests import mailinglists_helper
- >>> login("foo.bar@xxxxxxxxxxxxx")
- >>> mailinglists_helper.mailman.act()
- >>> transaction.commit()
- >>> logout()
-
-What if Mailman failed to apply the change?
-
- >>> welcome_message.value = "This change will fail to propagate."
- >>> user_browser.getControl("Save").click()
-
- # Re-fetch the mailing list, this time from the utility.
- >>> from lp.registry.interfaces.mailinglist import IMailingListSet
- >>> login("foo.bar@xxxxxxxxxxxxx")
- >>> from zope.component import getUtility
- >>> mailing_list = getUtility(IMailingListSet).get("aardvarks")
- >>> mailing_list.status
- <DBItem MailingListStatus.MODIFIED, (8) Modified>
-
- # Fake a failure on the Mailman side.
- >>> from lp.registry.interfaces.mailinglist import MailingListStatus
- >>> from zope.security.proxy import removeSecurityProxy
- >>> naked_list = removeSecurityProxy(mailing_list)
- >>> removeSecurityProxy(mailing_list).status = (
- ... MailingListStatus.MOD_FAILED
- ... )
- >>> transaction.commit()
- >>> logout()
-
- >>> user_browser.open("http://launchpad.test/~aardvarks/+mailinglist")
- >>> print(
- ... extract_text(
- ... find_tag_by_id(
- ... user_browser.contents, "mailing_list_status_message"
- ... ).strong
- ... )
- ... )
- This team's mailing list is in an inconsistent state...
diff --git a/lib/lp/registry/stories/team/xx-team-contactemail.rst b/lib/lp/registry/stories/team/xx-team-contactemail.rst
index cdb27fa..c5bf89b 100644
--- a/lib/lp/registry/stories/team/xx-team-contactemail.rst
+++ b/lib/lp/registry/stories/team/xx-team-contactemail.rst
@@ -28,7 +28,7 @@ or external contact address.
>>> from lp.services.beautifulsoup import BeautifulSoup
>>> soup = BeautifulSoup(browser.contents)
>>> print(soup.find(id="email-warning").decode())
- <p ... Email sent to a mailing list or external contact address may ...
+ <p ... Email sent to an external contact address may ...
As we can see, the landscape-developers team has no contact address.
diff --git a/lib/lp/registry/templates/team-contactaddress.pt b/lib/lp/registry/templates/team-contactaddress.pt
index 3188a65..ee86a08 100644
--- a/lib/lp/registry/templates/team-contactaddress.pt
+++ b/lib/lp/registry/templates/team-contactaddress.pt
@@ -42,14 +42,9 @@
</metal:widgets>
<div metal:fill-slot="extra_bottom">
- <p id="set-up-a-mailing-list">
- If you <a href="+mailinglist">set up a mailing list for this
- team</a>, you can use the mailing list as the team contact
- address.
- </p>
<p id="email-warning" class="block-sprite large-warning">
- Email sent to a mailing list or external contact address may
+ Email sent to an external contact address may
be publicly accessible.<br/>If this team is subscribed to private
bug or branches, private information may be disclosed.<br/>
The safest option to avoid leaking private information is to
diff --git a/lib/lp/registry/templates/team-index.pt b/lib/lp/registry/templates/team-index.pt
index 25db8f5..eb29050 100644
--- a/lib/lp/registry/templates/team-index.pt
+++ b/lib/lp/registry/templates/team-index.pt
@@ -91,7 +91,6 @@
<!-- First portlet column. -->
<div class="first yui-u">
<div tal:content="structure context/@@+portlet-ppas" />
- <div tal:content="structure context/@@+portlet-mailinglist" />
</div>
<!-- Second portlet column. -->
Follow ups