← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wallyworld/launchpad/remove-new-team-contact-email into lp:launchpad

 

Ian Booth has proposed merging lp:~wallyworld/launchpad/remove-new-team-contact-email into lp:launchpad.

Requested reviews:
  Curtis Hovey (sinzui)
Related bugs:
  Bug #31590 in Launchpad itself: "Team's contact e-mail address options should be more obvious"
  https://bugs.launchpad.net/launchpad/+bug/31590
  Bug #1020296 in Launchpad itself: "Setting a team contact address is prominently displayed, but is not recommended"
  https://bugs.launchpad.net/launchpad/+bug/1020296

For more details, see:
https://code.launchpad.net/~wallyworld/launchpad/remove-new-team-contact-email/+merge/114549

== Implementation ==

After much discussion, it was decided that this branch would:
- remove the contact address from the new team form
- add a warning to the contact address form saying that if a mailing list or external contact address were used, private information may be disclosed.

This branch also changes the Email edit link on the team home page to say "None, members emailed directly" when there is no contact address set.

Attempts to find a rational, universally applicable solution for limiting the use of mailing lists or external contact addresses for exclusive teams did not prove feasible. So a warning will suffice.

I confirmed that if an external contact address which is already registered in Lp is used, it is rejected.

== Demo ==

Screenshot of the contact address page with the new warning:

http://people.canonical.com/~ianb/team-contact-address.png

== Tests ==

Adjust various doc tests as necessary.
Adjust the TestTeamCreationView test.

== Lint ==

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/app/stories/form/xx-form-layout.txt
  lib/lp/registry/browser/team.py
  lib/lp/registry/browser/tests/test_person.py
  lib/lp/registry/interfaces/person.py
  lib/lp/registry/stories/team/xx-team-contactemail.txt
  lib/lp/registry/stories/team/xx-team-home.txt
  lib/lp/registry/stories/teammembership/xx-teammembership.txt
  lib/lp/registry/templates/person-macros.pt
  lib/lp/registry/templates/team-contactaddress.pt

There was just the usual "narrative uses a moin header" stuff from the doc tests.
-- 
https://code.launchpad.net/~wallyworld/launchpad/remove-new-team-contact-email/+merge/114549
Your team Launchpad code reviewers is subscribed to branch lp:launchpad.
=== modified file 'lib/lp/app/stories/form/xx-form-layout.txt'
--- lib/lp/app/stories/form/xx-form-layout.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/app/stories/form/xx-form-layout.txt	2012-07-12 01:18:23 +0000
@@ -40,10 +40,10 @@
     <tr>
     <td colspan="2">
     <div>
-    <label for="field.contactemail">Contact Email Address:</label>
+    <label for="field.defaultmembershipperiod">Subscription period:</label>
     <span class="fieldRequired">(Optional)</span>
     <div>
-    <input ... id="field.contactemail" ... />
+    <input ... id="field.defaultmembershipperiod" ... />
     </div>
     <p class="formHelp">...</p>
     </div>

=== modified file 'lib/lp/registry/browser/team.py'
--- lib/lp/registry/browser/team.py	2012-07-03 08:04:35 +0000
+++ lib/lp/registry/browser/team.py	2012-07-12 01:18:23 +0000
@@ -123,7 +123,6 @@
     IPersonSet,
     ITeam,
     ITeamContactAddressForm,
-    ITeamCreation,
     ITeamReassignment,
     OPEN_TEAM_POLICY,
     PersonVisibility,
