summaryrefslogtreecommitdiff
path: root/src/mailman/model/docs/users.rst
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/model/docs/users.rst')
-rw-r--r--src/mailman/model/docs/users.rst324
1 files changed, 324 insertions, 0 deletions
diff --git a/src/mailman/model/docs/users.rst b/src/mailman/model/docs/users.rst
new file mode 100644
index 000000000..6d0c9a5b0
--- /dev/null
+++ b/src/mailman/model/docs/users.rst
@@ -0,0 +1,324 @@
+=====
+Users
+=====
+
+Users are entities that represent people. A user has a real name and a
+optional encoded password. A user may also have an optional preferences and a
+set of addresses they control. They can even have a *preferred address*,
+i.e. one that they use by default.
+
+See `usermanager.txt`_ for examples of how to create, delete, and find users.
+
+ >>> from mailman.interfaces.usermanager import IUserManager
+ >>> from zope.component import getUtility
+ >>> user_manager = getUtility(IUserManager)
+
+
+User data
+=========
+
+Users may have a real name and a password.
+
+ >>> user_1 = user_manager.create_user()
+ >>> user_1.password = b'my password'
+ >>> user_1.real_name = 'Zoe Person'
+ >>> dump_list(user.real_name for user in user_manager.users)
+ Zoe Person
+ >>> dump_list(user.password for user in user_manager.users)
+ my password
+
+The password and real name can be changed at any time.
+
+ >>> user_1.real_name = 'Zoe X. Person'
+ >>> user_1.password = b'another password'
+ >>> dump_list(user.real_name for user in user_manager.users)
+ Zoe X. Person
+ >>> dump_list(user.password for user in user_manager.users)
+ another password
+
+
+Basic user identification
+=========================
+
+Although rarely visible to users, every user has a unique ID in Mailman, which
+never changes. This ID is generated randomly at the time the user is created,
+and is represented by a UUID.
+
+ >>> print user_1.user_id
+ 00000000-0000-0000-0000-000000000001
+
+The user id cannot change.
+
+ >>> user_1.user_id = 'foo'
+ Traceback (most recent call last):
+ ...
+ AttributeError: can't set attribute
+
+User records also have a date on which they where created.
+
+ # The test suite uses a predictable timestamp.
+ >>> print user_1.created_on
+ 2005-08-01 07:49:23
+
+
+Users addresses
+===============
+
+One of the pieces of information that a user links to is a set of email
+addresses they control, in the form of IAddress objects. A user can control
+many addresses, but addresses may be controlled by only one user.
+
+The easiest way to link a user to an address is to just register the new
+address on a user object.
+
+ >>> user_1.register('zperson@example.com', 'Zoe Person')
+ <Address: Zoe Person <zperson@example.com> [not verified] at 0x...>
+ >>> user_1.register('zperson@example.org')
+ <Address: zperson@example.org [not verified] at 0x...>
+ >>> dump_list(address.email for address in user_1.addresses)
+ zperson@example.com
+ zperson@example.org
+ >>> dump_list(address.real_name for address in user_1.addresses)
+ <BLANKLINE>
+ Zoe Person
+
+You can also create the address separately and then link it to the user.
+
+ >>> address_1 = user_manager.create_address('zperson@example.net')
+ >>> user_1.link(address_1)
+ >>> dump_list(address.email for address in user_1.addresses)
+ zperson@example.com
+ zperson@example.net
+ zperson@example.org
+ >>> dump_list(address.real_name for address in user_1.addresses)
+ <BLANKLINE>
+ <BLANKLINE>
+ Zoe Person
+
+But don't try to link an address to more than one user.
+
+ >>> another_user = user_manager.create_user()
+ >>> another_user.link(address_1)
+ Traceback (most recent call last):
+ ...
+ AddressAlreadyLinkedError: zperson@example.net
+
+You can also ask whether a given user controls a given address.
+
+ >>> user_1.controls(address_1.email)
+ True
+ >>> user_1.controls('bperson@example.com')
+ False
+
+Given a text email address, the user manager can find the user that controls
+that address.
+
+ >>> user_manager.get_user('zperson@example.com') is user_1
+ True
+ >>> user_manager.get_user('zperson@example.net') is user_1
+ True
+ >>> user_manager.get_user('zperson@example.org') is user_1
+ True
+ >>> print user_manager.get_user('bperson@example.com')
+ None
+
+Addresses can also be unlinked from a user.
+
+ >>> user_1.unlink(address_1)
+ >>> user_1.controls('zperson@example.net')
+ False
+ >>> print user_manager.get_user('aperson@example.net')
+ None
+
+But don't try to unlink the address from a user it's not linked to.
+
+ >>> user_1.unlink(address_1)
+ Traceback (most recent call last):
+ ...
+ AddressNotLinkedError: zperson@example.net
+ >>> another_user.unlink(address_1)
+ Traceback (most recent call last):
+ ...
+ AddressNotLinkedError: zperson@example.net
+
+
+Preferred address
+=================
+
+Users can register a preferred address. When subscribing to a mailing list,
+unless some other address is explicitly specified, the user will be subscribed
+with their preferred address. This allows them to change their preferred
+address once, and have all their subscriptions automatically track this
+change.
+
+By default, a user has no preferred address.
+
+ >>> user_2 = user_manager.create_user()
+ >>> print user_2.preferred_address
+ None
+
+Even when a user registers an address, this address will not be set as the
+preferred address.
+
+ >>> anne = user_2.register('anne@example.com', 'Anne Person')
+ >>> print user_2.preferred_address
+ None
+
+The preferred address must be explicitly registered, however only verified
+address may be registered as preferred.
+
+ >>> anne
+ <Address: Anne Person <anne@example.com> [not verified] at ...>
+ >>> user_2.preferred_address = anne
+ Traceback (most recent call last):
+ ...
+ UnverifiedAddressError: Anne Person <anne@example.com>
+
+Once the address has been verified though, it can be set as the preferred
+address, but only if the address is either controlled by the user or
+uncontrolled. In the latter case, setting it as the preferred address makes
+it controlled by the user.
+::
+
+ >>> from mailman.utilities.datetime import now
+ >>> anne.verified_on = now()
+ >>> anne
+ <Address: Anne Person <anne@example.com> [verified] at ...>
+ >>> user_2.controls(anne.email)
+ True
+ >>> user_2.preferred_address = anne
+ >>> user_2.preferred_address
+ <Address: Anne Person <anne@example.com> [verified] at ...>
+
+ >>> aperson = user_manager.create_address('aperson@example.com')
+ >>> user_2.controls(aperson.email)
+ False
+ >>> aperson.verified_on = now()
+ >>> user_2.preferred_address = aperson
+ >>> user_2.controls(aperson.email)
+ True
+
+ >>> zperson = list(user_1.addresses)[0]
+ >>> zperson.verified_on = now()
+ >>> user_2.controls(zperson.email)
+ False
+ >>> user_1.controls(zperson.email)
+ True
+ >>> user_2.preferred_address = zperson
+ Traceback (most recent call last):
+ ...
+ AddressAlreadyLinkedError: Zoe Person <zperson@example.com>
+
+A user can disavow their preferred address.
+
+ >>> user_2.preferred_address
+ <Address: aperson@example.com [verified] at ...>
+ >>> del user_2.preferred_address
+ >>> print user_2.preferred_address
+ None
+
+The preferred address always shows up in the set of addresses controlled by
+this user.
+
+ >>> for address in user_2.addresses:
+ ... print address.email
+ anne@example.com
+ aperson@example.com
+
+
+Users and preferences
+=====================
+
+This is a helper function for the following section.
+
+ >>> def show_prefs(prefs):
+ ... print 'acknowledge_posts :', prefs.acknowledge_posts
+ ... print 'preferred_language :', prefs.preferred_language
+ ... print 'receive_list_copy :', prefs.receive_list_copy
+ ... print 'receive_own_postings :', prefs.receive_own_postings
+ ... print 'delivery_mode :', prefs.delivery_mode
+
+Users have preferences, but these preferences have no default settings.
+
+ >>> from mailman.interfaces.preferences import IPreferences
+ >>> show_prefs(user_1.preferences)
+ acknowledge_posts : None
+ preferred_language : None
+ receive_list_copy : None
+ receive_own_postings : None
+ delivery_mode : None
+
+Some of these preferences are booleans and they can be set to ``True`` or
+``False``.
+::
+
+ >>> from mailman.interfaces.languages import ILanguageManager
+ >>> getUtility(ILanguageManager).add('it', 'iso-8859-1', 'Italian')
+
+ >>> from mailman.core.constants import DeliveryMode
+ >>> prefs = user_1.preferences
+ >>> prefs.acknowledge_posts = True
+ >>> prefs.preferred_language = 'it'
+ >>> prefs.receive_list_copy = False
+ >>> prefs.receive_own_postings = False
+ >>> prefs.delivery_mode = DeliveryMode.regular
+ >>> show_prefs(user_1.preferences)
+ acknowledge_posts : True
+ preferred_language : <Language [it] Italian>
+ receive_list_copy : False
+ receive_own_postings : False
+ delivery_mode : DeliveryMode.regular
+
+
+Subscriptions
+=============
+
+Users know which mailing lists they are subscribed to, regardless of
+membership role.
+::
+
+ >>> user_1.link(address_1)
+ >>> dump_list(address.email for address in user_1.addresses)
+ zperson@example.com
+ zperson@example.net
+ zperson@example.org
+ >>> com = user_manager.get_address('zperson@example.com')
+ >>> org = user_manager.get_address('zperson@example.org')
+ >>> net = user_manager.get_address('zperson@example.net')
+
+ >>> mlist_1 = create_list('xtest_1@example.com')
+ >>> mlist_2 = create_list('xtest_2@example.com')
+ >>> mlist_3 = create_list('xtest_3@example.com')
+ >>> from mailman.interfaces.member import MemberRole
+
+ >>> mlist_1.subscribe(com, MemberRole.member)
+ <Member: Zoe Person <zperson@example.com> on xtest_1@example.com as
+ MemberRole.member>
+ >>> mlist_2.subscribe(org, MemberRole.member)
+ <Member: zperson@example.org on xtest_2@example.com as MemberRole.member>
+ >>> mlist_2.subscribe(org, MemberRole.owner)
+ <Member: zperson@example.org on xtest_2@example.com as MemberRole.owner>
+ >>> mlist_3.subscribe(net, MemberRole.moderator)
+ <Member: zperson@example.net on xtest_3@example.com as
+ MemberRole.moderator>
+
+ >>> memberships = user_1.memberships
+ >>> from mailman.interfaces.roster import IRoster
+ >>> from zope.interface.verify import verifyObject
+ >>> verifyObject(IRoster, memberships)
+ True
+ >>> members = sorted(memberships.members)
+ >>> len(members)
+ 4
+ >>> def sortkey(member):
+ ... return (member.address.email, member.mailing_list,
+ ... int(member.role))
+ >>> for member in sorted(members, key=sortkey):
+ ... print member.address.email, member.mailing_list, member.role
+ zperson@example.com xtest_1@example.com MemberRole.member
+ zperson@example.net xtest_3@example.com MemberRole.moderator
+ zperson@example.org xtest_2@example.com MemberRole.member
+ zperson@example.org xtest_2@example.com MemberRole.owner
+
+
+.. _`usermanager.txt`: usermanager.html