diff options
Diffstat (limited to '')
| -rw-r--r-- | src/mailman/rest/docs/membership.rst | 136 | ||||
| -rw-r--r-- | src/mailman/rest/docs/moderation.rst | 361 | ||||
| -rw-r--r-- | src/mailman/rest/docs/post-moderation.rst | 193 | ||||
| -rw-r--r-- | src/mailman/rest/docs/sub-moderation.rst | 110 |
4 files changed, 330 insertions, 470 deletions
diff --git a/src/mailman/rest/docs/membership.rst b/src/mailman/rest/docs/membership.rst index 3cb83a0c8..f3a09cfe9 100644 --- a/src/mailman/rest/docs/membership.rst +++ b/src/mailman/rest/docs/membership.rst @@ -598,14 +598,17 @@ A user can be subscribed to a mailing list via the REST API, either by a specific address, or more generally by their preferred address. A subscribed user is called a member. -Elly wants to subscribes to the `ant` mailing list. Since Elly's email -address is not yet known to Mailman, a user is created for her. By default, -get gets a regular delivery. +The list owner wants to subscribe Elly to the `ant` mailing list. Since +Elly's email address is not yet known to Mailman, a user is created for her. +By default, get gets a regular delivery. >>> dump_json('http://localhost:9001/3.0/members', { ... 'list_id': 'ant.example.com', ... 'subscriber': 'eperson@example.com', ... 'display_name': 'Elly Person', + ... 'pre_verified': True, + ... 'pre_confirmed': True, + ... 'pre_approved': True, ... }) content-length: 0 date: ... @@ -659,6 +662,9 @@ list with her preferred address. >>> dump_json('http://localhost:9001/3.0/members', { ... 'list_id': 'ant.example.com', ... 'subscriber': user_id, + ... 'pre_verified': True, + ... 'pre_confirmed': True, + ... 'pre_approved': True, ... }) content-length: 0 date: ... @@ -729,94 +735,6 @@ Elly is no longer a member of the mailing list. set() -Digest delivery -=============== - -Fred joins the `ant` mailing list but wants MIME digest delivery. -:: - - >>> transaction.abort() - >>> dump_json('http://localhost:9001/3.0/members', { - ... 'list_id': 'ant.example.com', - ... 'subscriber': 'fperson@example.com', - ... 'display_name': 'Fred Person', - ... 'delivery_mode': 'mime_digests', - ... }) - content-length: 0 - date: ... - location: http://localhost:9001/3.0/members/10 - server: ... - status: 201 - - >>> fred = user_manager.get_user('fperson@example.com') - >>> memberships = list(fred.memberships.members) - >>> len(memberships) - 1 - -Fred is getting MIME deliveries. - - >>> memberships[0] - <Member: Fred Person <fperson@example.com> - on ant@example.com as MemberRole.member> - >>> print(memberships[0].delivery_mode) - DeliveryMode.mime_digests - - >>> dump_json('http://localhost:9001/3.0/members/10') - address: http://localhost:9001/3.0/addresses/fperson@example.com - delivery_mode: mime_digests - email: fperson@example.com - http_etag: "..." - list_id: ant.example.com - member_id: 10 - role: member - self_link: http://localhost:9001/3.0/members/10 - user: http://localhost:9001/3.0/users/7 - -Fred wants to change his delivery from MIME digest back to regular delivery. -This can be done by PATCH'ing his member with the `delivery_mode` parameter. -:: - - >>> transaction.abort() - >>> dump_json('http://localhost:9001/3.0/members/10', { - ... 'delivery_mode': 'regular', - ... }, method='PATCH') - content-length: 0 - date: ... - server: ... - status: 204 - - >>> dump_json('http://localhost:9001/3.0/members/10') - address: http://localhost:9001/3.0/addresses/fperson@example.com - delivery_mode: regular - email: fperson@example.com - http_etag: "..." - list_id: ant.example.com - member_id: 10 - role: member - self_link: http://localhost:9001/3.0/members/10 - user: http://localhost:9001/3.0/users/7 - -If a PATCH request changes no attributes, nothing happens. -:: - - >>> dump_json('http://localhost:9001/3.0/members/10', {}, method='PATCH') - content-length: 0 - date: ... - server: ... - status: 204 - - >>> dump_json('http://localhost:9001/3.0/members/10') - address: http://localhost:9001/3.0/addresses/fperson@example.com - delivery_mode: regular - email: fperson@example.com - http_etag: "..." - list_id: ant.example.com - member_id: 10 - role: member - self_link: http://localhost:9001/3.0/members/10 - user: http://localhost:9001/3.0/users/7 - - Changing delivery address ========================= @@ -849,30 +767,30 @@ addresses. >>> dump_json('http://localhost:9001/3.0/members') entry 0: ... - entry 5: + entry 4: address: http://localhost:9001/3.0/addresses/herb@example.com delivery_mode: regular email: herb@example.com http_etag: "..." list_id: ant.example.com - member_id: 11 + member_id: 10 role: member - self_link: http://localhost:9001/3.0/members/11 - user: http://localhost:9001/3.0/users/8 + self_link: http://localhost:9001/3.0/members/10 + user: http://localhost:9001/3.0/users/7 ... - entry 10: + entry 9: address: http://localhost:9001/3.0/addresses/herb@example.com delivery_mode: regular email: herb@example.com http_etag: "..." list_id: bee.example.com - member_id: 12 + member_id: 11 role: member - self_link: http://localhost:9001/3.0/members/12 - user: http://localhost:9001/3.0/users/8 + self_link: http://localhost:9001/3.0/members/11 + user: http://localhost:9001/3.0/users/7 http_etag: "..." start: 0 - total_size: 11 + total_size: 10 In order to change all of his subscriptions to use a different email address, Herb must iterate through his memberships explicitly. @@ -883,13 +801,13 @@ Herb must iterate through his memberships explicitly. >>> memberships = [entry['self_link'] for entry in content['entries']] >>> for url in sorted(memberships): ... print(url) + http://localhost:9001/3.0/members/10 http://localhost:9001/3.0/members/11 - http://localhost:9001/3.0/members/12 For each membership resource, the subscription address is changed by PATCH'ing the `address` attribute. - >>> dump_json('http://localhost:9001/3.0/members/11', { + >>> dump_json('http://localhost:9001/3.0/members/10', { ... 'address': 'hperson@example.com', ... }, method='PATCH') content-length: 0 @@ -897,7 +815,7 @@ the `address` attribute. server: ... status: 204 - >>> dump_json('http://localhost:9001/3.0/members/12', { + >>> dump_json('http://localhost:9001/3.0/members/11', { ... 'address': 'hperson@example.com', ... }, method='PATCH') content-length: 0 @@ -924,20 +842,20 @@ his membership ids have not changed. email: hperson@example.com http_etag: "..." list_id: ant.example.com - member_id: 11 + member_id: 10 role: member - self_link: http://localhost:9001/3.0/members/11 - user: http://localhost:9001/3.0/users/8 + self_link: http://localhost:9001/3.0/members/10 + user: http://localhost:9001/3.0/users/7 entry 1: address: http://localhost:9001/3.0/addresses/hperson@example.com delivery_mode: regular email: hperson@example.com http_etag: "..." list_id: bee.example.com - member_id: 12 + member_id: 11 role: member - self_link: http://localhost:9001/3.0/members/12 - user: http://localhost:9001/3.0/users/8 + self_link: http://localhost:9001/3.0/members/11 + user: http://localhost:9001/3.0/users/7 http_etag: "..." start: 0 total_size: 2 diff --git a/src/mailman/rest/docs/moderation.rst b/src/mailman/rest/docs/moderation.rst deleted file mode 100644 index 0a9fd59a8..000000000 --- a/src/mailman/rest/docs/moderation.rst +++ /dev/null @@ -1,361 +0,0 @@ -========== -Moderation -========== - -There are two kinds of moderation tasks a list administrator may need to -perform. Messages which are held for approval can be accepted, rejected, -discarded, or deferred. Subscription (and sometimes unsubscription) requests -can similarly be accepted, discarded, rejected, or deferred. - - -Message moderation -================== - -Viewing the list of held messages ---------------------------------- - -Held messages can be moderated through the REST API. A mailing list starts -with no held messages. - - >>> ant = create_list('ant@example.com') - >>> transaction.commit() - >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/held') - http_etag: "..." - start: 0 - total_size: 0 - -When a message gets held for moderator approval, it shows up in this list. -:: - - >>> msg = message_from_string("""\ - ... From: anne@example.com - ... To: ant@example.com - ... Subject: Something - ... Message-ID: <alpha> - ... - ... Something else. - ... """) - - >>> from mailman.app.moderator import hold_message - >>> request_id = hold_message(ant, msg, {'extra': 7}, 'Because') - >>> transaction.commit() - - >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/held') - entry 0: - extra: 7 - hold_date: 2005-08-01T07:49:23 - http_etag: "..." - message_id: <alpha> - msg: From: anne@example.com - To: ant@example.com - Subject: Something - Message-ID: <alpha> - X-Message-ID-Hash: GCSMSG43GYWWVUMO6F7FBUSSPNXQCJ6M - <BLANKLINE> - Something else. - <BLANKLINE> - reason: Because - request_id: 1 - sender: anne@example.com - subject: Something - http_etag: "..." - start: 0 - total_size: 1 - -You can get an individual held message by providing the *request id* for that -message. This will include the text of the message. -:: - - >>> def url(request_id): - ... return ('http://localhost:9001/3.0/lists/' - ... 'ant@example.com/held/{0}'.format(request_id)) - - >>> dump_json(url(request_id)) - extra: 7 - hold_date: 2005-08-01T07:49:23 - http_etag: "..." - message_id: <alpha> - msg: From: anne@example.com - To: ant@example.com - Subject: Something - Message-ID: <alpha> - X-Message-ID-Hash: GCSMSG43GYWWVUMO6F7FBUSSPNXQCJ6M - <BLANKLINE> - Something else. - <BLANKLINE> - reason: Because - request_id: 1 - sender: anne@example.com - subject: Something - - -Disposing of held messages --------------------------- - -Individual messages can be moderated through the API by POSTing back to the -held message's resource. The POST data requires an action of one of the -following: - - * discard - throw the message away. - * reject - bounces the message back to the original author. - * defer - defer any action on the message (continue to hold it) - * accept - accept the message for posting. - -Let's see what happens when the above message is deferred. - - >>> dump_json(url(request_id), { - ... 'action': 'defer', - ... }) - content-length: 0 - date: ... - server: ... - status: 204 - -The message is still in the moderation queue. - - >>> dump_json(url(request_id)) - extra: 7 - hold_date: 2005-08-01T07:49:23 - http_etag: "..." - message_id: <alpha> - msg: From: anne@example.com - To: ant@example.com - Subject: Something - Message-ID: <alpha> - X-Message-ID-Hash: GCSMSG43GYWWVUMO6F7FBUSSPNXQCJ6M - <BLANKLINE> - Something else. - <BLANKLINE> - reason: Because - request_id: 1 - sender: anne@example.com - subject: Something - -The held message can be discarded. - - >>> dump_json(url(request_id), { - ... 'action': 'discard', - ... }) - content-length: 0 - date: ... - server: ... - status: 204 - -Messages can also be accepted via the REST API. Let's hold a new message for -moderation. -:: - - >>> del msg['message-id'] - >>> msg['Message-ID'] = '<bravo>' - >>> request_id = hold_message(ant, msg) - >>> transaction.commit() - - >>> results = call_http(url(request_id)) - >>> print(results['message_id']) - <bravo> - - >>> dump_json(url(request_id), { - ... 'action': 'accept', - ... }) - content-length: 0 - date: ... - server: ... - status: 204 - - >>> from mailman.testing.helpers import get_queue_messages - >>> messages = get_queue_messages('pipeline') - >>> len(messages) - 1 - >>> print(messages[0].msg['message-id']) - <bravo> - -Messages can be rejected via the REST API too. These bounce the message back -to the original author. -:: - - >>> del msg['message-id'] - >>> msg['Message-ID'] = '<charlie>' - >>> request_id = hold_message(ant, msg) - >>> transaction.commit() - - >>> results = call_http(url(request_id)) - >>> print(results['message_id']) - <charlie> - - >>> dump_json(url(request_id), { - ... 'action': 'reject', - ... }) - content-length: 0 - date: ... - server: ... - status: 204 - - >>> from mailman.testing.helpers import get_queue_messages - >>> messages = get_queue_messages('virgin') - >>> len(messages) - 1 - >>> print(messages[0].msg['subject']) - Request to mailing list "Ant" rejected - - -Subscription moderation -======================= - -Viewing subscription requests ------------------------------ - -Subscription and unsubscription requests can be moderated via the REST API as -well. A mailing list starts with no pending subscription or unsubscription -requests. - - >>> ant.admin_immed_notify = False - >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests') - http_etag: "..." - start: 0 - total_size: 0 - -When Anne tries to subscribe to the Ant list, her subscription is held for -moderator approval. -:: - - >>> from mailman.app.moderator import hold_subscription - >>> from mailman.interfaces.member import DeliveryMode - >>> from mailman.interfaces.subscriptions import RequestRecord - - >>> sub_req_id = hold_subscription( - ... ant, RequestRecord('anne@example.com', 'Anne Person', - ... DeliveryMode.regular, 'en')) - >>> transaction.commit() - -The subscription request is available from the mailing list. - - >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests') - entry 0: - delivery_mode: regular - display_name: Anne Person - email: anne@example.com - http_etag: "..." - language: en - request_id: ... - type: subscription - when: 2005-08-01T07:49:23 - http_etag: "..." - start: 0 - total_size: 1 - - -Viewing unsubscription requests -------------------------------- - -Bart tries to leave a mailing list, but he may not be allowed to. - - >>> from mailman.testing.helpers import subscribe - >>> from mailman.app.moderator import hold_unsubscription - >>> bart = subscribe(ant, 'Bart', email='bart@example.com') - >>> unsub_req_id = hold_unsubscription(ant, 'bart@example.com') - >>> transaction.commit() - -The unsubscription request is also available from the mailing list. - - >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests') - entry 0: - delivery_mode: regular - display_name: Anne Person - email: anne@example.com - http_etag: "..." - language: en - request_id: ... - type: subscription - when: 2005-08-01T07:49:23 - entry 1: - email: bart@example.com - http_etag: "..." - request_id: ... - type: unsubscription - http_etag: "..." - start: 0 - total_size: 2 - - -Viewing individual requests ---------------------------- - -You can view an individual membership change request by providing the -request id. Anne's subscription request looks like this. - - >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/' - ... 'requests/{}'.format(sub_req_id)) - delivery_mode: regular - display_name: Anne Person - email: anne@example.com - http_etag: "..." - language: en - request_id: ... - type: subscription - when: 2005-08-01T07:49:23 - -Bart's unsubscription request looks like this. - - >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/' - ... 'requests/{}'.format(unsub_req_id)) - email: bart@example.com - http_etag: "..." - request_id: ... - type: unsubscription - - -Disposing of subscription requests ----------------------------------- - -Similar to held messages, you can dispose of held subscription and -unsubscription requests by POSTing back to the request's resource. The POST -data requires an action of one of the following: - - * discard - throw the request away. - * reject - the request is denied and a notification is sent to the email - address requesting the membership change. - * defer - defer any action on this membership change (continue to hold it). - * accept - accept the membership change. - -Anne's subscription request is accepted. - - >>> dump_json('http://localhost:9001/3.0/lists/' - ... 'ant@example.com/requests/{}'.format(sub_req_id), - ... {'action': 'accept'}) - content-length: 0 - date: ... - server: ... - status: 204 - -Anne is now a member of the mailing list. - - >>> transaction.abort() - >>> ant.members.get_member('anne@example.com') - <Member: Anne Person <anne@example.com> on ant@example.com - as MemberRole.member> - >>> transaction.abort() - -Bart's unsubscription request is discarded. - - >>> dump_json('http://localhost:9001/3.0/lists/' - ... 'ant@example.com/requests/{}'.format(unsub_req_id), - ... {'action': 'discard'}) - content-length: 0 - date: ... - server: ... - status: 204 - -Bart is still a member of the mailing list. - - >>> transaction.abort() - >>> print(ant.members.get_member('bart@example.com')) - <Member: Bart Person <bart@example.com> on ant@example.com - as MemberRole.member> - >>> transaction.abort() - -There are no more membership change requests. - - >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests') - http_etag: "..." - start: 0 - total_size: 0 diff --git a/src/mailman/rest/docs/post-moderation.rst b/src/mailman/rest/docs/post-moderation.rst new file mode 100644 index 000000000..6dd96e71a --- /dev/null +++ b/src/mailman/rest/docs/post-moderation.rst @@ -0,0 +1,193 @@ +=============== +Post Moderation +=============== + +Messages which are held for approval can be accepted, rejected, discarded, or +deferred by the list moderators. + + +Viewing the list of held messages +================================= + +Held messages can be moderated through the REST API. A mailing list starts +with no held messages. + + >>> ant = create_list('ant@example.com') + >>> transaction.commit() + >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/held') + http_etag: "..." + start: 0 + total_size: 0 + +When a message gets held for moderator approval, it shows up in this list. +:: + + >>> msg = message_from_string("""\ + ... From: anne@example.com + ... To: ant@example.com + ... Subject: Something + ... Message-ID: <alpha> + ... + ... Something else. + ... """) + + >>> from mailman.app.moderator import hold_message + >>> request_id = hold_message(ant, msg, {'extra': 7}, 'Because') + >>> transaction.commit() + + >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/held') + entry 0: + extra: 7 + hold_date: 2005-08-01T07:49:23 + http_etag: "..." + message_id: <alpha> + msg: From: anne@example.com + To: ant@example.com + Subject: Something + Message-ID: <alpha> + X-Message-ID-Hash: GCSMSG43GYWWVUMO6F7FBUSSPNXQCJ6M + <BLANKLINE> + Something else. + <BLANKLINE> + reason: Because + request_id: 1 + sender: anne@example.com + subject: Something + http_etag: "..." + start: 0 + total_size: 1 + +You can get an individual held message by providing the *request id* for that +message. This will include the text of the message. +:: + + >>> def url(request_id): + ... return ('http://localhost:9001/3.0/lists/' + ... 'ant@example.com/held/{0}'.format(request_id)) + + >>> dump_json(url(request_id)) + extra: 7 + hold_date: 2005-08-01T07:49:23 + http_etag: "..." + message_id: <alpha> + msg: From: anne@example.com + To: ant@example.com + Subject: Something + Message-ID: <alpha> + X-Message-ID-Hash: GCSMSG43GYWWVUMO6F7FBUSSPNXQCJ6M + <BLANKLINE> + Something else. + <BLANKLINE> + reason: Because + request_id: 1 + sender: anne@example.com + subject: Something + + +Disposing of held messages +========================== + +Individual messages can be moderated through the API by POSTing back to the +held message's resource. The POST data requires an action of one of the +following: + + * discard - throw the message away. + * reject - bounces the message back to the original author. + * defer - defer any action on the message (continue to hold it) + * accept - accept the message for posting. + +Let's see what happens when the above message is deferred. + + >>> dump_json(url(request_id), { + ... 'action': 'defer', + ... }) + content-length: 0 + date: ... + server: ... + status: 204 + +The message is still in the moderation queue. + + >>> dump_json(url(request_id)) + extra: 7 + hold_date: 2005-08-01T07:49:23 + http_etag: "..." + message_id: <alpha> + msg: From: anne@example.com + To: ant@example.com + Subject: Something + Message-ID: <alpha> + X-Message-ID-Hash: GCSMSG43GYWWVUMO6F7FBUSSPNXQCJ6M + <BLANKLINE> + Something else. + <BLANKLINE> + reason: Because + request_id: 1 + sender: anne@example.com + subject: Something + +The held message can be discarded. + + >>> dump_json(url(request_id), { + ... 'action': 'discard', + ... }) + content-length: 0 + date: ... + server: ... + status: 204 + +Messages can also be accepted via the REST API. Let's hold a new message for +moderation. +:: + + >>> del msg['message-id'] + >>> msg['Message-ID'] = '<bravo>' + >>> request_id = hold_message(ant, msg) + >>> transaction.commit() + + >>> results = call_http(url(request_id)) + >>> print(results['message_id']) + <bravo> + + >>> dump_json(url(request_id), { + ... 'action': 'accept', + ... }) + content-length: 0 + date: ... + server: ... + status: 204 + + >>> from mailman.testing.helpers import get_queue_messages + >>> messages = get_queue_messages('pipeline') + >>> len(messages) + 1 + >>> print(messages[0].msg['message-id']) + <bravo> + +Messages can be rejected via the REST API too. These bounce the message back +to the original author. +:: + + >>> del msg['message-id'] + >>> msg['Message-ID'] = '<charlie>' + >>> request_id = hold_message(ant, msg) + >>> transaction.commit() + + >>> results = call_http(url(request_id)) + >>> print(results['message_id']) + <charlie> + + >>> dump_json(url(request_id), { + ... 'action': 'reject', + ... }) + content-length: 0 + date: ... + server: ... + status: 204 + + >>> from mailman.testing.helpers import get_queue_messages + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 1 + >>> print(messages[0].msg['subject']) + Request to mailing list "Ant" rejected diff --git a/src/mailman/rest/docs/sub-moderation.rst b/src/mailman/rest/docs/sub-moderation.rst new file mode 100644 index 000000000..52b2b89fd --- /dev/null +++ b/src/mailman/rest/docs/sub-moderation.rst @@ -0,0 +1,110 @@ +========================= + Subscription moderation +========================= + +Subscription (and sometimes unsubscription) requests can similarly be +accepted, discarded, rejected, or deferred by the list moderators. + + +Viewing subscription requests +============================= + +A mailing list starts with no pending subscription or unsubscription requests. + + >>> ant = create_list('ant@example.com') + >>> ant.admin_immed_notify = False + >>> from mailman.interfaces.mailinglist import SubscriptionPolicy + >>> ant.subscription_policy = SubscriptionPolicy.moderate + >>> transaction.commit() + >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests') + http_etag: "..." + start: 0 + total_size: 0 + +When Anne tries to subscribe to the Ant list, her subscription is held for +moderator approval. + + >>> from mailman.interfaces.registrar import IRegistrar + >>> from mailman.interfaces.usermanager import IUserManager + >>> from zope.component import getUtility + >>> registrar = IRegistrar(ant) + >>> manager = getUtility(IUserManager) + >>> anne = manager.create_address('anne@example.com', 'Anne Person') + >>> token, token_owner, member = registrar.register( + ... anne, pre_verified=True, pre_confirmed=True) + >>> print(member) + None + +The message is being held for moderator approval. + + >>> print(token_owner.name) + moderator + +The subscription request can be viewed in the REST API. + + >>> transaction.commit() + >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests') + entry 0: + display_name: Anne Person + email: anne@example.com + http_etag: "..." + list_id: ant.example.com + token: ... + token_owner: moderator + when: 2005-08-01T07:49:23 + http_etag: "..." + start: 0 + total_size: 1 + + +Viewing individual requests +=========================== + +You can view an individual membership change request by providing the token +(a.k.a. request id). Anne's subscription request looks like this. + + >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/' + ... 'requests/{}'.format(token)) + display_name: Anne Person + email: anne@example.com + http_etag: "..." + list_id: ant.example.com + token: ... + token_owner: moderator + when: 2005-08-01T07:49:23 + + +Disposing of subscription requests +================================== + +Moderators can dispose of held subscription requests by POSTing back to the +request's resource. The POST data requires an action of one of the following: + + * discard - throw the request away. + * reject - the request is denied and a notification is sent to the email + address requesting the membership change. + * defer - defer any action on this membership change (continue to hold it). + * accept - accept the membership change. + +Anne's subscription request is accepted. + + >>> dump_json('http://localhost:9001/3.0/lists/' + ... 'ant@example.com/requests/{}'.format(token), + ... {'action': 'accept'}) + content-length: 0 + date: ... + server: ... + status: 204 + +Anne is now a member of the mailing list. + + >>> ant.members.get_member('anne@example.com') + <Member: Anne Person <anne@example.com> on ant@example.com + as MemberRole.member> + +There are no more membership change requests. + + >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests') + http_etag: "..." + start: 0 + total_size: 0 |
