Membership

The REST API can be used to subscribe and unsubscribe users to mailing lists. A subscribed user is called a member. There is a top level collection that returns all the members of all known mailing lists.

There are no mailing lists and no members yet.

>>> dump_json('http://localhost:8001/3.0/members')
http_etag: "..."
start: 0
total_size: 0

We create a mailing list, which starts out with no members.

>>> mlist_one = create_list('test-one@example.com')
>>> transaction.commit()

>>> dump_json('http://localhost:8001/3.0/members')
http_etag: "..."
start: 0
total_size: 0

Subscribers

After Bart subscribes to the mailing list, his subscription is available via the REST interface.

>>> from mailman.interfaces.member import MemberRole
>>> from mailman.interfaces.usermanager import IUserManager
>>> from zope.component import getUtility
>>> user_manager = getUtility(IUserManager)

>>> from mailman.testing.helpers import subscribe
>>> subscribe(mlist_one, 'Bart')
>>> dump_json('http://localhost:8001/3.0/members')
entry 0:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/bperson@example.com
http_etag: "..."
start: 0
total_size: 1

When Cris also joins the mailing list, her subscription is also available via the REST interface.

>>> subscribe(mlist_one, 'Cris')
>>> dump_json('http://localhost:8001/3.0/members')
entry 0:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/bperson@example.com
entry 1:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/cperson@example.com
http_etag: "..."
start: 0
total_size: 2

The subscribed members are returned in alphabetical order, so when Anna subscribes, she is returned first.

>>> subscribe(mlist_one, 'Anna')

>>> dump_json('http://localhost:8001/3.0/members')
entry 0:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/aperson@example.com
entry 1:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/bperson@example.com
entry 2:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/cperson@example.com
http_etag: "..."
start: 0
total_size: 3

Subscriptions are also returned alphabetically by mailing list posting address. Anna and Cris subscribe to this new mailing list.

>>> mlist_two = create_list('alpha@example.com')
>>> subscribe(mlist_two, 'Anna')
>>> subscribe(mlist_two, 'Cris')

>>> dump_json('http://localhost:8001/3.0/members')
entry 0:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/alpha@example.com/member/aperson@example.com
entry 1:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/alpha@example.com/member/cperson@example.com
entry 2:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/aperson@example.com
entry 3:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/bperson@example.com
entry 4:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/cperson@example.com
http_etag: "..."
start: 0
total_size: 5

We can also get just the members of a single mailing list.

>>> dump_json(
...     'http://localhost:8001/3.0/lists/alpha@example.com/roster/members')
entry 0:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/alpha@example.com/member/aperson@example.com
entry 1:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/alpha@example.com/member/cperson@example.com
http_etag: ...
start: 0
total_size: 2

Owners and moderators

Mailing list owners and moderators also show up in the REST API. Cris becomes an owner of the alpha mailing list and Dave becomes a moderator of the test-one mailing list.

>>> subscribe(mlist_one, 'Cris', MemberRole.owner)
>>> subscribe(mlist_two, 'Dave', MemberRole.moderator)

>>> dump_json('http://localhost:8001/3.0/members')
entry 0:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/alpha@example.com/moderator/dperson@example.com
entry 1:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/alpha@example.com/member/aperson@example.com
entry 2:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/alpha@example.com/member/cperson@example.com
entry 3:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/owner/cperson@example.com
entry 4:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/aperson@example.com
entry 5:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/bperson@example.com
entry 6:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/test-one@example.com/member/cperson@example.com
http_etag: "..."
start: 0
total_size: 7

Joining a mailing list

A user can be subscribed to a mailing list via the REST API. Actually, addresses not users are subscribed to mailing lists, but addresses are always tied to users. A subscribed user is called a member.

Elly subscribes to the alpha mailing list. By default, get gets a regular delivery. Since Elly’s email address is not yet known to Mailman, a user is created for her.

>>> dump_json('http://localhost:8001/3.0/members', {
...           'fqdn_listname': 'alpha@example.com',
...           'address': 'eperson@example.com',
...           'real_name': 'Elly Person',
...           })
content-length: 0
date: ...
location: http://localhost:8001/3.0/lists/alpha@example.com/member/eperson@example.com
...

Elly is now a member of the mailing list.

