New template system. Closes #249
[mailman.git] / src / mailman / rest / docs / listconf.rst
blob922da5ea42122405a6e23faa48f8d48fcd28cac9
1 ==========================
2 Mailing list configuration
3 ==========================
5 Mailing lists can be configured via the REST API.
7     >>> mlist = create_list('ant@example.com')
8     >>> transaction.commit()
11 Reading a configuration
12 =======================
14 All readable attributes for a list are available on a sub-resource.
16     >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/config')
17     acceptable_aliases: []
18     admin_immed_notify: True
19     admin_notify_mchanges: False
20     administrivia: True
21     advertised: True
22     allow_list_posts: True
23     anonymous_list: False
24     archive_policy: public
25     autorespond_owner: none
26     autorespond_postings: none
27     autorespond_requests: none
28     autoresponse_grace_period: 90d
29     autoresponse_owner_text:
30     autoresponse_postings_text:
31     autoresponse_request_text:
32     bounces_address: ant-bounces@example.com
33     collapse_alternatives: True
34     convert_html_to_plaintext: False
35     created_at: 20...T...
36     default_member_action: defer
37     default_nonmember_action: hold
38     description:
39     digest_footer_uri:
40     digest_header_uri:
41     digest_last_sent_at: None
42     digest_send_periodic: True
43     digest_size_threshold: 30.0
44     digest_volume_frequency: monthly
45     digests_enabled: True
46     display_name: Ant
47     filter_content: False
48     first_strip_reply_to: False
49     footer_uri:
50     fqdn_listname: ant@example.com
51     goodbye_message_uri:
52     header_uri:
53     http_etag: "..."
54     include_rfc2369_headers: True
55     join_address: ant-join@example.com
56     last_post_at: None
57     leave_address: ant-leave@example.com
58     list_name: ant
59     mail_host: example.com
60     moderator_password: None
61     next_digest_number: 1
62     no_reply_address: noreply@example.com
63     owner_address: ant-owner@example.com
64     post_id: 1
65     posting_address: ant@example.com
66     posting_pipeline: default-posting-pipeline
67     reply_goes_to_list: no_munging
68     reply_to_address:
69     request_address: ant-request@example.com
70     send_welcome_message: True
71     subject_prefix: [Ant]
72     subscription_policy: confirm
73     volume: 1
74     welcome_message_uri:
77 Changing the full configuration
78 ===============================
80 Not all of the readable attributes can be set through the web interface.  The
81 ones that can, can either be set via ``PUT`` or ``PATCH``.  ``PUT`` changes
82 all the writable attributes in one request.
84 When using ``PUT``, all writable attributes must be included.
86     >>> dump_json('http://localhost:9001/3.0/lists/'
87     ...           'ant@example.com/config',
88     ...           dict(
89     ...             acceptable_aliases=['one@example.com', 'two@example.com'],
90     ...             admin_immed_notify=False,
91     ...             admin_notify_mchanges=True,
92     ...             administrivia=False,
93     ...             advertised=False,
94     ...             anonymous_list=True,
95     ...             archive_policy='never',
96     ...             autorespond_owner='respond_and_discard',
97     ...             autorespond_postings='respond_and_continue',
98     ...             autorespond_requests='respond_and_discard',
99     ...             autoresponse_grace_period='45d',
100     ...             autoresponse_owner_text='the owner',
101     ...             autoresponse_postings_text='the mailing list',
102     ...             autoresponse_request_text='the robot',
103     ...             display_name='Fnords',
104     ...             description='This is my mailing list',
105     ...             include_rfc2369_headers=False,
106     ...             allow_list_posts=False,
107     ...             digest_send_periodic=False,
108     ...             digest_size_threshold=10.5,
109     ...             digest_volume_frequency='yearly',
110     ...             digests_enabled=False,
111     ...             posting_pipeline='virgin',
112     ...             filter_content=True,
113     ...             first_strip_reply_to=True,
114     ...             convert_html_to_plaintext=True,
115     ...             collapse_alternatives=False,
116     ...             reply_goes_to_list='point_to_list',
117     ...             reply_to_address='bee@example.com',
118     ...             send_welcome_message=False,
119     ...             subject_prefix='[ant]',
120     ...             subscription_policy='moderate',
121     ...             default_member_action='hold',
122     ...             default_nonmember_action='discard',
123     ...             moderator_password='password',
124     ...             ),
125     ...           'PUT')
126     content-length: 0
127     date: ...
128     server: WSGIServer/...
129     status: 204
131 These values are changed permanently.
133     >>> dump_json('http://localhost:9001/3.0/lists/'
134     ...           'ant@example.com/config')
135     acceptable_aliases: ['one@example.com', 'two@example.com']
136     admin_immed_notify: False
137     admin_notify_mchanges: True
138     administrivia: False
139     advertised: False
140     allow_list_posts: False
141     anonymous_list: True
142     archive_policy: never
143     autorespond_owner: respond_and_discard
144     autorespond_postings: respond_and_continue
145     autorespond_requests: respond_and_discard
146     autoresponse_grace_period: 45d
147     autoresponse_owner_text: the owner
148     autoresponse_postings_text: the mailing list
149     autoresponse_request_text: the robot
150     ...
151     collapse_alternatives: False
152     convert_html_to_plaintext: True
153     ...
154     default_member_action: hold
155     default_nonmember_action: discard
156     description: This is my mailing list
157     ...
158     digest_send_periodic: False
159     digest_size_threshold: 10.5
160     digest_volume_frequency: yearly
161     digests_enabled: False
162     display_name: Fnords
163     filter_content: True
164     first_strip_reply_to: True
165     footer_uri:
166     fqdn_listname: ant@example.com
167     ...
168     include_rfc2369_headers: False
169     ...
170     moderator_password: {plaintext}password
171     ...
172     posting_pipeline: virgin
173     reply_goes_to_list: point_to_list
174     reply_to_address: bee@example.com
175     ...
176     send_welcome_message: False
177     subject_prefix: [ant]
178     subscription_policy: moderate
179     ...
182 Changing a partial configuration
183 ================================
185 Using ``PATCH``, you can change just one attribute.
187     >>> dump_json('http://localhost:9001/3.0/lists/'
188     ...           'ant@example.com/config',
189     ...           dict(display_name='My List'),
190     ...           'PATCH')
191     content-length: 0
192     date: ...
193     server: ...
194     status: 204
196 These values are changed permanently.
198     >>> print(mlist.display_name)
199     My List
202 Sub-resources
203 =============
205 Mailing list configuration variables are actually available as sub-resources
206 on the mailing list.  Their values can be retrieved and set through the
207 sub-resource.
210 Simple resources
211 ----------------
213 You can view the current value of the sub-resource.
215     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
216     ...           '/config/display_name')
217     display_name: My List
218     http_etag: ...
220 The resource can be changed by PUTting to it.  Note that the value still
221 requires a dictionary, and that dictionary must have a single key matching the
222 name of the resource.
225     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
226     ...           '/config/display_name',
227     ...           dict(display_name='Your List'),
228     ...           'PUT')
229     content-length: 0
230     date: ...
231     server: ...
232     status: 204
234     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
235     ...           '/config/display_name')
236     display_name: Your List
237     http_etag: ...
239 PATCH works the same way, with the same effect, so you can choose to use
240 either method.
242     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
243     ...           '/config/display_name',
244     ...           dict(display_name='Their List'),
245     ...           'PATCH')
246     content-length: 0
247     date: ...
248     server: ...
249     status: 204
251     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
252     ...           '/config/display_name')
253     display_name: Their List
254     http_etag: ...
257 Acceptable aliases
258 ------------------
260 These are recipient aliases that can be used in the ``To:`` and ``CC:``
261 headers instead of the posting address.  They are often used in forwarded
262 emails.  By default, a mailing list has no acceptable aliases.
264     >>> from mailman.interfaces.mailinglist import IAcceptableAliasSet
265     >>> IAcceptableAliasSet(mlist).clear()
266     >>> transaction.commit()
267     >>> dump_json('http://localhost:9001/3.0/lists/'
268     ...           'ant@example.com/config/acceptable_aliases')
269     acceptable_aliases: []
270     http_etag: "..."
272 We can add a few by ``PUT``-ing them on the sub-resource.  The keys in the
273 dictionary are ignored.
275     >>> dump_json('http://localhost:9001/3.0/lists/'
276     ...           'ant@example.com/config/acceptable_aliases',
277     ...           dict(acceptable_aliases=['foo@example.com',
278     ...                                    'bar@example.net']),
279     ...           'PUT')
280     content-length: 0
281     date: ...
282     server: WSGIServer/...
283     status: 204
285 Aliases are returned as a list on the ``aliases`` key.
287     >>> response = call_http(
288     ...     'http://localhost:9001/3.0/lists/'
289     ...     'ant@example.com/config/acceptable_aliases')
290     >>> for alias in response['acceptable_aliases']:
291     ...     print(alias)
292     bar@example.net
293     foo@example.com
295 The mailing list has its aliases set.
297     >>> from mailman.interfaces.mailinglist import IAcceptableAliasSet
298     >>> aliases = IAcceptableAliasSet(mlist)
299     >>> for alias in sorted(aliases.aliases):
300     ...     print(alias)
301     bar@example.net
302     foo@example.com
305 Header matches
306 --------------
308 Mailman can do pattern based header matching during its normal rule
309 processing.  Each mailing list can also be configured with a set of header
310 matching regular expression rules.  These can be used to impose list-specific
311 header filtering with the same semantics as the global ``[antispam]`` section,
312 or to have a different action.
314 The list of header matches for a mailing list are returned on the
315 ``header-matches`` child of this list.
317     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
318     ...           '/header-matches')
319     http_etag: "..."
320     start: 0
321     total_size: 0
323 New header matches can be created by POSTing to the resource.
326     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
327     ...           '/header-matches', {
328     ...           'header': 'X-Spam-Flag',
329     ...           'pattern': '^Yes',
330     ...           })
331     content-length: 0
332     ...
333     location: .../3.0/lists/ant.example.com/header-matches/0
334     ...
335     status: 201
337     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
338     ...           '/header-matches/0')
339     header: x-spam-flag
340     http_etag: "..."
341     pattern: ^Yes
342     position: 0
343     self_link: http://localhost:9001/3.0/lists/ant.example.com/header-matches/0
345 To follow the global antispam action, the header match rule must not specify
346 an ``action`` key, which names the chain to jump to if the rule matches.  If
347 the default antispam action is changed in the configuration file and Mailman
348 is restarted, those rules will get the new jump action.  If a specific action
349 is desired, the ``action`` key must name a valid chain to jump to.
352     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
353     ...           '/header-matches', {
354     ...           'header': 'X-Spam-Status',
355     ...           'pattern': '^Yes',
356     ...           'action': 'discard',
357     ...           })
358     content-length: 0
359     ...
360     location: .../3.0/lists/ant.example.com/header-matches/1
361     ...
362     status: 201
364     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
365     ...           '/header-matches/1')
366     action: discard
367     header: x-spam-status
368     http_etag: "..."
369     pattern: ^Yes
370     position: 1
371     self_link: http://localhost:9001/3.0/lists/ant.example.com/header-matches/1
373 The resource can be changed by PATCHing it.  The ``position`` key can be used
374 to change the priority of the header match in the list.  If it is not supplied,
375 the priority is not changed.
378     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
379     ...           '/header-matches/1',
380     ...           dict(pattern='^No', action='accept'),
381     ...           'PATCH')
382     content-length: 0
383     date: ...
384     server: ...
385     status: 204
386     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
387     ...           '/header-matches/1')
388     action: accept
389     header: x-spam-status
390     http_etag: "..."
391     pattern: ^No
392     position: 1
393     self_link: http://localhost:9001/3.0/lists/ant.example.com/header-matches/1
395     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
396     ...           '/header-matches/1',
397     ...           dict(position=0),
398     ...           'PATCH')
399     content-length: 0
400     date: ...
401     server: ...
402     status: 204
403     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
404     ...           '/header-matches')
405     entry 0:
406         action: accept
407         header: x-spam-status
408         http_etag: "..."
409         pattern: ^No
410         position: 0
411         self_link: .../lists/ant.example.com/header-matches/0
412     entry 1:
413         header: x-spam-flag
414         http_etag: "..."
415         pattern: ^Yes
416         position: 1
417         self_link: .../lists/ant.example.com/header-matches/1
418     http_etag: "..."
419     start: 0
420     total_size: 2
422 The PUT method can replace an entire header match.  The ``position`` key is
423 optional; if it is omitted, the order will not be changed.
426     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
427     ...           '/header-matches/1',
428     ...           dict(header='X-Spam-Status',
429     ...                pattern='^Yes',
430     ...                action='hold',
431     ...           ), 'PUT')
432     content-length: 0
433     date: ...
434     server: ...
435     status: 204
437     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
438     ...           '/header-matches/1')
439     action: hold
440     header: x-spam-status
441     http_etag: "..."
442     pattern: ^Yes
443     position: 1
444     self_link: http://localhost:9001/3.0/lists/ant.example.com/header-matches/1
446 A header match can be removed using the DELETE method.
449     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
450     ...           '/header-matches/1',
451     ...           method='DELETE')
452     content-length: 0
453     ...
454     status: 204
456     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
457     ...           '/header-matches')
458     entry 0:
459         action: accept
460         header: x-spam-status
461         http_etag: "..."
462         pattern: ^No
463         position: 0
464         self_link: .../lists/ant.example.com/header-matches/0
465     http_etag: "..."
466     start: 0
467     total_size: 1
469 The mailing list's header matches can be cleared by issuing a DELETE request on
470 the top resource.
473     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
474     ...           '/header-matches',
475     ...           method='DELETE')
476     content-length: 0
477     ...
478     status: 204
480     >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
481     ...           '/header-matches')
482     http_etag: "..."
483     start: 0
484     total_size: 0