The SubscriptionWorkflow class doesn't need to include the expiry date in its
[mailman.git] / src / mailman / app / membership.py
blob85a6cba0860cc12ef11e53f8af3af643a9179361
1 # Copyright (C) 2007-2015 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
8 # any later version.
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 # more details.
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18 """Application support for membership management."""
20 __all__ = [
21 'add_member',
22 'delete_member',
23 'handle_SubscriptionEvent',
27 from email.utils import formataddr
28 from mailman.app.notifications import (
29 send_goodbye_message, send_welcome_message)
30 from mailman.core.i18n import _
31 from mailman.email.message import OwnerNotification
32 from mailman.interfaces.bans import IBanManager
33 from mailman.interfaces.member import (
34 AlreadySubscribedError, MemberRole, MembershipIsBannedError,
35 NotAMemberError, SubscriptionEvent)
36 from mailman.interfaces.usermanager import IUserManager
37 from mailman.utilities.i18n import make
38 from zope.component import getUtility
42 def add_member(mlist, record, role=MemberRole.member):
43 """Add a member right now.
45 The member's subscription must be approved by whatever policy the list
46 enforces.
48 :param mlist: The mailing list to add the member to.
49 :type mlist: `IMailingList`
50 :param record: a subscription request record.
51 :type record: RequestRecord
52 :param role: The membership role for this subscription.
53 :type role: `MemberRole`
54 :return: The just created member.
55 :rtype: `IMember`
56 :raises AlreadySubscribedError: if the user is already subscribed to
57 the mailing list.
58 :raises InvalidEmailAddressError: if the email address is not valid.
59 :raises MembershipIsBannedError: if the membership is not allowed.
60 """
61 # Check to see if the email address is banned.
62 if IBanManager(mlist).is_banned(record.email):
63 raise MembershipIsBannedError(mlist, record.email)
64 # Make sure there is a user linked with the given address.
65 user_manager = getUtility(IUserManager)
66 user = user_manager.make_user(record.email, record.display_name)
67 user.preferences.preferred_language = record.language
68 # Subscribe the address, not the user.
69 # We're looking for two versions of the email address, the case
70 # preserved version and the case insensitive version. We'll
71 # subscribe the version with matching case if it exists, otherwise
72 # we'll use one of the matching case-insensitively ones. It's
73 # undefined which one we pick.
74 case_preserved = None
75 case_insensitive = None
76 for address in user.addresses:
77 if address.original_email == record.email:
78 case_preserved = address
79 if address.email == record.email.lower():
80 case_insensitive = address
81 assert case_preserved is not None or case_insensitive is not None, (
82 'Could not find a linked address for: {}'.format(record.email))
83 address = (case_preserved if case_preserved is not None
84 else case_insensitive)
85 # Create the member and set the appropriate preferences. It's
86 # possible we're subscribing the lower cased version of the address;
87 # if that's already subscribed re-issue the exception with the correct
88 # email address (i.e. the one passed in here).
89 try:
90 member = mlist.subscribe(address, role)
91 except AlreadySubscribedError as error:
92 raise AlreadySubscribedError(
93 error.fqdn_listname, record.email, error.role)
94 member.preferences.preferred_language = record.language
95 member.preferences.delivery_mode = record.delivery_mode
96 return member
100 def delete_member(mlist, email, admin_notif=None, userack=None):
101 """Delete a member right now.
103 :param mlist: The mailing list to remove the member from.
104 :type mlist: `IMailingList`
105 :param email: The email address to unsubscribe.
106 :type email: string
107 :param admin_notif: Whether the list administrator should be notified that
108 this member was deleted.
109 :type admin_notif: bool, or None to let the mailing list's
110 `admin_notify_mchange` attribute decide.
111 :raises NotAMemberError: if the address is not a member of the
112 mailing list.
114 if userack is None:
115 userack = mlist.send_goodbye_message
116 if admin_notif is None:
117 admin_notif = mlist.admin_notify_mchanges
118 # Delete a member, for which we know the approval has been made.
119 member = mlist.members.get_member(email)
120 if member is None:
121 raise NotAMemberError(mlist, email)
122 language = member.preferred_language
123 member.unsubscribe()
124 # And send an acknowledgement to the user...
125 if userack:
126 send_goodbye_message(mlist, email, language)
127 # ...and to the administrator.
128 if admin_notif:
129 user = getUtility(IUserManager).get_user(email)
130 display_name = user.display_name
131 subject = _('$mlist.display_name unsubscription notification')
132 text = make('adminunsubscribeack.txt',
133 mailing_list=mlist,
134 listname=mlist.display_name,
135 member=formataddr((display_name, email)),
137 msg = OwnerNotification(mlist, subject, text,
138 roster=mlist.administrators)
139 msg.send(mlist)
143 def handle_SubscriptionEvent(event):
144 if not isinstance(event, SubscriptionEvent):
145 return
146 # Only send a notification message if the mailing list is configured to do
147 # so, and the member being added is a list member (as opposed to a
148 # moderator, non-member, or owner).
149 member = event.member
150 if member.role is not MemberRole.member:
151 return
152 mlist = member.mailing_list
153 if not mlist.send_welcome_message:
154 return
155 send_welcome_message(mlist, member, member.preferred_language)