summaryrefslogtreecommitdiff
path: root/src/mailman/commands/docs/members.rst
blob: c40afb1224504c1d64797daa335fb98da8eda6db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
================
Managing members
================

The ``mailman members`` command allows a site administrator to display, add,
and remove members from a mailing list.
::

    >>> ant = create_list('ant@example.com')

    >>> class FakeArgs:
    ...     input_filename = None
    ...     output_filename = None
    ...     list = []
    ...     regular = False
    ...     digest = None
    ...     nomail = None
    ...     role = None
    >>> args = FakeArgs()

    >>> from mailman.commands.cli_members import Members
    >>> command = Members()


Listing members
===============

You can list all the members of a mailing list by calling the command with no
options.  To start with, there are no members of the mailing list.

    >>> args.list = ['ant.example.com']
    >>> command.process(args)
    ant.example.com has no members

Once the mailing list add some members, they will be displayed.

    >>> from mailman.testing.helpers import subscribe
    >>> subscribe(ant, 'Anne', email='anne@example.com')
    <Member: Anne Person <anne@example.com> on ant@example.com
             as MemberRole.member>
    >>> subscribe(ant, 'Bart', email='bart@example.com')
    <Member: Bart Person <bart@example.com> on ant@example.com
             as MemberRole.member>
    >>> command.process(args)
    Anne Person <anne@example.com>
    Bart Person <bart@example.com>

Members are displayed in alphabetical order based on their address.
::

    >>> subscribe(ant, 'Anne', email='anne@aaaxample.com')
    <Member: Anne Person <anne@aaaxample.com> on ant@example.com
             as MemberRole.member>
    >>> command.process(args)
    Anne Person <anne@aaaxample.com>
    Anne Person <anne@example.com>
    Bart Person <bart@example.com>

You can also output this list to a file.

    >>> from tempfile import NamedTemporaryFile
    >>> with NamedTemporaryFile() as outfp:
    ...     args.output_filename = outfp.name
    ...     command.process(args)
    ...     with open(args.output_filename) as infp:
    ...         print(infp.read())
    Anne Person <anne@aaaxample.com>
    Anne Person <anne@example.com>
    Bart Person <bart@example.com>
    >>> args.output_filename = None

The output file can also be standard out.

    >>> args.output_filename = '-'
    >>> command.process(args)
    Anne Person <anne@aaaxample.com>
    Anne Person <anne@example.com>
    Bart Person <bart@example.com>
    >>> args.output_filename = None


Filtering on delivery mode
--------------------------

You can limit output to just the regular non-digest members...

    >>> from mailman.interfaces.member import DeliveryMode
    >>> args.regular = True
    >>> member = ant.members.get_member('anne@example.com')
    >>> member.preferences.delivery_mode = DeliveryMode.plaintext_digests
    >>> command.process(args)
    Anne Person <anne@aaaxample.com>
    Bart Person <bart@example.com>

...or just the digest members.  Furthermore, you can either display all digest
members...

    >>> member = ant.members.get_member('anne@aaaxample.com')
    >>> member.preferences.delivery_mode = DeliveryMode.mime_digests
    >>> args.regular = False
    >>> args.digest = 'any'
    >>> command.process(args)
    Anne Person <anne@aaaxample.com>
    Anne Person <anne@example.com>

...just plain text digest members...

    >>> args.digest = 'plaintext'
    >>> command.process(args)
    Anne Person <anne@example.com>

...just MIME digest members.
::

    >>> args.digest = 'mime'
    >>> command.process(args)
    Anne Person <anne@aaaxample.com>

    # Reset for following tests.
    >>> args.digest = None


Filtering on delivery status
----------------------------

You can also filter the display on the member's delivery status.  By default,
all members are displayed, but you can filter out only those whose delivery
status is enabled...
::

    >>> from mailman.interfaces.member import DeliveryStatus

    >>> member = ant.members.get_member('anne@aaaxample.com')
    >>> member.preferences.delivery_status = DeliveryStatus.by_moderator
    >>> member = ant.members.get_member('bart@example.com')
    >>> member.preferences.delivery_status = DeliveryStatus.by_user

    >>> member = subscribe(ant, 'Cris', email='cris@example.com')
    >>> member.preferences.delivery_status = DeliveryStatus.unknown
    >>> member = subscribe(ant, 'Dave', email='dave@example.com')
    >>> member.preferences.delivery_status = DeliveryStatus.enabled
    >>> member = subscribe(ant, 'Elle', email='elle@example.com')
    >>> member.preferences.delivery_status = DeliveryStatus.by_bounces

    >>> args.nomail = 'enabled'
    >>> command.process(args)
    Anne Person <anne@example.com>
    Dave Person <dave@example.com>

