Add NEWS, tweak an interface, and rewrite a unit test.
[mailman.git] / src / mailman / interfaces / subscriptions.py
blob382a23ac0ae19b97152b5531a02fd0ac1f61615a
1 # Copyright (C) 2009-2016 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 """Membership interface for REST."""
20 from collections import namedtuple
21 from enum import Enum
22 from mailman import public
23 from mailman.interfaces.errors import MailmanError
24 from mailman.interfaces.member import DeliveryMode, MembershipError
25 from zope.interface import Interface
28 @public
29 class MissingUserError(MailmanError):
30 """A an invalid user id was given."""
32 def __init__(self, user_id):
33 super().__init__()
34 self.user_id = user_id
36 def __str__(self):
37 return self.user_id
40 @public
41 class SubscriptionPendingError(MailmanError):
42 def __init__(self, mlist, email):
43 super().__init__()
44 self.mlist = mlist
45 self.email = email
48 @public
49 class TooManyMembersError(MembershipError):
50 def __init__(self, subscriber, list_id, role):
51 super().__init__()
52 self.subscriber = subscriber
53 self.list_id = list_id
54 self.role = role
57 _RequestRecord = namedtuple(
58 'RequestRecord',
59 'email display_name delivery_mode, language')
62 @public
63 def RequestRecord(email, display_name='',
64 delivery_mode=DeliveryMode.regular,
65 language=None):
66 if language is None:
67 from mailman.core.constants import system_preferences
68 language = system_preferences.preferred_language
69 return _RequestRecord(email, display_name, delivery_mode, language)
72 @public
73 class TokenOwner(Enum):
74 """Who 'owns' the token returned from the registrar?"""
75 no_one = 0
76 subscriber = 1
77 moderator = 2
80 @public
81 class ISubscriptionService(Interface):
82 """General Subscription services."""
84 def get_members():
85 """Return a sequence of all members of all mailing lists.
87 The members are sorted first by fully-qualified mailing list name,
88 then by subscribed email address, then by role. Because the user may
89 be a member of the list under multiple roles (e.g. as an owner and as
90 a digest member), the member can appear multiple times in this list.
91 Roles are sorted by: owner, moderator, member.
93 :return: The list of all members.
94 :rtype: list of `IMember`
95 """
97 def get_member(member_id):
98 """Return a member record matching the member id.
100 :param member_id: A member id.
101 :type member_id: int
102 :return: The matching member, or None if no matching member is found.
103 :rtype: `IMember`
106 def find_members(subscriber=None, list_id=None, role=None):
107 """Search for members matching some criteria.
109 The members are sorted first by list-id, then by subscribed
110 email address, then by role. Because the user may be a member
111 of the list under multiple roles (e.g. as an owner and as a
112 digest member), the member can appear multiple times in this
113 list.
115 :param subscriber: The email address or user id of the user getting
116 subscribed. This argument may contain asterisks, which will be
117 interpreted as wildcards in the search pattern.
118 :type subscriber: string or int
119 :param list_id: The list id of the mailing list to search for the
120 subscriber's memberships on.
121 :type list_id: string
122 :param role: The member role.
123 :type role: `MemberRole`
124 :return: The list of all memberships, which may be empty.
125 :rtype: list of `IMember`
128 def find_member(subscriber=None, list_id=None, role=None):
129 """Search for a member matching some criteria.
131 This is like find_members() but is guaranteed to return exactly
132 one member.
134 :param subscriber: The email address or user id of the user getting
135 subscribed.
136 :type subscriber: string or int
137 :param list_id: The list id of the mailing list to search for the
138 subscriber's memberships on.
139 :type list_id: string
140 :param role: The member role.
141 :type role: `MemberRole`
142 :return: The member matching the given criteria or None if no
143 members match the criteria.
144 :rtype: `IMember` or None
145 :raises TooManyMembersError: when the given criteria matches
146 more than one membership.
149 def __iter__():
150 """See `get_members()`."""
152 def leave(list_id, email):
153 """Unsubscribe from a mailing list.
155 :param list_id: The list id of the mailing list the user is
156 unsubscribing from.
157 :type list_id: string
158 :param email: The email address of the user getting unsubscribed.
159 :type email: string
160 :raises InvalidEmailAddressError: if the email address is not valid.
161 :raises NoSuchListError: if the named mailing list does not exist.
162 :raises NotAMemberError: if the given address is not a member of the
163 mailing list.
166 def unsubscribe_members(list_id, emails):
167 """Unsubscribe a batch of members from a mailing list.
169 :param list_id: The list id to operate on.
170 :type list_id: string
171 :param emails: A list of email addresses of the members getting
172 unsubscribed. Only list members with a role of `member` can be
173 unsubscribed via this interface.
174 :type emails: list of strings
175 :return: A two item tuple whose first item is a set of all the
176 successfully unsubscribed email addresses and second item is
177 a set of all unsuccessful email addresses.
178 :rtype: 2-tuple of (set-of-strings, set-of-strings)
179 :raises NoSuchListError: if the named mailing list does not exist.