← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~thomir/launchpad/devel-fix-editemails-link into lp:launchpad

 

Thomi Richards has proposed merging lp:~thomir/launchpad/devel-fix-editemails-link into lp:launchpad.

Commit message:
Add new page to manage mailing list subscriptions.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~thomir/launchpad/devel-fix-editemails-link/+merge/251176

This branch splits the +editemails page in 'twain:

* +editemails continues to be the place to add email addresses to an account, and to configure your preferred contact address. It requires a fresh login, since this is changing your login credentials.

* +editmailinglists is a new page which allows you to configure the mailing lists you're subscribed to, as well as set your mailing list auto-subscribe policy.

This branch fixes two bugs:

https://bugs.launchpad.net/launchpad/+bug/1425646 (it's too hard to find the +editemails page & configure mailing lists)

and

https://bugs.launchpad.net/launchpad/+bug/1425649 (it's too easy to click the wrong form button on +editemails, resulting in your settings not being saved).
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~thomir/launchpad/devel-fix-editemails-link into lp:launchpad.
=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml	2015-02-06 13:37:58 +0000
+++ lib/lp/registry/browser/configure.zcml	2015-02-26 21:41:09 +0000
@@ -1009,6 +1009,14 @@
                 name="+editemails"
                 template="../templates/person-editemails.pt"/>
         </browser:pages>
+        <browser:pages
+            for="lp.registry.interfaces.person.IPerson"
+            permission="launchpad.Edit"
+            class="lp.registry.browser.person.PersonEditMailingListsView">
+            <browser:page
+                name="+editmailinglists"
+                template="../templates/person-editmailinglists.pt"/>
+        </browser:pages>
         <browser:page
             name="+oauth-tokens"
             for="lp.registry.interfaces.person.IPerson"

=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py	2015-02-06 13:37:58 +0000
+++ lib/lp/registry/browser/person.py	2015-02-26 21:41:09 +0000
@@ -723,6 +723,7 @@
         'branding',
         'editemailaddresses',
         'editlanguages',
+        'editmailinglists',
         'editircnicknames',
         'editjabberids',
         'editsshkeys',
@@ -782,6 +783,12 @@
         return Link(target, text, icon='edit')
 
     @enabled_with_permission('launchpad.Edit')
+    def editmailinglists(self):
+        target = '+editmailinglists'
+        text = 'Manage mailing list subscriptions'
+        return Link(target, text, icon='edit')
+
+    @enabled_with_permission('launchpad.Edit')
     def editircnicknames(self):
         target = '+editircnicknames'
         text = 'Update IRC nicknames'
@@ -2722,8 +2729,6 @@
                   orientation='vertical')
     custom_widget('UNVALIDATED_SELECTED', LaunchpadRadioWidget,
                   orientation='vertical')
-    custom_widget('mailing_list_auto_subscribe_policy',
-                  LaunchpadRadioWidgetWithDescription)
 
     label = 'Change your e-mail settings'
 
@@ -2746,9 +2751,7 @@
         self.form_fields = (self._validated_emails_field() +
                             self._unvalidated_emails_field() +
                             FormFields(TextLine(__name__='newemail',
-                                                title=u'Add a new address'))
-                            + self._mailing_list_fields()
-                            + self._autosubscribe_policy_fields())
+                                                title=u'Add a new address')))
 
     @property
     def initial_values(self):
@@ -2769,20 +2772,8 @@
         unvalidated = self.unvalidated_addresses
         if len(unvalidated) > 0:
             unvalidated = unvalidated.pop()
