Mail notifications for membership changes ========================================= Whenever a membership status is changed, we should notify the team admins and the member whose membership changed. There's a few cases where we might want to notify only the team admins, but in most of the cases we'll be sending two similar (but not identical) notifications: one for all team admins and another for the member. >>> def by_to_addrs(a, b): ... return cmp(a[1], b[1]) >>> def setStatus(membership, status, reviewer=None, comment=None, ... silent=False): ... """Set the status of the given membership. ... ... Also sets the reviewer and comment, calling flush_database_updates ... and transaction.commit after, to ensure the changes are flushed to ... the database. ... """ ... membership.setStatus(status, reviewer, comment=comment, ... silent=silent) ... flush_database_updates() ... transaction.commit() >>> from lp.services.mail import stub >>> from lp.testing.mail_helpers import ( ... pop_notifications, print_distinct_emails, run_mail_jobs) >>> from zope.component import getUtility >>> from lp.registry.interfaces.person import ( ... IPersonSet, ... TeamMembershipRenewalPolicy, ... TeamSubscriptionPolicy, ... ) >>> from lp.registry.interfaces.teammembership import ( ... ITeamMembershipSet, ... TeamMembershipStatus, ... ) >>> personset = getUtility(IPersonSet) >>> membershipset = getUtility(ITeamMembershipSet) >>> mark = personset.getByName('mark') >>> kamion = personset.getByName('kamion') >>> sampleperson = personset.getByName('name12') >>> ubuntu_team = personset.getByName('ubuntu-team') >>> open_team = factory.makeTeam( ... subscription_policy=TeamSubscriptionPolicy.OPEN) >>> from lp.testing.sampledata import ADMIN_EMAIL >>> admin_person = personset.getByEmail(ADMIN_EMAIL) In open teams joining and leaving the team generates no notifications. >>> login_person(admin_person) >>> base_mails = len(stub.test_emails) >>> new_person = factory.makePerson() >>> login_person(new_person) >>> new_person.join(open_team) >>> membership = membershipset.getByPersonAndTeam(new_person, open_team) >>> membership.status.title 'Approved' >>> run_mail_jobs() >>> len(stub.test_emails) - base_mails 0 >>> new_person.leave(open_team) >>> run_mail_jobs() >>> len(stub.test_emails) - base_mails 0 Now Robert Collins proposes himself as a member of the Ubuntu Team. This generates a notification email only to Ubuntu Team administrators. >>> lifeless = personset.getByName('lifeless') >>> login_person(lifeless) >>> lifeless.join(ubuntu_team) >>> membership = membershipset.getByPersonAndTeam(lifeless, ubuntu_team) >>> membership.status.title 'Proposed' >>> run_mail_jobs() >>> len(stub.test_emails) 5 >>> print_distinct_emails(include_reply_to=True) From: Ubuntu Team To: colin.watson@ubuntulinux.com, foo.bar@canonical.com, jeff.waugh@ubuntulinux.com, limi@plone.org Reply-To: robertc@robertcollins.net X-Launchpad-Message-Rationale: Admin (ubuntu-team) Subject: lifeless wants to join Robert Collins (lifeless) wants to be a member of Ubuntu Team (ubuntu- team), but this is a moderated team, so that membership has to be approved. You can approve, decline or leave it as proposed by following the link below. http://launchpad.dev/~ubuntu-team/+member/lifeless -- = You received this email because you are an admin of the Ubuntu Team team. ---------------------------------------- From: Ubuntu Team To: mark@example.com Reply-To: robertc@robertcollins.net X-Launchpad-Message-Rationale: Owner (ubuntu-team) Subject: lifeless wants to join ... You received this email because you are the owner of the Ubuntu Team team. ---------------------------------------- Declining a proposed member should generate notifications for both the member and each of the team's admins. # Need to be logged in as a team admin to be able to change memberships of # that team. >>> login('mark@example.com') >>> setStatus(membership, TeamMembershipStatus.DECLINED, reviewer=mark) addMember() has queued up a job to send out the emails. We'll run the job now. >>> run_mail_jobs() >>> len(stub.test_emails) 6 >>> print_distinct_emails() From: Ubuntu Team To: colin.watson@ubuntulinux.com, foo.bar@canonical.com, jeff.waugh@ubuntulinux.com, limi@plone.org, mark@example.com Subject: lifeless declined by mark The membership status of Robert Collins (lifeless) in the team Ubuntu Team (ubuntu-team) was changed by Mark Shuttleworth (mark) from Proposed to Declined. ---------------------------------------- From: Ubuntu Team To: robertc@robertcollins.net Subject: lifeless declined by mark The status of your membership in the team Ubuntu Team (ubuntu-team) was changed by Mark Shuttleworth (mark) from Proposed to Declined. ---------------------------------------- The same goes for approving a proposed member. >>> daf = getUtility(IPersonSet).getByName('daf') >>> daf.join(ubuntu_team) >>> daf_membership = membershipset.getByPersonAndTeam(daf, ubuntu_team) >>> daf_membership.status.title 'Proposed' # Remove notification of daf's membership pending approval from # stub.test_emails >>> transaction.commit() >>> dummy = pop_notifications() >>> setStatus(daf_membership, TeamMembershipStatus.APPROVED, ... reviewer=mark, comment='This is a nice guy; I like him') >>> run_mail_jobs() >>> stub.test_emails.sort(by_to_addrs) >>> len(stub.test_emails) 6 >>> print_distinct_emails() From: Ubuntu Team To: colin.watson@ubuntulinux.com, foo.bar@canonical.com, jeff.waugh@ubuntulinux.com, limi@plone.org, mark@example.com Subject: daf approved by mark The membership status of Dafydd Harries (daf) in the team Ubuntu Team (ubuntu-team) was changed by Mark Shuttleworth (mark) from Proposed to Approved. Mark Shuttleworth said: This is a nice guy; I like him ---------------------------------------- From: Ubuntu Team To: daf@canonical.com Subject: daf approved by mark The status of your membership in the team Ubuntu Team (ubuntu-team) was changed by Mark Shuttleworth (mark) from Proposed to Approved. Mark Shuttleworth said: This is a nice guy; I like him ---------------------------------------- The same for deactivating a membership. >>> setStatus(daf_membership, TeamMembershipStatus.DEACTIVATED, ... reviewer=mark) >>> run_mail_jobs() >>> stub.test_emails.sort(by_to_addrs) >>> len(stub.test_emails) 6 >>> print_distinct_emails() From: Ubuntu Team To: colin.watson@ubuntulinux.com, foo.bar@canonical.com, jeff.waugh@ubuntulinux.com, limi@plone.org, mark@example.com Subject: daf deactivated by mark The membership status of Dafydd Harries (daf) in the team Ubuntu Team (ubuntu-team) was changed by Mark Shuttleworth (mark) from Approved to Deactivated. ---------------------------------------- From: Ubuntu Team To: daf@canonical.com Subject: daf deactivated by mark The status of your membership in the team Ubuntu Team (ubuntu-team) was changed by Mark Shuttleworth (mark) from Approved to Deactivated. ---------------------------------------- Team admins can propose their teams using the join() method as well, but in that case we'll use the requester's (the person proposing the team as the other's member) email address in the 'Reply-To' header of the message sent. >>> admins = personset.getByName('admins') >>> admins.join(ubuntu_team, requester=mark) >>> run_mail_jobs() >>> len(stub.test_emails) 5 >>> print_distinct_emails(include_reply_to=True) From: Ubuntu Team To: colin.watson@ubuntulinux.com, foo.bar@canonical.com, jeff.waugh@ubuntulinux.com, limi@plone.org Reply-To: mark@example.com X-Launchpad-Message-Rationale: Admin (ubuntu-team) Subject: admins wants to join Mark Shuttleworth (mark) wants to make Launchpad Administrators (admins) a member of Ubuntu Team (ubuntu-team), but this is a moderated team, so that membership has to be approved. You can approve, decline or leave it as proposed by following the link below. http://launchpad.dev/~ubuntu-team/+member/admins -- = You received this email because you are an admin of the Ubuntu Team team. ---------------------------------------- From: Ubuntu Team To: mark@example.com Reply-To: mark@example.com X-Launchpad-Message-Rationale: Owner (ubuntu-team) Subject: admins wants to join ... You received this email because you are the owner of the Ubuntu Team team. ---------------------------------------- Adding new members ------------------ When a person is added as a member of a team by one of that team's administrators, an email is sent to all team administrators and to the new member. >>> cprov = personset.getByName('cprov') >>> marilize = personset.getByName('marilize') >>> ignored = ubuntu_team.addMember(marilize, reviewer=cprov) >>> run_mail_jobs() Now, the emails have been sent. >>> len(stub.test_emails) 6 >>> print_distinct_emails() From: Ubuntu Team To: marilize@hbd.com X-Launchpad-Message-Rationale: Member (ubuntu-team) Subject: You have been added to ubuntu-team Celso Providelo (cprov) added you as a member of Ubuntu Team (ubuntu- team). -- = You received this email because you are the new member. ---------------------------------------- From: Ubuntu Team To: colin.watson@ubuntulinux.com, foo.bar@canonical.com, jeff.waugh@ubuntulinux.com, limi@plone.org X-Launchpad-Message-Rationale: Admin (ubuntu-team) Subject: marilize joined ubuntu-team Marilize Coetzee (marilize) has been added as a member of Ubuntu Team (ubuntu-team) by Celso Providelo (cprov). Follow the link below for more details. http://launchpad.dev/~ubuntu-team/+member/marilize -- = You received this email because you are an admin of the Ubuntu Team team. ---------------------------------------- From: Ubuntu Team To: mark@example.com X-Launchpad-Message-Rationale: Owner (ubuntu-team) Subject: marilize joined ubuntu-team ... You received this email because you are the owner of the Ubuntu Team team. ---------------------------------------- By default, if the newly added member is actually a team, we'll only send an invitation to the team's admins, telling them that the membership will only be activated if they accept the invitation. >>> mirror_admins = personset.getByName('ubuntu-mirror-admins') >>> mirror_admins.getTeamAdminsEmailAddresses() ['mark@example.com'] >>> ignored = ubuntu_team.addMember(mirror_admins, reviewer=cprov) >>> run_mail_jobs() >>> len(stub.test_emails) 1 >>> print_distinct_emails() From: Ubuntu Team To: mark@example.com Subject: Invitation for ubuntu-mirror-admins to join Celso Providelo (cprov) has invited Mirror Administrators (ubuntu- mirror-admins) (which you are an administrator of) to join Ubuntu Team (ubuntu-team). You can accept or decline this invitation on the following page: http://launchpad.dev/~ubuntu-mirror-admins/+invitation/ubuntu-team Regards, The Launchpad team ---------------------------------------- If one of the admins accept the invitation, then a notification is sent to the team which just became a member and to the admins of the hosting team. >>> comment = "Of course I want to be part of ubuntu!" >>> mirror_admins.acceptInvitationToBeMemberOf(ubuntu_team, comment) >>> flush_database_updates() >>> run_mail_jobs() >>> len(stub.test_emails) 6 >>> print_distinct_emails() From: Ubuntu Team To: colin.watson@ubuntulinux.com, foo.bar@canonical.com, jeff.waugh@ubuntulinux.com, karl@canonical.com, limi@plone.org, mark@example.com Subject: Invitation to ubuntu-mirror-admins accepted by mark Mark Shuttleworth (mark) has accepted the invitation to make Mirror Administrators (ubuntu-mirror-admins) a member of Ubuntu Team (ubuntu- team). Mark Shuttleworth said: Of course I want to be part of ubuntu! ---------------------------------------- Similarly, a notification is sent if the invitation is declined. >>> landscape = personset.getByName('landscape-developers') >>> ignored = ubuntu_team.addMember(landscape, reviewer=cprov) # Reset stub.test_emails as we don't care about the notification triggered # by the addMember() call. >>> transaction.commit() >>> stub.test_emails = [] >>> comment = "Landscape has nothing to do with ubuntu, unfortunately." >>> landscape.declineInvitationToBeMemberOf(ubuntu_team, comment) >>> flush_database_updates() >>> run_mail_jobs() >>> len(stub.test_emails) 7 >>> print_distinct_emails() From: Ubuntu Team To: colin.watson@ubuntulinux.com, foo.bar@canonical.com, guilherme.salgado@canonical.com, jeff.waugh@ubuntulinux.com, limi@plone.org, mark@example.com, test@canonical.com Subject: Invitation to landscape-developers declined by mark Mark Shuttleworth (mark) has declined the invitation to make Landscape Developers (landscape-developers) a member of Ubuntu Team (ubuntu-team). Mark Shuttleworth said: Landscape has nothing to do with ubuntu, unfortunately. ---------------------------------------- It's also possible to forcibly add a team as a member of another one, by passing force_team_add=True to the addMember() method. >>> launchpad = personset.getByName('launchpad') >>> ignored = ubuntu_team.addMember( ... launchpad, reviewer=cprov, force_team_add=True) >>> flush_database_updates() >>> run_mail_jobs() >>> len(stub.test_emails) 5 >>> print_distinct_emails() From: Ubuntu Team To: foo.bar@canonical.com X-Launchpad-Message-Rationale: Indirect member (ubuntu-team) Subject: launchpad joined ubuntu-team ... You received this email because launchpad is the new member. ---------------------------------------- From: Ubuntu Team To: colin.watson@ubuntulinux.com, jeff.waugh@ubuntulinux.com, limi@plone.org X-Launchpad-Message-Rationale: Admin (ubuntu-team) Subject: launchpad joined ubuntu-team Launchpad Developers (launchpad) has been added as a member of Ubuntu Team (ubuntu-team) by Celso Providelo (cprov). Follow the link below for more details. http://launchpad.dev/~ubuntu-team/+member/launchpad -- = You received this email because you are an admin of the Ubuntu Team team. ---------------------------------------- From: Ubuntu Team To: mark@example.com X-Launchpad-Message-Rationale: Owner (ubuntu-team) Subject: launchpad joined ubuntu-team ... You received this email because you are the owner of the Ubuntu Team team. ---------------------------------------- Membership expiration warnings ------------------------------ When we get close to the expiration date of a given membership, an expiration warning is sent to the member, so that he can contact the team's administrators (or renew it himself when he has necessary rights) in case he wants to retain that membership. This is done by the flag- expired-memberships cronscript, which uses ITeamMembership.sendExpirationWarningEmail to do its job. >>> import pytz >>> from datetime import datetime, timedelta >>> utc_now = datetime.now(pytz.timezone('UTC')) In the case of the beta-testers team, the email is sent only to the team's owner, which doesn't have the necessary rights to renew the membership of his team, so he's instructed to contact one of the ubuntu- team's admins. >>> beta_testers = personset.getByName('launchpad-beta-testers') >>> beta_testers_on_ubuntu_team = membershipset.getByPersonAndTeam( ... beta_testers, ubuntu_team) >>> beta_testers_on_ubuntu_team.setExpirationDate( ... utc_now + timedelta(days=9), mark) >>> flush_database_updates() >>> beta_testers_on_ubuntu_team.sendExpirationWarningEmail() >>> transaction.commit() >>> print_distinct_emails() From: Ubuntu Team To: beta-admin@launchpad.net Subject: launchpad-beta-testers will expire soon from ubuntu-team On ..., 9 days from now, the membership of Launchpad Beta Testers (launchpad-beta-testers) (which you are the owner of) in the Ubuntu Team (ubuntu-team) Launchpad team is due to expire. To prevent this membership from expiring, you should get in touch with one of the team's administrators: Alexander Limi (limi) Colin Watson (kamion) Foo Bar (name16) Jeff Waugh (jdub) Mark Shuttleworth (mark) If the membership does expire, we'll send you one more message to let you know it's happened. Thanks for using Launchpad! ---------------------------------------- If the team's renewal policy is ONDEMAND, though, the member is invited to renew his own membership. >>> ubuntu_team.renewal_policy = TeamMembershipRenewalPolicy.ONDEMAND >>> ubuntu_team.defaultrenewalperiod = 365 >>> kamion_on_ubuntu_team = membershipset.getByPersonAndTeam( ... kamion, ubuntu_team) >>> kamion_on_ubuntu_team.setExpirationDate( ... utc_now + timedelta(days=9), mark) >>> flush_database_updates() >>> kamion_on_ubuntu_team.sendExpirationWarningEmail() >>> transaction.commit() >>> print_distinct_emails() From: Ubuntu Team To: colin.watson@ubuntulinux.com Subject: Your membership in ubuntu-team is about to expire On ..., 9 days from now, your membership in the Ubuntu Team (ubuntu-team) Launchpad team is due to expire. If you want, you can renew this membership at If your membership does expire, we'll send you one more message to let you know it's happened. Thanks for using Launchpad! ---------------------------------------- >>> beta_testers_on_ubuntu_team.sendExpirationWarningEmail() >>> transaction.commit() >>> print_distinct_emails() From: Ubuntu Team To: beta-admin@launchpad.net Subject: launchpad-beta-testers will expire soon from ubuntu-team On ..., 9 days from now, the membership of Launchpad Beta Testers (launchpad-beta-testers) (which you are the owner of) in the Ubuntu Team (ubuntu-team) Launchpad team is due to expire. If you want, you can renew this membership at If the membership does expire, we'll send you one more message to let you know it's happened. Thanks for using Launchpad! ---------------------------------------- If the team's renewal policy is NONE but the member has the necessary rights to change the expiration date of his own membership (i.e. by being the team's owner), the notification he gets will contain a link to his memberhip page, where he can extend it. >>> landscape.renewal_policy = TeamMembershipRenewalPolicy.NONE >>> landscape.teamowner.preferredemail.email u'test@canonical.com' >>> sampleperson_on_landscape = membershipset.getByPersonAndTeam( ... sampleperson, landscape) >>> sampleperson_on_landscape.setExpirationDate( ... utc_now + timedelta(days=9), sampleperson) >>> flush_database_updates() >>> sampleperson_on_landscape.sendExpirationWarningEmail() >>> transaction.commit() >>> print_distinct_emails() From: Landscape Developers To: test@canonical.com Subject: Your membership in landscape-developers is about to expire On ..., 9 days from now, your membership in the Landscape Developers (landscape-developers) Launchpad team is due to expire. To stay a member of this team you should extend your membership at If your membership does expire, we'll send you one more message to let you know it's happened. Thanks for using Launchpad! ---------------------------------------- Membership expiration notification ---------------------------------- For teams with a renewal policy other than AUTOMATIC, if a membership is not renewed before its expiration date it'll be flagged as expired and a notification is sent to the team admins and to the member whose membership expired. If the renewal policy is AUTOMATIC, though, the memberships that should expire will retain their status and have their dateexpires update. A notification is also sent to the member and to team admins when a membership is automatically renewed. >>> from zope.security.proxy import removeSecurityProxy >>> utc_now = datetime.now(pytz.timezone('UTC')) >>> lp_admins = personset.getByName('admins') >>> print lp_admins.renewal_policy.title invite them to apply for renewal >>> mark_on_admins = membershipset.getByPersonAndTeam(mark, lp_admins) # Need to cheat here and set the expiry date manually because the expiry # date given to setExpirationDate() must be in the future. >>> removeSecurityProxy(mark_on_admins).dateexpires = utc_now >>> ubuntu_team = personset.getByName('ubuntu-team') >>> ubuntu_team.renewal_policy = TeamMembershipRenewalPolicy.AUTOMATIC >>> ubuntu_team.defaultrenewalperiod = 365 >>> jdub = personset.getByName('jdub') >>> jdub_on_ubuntu_team = membershipset.getByPersonAndTeam( ... jdub, ubuntu_team) >>> removeSecurityProxy(jdub_on_ubuntu_team).dateexpires = utc_now >>> flush_database_updates() >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities >>> membershipset.handleMembershipsExpiringToday( ... reviewer=getUtility(ILaunchpadCelebrities).janitor) >>> flush_database_updates() >>> run_mail_jobs() >>> len(stub.test_emails) 8 >>> print_distinct_emails() From: Ubuntu Team To: colin.watson@ubuntulinux.com, foo.bar@canonical.com, limi@plone.org, mark@example.com Subject: jdub renewed automatically The membership of Jeff Waugh (jdub) in the Ubuntu Team (ubuntu-team) team has been automatically renewed until ... Regards, The Launchpad team ---------------------------------------- From: Launchpad Administrators To: foo.bar@canonical.com, guilherme.salgado@canonical.com Subject: mark expired from team The membership of Mark Shuttleworth (mark) in the Launchpad Administrators (admins) team has expired. Regards, The Launchpad team ---------------------------------------- From: Launchpad Administrators To: mark@example.com Subject: mark expired from team Your membership in the Launchpad Administrators (admins) team has expired. Regards, The Launchpad team ---------------------------------------- From: Ubuntu Team To: jeff.waugh@ubuntulinux.com Subject: jdub renewed automatically Your membership in the Ubuntu Team (ubuntu-team) team has been automatically renewed until ... Regards, The Launchpad team ---------------------------------------- Memberships renewed by the members themselves --------------------------------------------- Another possible renewal policy for teams is ONDEMAND, which means that team members are invited to renew their membership once it gets close to their expiration date. When a member renew his own membership, a notification is sent to all team admins. >>> karl = personset.getByName('karl') >>> mirror_admins = personset.getByName('ubuntu-mirror-admins') >>> karl_on_mirroradmins = membershipset.getByPersonAndTeam( ... karl, mirror_admins) >>> tomorrow = datetime.now(pytz.timezone('UTC')) + timedelta(days=1) >>> print karl_on_mirroradmins.status.title Approved >>> print karl_on_mirroradmins.dateexpires None >>> login_person(mirror_admins.teamowner) >>> karl_on_mirroradmins.setExpirationDate( ... tomorrow, mirror_admins.teamowner) >>> ondemand = TeamMembershipRenewalPolicy.ONDEMAND >>> karl_on_mirroradmins.team.renewal_policy = ondemand >>> mirror_admins.defaultrenewalperiod = 365 >>> flush_database_updates() >>> login_person(karl) >>> karl.renewTeamMembership(mirror_admins) >>> run_mail_jobs() >>> len(stub.test_emails) 1 >>> print_distinct_emails() From: Mirror Administrators To: mark@example.com Subject: karl extended their membership Karl Tilbury (karl) renewed their own membership in the Mirror Administrators (ubuntu-mirror-admins) team until ... Regards, The Launchpad team ---------------------------------------- Some special cases ------------------ When creating a new team, the owner has his membership's status changed from approved to admin, but he won't get a notification of that. >>> team = personset.newTeam(mark, 'testteam', 'Test') >>> run_mail_jobs() >>> len(stub.test_emails) 0 # Other tests expect an empty stub.test_emails, but if this one above # fails, I don't want a non-empty stub.test_emails to cause the tests # below to fail too. >>> stub.test_emails = [] If cprov is made an administrator of ubuntu_team, he'll only get one email notification. >>> cprov = personset.getByName('cprov') >>> cprov_membership = membershipset.getByPersonAndTeam( ... cprov, ubuntu_team) >>> login('mark@example.com') >>> setStatus( ... cprov_membership, TeamMembershipStatus.ADMIN, reviewer=mark) >>> run_mail_jobs() >>> len(stub.test_emails) 6 >>> print_distinct_emails() From: Ubuntu Team To: colin.watson@ubuntulinux.com, foo.bar@canonical.com, jeff.waugh@ubuntulinux.com, limi@plone.org, mark@example.com Subject: cprov made admin by mark The membership status of Celso Providelo (cprov) in the team Ubuntu Team (ubuntu-team) was changed by Mark Shuttleworth (mark) from Approved to Administrator. ---------------------------------------- From: Ubuntu Team To: celso.providelo@canonical.com Subject: cprov made admin by mark The status of your membership in the team Ubuntu Team (ubuntu-team) was changed by Mark Shuttleworth (mark) from Approved to Administrator. ---------------------------------------- If a team admin changes his own membership, the notification sent will clearly say that the change was performed by the user himself, and it will only be sent to the team administrators. >>> jdub = getUtility(IPersonSet).getByName('jdub') >>> jdub_membership = membershipset.getByPersonAndTeam(jdub, ubuntu_team) >>> setStatus(jdub_membership, TeamMembershipStatus.APPROVED, ... reviewer=jdub) >>> run_mail_jobs() >>> len(stub.test_emails) 5 >>> print_distinct_emails() From: Ubuntu Team To: celso.providelo@canonical.com, colin.watson@ubuntulinux.com, foo.bar@canonical.com, limi@plone.org, mark@example.com Subject: Membership change: jdub in ubuntu-team The membership status of Jeff Waugh (jdub) in the team Ubuntu Team (ubuntu-team) was changed by the user himself from Administrator to Approved. ---------------------------------------- Deactivating the membership of a team also generates notifications for the team which had the membership deactivated and to the administrators of the hosting team. Note that the notification sent to the team whose membership was deactivated will not talk about "your membership" as it wouldn't make sense to the members of the team reading it. >>> mirror_admins_membership = membershipset.getByPersonAndTeam( ... mirror_admins, ubuntu_team) >>> setStatus(mirror_admins_membership, TeamMembershipStatus.DEACTIVATED, ... reviewer=mark, silent=False) >>> run_mail_jobs() >>> len(stub.test_emails) 6 >>> print_distinct_emails() From: Ubuntu Team To: celso.providelo@canonical.com, colin.watson@ubuntulinux.com, foo.bar@canonical.com, karl@canonical.com, limi@plone.org, mark@example.com Subject: ubuntu-mirror-admins deactivated by mark The membership status of Mirror Administrators (ubuntu-mirror-admins) in the team Ubuntu Team (ubuntu-team) was changed by Mark Shuttleworth (mark) from Approved to Deactivated. ---------------------------------------- Deactivating memberships can also be done silently (no email notifications sent) by Launchpad Administrators. >>> dumper = getUtility(IPersonSet).getByName('dumper') >>> hwdb_admins = personset.getByName('hwdb-team') >>> dumper_hwdb_membership = membershipset.getByPersonAndTeam(dumper, ... hwdb_admins) >>> print dumper_hwdb_membership.status.title Approved >>> login_person(admin_person) >>> setStatus(dumper_hwdb_membership, TeamMembershipStatus.DEACTIVATED, ... reviewer=admin_person, silent=True) >>> run_mail_jobs() >>> len(stub.test_emails) 0 >>> print dumper_hwdb_membership.status.title Deactivated People who are not Launchpad Administrators, may not change other's membership statues silently. >>> kamion = getUtility(IPersonSet).getByName('kamion') >>> stevea = getUtility(IPersonSet).getByName('stevea') >>> login_person(kamion) >>> ubuntu_team = personset.getByName('ubuntu-team') >>> kamion_ubuntu_team_membership = membershipset.getByPersonAndTeam( ... kamion, ubuntu_team) >>> stevea_ubuntu_team_membership = membershipset.getByPersonAndTeam( ... stevea, ubuntu_team) >>> print kamion_ubuntu_team_membership.status.title Administrator >>> print stevea_ubuntu_team_membership.status.title Approved >>> setStatus(stevea_ubuntu_team_membership, ... TeamMembershipStatus.DEACTIVATED, reviewer=kamion, silent=True) Traceback (most recent call last): UserCannotChangeMembershipSilently: ... >>> print stevea_ubuntu_team_membership.status.title Approved Joining a team with a mailing list ---------------------------------- When a user joins a team with a mailing list, the new member's notification email contain subscription information. >>> owner = factory.makePerson(name='team-owner') >>> login_person(owner) >>> team_one, list_one = factory.makeTeamAndMailingList( ... 'team-one', owner.name) >>> dummy = pop_notifications() >>> member = factory.makePerson( ... name='team-member', email='team-member@example.com') >>> ignored = team_one.addMember(member, owner) >>> print_distinct_emails() From: Team One ... To: team-member... X-Launchpad-Message-Rationale: Member (team-one) Subject: You have been added to team-one Team-owner (team-owner) added you as a member of Team One (team-one). If you would like to subscribe to the team list, use the link below to update your Mailing List Subscription preferences. -- = You received this email because you are the new member. ---------------------------------------- When a team join a team with a mailing list, the new member notification emails contain subscription information. >>> team_two = factory.makeTeam( ... name='team-two', email='team-two@example.com', owner=owner) >>> ignored = team_one.addMember(team_two, owner, force_team_add=True) >>> print_distinct_emails() From: Team One ... To: team-two... X-Launchpad-Message-Rationale: Indirect member (team-one) Subject: team-two joined team-one Team-owner (team-owner) added Team Two (team-two) (which you are a member of) as a member of Team One (team-one). If you would like to subscribe to the team list, use the link below to update your Mailing List Subscription preferences. -- = You received this email because team-two is the new member. ----------------------------------------