Merge lp:~sinzui/launchpad/contact-team into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 14780
Proposed branch: lp:~sinzui/launchpad/contact-team
Merge into: lp:launchpad
Diff against target: 488 lines (+59/-213)
6 files modified
lib/lp/registry/browser/person.py (+25/-54)
lib/lp/registry/browser/tests/person-views.txt (+1/-22)
lib/lp/registry/browser/tests/team-views.txt (+2/-2)
lib/lp/registry/browser/tests/user-to-user-views.txt (+20/-116)
lib/lp/registry/stories/team/xx-team-home.txt (+10/-18)
lib/lp/registry/templates/contact-user.pt (+1/-1)
To merge this branch: bzr merge lp:~sinzui/launchpad/contact-team
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+92589@code.launchpad.net

Commit message

[r=bac][bug=246022] Allow team members to contact all the team members, non-members contact the team admins.

Description of the change

Allow team members to contact all the team members, non-members contact the
team admins.

    Launchpad bug: https://bugs.launchpad.net/bugs/246022
    Pre-implementation: lifeless

A team administrator cannot contact all members at once if, for example,
the contact address for the team is a mailing list, and not all members
are subscribed to it.

This issues has mutated over the years. When bug mail was out of control,
we decided to honour a team's choice to set a contact address to send
all email to a black hole. Bug mail is no longer an issue. When a member
choose the contact-this-team form, Launchpad will send email to all members
so that important messages can be sent. contact-this-team is still not
substitute for mailing lists because we limit its use to 3 time a day.

While discussing the rules recently, we discovered that we did not update
contact-this-team to contact team admins instead of the owner. We allow
the owner to leave the team, delegating all responsibility to the admins.

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

RULES

    * Delete the rules for when the team has a contact address, allowing
      the team member rules to be the only rule in play.
    * Change the contact owner rule to contact admins.
    * ADDENDUM: I cannot 'hit' the cancel link. I do not think the cancel
      is a verb and suffices.

QA

    * Visit https://qastaging.launchpad.net/~registry
    * Set the contact address to the mailing list.
    * Choose contact this team's members.
    * Verify the page says
      You are contacting nn members of the Registry Administrators (registry)
      team directly.
    * Verify the page says
       If you do not want Registry Administrators to know your email address,
       _cancel_ now.

    * Visit https://qastaging.launchpad.net/~bzr (or a
      team you are not a member of)
    * Choose contact this team's admins.
    * Verify the page says
      You are contacting the Bazaar Developers (bzr) team admins.

LINT

    lib/lp/registry/browser/person.py
    lib/lp/registry/browser/tests/person-views.txt
    lib/lp/registry/browser/tests/team-views.txt
    lib/lp/registry/browser/tests/user-to-user-views.txt
    lib/lp/registry/stories/team/xx-team-home.txt
    lib/lp/registry/templates/contact-user.pt

TEST

    ./bin/test -vcc -t user-to-user-views -t person-views -t team-views \
        -t xx-team-home lp.registry

IMPLEMENTATION

Removed the TO_TEAM state of ContactViaWebNotificationRecipientSet, deleted
all callsites that accessed it, deleted tests that check that the team
email address was contacted.

Renamed TO_OWNER to TO_ADMINS and revised the descriptive test. Updated
tests to verify team admins are contacted. Replaced
_getPrimaryRecipient() with a block to get the team admins (make the
tests pass). While there was no method that returned person and email
address, my implementation uses the get_recipients helper that caches
the user/team email addresses.

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