>>> elly = user_manager.get_user('eperson@example.com')
>>> elly
<User "Elly Person" at ...>

>>> set(member.mailing_list for member in elly.memberships.members)
set([u'alpha@example.com'])

>>> dump_json('http://localhost:8001/3.0/members')
entry 0:
...
entry 3:
    http_etag: ...
    self_link: http://localhost:8001/3.0/lists/alpha@example.com/member/eperson@example.com
...

Leaving a mailing list

Elly decides she does not want to be a member of the mailing list after all, so she leaves from the mailing list.

# Ensure our previous reads don't keep the database lock.
>>> transaction.abort()
>>> dump_json('http://localhost:8001/3.0/lists/alpha@example.com'
...           '/member/eperson@example.com',
...           method='DELETE')
content-length: 0
...
status: 200

Elly is no longer a member of the mailing list.

>>> set(member.mailing_list for member in elly.memberships.members)
set([])

Digest delivery

Fred joins the alpha mailing list but wants MIME digest delivery.

>>> transaction.abort()
>>> dump_json('http://localhost:8001/3.0/members', {
...           'fqdn_listname': 'alpha@example.com',
...           'address': 'fperson@example.com',
...           'real_name': 'Fred Person',
...           'delivery_mode': 'mime_digests',
...           })
content-length: 0
...
location: http://localhost:8001/3.0/lists/alpha@example.com/member/fperson@example.com
...
status: 201

>>> fred = user_manager.get_user('fperson@example.com')
>>> memberships = list(fred.memberships.members)
>>> len(memberships)
1
>>> memberships[0]
<Member: Fred Person <fperson@example.com>
         on alpha@example.com as MemberRole.member>

Corner cases

For some reason Elly tries to join a mailing list that does not exist.

>>> dump_json('http://localhost:8001/3.0/members', {
...           'fqdn_listname': 'beta@example.com',
...           'address': 'eperson@example.com',
...           'real_name': 'Elly Person',
...           })
Traceback (most recent call last):
...
HTTPError: HTTP Error 400: No such list

Then, she tries to leave a mailing list that does not exist.

>>> dump_json('http://localhost:8001/3.0/lists/beta@example.com'
...           '/members/eperson@example.com',
...           method='DELETE')
Traceback (most recent call last):
...
HTTPError: HTTP Error 404: 404 Not Found

She then tries to leave a mailing list with a bogus address.

>>> dump_json('http://localhost:8001/3.0/lists/alpha@example.com'
...           '/members/elly',
...           method='DELETE')
Traceback (most recent call last):
...
HTTPError: HTTP Error 404: 404 Not Found

For some reason, Elly tries to leave the mailing list again, but she’s already been unsubscribed.

>>> dump_json('http://localhost:8001/3.0/lists/alpha@example.com'
...           '/members/eperson@example.com',
...           method='DELETE')
Traceback (most recent call last):
...
HTTPError: HTTP Error 404: 404 Not Found

Anna tries to join a mailing list she’s already a member of.

>>> dump_json('http://localhost:8001/3.0/members', {
...           'fqdn_listname': 'alpha@example.com',
...           'address': 'aperson@example.com',
...           })
Traceback (most recent call last):
...
HTTPError: HTTP Error 409: Member already subscribed

Gwen tries to join the alpha mailing list using an invalid delivery mode.

>>> dump_json('http://localhost:8001/3.0/members', {
...           'fqdn_listname': 'alpha@example.com',
...           'address': 'gperson@example.com',
...           'real_name': 'Gwen Person',
...           'delivery_mode': 'in_digests',
...           })
Traceback (most recent call last):
...
HTTPError: HTTP Error 400: Cannot convert parameters: delivery_mode

Even using an address with “funny” characters Hugh can join the mailing list.

>>> transaction.abort()
>>> dump_json('http://localhost:8001/3.0/members', {
...           'fqdn_listname': 'alpha@example.com',
...           'address': 'hugh/person@example.com',
...           'real_name': 'Hugh Person',
...           })
content-length: 0
date: ...
location: http://localhost:8001/3.0/lists/alpha@example.com/member/hugh%2Fperson@example.com
...

Table Of Contents

Previous topic

Mailing lists

Next topic

Administrivia

This Page