-        initial = dict(VALIDATED_SELECTED=validated,
-                       UNVALIDATED_SELECTED=unvalidated)
-
-        # Defaults for the mailing list autosubscribe buttons.
-        policy = self.context.mailing_list_auto_subscribe_policy
-        initial.update(mailing_list_auto_subscribe_policy=policy)
-
-        return initial
-
-    def setUpWidgets(self, context=None):
-        """See `LaunchpadFormView`."""
-        super(PersonEditEmailsView, self).setUpWidgets(context)
-        widget = self.widgets['mailing_list_auto_subscribe_policy']
-        widget.display_label = False
+        return dict(VALIDATED_SELECTED=validated,
+                    UNVALIDATED_SELECTED=unvalidated)
 
     def _validated_emails_field(self):
         """Create a field with a vocabulary of validated emails.
@@ -2824,75 +2815,6 @@
                    source=SimpleVocabulary(terms)),
             custom_widget=self.custom_widgets['UNVALIDATED_SELECTED'])
 
-    def _mailing_list_subscription_type(self, mailing_list):
-        """Return the context user's subscription type for the given list.
-
-        This is 'Preferred address' if the user is subscribed using her
-        preferred address and 'Don't subscribe' if the user is not
-        subscribed at all. Otherwise it's the EmailAddress under
-        which the user is subscribed to this mailing list.
-        """
-        subscription = mailing_list.getSubscription(self.context)
-        if subscription is None:
-            return "Don't subscribe"
-        elif subscription.email_address is None:
-            return 'Preferred address'
-        else:
-            return subscription.email_address
-
-    def _mailing_list_fields(self):
-        """Creates a field for each mailing list the user can subscribe to.
-
-        If a team doesn't have a mailing list, or the mailing list
-        isn't usable, it's not included.
-        """
-        mailing_list_set = getUtility(IMailingListSet)
-        fields = []
-        terms = [
-            SimpleTerm("Preferred address"),
-            SimpleTerm("Don't subscribe"),
-            ]
-        for email in self.validated_addresses:
-            terms.append(SimpleTerm(email, email.email))
-        for team in self.context.teams_participated_in:
-            mailing_list = mailing_list_set.get(team.name)
-            if mailing_list is not None and mailing_list.is_usable:
-                name = 'subscription.%s' % team.name
-                value = self._mailing_list_subscription_type(mailing_list)
-                field = Choice(__name__=name,
-                               title=team.name,
-                               source=SimpleVocabulary(terms), default=value)
-                fields.append(field)
-        return FormFields(*fields)
-
-    def _autosubscribe_policy_fields(self):
-        """Create a field for each mailing list auto-subscription option."""
-        return FormFields(
-            Choice(__name__='mailing_list_auto_subscribe_policy',
-                   title=_('When should Launchpad automatically subscribe '
-                           'you to a team&#x2019;s mailing list?'),
-                   source=MailingListAutoSubscribePolicy))
-
-    @property
-    def mailing_list_widgets(self):
-        """Return all the mailing list subscription widgets."""
-        mailing_list_set = getUtility(IMailingListSet)
-        widgets = []
-        for widget in self.widgets:
-            if widget.name.startswith('field.subscription.'):
-                team_name = widget.label
-                mailing_list = mailing_list_set.get(team_name)
-                assert mailing_list is not None, 'Missing mailing list'
-                widget_dict = dict(
-                    team=mailing_list.team,
-                    widget=widget,
-                    )
-                widgets.append(widget_dict)
-                # We'll put the label in the first column, so don't include it
-                # in the second column.
-                widget.display_label = False
-        return widgets
-
     def _validate_selected_address(self, data, field='VALIDATED_SELECTED'):
         """A generic validator for this view's actions.
 
@@ -3125,6 +3047,139 @@
                 "message for up to an hour or two.)" % newemail)
         self.next_url = self.action_url
 