This code looks good Curtis and is a welcome improvement.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py 2012-02-03 22:03:43 +0000
+++ lib/lp/registry/browser/person.py 2012-02-10 21:51:21 +0000
@@ -217,6 +217,7 @@
217 Milestone,217 Milestone,
218 milestone_sort_key,218 milestone_sort_key,
219 )219 )
220from lp.registry.model.person import get_recipients
220from lp.services.config import config221from lp.services.config import config
221from lp.services.database.sqlbase import flush_database_updates222from lp.services.database.sqlbase import flush_database_updates
222from lp.services.feeds.browser import FeedsMixin223from lp.services.feeds.browser import FeedsMixin
@@ -2581,8 +2582,7 @@
2581 :return: the recipients of the message.2582 :return: the recipients of the message.
2582 :rtype: `ContactViaWebNotificationRecipientSet` constant:2583 :rtype: `ContactViaWebNotificationRecipientSet` constant:
2583 TO_USER2584 TO_USER
2584 TO_TEAM (Send to team's preferredemail)2585 TO_ADMINS
2585 TO_OWNER
2586 TO_MEMBERS2586 TO_MEMBERS
2587 """2587 """
2588 return ContactViaWebNotificationRecipientSet(2588 return ContactViaWebNotificationRecipientSet(
@@ -2597,13 +2597,10 @@
2597 return 'Send an email to yourself through Launchpad'2597 return 'Send an email to yourself through Launchpad'
2598 else:2598 else:
2599 return 'Send an email to this user through Launchpad'2599 return 'Send an email to this user through Launchpad'
2600 elif self.group_to_contact == ContactViaWeb.TO_TEAM:
2601 return ("Send an email to your team's contact email address "
2602 "through Launchpad")
2603 elif self.group_to_contact == ContactViaWeb.TO_MEMBERS:2600 elif self.group_to_contact == ContactViaWeb.TO_MEMBERS:
2604 return "Send an email to your team's members through Launchpad"2601 return "Send an email to your team's members through Launchpad"
2605 elif self.group_to_contact == ContactViaWeb.TO_OWNER:2602 elif self.group_to_contact == ContactViaWeb.TO_ADMINS:
2606 return "Send an email to this team's owner through Launchpad"2603 return "Send an email to this team's admins through Launchpad"
2607 else:2604 else:
2608 raise AssertionError('Unknown group to contact.')2605 raise AssertionError('Unknown group to contact.')
26092606
@@ -2615,12 +2612,10 @@
2615 # Note that we explicitly do not change the text to "Contact2612 # Note that we explicitly do not change the text to "Contact
2616 # yourself" when viewing your own page.2613 # yourself" when viewing your own page.
2617 return 'Contact this user'2614 return 'Contact this user'
2618 elif self.group_to_contact == ContactViaWeb.TO_TEAM:
2619 return "Contact this team's email address"
2620 elif self.group_to_contact == ContactViaWeb.TO_MEMBERS:2615 elif self.group_to_contact == ContactViaWeb.TO_MEMBERS:
2621 return "Contact this team's members"2616 return "Contact this team's members"
2622 elif self.group_to_contact == ContactViaWeb.TO_OWNER:2617 elif self.group_to_contact == ContactViaWeb.TO_ADMINS:
2623 return "Contact this team's owner"2618 return "Contact this team's admins"
2624 else:2619 else:
2625 raise AssertionError('Unknown group to contact.')2620 raise AssertionError('Unknown group to contact.')
26262621
@@ -4823,9 +4818,8 @@
48234818
4824 # Primary reason enumerations.4819 # Primary reason enumerations.
4825 TO_USER = object()4820 TO_USER = object()
4826 TO_TEAM = object()
4827 TO_MEMBERS = object()4821 TO_MEMBERS = object()
4828 TO_OWNER = object()4822 TO_ADMINS = object()
48294823
4830 def __init__(self, user, person_or_team):4824 def __init__(self, user, person_or_team):
4831 """Initialize the state based on the context and the user.4825 """Initialize the state based on the context and the user.
@@ -4861,36 +4855,16 @@
4861 """4855 """
4862 if person_or_team.is_team:4856 if person_or_team.is_team:
4863 if self.user.inTeam(person_or_team):4857 if self.user.inTeam(person_or_team):
4864 if removeSecurityProxy(person_or_team).preferredemail is None:4858 return self.TO_MEMBERS
4865 # Send to each team member.
4866 return self.TO_MEMBERS
4867 else:
4868 # Send to the team's contact address.
4869 return self.TO_TEAM
4870 else:4859 else:
4871 # A non-member can only send emails to a single person to4860 # A non-member can only send emails to a single person to
4872 # hinder spam and to prevent leaking membership4861 # hinder spam and to prevent leaking membership
4873 # information for private teams when the members reply.4862 # information for private teams when the members reply.
4874 return self.TO_OWNER4863 return self.TO_ADMINS
4875 else:4864 else:
4876 # Send to the user4865 # Send to the user
4877 return self.TO_USER4866 return self.TO_USER
48784867
4879 def _getPrimaryRecipient(self, person_or_team):
4880 """Return the primary recipient.
4881
4882 The primary recipient is the ``person_or_team`` in all cases
4883 except for when the email is restricted to a team owner.
4884
4885 :param person_or_team: The party that is the context of the email.
4886 :type person_or_team: `IPerson`.
4887 """
4888 if self._primary_reason is self.TO_OWNER:
4889 person_or_team = person_or_team.teamowner
4890 while person_or_team.is_team:
4891 person_or_team = person_or_team.teamowner
4892 return person_or_team
4893
4894 def _getReasonAndHeader(self, person_or_team):4868 def _getReasonAndHeader(self, person_or_team):
4895 """Return the reason and header why the email was received.4869 """Return the reason and header why the email was received.
48964870
@@ -4902,20 +4876,13 @@
4902 'using the "Contact this user" link on your profile page\n'4876 'using the "Contact this user" link on your profile page\n'
4903 '(%s)' % canonical_url(person_or_team))4877 '(%s)' % canonical_url(person_or_team))
4904 header = 'ContactViaWeb user'4878 header = 'ContactViaWeb user'
4905 elif self._primary_reason is self.TO_OWNER:4879 elif self._primary_reason is self.TO_ADMINS:
4906 reason = (4880 reason = (
4907 'using the "Contact this team\'s owner" link on the '4881 'using the "Contact this team\'s admins" link on the '
4908 '%s team page\n(%s)' % (4882 '%s team page\n(%s)' % (
4909 person_or_team.displayname,4883 person_or_team.displayname,
4910 canonical_url(person_or_team)))4884 canonical_url(person_or_team)))
4911 header = 'ContactViaWeb owner (%s team)' % person_or_team.name4885 header = 'ContactViaWeb owner (%s team)' % person_or_team.name
4912 elif self._primary_reason is self.TO_TEAM:
4913 reason = (
4914 'using the "Contact this team" link on the '
4915 '%s team page\n(%s)' % (
4916 person_or_team.displayname,
4917 canonical_url(person_or_team)))
4918 header = 'ContactViaWeb member (%s team)' % person_or_team.name
4919 else:4886 else:
4920 # self._primary_reason is self.TO_MEMBERS.4887 # self._primary_reason is self.TO_MEMBERS.
4921 reason = (4888 reason = (
@@ -4937,15 +4904,9 @@
4937 return (4904 return (
4938 'You are contacting %s (%s).' %4905 'You are contacting %s (%s).' %
4939 (person_or_team.displayname, person_or_team.name))4906 (person_or_team.displayname, person_or_team.name))
4940 elif self._primary_reason is self.TO_OWNER:4907 elif self._primary_reason is self.TO_ADMINS:
4941 return (4908 return (
4942 'You are contacting the %s (%s) team owner, %s (%s).' %4909 'You are contacting the %s (%s) team admins.' %
4943 (person_or_team.displayname, person_or_team.name,
4944 self._primary_recipient.displayname,
4945 self._primary_recipient.name))
4946 elif self._primary_reason is self.TO_TEAM:
4947 return (
4948 'You are contacting the %s (%s) team.' %
4949 (person_or_team.displayname, person_or_team.name))4910 (person_or_team.displayname, person_or_team.name))
4950 else:4911 else:
4951 # This is a team without a contact address (self.TO_MEMBERS).4912 # This is a team without a contact address (self.TO_MEMBERS).
@@ -4968,6 +4929,16 @@
4968 for recipient in team.getMembersWithPreferredEmails():4929 for recipient in team.getMembersWithPreferredEmails():
4969 email = removeSecurityProxy(recipient).preferredemail.email4930 email = removeSecurityProxy(recipient).preferredemail.email
4970 all_recipients[email] = recipient4931 all_recipients[email] = recipient
4932 elif self._primary_reason is self.TO_ADMINS:
4933 team = self._primary_recipient
4934 for admin in team.adminmembers:
4935 # This method is similar to getTeamAdminsEmailAddresses, but
4936 # this case needs to know the user. Since both methods
4937 # ultimately iterate over get_recipients, this case is not
4938 # in a different performance class.
4939 for recipient in get_recipients(admin):
4940 email = removeSecurityProxy(recipient).preferredemail.email
4941 all_recipients[email] = recipient
4971 elif self._primary_recipient.is_valid_person_or_team:4942 elif self._primary_recipient.is_valid_person_or_team:
4972 email = removeSecurityProxy(4943 email = removeSecurityProxy(
4973 self._primary_recipient).preferredemail.email4944 self._primary_recipient).preferredemail.email
@@ -5040,7 +5011,7 @@
5040 """5011 """
5041 self._reset_state()5012 self._reset_state()
5042 self._primary_reason = self._getPrimaryReason(person)5013 self._primary_reason = self._getPrimaryReason(person)
5043 self._primary_recipient = self._getPrimaryRecipient(person)5014 self._primary_recipient = person
5044 if reason is None:5015 if reason is None:
5045 reason, header = self._getReasonAndHeader(person)5016 reason, header = self._getReasonAndHeader(person)
5046 self._reason = reason5017 self._reason = reason
50475018
=== modified file 'lib/lp/registry/browser/tests/person-views.txt'
--- lib/lp/registry/browser/tests/person-views.txt 2012-01-15 13:32:27 +0000
+++ lib/lp/registry/browser/tests/person-views.txt 2012-02-10 21:51:21 +0000
@@ -525,14 +525,6 @@
525Non-member contacting a Team525Non-member contacting a Team
526----------------------------526----------------------------
527527
528Users can contact teams, but the behaviour depends upon whether the user
529is a member of the team. No Privileges Person is not a member of the
530Landscape Developers team.
531
532 >>> view = create_initialized_view(landscape_developers, '+index')
533 >>> print view.contact_link_title
534 Send an email to this team's owner through Launchpad
535
536The EmailToPersonView can be used by non-members to contact the team528The EmailToPersonView can be used by non-members to contact the team
537owner.529owner.
538530
@@ -545,7 +537,7 @@
545537
546 >>> print view.recipients.description538 >>> print view.recipients.description
547 You are contacting the Landscape Developers (landscape-developers) team539 You are contacting the Landscape Developers (landscape-developers) team
548 owner, Sample Person (name12).540 admins.
549541
550 >>> [recipient.name for recipient in view.recipients]542 >>> [recipient.name for recipient in view.recipients]
551 [u'name12']543 [u'name12']
@@ -608,19 +600,6 @@
608 >>> [recipient.name for recipient in view.recipients]600 >>> [recipient.name for recipient in view.recipients]
609 [u'name12']601 [u'name12']
610602
611EmailToPersonView will use the contact address when the team has one.
612
613 >>> email_address = factory.makeEmail(
614 ... 'landscapers@canonical.com', landscape_developers)
615 >>> landscape_developers.setContactAddress(email_address)
616
617 >>> view = create_initialized_view(landscape_developers, '+contactuser')
618 >>> print view.recipients.description
619 You are contacting the Landscape Developers (landscape-developers) team.
620
621 >>> [recipient.name for recipient in view.recipients]
622 [u'landscape-developers']
623
624603
625Contact this user/team valid addresses and quotas604Contact this user/team valid addresses and quotas
626-------------------------------------------------605-------------------------------------------------
627606
=== modified file 'lib/lp/registry/browser/tests/team-views.txt'
--- lib/lp/registry/browser/tests/team-views.txt 2012-01-09 12:39:11 +0000
+++ lib/lp/registry/browser/tests/team-views.txt 2012-02-10 21:51:21 +0000
@@ -213,9 +213,9 @@
213213
214 >>> view = create_initialized_view(guadamen, '+index')214 >>> view = create_initialized_view(guadamen, '+index')
215 >>> print view.contact_link_title215 >>> print view.contact_link_title
216 Send an email to this team's owner through Launchpad216 Send an email to this team's admins through Launchpad
217 >>> print view.specific_contact_text217 >>> print view.specific_contact_text
218 Contact this team's owner218 Contact this team's admins
219219
220220
221Mugshots221Mugshots
222222
=== modified file 'lib/lp/registry/browser/tests/user-to-user-views.txt'
--- lib/lp/registry/browser/tests/user-to-user-views.txt 2011-12-29 05:29:36 +0000
+++ lib/lp/registry/browser/tests/user-to-user-views.txt 2012-02-10 21:51:21 +0000
@@ -305,13 +305,14 @@
305 --305 --
306 This message was sent from Launchpad by306 This message was sent from Launchpad by
307 Bart (http://launchpad.dev/~bart)307 Bart (http://launchpad.dev/~bart)
308 using the "Contact this team's owner" link on the GuadaMen team page308 using the "Contact this team's admins" link on the GuadaMen team page
309 (http://launchpad.dev/~guadamen).309 (http://launchpad.dev/~guadamen).
310 For more information see310 For more information see
311 https://help.launchpad.net/YourAccount/ContactingPeople311 https://help.launchpad.net/YourAccount/ContactingPeople
312 # of Messages: 1312 # of Messages: 2
313 Recipients:313 Recipients:
314 Foo Bar <foo.bar@canonical.com>314 Foo Bar <foo.bar@canonical.com>
315 Ubuntu Team <support@ubuntu.com>
315316
316317
317Member to team318Member to team
@@ -363,110 +364,7 @@
363 Steve Alexander <steve.alexander@ubuntulinux.com>364 Steve Alexander <steve.alexander@ubuntulinux.com>
364 Ubuntu Team <support@ubuntu.com>365 Ubuntu Team <support@ubuntu.com>
365366
366The Guadamen team creates a mailing list but does not set it to be the367 >>> transaction.commit()
367contact address. The mailing list will not be used.
368
369 >>> team, mailing_list = factory.makeTeamAndMailingList(
370 ... guadamen.name, guadamen.teamowner.name)
371
372 # Ignore the 'new mailing list message'
373
374 >>> transaction.commit()
375 >>> del stub.test_emails[:]
376
377Foo Bar now contacts them again, which he can do because his quota is
378still not met. This message includes a "%s" combination; it is not a
379interpolation instruction.
380
381 >>> view = create_view(
382 ... foo_bar, guadamen, {
383 ... 'field.field.from_': 'foo.bar@canonical.com',
384 ... 'field.subject': 'My last question for Guadamen',
385 ... 'field.message': 'Can one of you help me with "%s" usage!',
386 ... 'field.actions.send': 'Send',
387 ... })
388 >>> print_notifications(view)
389 Message sent to GuadaMen
390
391Foo Bar's message gets sent to each individual member of he team.
392
393 >>> transaction.commit()
394 >>> print_messages()
395 Senders: set(['Foo Bar <foo.bar@canonical.com>'])
396 Subjects: set(['My last question for Guadamen'])
397 Bodies:
398 Can one of you help me with "%s" usage!
399 --
400 This message was sent from Launchpad by
401 Foo Bar (http://launchpad.dev/~name16)
402 to each member of the GuadaMen team
403 using the "Contact this team" link on the GuadaMen
404 team page (http://launchpad.dev/~guadamen).
405 For more information see
406 https://help.launchpad.net/YourAccount/ContactingPeople
407 # of Messages: 10
408 Recipients:
409 Alexander Limi <limi@plone.org>
410 Celso Providelo <celso.providelo@canonical.com>
411 Colin Watson <colin.watson@ubuntulinux.com>
412 Daniel Silverstone <daniel.silverstone@canonical.com>
413 Edgar Bursic <edgar@monteparadiso.hr>
414 Foo Bar <foo.bar@canonical.com>
415 Jeff Waugh <jeff.waugh@ubuntulinux.com>
416 Mark Shuttleworth <mark@example.com>
417 Steve Alexander <steve.alexander@ubuntulinux.com>
418 Ubuntu Team <support@ubuntu.com>
419
420The Guadamen team now registers an external contact address (they could
421have also used their Launchpad mailing list address).
422
423 >>> from lp.services.identity.interfaces.emailaddress import (
424 ... IEmailAddressSet)
425 >>> email_set = getUtility(IEmailAddressSet)
426 >>> address = email_set.new('guadamen@example.com', guadamen)
427 >>> guadamen.setContactAddress(address)
428
429Foo Bar contacts the Guadamen team again, which is allowed because his
430quota was not met by his first message. This time only one message is
431sent, and that to the new contact address.
432
433 >>> view = create_view(
434 ... foo_bar, guadamen, {
435 ... 'field.field.from_': 'foo.bar@canonical.com',
436 ... 'field.subject': 'Hello again Guadamen',
437 ... 'field.message': 'Can one of you help me?',
438 ... 'field.actions.send': 'Send',
439 ... })
440 >>> print_notifications(view)
441 Message sent to GuadaMen
442
443 >>> transaction.commit()
444 >>> len(stub.test_emails)
445 1
446
447 >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
448 >>> print from_addr, to_addrs
449 bounces@canonical.com [u'GuadaMen <guadamen@example.com>']
450
451 >>> print raw_msg
452 Content-Type: text/plain; charset="us-ascii"
453 ...
454 From: Foo Bar <foo.bar@canonical.com>
455 To: GuadaMen <guadamen@example.com>
456 Subject: Hello again Guadamen
457 ...
458 X-Launchpad-Message-Rationale: ContactViaWeb member (guadamen team)
459 ...
460 <BLANKLINE>
461 Can one of you help me?
462 --
463 This message was sent from Launchpad by
464 Foo Bar (http://launchpad.dev/~name16)
465 using the "Contact this team" link on the GuadaMen team page
466 (http://launchpad.dev/~guadamen).
467 For more information see
468 https://help.launchpad.net/YourAccount/ContactingPeople
469
470368
471Message quota369Message quota
472-------------370-------------
@@ -477,6 +375,22 @@
477emails again. The is_possible property will be False if is_allowed is375emails again. The is_possible property will be False if is_allowed is
478False.376False.
479377
378 >>> view = create_view(
379 ... foo_bar, guadamen, {
380 ... 'field.field.from_': 'foo.bar@canonical.com',
381 ... 'field.subject': 'Hello Guadamen',
382 ... 'field.message': 'Can one of you help me?',
383 ... 'field.actions.send': 'Send',
384 ... })
385 >>> view.contact_is_allowed
386 True
387
388 >>> view.contact_is_possible
389 True
390
391 >>> view.initialize()
392 >>> transaction.commit()
393
480Foo Bar has now reached his quota and can send no more contact messages394Foo Bar has now reached his quota and can send no more contact messages
481today.395today.
482396
@@ -500,16 +414,6 @@
500 Your message was not sent because you have exceeded your daily quota of414 Your message was not sent because you have exceeded your daily quota of
501 3 messages to contact users. Try again in ...415 3 messages to contact users. Try again in ...
502416
503Bart has sent 1 message, he may send more messages.
504
505 >>> login_person(bart)
506 >>> view = create_view(bart, guadamen)
507 >>> view.contact_is_allowed
508 True
509
510 >>> view.contact_is_possible
511 True
512
513417
514Identifying information418Identifying information
515-----------------------419-----------------------
516420
=== modified file 'lib/lp/registry/stories/team/xx-team-home.txt'
--- lib/lp/registry/stories/team/xx-team-home.txt 2011-01-04 16:08:57 +0000
+++ lib/lp/registry/stories/team/xx-team-home.txt 2012-02-10 21:51:21 +0000
@@ -1,4 +1,5 @@
1= A team's home page =1A team's home page
2==================
23
3The home page of a public team is visible to everyone.4The home page of a public team is visible to everyone.
45
@@ -133,18 +134,6 @@
133 ... find_tag_by_id(browser.contents, 'your-involvement'))134 ... find_tag_by_id(browser.contents, 'your-involvement'))
134 You are a member of this team...135 You are a member of this team...
135136
136He can also see the contact email address and a link to contact the team.
137
138 >>> browser.open('http://launchpad.dev/~ubuntu-team')
139 >>> print extract_text(
140 ... find_tag_by_id(browser.contents, 'contact-email'))
141 Email:
142 support@ubuntu.com
143 Set contact address
144 >>> print extract_text(
145 ... find_tag_by_id(browser.contents, 'contact-user'))
146 Contact this team's email address
147
148Member can contact their team even if the team does not have a contact137Member can contact their team even if the team does not have a contact
149address:138address:
150139
@@ -190,7 +179,8 @@
190 ...179 ...
191180
192181
193== Team admins ==182Team admins
183-----------
194184
195Team owners and admins can see a link to approve and decline applicants.185Team owners and admins can see a link to approve and decline applicants.
196186
@@ -207,7 +197,8 @@
207 <Link text='Approve or decline members' url='.../+editproposedmembers'>197 <Link text='Approve or decline members' url='.../+editproposedmembers'>
208198
209199
210== Non members ==200Non members
201-----------
211202
212No Privileges Person is not a member of the Ubuntu team.203No Privileges Person is not a member of the Ubuntu team.
213204
@@ -220,13 +211,14 @@
220He can see the contact address, and the link explains the email211He can see the contact address, and the link explains the email
221will actually go to the team's administrators.212will actually go to the team's administrators.
222213
223 >>> print extract_text(find_tag_by_id(user_browser.contents, 'contact-email'))214 >>> print extract_text(find_tag_by_id(
215 ... user_browser.contents, 'contact-email'))
224 Email:216 Email:
225 support@ubuntu.com217 support@ubuntu.com
226 >>> content = find_tag_by_id(user_browser.contents, 'contact-user')218 >>> content = find_tag_by_id(user_browser.contents, 'contact-user')
227 >>> print extract_text(content)219 >>> print extract_text(content)
228 Contact this team's owner220 Contact this team's admins
229221
230 >>> content.a222 >>> content.a
231 <a href="+contactuser"...223 <a href="+contactuser"...
232 title="Send an email to this team's owner through Launchpad">...224 title="Send an email to this team's admins through Launchpad">...
233225
=== modified file 'lib/lp/registry/templates/contact-user.pt'
--- lib/lp/registry/templates/contact-user.pt 2011-10-13 05:42:45 +0000
+++ lib/lp/registry/templates/contact-user.pt 2012-02-10 21:51:21 +0000
@@ -17,7 +17,7 @@
17 specified below. If you do not want17 specified below. If you do not want
18 <tal:person18 <tal:person
19 tal:content="context/displayname">Anne Person</tal:person>19 tal:content="context/displayname">Anne Person</tal:person>
20 to know your email address, hit20 to know your email address,
21 <a href="" tal:attributes="href view/cancel_url">Cancel</a> now.21 <a href="" tal:attributes="href view/cancel_url">Cancel</a> now.
22 </p>22 </p>
23 </div>23 </div>