...or disabled by the user...

    >>> args.nomail = 'byuser'
    >>> command.process(args)
    Bart Person <bart@example.com>

...or disabled by the list administrator (or moderator)...

    >>> args.nomail = 'byadmin'
    >>> command.process(args)
    Anne Person <anne@aaaxample.com>

...or by the bounce processor...

    >>> args.nomail = 'bybounces'
    >>> command.process(args)
    Elle Person <elle@example.com>

...or for unknown (legacy) reasons.

    >>> args.nomail = 'unknown'
    >>> command.process(args)
    Cris Person <cris@example.com>

You can also display all members who have delivery disabled for any reason.
::

    >>> args.nomail = 'any'
    >>> command.process(args)
    Anne Person <anne@aaaxample.com>
    Bart Person <bart@example.com>
    Cris Person <cris@example.com>
    Elle Person <elle@example.com>

    # Reset for following tests.
    >>> args.nomail = None


Adding members
==============

You can add members to a mailing list from the command line.  To do so, you
need a file containing email addresses and full names that can be parsed by
``email.utils.parseaddr()``.
::

    >>> bee = create_list('bee@example.com')
    >>> with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as fp:
    ...     for address in ('aperson@example.com',
    ...                     'Bart Person <bperson@example.com>',
    ...                     'cperson@example.com (Cate Person)',
    ...                     ):
    ...         print(address, file=fp)
    ...     fp.flush()
    ...     args.input_filename = fp.name
    ...     args.list = ['bee.example.com']
    ...     command.process(args)

    >>> from operator import attrgetter
    >>> dump_list(bee.members.addresses, key=attrgetter('email'))
    aperson@example.com
    Bart Person <bperson@example.com>
    Cate Person <cperson@example.com>

You can also specify ``-`` as the filename, in which case the addresses are
taken from standard input.
::

    >>> from io import StringIO
    >>> fp = StringIO()
    >>> for address in ('dperson@example.com',
    ...                 'Elly Person <eperson@example.com>',
    ...                 'fperson@example.com (Fred Person)',
    ...                 ):
    ...         print(address, file=fp)
    >>> args.input_filename = '-'
    >>> filepos = fp.seek(0)
    >>> import sys
    >>> try:
    ...     stdin = sys.stdin
    ...     sys.stdin = fp
    ...     command.process(args)
    ... finally:
    ...     sys.stdin = stdin

    >>> dump_list(bee.members.addresses, key=attrgetter('email'))
    aperson@example.com
    Bart Person <bperson@example.com>
    Cate Person <cperson@example.com>
    dperson@example.com
    Elly Person <eperson@example.com>
    Fred Person <fperson@example.com>

Blank lines and lines that begin with '#' are ignored.
::

    >>> with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as fp:
    ...     for address in ('gperson@example.com',
    ...                     '# hperson@example.com',
    ...                     '   ',
    ...                     '',
    ...                     'iperson@example.com',
    ...                     ):
    ...         print(address, file=fp)
    ...     args.input_filename = fp.name
    ...     command.process(args)

    >>> dump_list(bee.members.addresses, key=attrgetter('email'))
    aperson@example.com
    Bart Person <bperson@example.com>
    Cate Person <cperson@example.com>
    dperson@example.com
    Elly Person <eperson@example.com>
    Fred Person <fperson@example.com>
    gperson@example.com
    iperson@example.com

Addresses which are already subscribed are ignored, although a warning is
printed.
::

    >>> with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as fp:
    ...     for address in ('gperson@example.com',
    ...                     'aperson@example.com',
    ...                     'jperson@example.com',
    ...                     ):
    ...         print(address, file=fp)
    ...     args.input_filename = fp.name
    ...     command.process(args)
    Already subscribed (skipping): gperson@example.com
    Already subscribed (skipping): aperson@example.com

    >>> dump_list(bee.members.addresses, key=attrgetter('email'))
    aperson@example.com
    Bart Person <bperson@example.com>
    Cate Person <cperson@example.com>
    dperson@example.com
    Elly Person <eperson@example.com>
    Fred Person <fperson@example.com>
    gperson@example.com
    iperson@example.com
    jperson@example.com


Displaying members
==================

With no arguments, the command displays all members of the list.

    >>> args.input_filename = None
    >>> command.process(args)
    aperson@example.com
    Bart Person <bperson@example.com>
    Cate Person <cperson@example.com>
    dperson@example.com
    Elly Person <eperson@example.com>
    Fred Person <fperson@example.com>
    gperson@example.com
    iperson@example.com
    jperson@example.com