@@ -228,7 +227,7 @@
     * The user has a current commercial subscription.
     """
     field_names = [
-        "name", "visibility", "displayname", "contactemail",
+        "name", "visibility", "displayname",
         "teamdescription", "subscriptionpolicy",
         "defaultmembershipperiod", "renewal_policy",
         "defaultrenewalperiod", "teamowner",
@@ -302,14 +301,10 @@
     custom_widget('teamdescription', TextAreaWidget, height=10, width=30)
 
     def setUpFields(self):
-        """See `LaunchpadViewForm`.
-
-        When editing a team the contactemail field is not displayed.
-        """
+        """See `LaunchpadViewForm`."""
         # Make an instance copy of field_names so as to not modify the single
         # class list.
         self.field_names = list(self.field_names)
-        self.field_names.remove('contactemail')
         self.field_names.remove('teamowner')
         super(TeamEditView, self).setUpFields()
         self.setUpVisibilityField(render_context=True)
@@ -994,7 +989,7 @@
 class TeamAddView(TeamFormMixin, HasRenewalPolicyMixin, LaunchpadFormView):
     """View for adding a new team."""
 
-    schema = ITeamCreation
+    schema = ITeam
     page_title = 'Register a new team in Launchpad'
     label = page_title
 
@@ -1031,16 +1026,6 @@
         if visibility:
             team.transitionVisibility(visibility, self.user)
             del data['visibility']
-        email = data.get('contactemail')
-        if email is not None:
-            generateTokenAndValidationEmail(email, team)
-            self.request.response.addNotification(
-                "A confirmation message has been sent to '%s'. Follow the "
-                "instructions in that message to confirm the new "
-                "contact address for this team. "
-                "(If the message doesn't arrive in a few minutes, your mail "
-                "provider might use 'greylisting', which could delay the "
-                "message for up to an hour or two.)" % email)
 
         if self.request.is_ajax:
             return ''

=== modified file 'lib/lp/registry/browser/tests/test_person.py'
--- lib/lp/registry/browser/tests/test_person.py	2012-07-07 19:30:24 +0000
+++ lib/lp/registry/browser/tests/test_person.py	2012-07-12 01:18:23 +0000
@@ -611,7 +611,6 @@
     def test_team_creation_good_data(self):
         form = {
             'field.actions.create': 'Create Team',
-            'field.contactemail': 'contactemail@xxxxxxxxxxx',
             'field.displayname': 'liberty-land',
             'field.name': 'libertyland',
             'field.renewal_policy': 'NONE',
@@ -626,32 +625,6 @@
         self.assertTrue(team is not None)
         self.assertEqual('libertyland', team.name)
 
-    def test_validate_email_catches_taken_emails(self):
-        email_address = self.factory.getUniqueEmailAddress()
-        self.factory.makePerson(
-            name='libertylandaccount',
-            displayname='libertylandaccount',
-            email=email_address,
-            account_status=AccountStatus.NOACCOUNT)
-        form = {
-            'field.actions.create': 'Create Team',
-            'field.contactemail': email_address,
-            'field.displayname': 'liberty-land',
-            'field.name': 'libertyland',
-            'field.renewal_policy': 'NONE',
-            'field.renewal_policy-empty-marker': 1,
-            'field.subscriptionpolicy': 'RESTRICTED',
-            'field.subscriptionpolicy-empty-marker': 1,
-            }
-        person_set = getUtility(IPersonSet)
-        view = create_initialized_view(person_set, '+newteam', form=form)
-        expected_msg = (
-            '%s is already registered in Launchpad and is associated with '
-            '<a href="http://launchpad.dev/~libertylandaccount";>'
-            'libertylandaccount</a>.' % email_address)
-        error_msg = view.errors[0].errors[0]
-        self.assertEqual(expected_msg, error_msg)
-
 
 class TestPersonParticipationView(TestCaseWithFactory):
 

=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py	2012-07-07 19:30:24 +0000
+++ lib/lp/registry/interfaces/person.py	2012-07-12 01:18:23 +0000
@@ -28,7 +28,6 @@
     'ISoftwareCenterAgentApplication',
     'ITeam',
     'ITeamContactAddressForm',
-    'ITeamCreation',
     'ITeamReassignment',
     'ImmutableVisibilityError',
     'NoSuchPerson',
@@ -112,7 +111,6 @@
 from lp.app.validators import LaunchpadValidationError
 from lp.app.validators.email import email_validator
 from lp.app.validators.name import name_validator
-from lp.app.validators.validation import validate_new_team_email
 from lp.blueprints.interfaces.specificationtarget import IHasSpecifications
 from lp.bugs.interfaces.bugtarget import IHasBugs
 from lp.code.interfaces.hasbranches import (
@@ -2485,24 +2483,6 @@
         title=_('New'), vocabulary='ValidTeamOwner', required=True)
 
 
-class ITeamCreation(ITeam):
-    """An interface to be used by the team creation form.
-
-    We need this special interface so we can allow people to specify a contact
-    email address for a team upon its creation.
-    """
-
-    contactemail = TextLine(
-        title=_("Contact Email Address"), required=False, readonly=False,
-        description=_(
-            "This is the email address we'll send all notifications to this "
-            "team. If no contact address is chosen, notifications directed "
-            "to this team will be sent to all team members. After finishing "
-            "the team creation, a new message will be sent to this address "
-            "with instructions on how to finish its registration."),
-        constraint=validate_new_team_email)
-
-
 class TeamContactMethod(EnumeratedType):
     """The method used by Launchpad to contact a given team."""
 

=== modified file 'lib/lp/registry/stories/team/xx-team-contactemail.txt'
--- lib/lp/registry/stories/team/xx-team-contactemail.txt	2011-12-22 05:09:10 +0000
+++ lib/lp/registry/stories/team/xx-team-contactemail.txt	2012-07-12 01:18:23 +0000
@@ -21,6 +21,14 @@
     >>> browser.title
     'Landscape Developers contact address...
 
+A warning is rendered about the privacy implications of using a mailing list or
+external contact address.
+
+    >>> from BeautifulSoup import BeautifulSoup
+    >>> soup = BeautifulSoup(browser.contents)
+    >>> soup.find(id='email-warning')
+    <p ... E-mail sent to a mailing list or external contact address may ...
+
 As we can see, the landscape-developers team has no contact address.
 
     >>> from lp.testing.pages import strip_label

=== modified file 'lib/lp/registry/stories/team/xx-team-home.txt'
--- lib/lp/registry/stories/team/xx-team-home.txt	2012-07-07 14:00:30 +0000
+++ lib/lp/registry/stories/team/xx-team-home.txt	2012-07-12 01:18:23 +0000
@@ -142,8 +142,7 @@
     >>> print extract_text(
     ...     find_tag_by_id(sample_browser.contents, 'contact-email'))
     Email:
-    No contact email
-    Set contact address
+    None, members emailed directly
     >>> print extract_text(
     ...     find_tag_by_id(sample_browser.contents, 'contact-user'))
     Contact this team's members

=== modified file 'lib/lp/registry/stories/teammembership/xx-teammembership.txt'
--- lib/lp/registry/stories/teammembership/xx-teammembership.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/registry/stories/teammembership/xx-teammembership.txt	2012-07-12 01:18:23 +0000
@@ -35,52 +35,6 @@
     >>> [a.name for a in Person.byName('myemail').adminmembers]
     [u'name12']
 
-When creating a new team you can optionally provide a contact email address
-for that team. This contact address must not be registered in Launchpad.
-
-    >>> browser.open('http://launchpad.dev/people/+newteam')
-
-    >>> browser.getControl(name='field.name').value = 'newtestteam'
-    >>> browser.getControl('Display Name').value = 'newtestteam'
-    >>> browser.getControl(
-    ...     'Contact Email Address').value = 'test@xxxxxxxxxxxxx'
-    >>> browser.getControl('Team Description').value = 'another test team'
-    >>> browser.getControl('Moderated Team').selected = True
-    >>> browser.getControl('Create').click()
-
-    >>> for tag in find_tags_by_class(browser.contents, 'message'):
-    ...     print extract_text(tag)
-    There is 1 error.
-    test@xxxxxxxxxxxxx is already registered in Launchpad and is associated
-    with Sample Person.
-
-We also check if the given email address is syntatically valid.
-
-    >>> browser.getControl(
-    ...     'Contact Email Address').value = 'testcanonical.com'
-    >>> browser.getControl('Create').click()
-
-    >>> for tag in find_tags_by_class(browser.contents, 'message'):
-    ...     print extract_text(tag)
-    There is 1 error.
-    testcanonical.com isn't a valid email address.
-
-Now we enter a valid email address that's not registered in Launchpad.
-
-    >>> browser.getControl('Contact Email Address').value = 'team-foo@xxxxxxx'
-    >>> browser.getControl('Create').click()
-
-    >>> browser.url
-    'http://launchpad.dev/~newtestteam'
-
-    >>> for tag in find_tags_by_class(
-    ...     browser.contents, 'informational message'):
-    ...     print tag.renderContents()
-    A confirmation message has been sent to 'team-foo@xxxxxxx'. Follow the
-    instructions in that message to confirm the new contact address for this
-    team. (If the message doesn't arrive in a few minutes, your mail provider
-    might use 'greylisting', which could delay the message for up to an hour
-    or two.)
 
 == As an administrator ==
 

=== modified file 'lib/lp/registry/templates/person-macros.pt'
--- lib/lp/registry/templates/person-macros.pt	2012-06-15 16:23:50 +0000
+++ lib/lp/registry/templates/person-macros.pt	2012-07-12 01:18:23 +0000
@@ -20,7 +20,7 @@
 
         <tal:no_preferredemail
           condition="view/email_address_visibility/are_none_available">
-          No contact email
+          None, members emailed directly
         </tal:no_preferredemail>
 
         <tal:emails repeat="email view/visible_email_addresses">
@@ -31,7 +31,7 @@
           </div>
         </tal:emails>
         <a tal:condition="not: view/visible_email_addresses"
-           tal:replace="structure overview_menu/editemail/fmt:link"></a>
+           tal:replace="structure overview_menu/editemail/fmt:icon"></a>
       </dd>
     </dl>
 

=== modified file 'lib/lp/registry/templates/team-contactaddress.pt'
--- lib/lp/registry/templates/team-contactaddress.pt	2009-08-28 16:23:47 +0000
+++ lib/lp/registry/templates/team-contactaddress.pt	2012-07-12 01:18:23 +0000
@@ -41,11 +41,22 @@
       </div>
     </metal:widgets>
 
-    <p metal:fill-slot="extra_bottom" id="set-up-a-mailing-list">
+    <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="large-warning" style="padding:2px 2px 0 36px;">
+        E-mail sent to a mailing list or 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
+        send notifications to each member individually.
+    </p>
+    </div>
+
   </div>
 
 </div>


Follow ups