+
+class PersonEditMailingListsView(LaunchpadFormView):
+    """A view for editing a person's mailing list subscriptions."""
+
+    implements(IPersonEditMenu)
+
+    schema = IEmailAddress
+
+    custom_widget('mailing_list_auto_subscribe_policy',
+                  LaunchpadRadioWidgetWithDescription)
+
+    label = 'Change your mailing list subscriptions'
+
+    def initialize(self):
+        if self.context.is_team:
+            # +editmailinglists is not available on teams.
+            name = self.request['PATH_INFO'].split('/')[-1]
+            raise NotFound(self, name, request=self.request)
+        super(PersonEditMailingListsView, self).initialize()
+
+    def setUpFields(self):
+        """Set up fields for this view.
+
+        The main fields of interest are the selection fields with custom
+        vocabularies for the lists of validated and unvalidated email
+        addresses.
+        """
+        super(PersonEditMailingListsView, self).setUpFields()
+        self.form_fields = (self._mailing_list_fields()
+                            + self._autosubscribe_policy_fields())
+
+    @property
+    def initial_values(self):
+        """Set up default values for the radio widgets.
+
+        A radio widget must have a selected value, so we select the
+        first unvalidated and validated email addresses in the lists
+        to be the default for the corresponding widgets.
+
+        The only exception is if the user has a preferred email
+        address: then, that address is used as the default validated
+        email address.
+        """
+        # Defaults for the mailing list autosubscribe buttons.
+        return dict(mailing_list_auto_subscribe_policy=
+                    self.context.mailing_list_auto_subscribe_policy)
+
+    def setUpWidgets(self, context=None):
+        """See `LaunchpadFormView`."""
+        super(PersonEditMailingListsView, self).setUpWidgets(context)
+        widget = self.widgets['mailing_list_auto_subscribe_policy']
+        widget.display_label = False
+
+    def _mailing_list_subscription_type(self, mailing_list):
+        """Return the context user's subscription type for the given list.
+
+        This is 'Preferred address' if the user is subscribed using her
+        preferred address and 'Don't subscribe' if the user is not
+        subscribed at all. Otherwise it's the EmailAddress under
+        which the user is subscribed to this mailing list.
+        """
+        subscription = mailing_list.getSubscription(self.context)
+        if subscription is None:
+            return "Don't subscribe"
+        elif subscription.email_address is None:
+            return 'Preferred address'
+        else:
+            return subscription.email_address
+
+    def _mailing_list_fields(self):
+        """Creates a field for each mailing list the user can subscribe to.
+
+        If a team doesn't have a mailing list, or the mailing list
+        isn't usable, it's not included.
+        """
+        mailing_list_set = getUtility(IMailingListSet)
+        fields = []
+        terms = [
+            SimpleTerm("Preferred address"),
+            SimpleTerm("Don't subscribe"),
+            ]
+        for email in self.validated_addresses:
+            terms.append(SimpleTerm(email, email.email))
+        for team in self.context.teams_participated_in:
+            mailing_list = mailing_list_set.get(team.name)
+            if mailing_list is not None and mailing_list.is_usable:
+                name = 'subscription.%s' % team.name
+                value = self._mailing_list_subscription_type(mailing_list)
+                field = Choice(__name__=name,
+                               title=team.name,
+                               source=SimpleVocabulary(terms), default=value)
+                fields.append(field)
+        return FormFields(*fields)
+
+    def _autosubscribe_policy_fields(self):
+        """Create a field for each mailing list auto-subscription option."""
+        return FormFields(
+            Choice(__name__='mailing_list_auto_subscribe_policy',
+                   title=_('When should Launchpad automatically subscribe '
+                           'you to a team&#x2019;s mailing list?'),
+                   source=MailingListAutoSubscribePolicy))
+
+    @property
+    def mailing_list_widgets(self):
+        """Return all the mailing list subscription widgets."""
+        mailing_list_set = getUtility(IMailingListSet)
+        widgets = []
+        for widget in self.widgets:
+            if widget.name.startswith('field.subscription.'):
+                team_name = widget.label
+                mailing_list = mailing_list_set.get(team_name)
+                assert mailing_list is not None, 'Missing mailing list'
+                widget_dict = dict(
+                    team=mailing_list.team,
+                    widget=widget,
+                    )
+                widgets.append(widget_dict)
+                # We'll put the label in the first column, so don't include it
+                # in the second column.
+                widget.display_label = False
+        return widgets
+
+    @property
+    def validated_addresses(self):
+        """All of this person's validated email addresses, including
+        their preferred address (if any).
+        """
+        addresses = []
+        if self.context.preferredemail:
+            addresses.append(self.context.preferredemail)
+        addresses += [email for email in self.context.validatedemails]
+        return addresses
+
     def validate_action_update_subscriptions(self, action, data):
         """Make sure the user is subscribing using a valid address.
 

=== modified file 'lib/lp/registry/browser/tests/test_person.py'
--- lib/lp/registry/browser/tests/test_person.py	2014-11-29 00:21:14 +0000
+++ lib/lp/registry/browser/tests/test_person.py	2015-02-26 21:41:09 +0000
@@ -683,6 +683,13 @@
         browser = setupBrowserForUser(user=self.person)
         self.assertRaises(NotFound, browser.open, url)
 
+    def test_team_editmailinglists_not_found(self):
+        """Teams should not have a +editmailinglists page."""
+        team = self.factory.makeTeam(owner=self.person, members=[self.person])
+        url = '%s/+editmailinglists' % canonical_url(team)
+        browser = setupBrowserForUser(user=self.person)
+        self.assertRaises(NotFound, browser.open, url)
+
     def test_email_string_validation_no_email_prodvided(self):
         """+editemails should warn if no email is provided."""
         no_email = ''

=== modified file 'lib/lp/registry/doc/teammembership-email-notification.txt'
--- lib/lp/registry/doc/teammembership-email-notification.txt	2012-08-13 19:34:10 +0000
+++ lib/lp/registry/doc/teammembership-email-notification.txt	2015-02-26 21:41:09 +0000
@@ -818,7 +818,7 @@
     <BLANKLINE>
     If you would like to subscribe to the team list, use the link below
     to update your Mailing List Subscription preferences.
-      <http://launchpad.dev/people/+me/+editemails>
+      <http://launchpad.dev/people/+me/+editmailinglists>
     <BLANKLINE>
     -- =
     <BLANKLINE>
@@ -843,7 +843,7 @@
     <BLANKLINE>
     If you would like to subscribe to the team list, use the link below
     to update your Mailing List Subscription preferences.
-      <http://launchpad.dev/people/+me/+editemails>
+      <http://launchpad.dev/people/+me/+editmailinglists>
     <BLANKLINE>
     -- =
     <BLANKLINE>

=== modified file 'lib/lp/registry/mail/notification.py'
--- lib/lp/registry/mail/notification.py	2012-11-26 18:50:14 +0000
+++ lib/lp/registry/mail/notification.py	2015-02-26 21:41:09 +0000
@@ -148,7 +148,7 @@
                 'team-list-subscribe-block.txt', app='registry')
             editemails_url = urlappend(
                 canonical_url(getUtility(ILaunchpadRoot)),
-                'people/+me/+editemails')
+                'people/+me/+editmailinglists')
             list_instructions = template % dict(editemails_url=editemails_url)
         else:
             list_instructions = ''
@@ -257,7 +257,7 @@
     headers = {}
     subject = "New Mailing List for %s" % team.displayname
     template = get_email_template('new-mailing-list.txt', app='registry')
-    editemails_url = '%s/+editemails'
+    editemails_url = '%s/+editmailinglists'
 
     for person in team.allmembers:
         if person.is_team or person.preferredemail is None:

=== modified file 'lib/lp/registry/stories/mailinglists/subscriptions.txt'
--- lib/lp/registry/stories/mailinglists/subscriptions.txt	2014-01-30 15:04:06 +0000
+++ lib/lp/registry/stories/mailinglists/subscriptions.txt	2015-02-26 21:41:09 +0000
@@ -69,7 +69,7 @@
     >>> logout()
     >>> no_team_browser = setupBrowserFreshLogin(person)
 
-    >>> no_team_browser.open('http://launchpad.dev/people/+me/+editemails')
+    >>> no_team_browser.open('http://launchpad.dev/people/+me/+editmailinglists')
     >>> rosetta_admins = no_team_browser.getControl(
     ...     name='field.subscription.rosetta-admins')
     >>> rosetta_admins.displayOptions
@@ -89,11 +89,11 @@
     >>> logout()
     >>> carlos_browser = setupBrowserFreshLogin(carlos)
     >>> carlos_browser.open('http://launchpad.dev/~carlos')
-    >>> carlos_browser.getLink(url="+editemails").click()
+    >>> carlos_browser.getLink(url="+editmailinglists").click()
 
     >>> from lp.services.helpers import backslashreplace
     >>> print backslashreplace(carlos_browser.title)
-    Change your e-mail settings...
+    Change your mailing list subscriptions...
 
     >>> admins = carlos_browser.getControl(name='field.subscription.admins')
     >>> rosetta_admins = carlos_browser.getControl(
@@ -198,9 +198,9 @@
 Admins team, and he should know if the list is available.
 
     >>> carlos_browser.open('http://launchpad.dev/~carlos')
-    >>> carlos_browser.getLink(url="+editemails").click()
+    >>> carlos_browser.getLink(url="+editmailinglists").click()
     >>> print backslashreplace(carlos_browser.title)
-    Change your e-mail settings...
+    Change your mailing list subscriptions...
 
     >>> rosetta_admins = carlos_browser.getControl(
     ...     name='field.subscription.rosetta-admins')
@@ -236,9 +236,9 @@
     >>> logout()
     >>> jdub_browser = setupBrowserFreshLogin(jdub)
     >>> jdub_browser.open('http://launchpad.dev/~jdub')
-    >>> jdub_browser.getLink(url="+editemails").click()
+    >>> jdub_browser.getLink(url="+editmailinglists").click()
     >>> print jdub_browser.title
-    Change your e-mail settings...
+    Change your mailing list subscriptions...
 
     >>> jdub_browser.getControl(
     ...     name='field.subscription.rosetta-admins')
@@ -260,9 +260,9 @@
 His mailing list subscription is now available to be managed.
 
     >>> jdub_browser.open('http://launchpad.dev/~jdub')
-    >>> jdub_browser.getLink(url="+editemails").click()
+    >>> jdub_browser.getLink(url="+editmailinglists").click()
     >>> print jdub_browser.title
-    Change your e-mail settings...
+    Change your mailing list subscriptions...
 
     >>> rosetta_team = jdub_browser.getControl(
     ...     name='field.subscription.rosetta-admins')
@@ -327,14 +327,14 @@
     >>> carlos_browser.open('http://launchpad.dev/~admins')
     >>> carlos_browser.getLink('Subscribe to mailing list').click()
     >>> print carlos_browser.url
-    http://launchpad.dev/~carlos/+editemails
+    http://launchpad.dev/~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.dev/~carlos')
-    >>> carlos_browser.getLink(url="+editemails").click()
+    >>> carlos_browser.getLink(url="+editmailinglists").click()
     >>> rosetta_admins = carlos_browser.getControl(
     ...     name='field.subscription.rosetta-admins')
     >>> rosetta_admins.value = ['Preferred address']
@@ -469,9 +469,9 @@
 list based on a setting in the person's Email preferences page.
 
     >>> carlos_browser.open('http://launchpad.dev/~carlos')
-    >>> carlos_browser.getLink(url="+editemails").click()
+    >>> carlos_browser.getLink(url="+editmailinglists").click()
     >>> print backslashreplace(carlos_browser.title)
-    Change your e-mail settings...
+    Change your mailing list subscriptions...
 
 Carlos's default setting, 'Ask me when I join a team', is still in place.
 
@@ -549,7 +549,7 @@
     >>> logout()
     >>> browser = setupBrowserFreshLogin(james)
     >>> browser.open('http://launchpad.dev/~jblack')
-    >>> browser.getLink(url="+editemails").click()
+    >>> browser.getLink(url="+editmailinglists").click()
     >>> print_radio_button_field(browser.contents,
     ...     'mailing_list_auto_subscribe_policy')
     ( ) Never subscribe to mailing lists
@@ -566,7 +566,7 @@
 
     # Change James' setting
     >>> browser.open('http://launchpad.dev/~jblack')
-    >>> browser.getLink(url="+editemails").click()
+    >>> browser.getLink(url="+editmailinglists").click()
     >>> set_autosubscribe_policy_and_submit('ALWAYS', browser)
     ( ) Never subscribe to mailing lists
     ( ) Ask me when I join a team
@@ -582,7 +582,7 @@
 
     # Change James' setting
     >>> browser.open('http://launchpad.dev/~jblack')
-    >>> browser.getLink(url="+editemails").click()
+    >>> browser.getLink(url="+editmailinglists").click()
     >>> set_autosubscribe_policy_and_submit('NEVER', browser)
     (*) Never subscribe to mailing lists
     ( ) Ask me when I join a team

=== modified file 'lib/lp/registry/stories/team/xx-team-membership.txt'
--- lib/lp/registry/stories/team/xx-team-membership.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/registry/stories/team/xx-team-membership.txt	2015-02-26 21:41:09 +0000
@@ -247,7 +247,7 @@
     >>> user_browser.getLink('Register a team')
     <Link ... url='http://.../people/+newteam'>
     >>> user_browser.getLink('Change mailing list subscriptions')
-    <Link ... url='http://.../~no-priv/+editemails'>
+    <Link ... url='http://.../~no-priv/+editmailinglists'>
 
 Teams also have a participation page, but it does not include a mailing
 list column.

=== modified file 'lib/lp/registry/templates/person-editemails.pt'
--- lib/lp/registry/templates/person-editemails.pt	2012-06-28 01:47:35 +0000
+++ lib/lp/registry/templates/person-editemails.pt	2015-02-26 21:41:09 +0000
@@ -19,6 +19,10 @@
          id="no-contact-address">
           Currently you don't have a contact address in Launchpad.
       </p>
+      <p>Looking to <a
+        tal:define="link context/menu:overview/editmailinglists"
+        tal:attributes="href link/target" 
+        >configure your mailing list subscriptions?</a></p>
     </metal:extra-info>
 
     <metal:widgets fill-slot="widgets">
@@ -70,79 +74,6 @@
     </metal:widgets>
     <metal:widgets fill-slot="buttons" />
   </div>
-
-
-  <form action=""
-        method="post" enctype="multipart/form-data"
-        accept-charset="UTF-8" tal:condition="view/mailing_list_widgets">
-
-    <h2>Mailing list subscriptions</h2>
-
-    <p>You're a member of one or more teams with active mailing
-      lists. You can subscribe to a team's mailing list using any of
-      your verified email addresses.</p>
-
-    <table class="listing" style="width: 45em;">
-      <thead>
-        <th style="white-space:nowrap;">For mailing list</th>
-        <th>Subscribe with</th>
-      </thead>
-      <tbody>
-        <tr tal:repeat="widget view/mailing_list_widgets">
-          <td tal:content="structure widget/team/fmt:link">Team</td>
-          <td tal:content="structure widget/widget">Widget</td>
-        </tr>
-      </tbody>
-    </table>
-    <div style="padding-top: 1em; padding-bottom: 1em;">
-      <input tal:replace="structure view/action_update_subscriptions/render" />
-    </div>
-  </form>
-
-  <form action=""
-        method="post" enctype="multipart/form-data"
-        accept-charset="UTF-8">
-
-    <h2>Automatic subscription to mailing lists</h2>
-
-    <div id="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.</div>
-
-    <tal:widget
-       define="widget nocall:view/widgets/mailing_list_auto_subscribe_policy">
-      <table class="form">
-
-        <thead>
-          <tr>
-            <th style="white-space:nowrap; text-align:left;">
-              <div style="margin-bottom: 1em;">
-                <label tal:attributes="for widget/name"
-                       tal:content="structure widget/label" />
-              </div>
-            </th>
-            <td></td>
-          </tr>
-        </thead>
-
-        <tbody>
-          <metal:block use-macro="context/@@launchpad_form/widget_row" />
-          <tr>
-            <td>
-              <input
-                 tal:replace="structure
-                              view/action_update_autosubscribe_policy/render" />
-            </td>
-            <td></td>
-          </tr>
-        </tbody>
-
-      </table>
-    </tal:widget>
-  </form>
-
 </div>
 </body>
 </html>

=== added file 'lib/lp/registry/templates/person-editmailinglists.pt'
--- lib/lp/registry/templates/person-editmailinglists.pt	1970-01-01 00:00:00 +0000
+++ lib/lp/registry/templates/person-editmailinglists.pt	2015-02-26 21:41:09 +0000
@@ -0,0 +1,88 @@
+<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">
+  <form action=""
+        method="post" enctype="multipart/form-data"
+        accept-charset="UTF-8" tal:condition="view/mailing_list_widgets">
+
+    <h2>Mailing list subscriptions</h2>
+
+    <p>You're a member of one or more teams with active mailing
+      lists. You can subscribe to a team's mailing list using any of
+      your verified email addresses. You can <a
+        tal:define="link context/menu:overview/editemailaddresses"
+        tal:attributes="href link/target" 
+        >configure your verified email addresses here</a>.
+    </p>
+
+    <table class="listing" style="width: 45em;">
+      <thead>
+        <th style="white-space:nowrap;">For mailing list</th>
+        <th>Subscribe with</th>
+      </thead>
+      <tbody>
+        <tr tal:repeat="widget view/mailing_list_widgets">
+          <td tal:content="structure widget/team/fmt:link">Team</td>
+          <td tal:content="structure widget/widget">Widget</td>
+        </tr>
+      </tbody>
+    </table>
+    <div style="padding-top: 1em; padding-bottom: 1em;">
+      <input tal:replace="structure view/action_update_subscriptions/render" />
+    </div>
+  </form>
+
+  <form action=""
+        method="post" enctype="multipart/form-data"
+        accept-charset="UTF-8">
+
+    <h2>Automatic subscription to mailing lists</h2>
+
+    <div id="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.</div>
+
+    <tal:widget
+       define="widget nocall:view/widgets/mailing_list_auto_subscribe_policy">
+      <table class="form">
+
+        <thead>
+          <tr>
+            <th style="white-space:nowrap; text-align:left;">
+              <div style="margin-bottom: 1em;">
+                <label tal:attributes="for widget/name"
+                       tal:content="structure widget/label" />
+              </div>
+            </th>
+            <td></td>
+          </tr>
+        </thead>
+
+        <tbody>
+          <metal:block use-macro="context/@@launchpad_form/widget_row" />
+          <tr>
+            <td>
+              <input
+                 tal:replace="structure
+                              view/action_update_autosubscribe_policy/render" />
+            </td>
+            <td></td>
+          </tr>
+        </tbody>
+
+      </table>
+    </tal:widget>
+  </form>
+
+</div>
+</body>
+</html>

=== modified file 'lib/lp/registry/templates/person-portlet-contact-details.pt'
--- lib/lp/registry/templates/person-portlet-contact-details.pt	2012-06-15 16:23:50 +0000
+++ lib/lp/registry/templates/person-portlet-contact-details.pt	2015-02-26 21:41:09 +0000
@@ -15,9 +15,7 @@
         <dd tal:content="context/name"/>
     </dl>
     <dl id="email-addresses">
-      <dt>Email:
-        <a tal:replace="structure overview_menu/editemailaddresses/fmt:icon" />
-      </dt>
+      <dt>Email:</dt>
       <dd
         tal:attributes="title view/visible_email_address_description">
         <tal:not_logged_in
@@ -49,6 +47,16 @@
           </span>
         </tal:emails>
       </dd>
+      <dd>
+        <a
+        tal:define="link context/menu:overview/editemailaddresses"
+        tal:condition="link/enabled"
+        tal:content="structure link/fmt:link" /><br />
+        <a
+        tal:define="link context/menu:overview/editmailinglists"
+        tal:condition="link/enabled"
+        tal:content="structure link/fmt:link" />
+      </dd>
     </dl>
 
 

=== modified file 'lib/lp/registry/templates/team-portlet-mailinglist.pt'
--- lib/lp/registry/templates/team-portlet-mailinglist.pt	2012-07-06 06:02:33 +0000
+++ lib/lp/registry/templates/team-portlet-mailinglist.pt	2015-02-26 21:41:09 +0000
@@ -22,7 +22,7 @@
         <tal:can-subscribe-to-list
             condition="view/user_can_subscribe_to_list">
           <a id="link-list-subscribe" class="sprite add"
-              href="/people/+me/+editemails">Subscribe to mailing list</a>
+              href="/people/+me/+editmailinglists">Subscribe to mailing list</a>
           <br />
         </tal:can-subscribe-to-list>
         <tal:subscribed-to-list

=== modified file 'lib/lp/registry/tests/test_mailinglist.py'
--- lib/lp/registry/tests/test_mailinglist.py	2013-01-11 01:05:14 +0000
+++ lib/lp/registry/tests/test_mailinglist.py	2015-02-26 21:41:09 +0000
@@ -169,7 +169,7 @@
         self.assertEqual(
             'New Mailing List for Team', notifications[0]['subject'])
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            '.*To subscribe:.*http://launchpad.dev/~.*/\+editemails.*',
+            '.*To subscribe:.*http://launchpad.dev/~.*/\+editmailinglists.*',
             notifications[0].get_payload())
 
     def test_startConstructing_from_APPROVED(self):


Follow ups