Add NEWS, tweak an interface, and rewrite a unit test.
[mailman.git] / src / mailman / model / docs / subscriptions.rst
blob14cb36b717da836b69e1e51f50691449b0fd09a2
1 =====================
2 Subscription services
3 =====================
5 The ``ISubscriptionService`` utility provides higher level convenience methods
6 useful for searching, retrieving, iterating, and removing memberships across
7 all mailing lists on th esystem.  Adding new users is handled by the
8 ``IRegistrar`` interface.
10     >>> from mailman.interfaces.subscriptions import ISubscriptionService
11     >>> from zope.component import getUtility
12     >>> service = getUtility(ISubscriptionService)
14 You can use the service to get all members of all mailing lists, for any
15 membership role.  At first, there are no memberships.
17     >>> service.get_members()
18     []
19     >>> sum(1 for member in service)
20     0
21     >>> from uuid import UUID
22     >>> print(service.get_member(UUID(int=801)))
23     None
26 Listing members
27 ===============
29 When there are some members, of any role on any mailing list, they can be
30 retrieved through the subscription service.
32     >>> from mailman.app.lifecycle import create_list
33     >>> ant = create_list('ant@example.com')
34     >>> bee = create_list('bee@example.com')
35     >>> cat = create_list('cat@example.com')
37 Some people become members.
39     >>> from mailman.interfaces.member import MemberRole
40     >>> from mailman.testing.helpers import subscribe
41     >>> anne_1 = subscribe(ant, 'Anne')
42     >>> anne_2 = subscribe(ant, 'Anne', MemberRole.owner)
43     >>> bart_1 = subscribe(ant, 'Bart', MemberRole.moderator)
44     >>> bart_2 = subscribe(bee, 'Bart', MemberRole.owner)
45     >>> anne_3 = subscribe(cat, 'Anne', email='anne@example.com')
46     >>> cris_1 = subscribe(cat, 'Cris')
48 The service can be used to iterate over them.
50     >>> for member in service.get_members():
51     ...     print(member)
52     <Member: Anne Person <aperson@example.com>
53         on ant@example.com as MemberRole.owner>
54     <Member: Bart Person <bperson@example.com>
55         on ant@example.com as MemberRole.moderator>
56     <Member: Anne Person <aperson@example.com>
57         on ant@example.com as MemberRole.member>
58     <Member: Bart Person <bperson@example.com>
59         on bee@example.com as MemberRole.owner>
60     <Member: Anne Person <anne@example.com>
61         on cat@example.com as MemberRole.member>
62     <Member: Cris Person <cperson@example.com>
63         on cat@example.com as MemberRole.member>
65 The service can also be used to get the information about a single member.
67     >>> print(service.get_member(bart_2.member_id))
68     <Member: Bart Person <bperson@example.com>
69         on bee@example.com as MemberRole.owner>
71 There is an iteration shorthand for getting all the members.
73     >>> for member in service:
74     ...     print(member)
75     <Member: Anne Person <aperson@example.com>
76         on ant@example.com as MemberRole.owner>
77     <Member: Bart Person <bperson@example.com>
78         on ant@example.com as MemberRole.moderator>
79     <Member: Anne Person <aperson@example.com>
80         on ant@example.com as MemberRole.member>
81     <Member: Bart Person <bperson@example.com>
82         on bee@example.com as MemberRole.owner>
83     <Member: Anne Person <anne@example.com>
84         on cat@example.com as MemberRole.member>
85     <Member: Cris Person <cperson@example.com>
86         on cat@example.com as MemberRole.member>
89 Searching for members
90 =====================
92 The subscription service can be used to find memberships based on specific
93 search criteria.  For example, we can find all the mailing lists that Anne is
94 a member of with her ``aperson@example.com`` address.
96     >>> for member in service.find_members('aperson@example.com'):
97     ...     print(member)
98     <Member: Anne Person <aperson@example.com>
99         on ant@example.com as MemberRole.member>
100     <Member: Anne Person <aperson@example.com>
101         on ant@example.com as MemberRole.owner>
103 There may be no matching memberships.
105     >>> list(service.find_members('dave@example.com'))
106     []
108 The address may contain asterisks, which will be interpreted as a wildcard in
109 the search pattern.
111     >>> for member in service.find_members('*person*'):
112     ...     print(member)
113     <Member: Anne Person <aperson@example.com>
114         on ant@example.com as MemberRole.member>
115     <Member: Anne Person <aperson@example.com>
116         on ant@example.com as MemberRole.owner>
117     <Member: Bart Person <bperson@example.com>
118         on ant@example.com as MemberRole.moderator>
119     <Member: Bart Person <bperson@example.com>
120         on bee@example.com as MemberRole.owner>
121     <Member: Cris Person <cperson@example.com>
122         on cat@example.com as MemberRole.member>
124 Memberships can also be searched for by user id.
126     >>> for member in service.find_members(anne_1.user.user_id):
127     ...     print(member)
128     <Member: Anne Person <aperson@example.com>
129         on ant@example.com as MemberRole.member>
130     <Member: Anne Person <aperson@example.com>
131         on ant@example.com as MemberRole.owner>
133 You can find all the memberships for a specific mailing list.
135     >>> for member in service.find_members(list_id='ant.example.com'):
136     ...     print(member)
137     <Member: Anne Person <aperson@example.com>
138         on ant@example.com as MemberRole.member>
139     <Member: Anne Person <aperson@example.com>
140         on ant@example.com as MemberRole.owner>
141     <Member: Bart Person <bperson@example.com>
142         on ant@example.com as MemberRole.moderator>
144 You can find all the memberships for an address on a specific mailing list,
145 but you have to give it the list id, not the fqdn listname since the former is
146 stable but the latter could change if the list is moved.
148     >>> for member in service.find_members(
149     ...         'bperson@example.com', 'ant.example.com'):
150     ...     print(member)
151     <Member: Bart Person <bperson@example.com>
152         on ant@example.com as MemberRole.moderator>
154 You can find all the memberships for an address with a specific role.
156     >>> for member in service.find_members(
157     ...         list_id='ant.example.com', role=MemberRole.owner):
158     ...     print(member)
159     <Member: Anne Person <aperson@example.com>
160         on ant@example.com as MemberRole.owner>
162 You can also find a specific membership by all three criteria.
164     >>> for member in service.find_members(
165     ...         'bperson@example.com', 'bee.example.com', MemberRole.owner):
166     ...     print(member)
167     <Member: Bart Person <bperson@example.com>
168         on bee@example.com as MemberRole.owner>
171 Finding a single member
172 =======================
174 If you expect only zero or one member to match your criteria, you can use a
175 the more efficient ``find_member()`` method.  This takes exactly the same
176 criteria as ``find_members()``.
178 There may be no matching members.
180     >>> print(service.find_member('dave@example.com'))
181     None
183 But if there is exactly one membership, it is returned.
185     >>> service.find_member('bperson@example.com', 'ant.example.com')
186     <Member: Bart Person <bperson@example.com>
187         on ant@example.com as MemberRole.moderator>
190 Removing members
191 ================
193 Members can be removed via this service.
195     >>> len(service.get_members())
196     6
197     >>> service.leave('cat.example.com', 'cperson@example.com')
198     >>> len(service.get_members())
199     5
200     >>> for member in service:
201     ...     print(member)
202     <Member: Anne Person <aperson@example.com>
203         on ant@example.com as MemberRole.owner>
204     <Member: Bart Person <bperson@example.com>
205         on ant@example.com as MemberRole.moderator>
206     <Member: Anne Person <aperson@example.com>
207         on ant@example.com as MemberRole.member>
208     <Member: Bart Person <bperson@example.com>
209         on bee@example.com as MemberRole.owner>
210     <Member: Anne Person <anne@example.com>
211         on cat@example.com as MemberRole.member>
214 Mass Removal
215 ============
217 The subscription service can be used to perform mass removals.  You are
218 required to pass the list id of the respective mailing list and a list
219 of email addresses to be removed.
221     >>> bart_2 = subscribe(ant, 'Bart')
222     >>> cris_2 = subscribe(ant, 'Cris')
223     >>> for member in service:
224     ...     print(member)
225     <Member: Anne Person <aperson@example.com>
226         on ant@example.com as MemberRole.owner>
227     <Member: Bart Person <bperson@example.com>
228         on ant@example.com as MemberRole.moderator>
229     <Member: Anne Person <aperson@example.com>
230         on ant@example.com as MemberRole.member>
231     <Member: Bart Person <bperson@example.com>
232         on ant@example.com as MemberRole.member>
233     <Member: Cris Person <cperson@example.com>
234         on ant@example.com as MemberRole.member>
235     <Member: Bart Person <bperson@example.com>
236         on bee@example.com as MemberRole.owner>
237     <Member: Anne Person <anne@example.com>
238         on cat@example.com as MemberRole.member>
240 There are now two more memberships.
242     >>> len(service.get_members())
243     7
245 But this address is not subscribed to any mailing list.
247     >>> print(service.find_member('bogus@example.com'))
248     None
250 We can unsubscribe some addresses from the ant mailing list.  Note that even
251 though Anne is subscribed several times, only her ant membership with role
252 ``member`` will be removed.
254     >>> success, fail = service.unsubscribe_members(
255     ...     'ant.example.com', [
256     ...         'aperson@example.com',
257     ...         'cperson@example.com',
258     ...         'bogus@example.com',
259     ...         ])
261 There were some successes...
263     >>> dump_list(success)
264     aperson@example.com
265     cperson@example.com
267 ...and some failures.
269     >>> dump_list(fail)
270     bogus@example.com
272 And now there are 5 memberships again.
274     >>> for member in service:
275     ...     print(member)
276     <Member: Anne Person <aperson@example.com>
277         on ant@example.com as MemberRole.owner>
278     <Member: Bart Person <bperson@example.com>
279         on ant@example.com as MemberRole.moderator>
280     <Member: Bart Person <bperson@example.com>
281         on ant@example.com as MemberRole.member>
282     <Member: Bart Person <bperson@example.com>
283         on bee@example.com as MemberRole.owner>
284     <Member: Anne Person <anne@example.com>
285         on cat@example.com as MemberRole.member>
286     >>> len(service.get_members())
287     5