diff options
| author | Barry Warsaw | 2011-01-01 11:28:29 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2011-01-01 11:28:29 -0500 |
| commit | 3f1f5a2826feb9c5fb202ae266ba7f0ff76ebe21 (patch) | |
| tree | 1bab06750e306942180b18383c06ad2804f98677 /src | |
| parent | d0f8e9e03d3c55641165b73a4d8971ec514a9cdc (diff) | |
| download | mailman-3f1f5a2826feb9c5fb202ae266ba7f0ff76ebe21.tar.gz mailman-3f1f5a2826feb9c5fb202ae266ba7f0ff76ebe21.tar.zst mailman-3f1f5a2826feb9c5fb202ae266ba7f0ff76ebe21.zip | |
Diffstat (limited to 'src')
33 files changed, 429 insertions, 352 deletions
diff --git a/src/mailman/app/docs/lifecycle.txt b/src/mailman/app/docs/lifecycle.txt index 43ba1f565..b421d1800 100644 --- a/src/mailman/app/docs/lifecycle.txt +++ b/src/mailman/app/docs/lifecycle.txt @@ -77,20 +77,27 @@ You can also specify a list of owner email addresses. If these addresses are not yet known, they will be registered, and new users will be linked to them. However the addresses are not verified. - >>> owners = ['aperson@example.com', 'bperson@example.com', - ... 'cperson@example.com', 'dperson@example.com'] + >>> owners = [ + ... 'aperson@example.com', + ... 'bperson@example.com', + ... 'cperson@example.com', + ... 'dperson@example.com', + ... ] >>> mlist_2 = create_list('test_2@example.com', owners) >>> print mlist_2.fqdn_listname test_2@example.com >>> print mlist_2.msg_footer test footer - >>> sorted(addr.address for addr in mlist_2.owners.addresses) - [u'aperson@example.com', u'bperson@example.com', - u'cperson@example.com', u'dperson@example.com'] + >>> dump_list(address.email for address in mlist_2.owners.addresses) + aperson@example.com + bperson@example.com + cperson@example.com + dperson@example.com None of the owner addresses are verified. - >>> any(addr.verified_on is not None for addr in mlist_2.owners.addresses) + >>> any(address.verified_on is not None + ... for address in mlist_2.owners.addresses) False However, all addresses are linked to users. @@ -117,8 +124,11 @@ the system, they won't be created again. >>> user_d.real_name = 'Dirk Person' >>> mlist_3 = create_list('test_3@example.com', owners) - >>> sorted(user.real_name for user in mlist_3.owners.users) - [u'Anne Person', u'Bart Person', u'Caty Person', u'Dirk Person'] + >>> dump_list(user.real_name for user in mlist_3.owners.users) + Anne Person + Bart Person + Caty Person + Dirk Person Deleting a list @@ -139,6 +149,8 @@ artifacts. We should now be able to completely recreate the mailing list. >>> mlist_2a = create_list('test_2@example.com', owners) - >>> sorted(addr.address for addr in mlist_2a.owners.addresses) - [u'aperson@example.com', u'bperson@example.com', - u'cperson@example.com', u'dperson@example.com'] + >>> dump_list(address.email for address in mlist_2a.owners.addresses) + aperson@example.com + bperson@example.com + cperson@example.com + dperson@example.com diff --git a/src/mailman/app/registrar.py b/src/mailman/app/registrar.py index c68c98691..d939da4c8 100644 --- a/src/mailman/app/registrar.py +++ b/src/mailman/app/registrar.py @@ -53,15 +53,15 @@ class Registrar: implements(IRegistrar) - def register(self, mlist, address, real_name=None): + def register(self, mlist, email, real_name=None): """See `IUserRegistrar`.""" # First, do validation on the email address. If the address is # invalid, it will raise an exception, otherwise it just returns. - validate(address) + validate(email) # Create a pendable for the registration. pendable = PendableRegistration( type=PendableRegistration.PEND_KEY, - address=address, + email=email, real_name=real_name) pendable['list_name'] = mlist.fqdn_listname token = getUtility(IPendings).add(pendable) @@ -74,12 +74,12 @@ class Registrar: confirm_address = mlist.confirm_address(token) # For i18n interpolation. confirm_url = mlist.domain.confirm_url(token) - email_address = address + email_address = email domain_name = mlist.domain.email_host contact_address = mlist.domain.contact_address # Send a verification email to the address. text = _(resource_string('mailman.templates.en', 'verify.txt')) - msg = UserNotification(address, confirm_address, subject, text) + msg = UserNotification(email, confirm_address, subject, text) msg.send(mlist) return token @@ -90,7 +90,7 @@ class Registrar: if pendable is None: return False missing = object() - address = pendable.get('address', missing) + email = pendable.get('email', missing) real_name = pendable.get('real_name', missing) list_name = pendable.get('list_name', missing) if pendable.get('type') != PendableRegistration.PEND_KEY: @@ -104,41 +104,41 @@ class Registrar: # and an IUser linked to this IAddress. See if any of these objects # currently exist in our database. user_manager = getUtility(IUserManager) - addr = (user_manager.get_address(address) - if address is not missing else None) - user = (user_manager.get_user(address) - if address is not missing else None) + address = (user_manager.get_address(email) + if email is not missing else None) + user = (user_manager.get_user(email) + if email is not missing else None) # If there is neither an address nor a user matching the confirmed # record, then create the user, which will in turn create the address # and link the two together - if addr is None: + if address is None: assert user is None, 'How did we get a user but not an address?' - user = user_manager.create_user(address, real_name) + user = user_manager.create_user(email, real_name) # Because the database changes haven't been flushed, we can't use # IUserManager.get_address() to find the IAddress just created # under the hood. Instead, iterate through the IUser's addresses, # of which really there should be only one. - for addr in user.addresses: - if addr.address == address: + for address in user.addresses: + if address.email == email: break else: raise AssertionError('Could not find expected IAddress') elif user is None: user = user_manager.create_user() user.real_name = real_name - user.link(addr) + user.link(address) else: # The IAddress and linked IUser already exist, so all we need to # do is verify the address. pass - addr.verified_on = datetime.datetime.now() + address.verified_on = datetime.datetime.now() # If this registration is tied to a mailing list, subscribe the person # to the list right now. list_name = pendable.get('list_name') if list_name is not None: mlist = getUtility(IListManager).get(list_name) if mlist: - addr.subscribe(mlist, MemberRole.member) + address.subscribe(mlist, MemberRole.member) return True def discard(self, token): diff --git a/src/mailman/commands/cli_members.py b/src/mailman/commands/cli_members.py index e18b4b686..e3159a589 100644 --- a/src/mailman/commands/cli_members.py +++ b/src/mailman/commands/cli_members.py @@ -156,21 +156,21 @@ class Members: if len(addresses) == 0: print >> fp, mlist.fqdn_listname, 'has no members' return - for address in sorted(addresses, key=attrgetter('address')): + for address in sorted(addresses, key=attrgetter('email')): if args.regular: - member = mlist.members.get_member(address.address) + member = mlist.members.get_member(address.email) if member.delivery_mode != DeliveryMode.regular: continue if args.digest is not None: - member = mlist.members.get_member(address.address) + member = mlist.members.get_member(address.email) if member.delivery_mode not in digest_types: continue if args.nomail is not None: - member = mlist.members.get_member(address.address) + member = mlist.members.get_member(address.email) if member.delivery_status not in status_types: continue print >> fp, formataddr( - (address.real_name, address.original_address)) + (address.real_name, address.original_email)) finally: if fp is not sys.stdout: fp.close() diff --git a/src/mailman/commands/docs/create.txt b/src/mailman/commands/docs/create.txt index 70fb44d8f..31663a851 100644 --- a/src/mailman/commands/docs/create.txt +++ b/src/mailman/commands/docs/create.txt @@ -77,8 +77,8 @@ Setting the owner By default, no list owners are specified. - >>> print list(mlist.owners.addresses) - [] + >>> dump_list(mlist.owners.addresses) + *Empty* But you can specify an owner address on the command line when you create the mailing list. @@ -91,8 +91,8 @@ mailing list. Created mailing list: test4@example.com >>> mlist = list_manager.get('test4@example.com') - >>> print list(mlist.owners.addresses) - [<Address: foo@example.org [not verified] at ...>] + >>> dump_list(repr(address) for address in mlist.owners.addresses) + <Address: foo@example.org [not verified] at ...> You can even specify more than one address for the owners. :: @@ -104,10 +104,10 @@ You can even specify more than one address for the owners. >>> mlist = list_manager.get('test5@example.com') >>> from operator import attrgetter - >>> print sorted(mlist.owners.addresses, key=attrgetter('address')) - [<Address: bar@example.net [not verified] at ...>, - <Address: baz@example.net [not verified] at ...>, - <Address: foo@example.net [not verified] at ...>] + >>> dump_list(repr(address) for address in mlist.owners.addresses) + <Address: bar@example.net [not verified] at ...> + <Address: baz@example.net [not verified] at ...> + <Address: foo@example.net [not verified] at ...> Setting the language diff --git a/src/mailman/commands/docs/members.txt b/src/mailman/commands/docs/members.txt index 602e1bbe5..18a916781 100644 --- a/src/mailman/commands/docs/members.txt +++ b/src/mailman/commands/docs/members.txt @@ -205,8 +205,6 @@ need a file containing email addresses and full names that can be parsed by :: >>> mlist2 = create_list('test2@example.com') - >>> addresses = [ - ... ] >>> import os >>> path = os.path.join(config.VAR_DIR, 'addresses.txt') @@ -221,8 +219,11 @@ need a file containing email addresses and full names that can be parsed by >>> args.listname = [mlist2.fqdn_listname] >>> command.process(args) - >>> sorted(address.address for address in mlist2.members.addresses) - [u'aperson@example.com', u'bperson@example.com', u'cperson@example.com'] + >>> from operator import attrgetter + >>> dump_list(mlist2.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. @@ -244,9 +245,13 @@ taken from standard input. >>> command.process(args) >>> sys.stdin = sys.__stdin__ - >>> sorted(address.address for address in mlist2.members.addresses) - [u'aperson@example.com', u'bperson@example.com', u'cperson@example.com', - u'dperson@example.com', u'eperson@example.com', u'fperson@example.com'] + >>> dump_list(mlist2.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. :: @@ -262,10 +267,15 @@ Blank lines and lines that begin with '#' are ignored. >>> args.input_filename = path >>> command.process(args) - >>> sorted(address.address for address in mlist2.members.addresses) - [u'aperson@example.com', u'bperson@example.com', u'cperson@example.com', - u'dperson@example.com', u'eperson@example.com', u'fperson@example.com', - u'gperson@example.com', u'iperson@example.com'] + >>> dump_list(mlist2.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. @@ -282,10 +292,16 @@ printed. Already subscribed (skipping): gperson@example.com Already subscribed (skipping): aperson@example.com - >>> sorted(address.address for address in mlist2.members.addresses) - [u'aperson@example.com', u'bperson@example.com', u'cperson@example.com', - u'dperson@example.com', u'eperson@example.com', u'fperson@example.com', - u'gperson@example.com', u'iperson@example.com', u'jperson@example.com'] + >>> dump_list(mlist2.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 diff --git a/src/mailman/commands/docs/membership.txt b/src/mailman/commands/docs/membership.txt index 3d611a160..0da7ffadf 100644 --- a/src/mailman/commands/docs/membership.txt +++ b/src/mailman/commands/docs/membership.txt @@ -281,7 +281,7 @@ to unsubscribe Anne from the alpha mailing list. >>> print unicode(results) The results of your email command are provided below. <BLANKLINE> - Invalid or unverified address: anne.person@example.org + Invalid or unverified email address: anne.person@example.org <BLANKLINE> >>> print mlist.members.get_member('anne@example.com') diff --git a/src/mailman/commands/eml_membership.py b/src/mailman/commands/eml_membership.py index 26d96ce3c..7da742b39 100644 --- a/src/mailman/commands/eml_membership.py +++ b/src/mailman/commands/eml_membership.py @@ -144,36 +144,36 @@ class Leave: def process(self, mlist, msg, msgdata, arguments, results): """See `IEmailCommand`.""" - address = msg.sender - if not address: + email = msg.sender + if not email: print >> results, _( - '$self.name: No valid address found to unsubscribe') + '$self.name: No valid email address found to unsubscribe') return ContinueProcessing.no user_manager = getUtility(IUserManager) - user = user_manager.get_user(address) + user = user_manager.get_user(email) if user is None: - print >> results, _('No registered user for address: $address') + print >> results, _('No registered user for email address: $email') return ContinueProcessing.no # The address that the -leave command was sent from, must be verified. # Otherwise you could link a bogus address to anyone's account, and # then send a leave command from that address. - if user_manager.get_address(address).verified_on is None: - print >> results, _('Invalid or unverified address: $address') + if user_manager.get_address(email).verified_on is None: + print >> results, _('Invalid or unverified email address: $email') return ContinueProcessing.no for user_address in user.addresses: # Only recognize verified addresses. if user_address.verified_on is None: continue - member = mlist.members.get_member(user_address.address) + member = mlist.members.get_member(user_address.email) if member is not None: break else: # None of the user's addresses are subscribed to this mailing list. print >> results, _( - '$self.name: $address is not a member of $mlist.fqdn_listname') + '$self.name: $email is not a member of $mlist.fqdn_listname') return ContinueProcessing.no member.unsubscribe() - person = formataddr((user.real_name, address)) + person = formataddr((user.real_name, email)) print >> results, _('$person left $mlist.fqdn_listname') return ContinueProcessing.yes diff --git a/src/mailman/database/mailman.sql b/src/mailman/database/mailman.sql index cfe44c8e1..bb7f9dc57 100644 --- a/src/mailman/database/mailman.sql +++ b/src/mailman/database/mailman.sql @@ -23,7 +23,7 @@ CREATE INDEX ix_acceptablealias_alias CREATE TABLE address ( id INTEGER NOT NULL, - address TEXT, + email TEXT, _original TEXT, real_name TEXT, verified_on TIMESTAMP, diff --git a/src/mailman/interfaces/address.py b/src/mailman/interfaces/address.py index 89a4c5b3b..72d021ae0 100644 --- a/src/mailman/interfaces/address.py +++ b/src/mailman/interfaces/address.py @@ -60,20 +60,20 @@ class InvalidEmailAddressError(AddressError): class IAddress(Interface): """Email address related information.""" - address = Attribute( + email = Attribute( """Read-only text email address.""") - original_address = Attribute( - """Read-only original case-preserved address. + original_email = Attribute( + """Read-only original case-preserved email address. - For almost all intents and purposes, addresses in Mailman are case - insensitive, however because RFC 2821 allows for case sensitive local - parts, Mailman preserves the case of the original address when - emailing the user. + For almost all intents and purposes, email addresses in Mailman are + case insensitive, however because RFC 2821 allows for case sensitive + local parts, Mailman preserves the case of the original email address + when delivering a message to the user. - `original_address` will be the same as address if the original address - was all lower case. Otherwise `original_address` will be the case - preserved address; `address` will always be lower case. + `original_email` will be the same as `email` if the original email + address was all lower case. Otherwise `original_email` will be the + case preserved email address; `email` will always be lower case. """) real_name = Attribute( diff --git a/src/mailman/interfaces/registrar.py b/src/mailman/interfaces/registrar.py index c85a0103c..fefd13698 100644 --- a/src/mailman/interfaces/registrar.py +++ b/src/mailman/interfaces/registrar.py @@ -35,28 +35,29 @@ from zope.interface import Interface class IRegistrar(Interface): - """Interface for registering and verifying addresses and users. + """Interface for registering and verifying email addresses and users. - This is a higher level interface to user registration, address + This is a higher level interface to user registration, email address confirmation, etc. than the IUserManager. The latter does no validation, syntax checking, or confirmation, while this interface does. """ - def register(mlist, address, real_name=None): + def register(mlist, email, real_name=None): """Register the email address, requesting verification. - No IAddress or IUser is created during this step, but after successful - confirmation, it is guaranteed that an IAddress with a linked IUser - will exist. When a verified IAddress matching address already exists, - this method will do nothing, except link a new IUser to the IAddress - if one is not yet associated with the address. + No `IAddress` or `IUser` is created during this step, but after + successful confirmation, it is guaranteed that an `IAddress` with a + linked `IUser` will exist. When a verified `IAddress` matching + `email` already exists, this method will do nothing, except link a new + `IUser` to the `IAddress` if one is not yet associated with the + email address. In all cases, the email address is sanity checked for validity first. :param mlist: The mailing list that is the focus of this registration. :type mlist: `IMailingList` - :param address: The email address to register. - :type address: str + :param email: The email address to register. + :type email: str :param real_name: The optional real name of the user. :type real_name: str :return: The confirmation token string. diff --git a/src/mailman/interfaces/user.py b/src/mailman/interfaces/user.py index 824f6e99c..21ccc460b 100644 --- a/src/mailman/interfaces/user.py +++ b/src/mailman/interfaces/user.py @@ -33,29 +33,31 @@ class IUser(Interface): """A basic user.""" real_name = Attribute( - """This user's Real Name.""") + """This user's real name.""") password = Attribute( """This user's password information.""") addresses = Attribute( - """An iterator over all the IAddresses controlled by this user.""") + """An iterator over all the `IAddresses` controlled by this user.""") memberships = Attribute( """A roster of this user's memberships.""") - def register(address, real_name=None): + def register(email, real_name=None): """Register the given email address and link it to this user. - In this case, 'address' is a text email address, not an IAddress - object. If real_name is not given, the empty string is used. - - Raises AddressAlreadyLinkedError if this IAddress is already linked to - another user. If the corresponding IAddress already exists but is not - linked, then it is simply linked to the user, in which case - real_name is ignored. - - Return the new IAddress object. + :param email: The text email address to register. + :type email: str + :param real_name: The user's real name. If not given the empty string + is used. + :type real_name: str + :return: The address object linked to the user. If the associated + address object already existed (unlinked to a user) then the + `real_name` parameter is ignored. + :rtype: `IAddress` + :raises AddressAlreadyLinkedError: if this `IAddress` is already + linked to another user. """ def link(address): @@ -73,11 +75,13 @@ class IUser(Interface): some other user. """ - def controls(address): + def controls(email): """Determine whether this user controls the given email address. - 'address' is a text email address. This method returns true if the - user controls the given email address, otherwise false. + :param email: The text email address to register. + :type email: str + :return: True if the user controls the given email address. + :rtype: bool """ preferences = Attribute( diff --git a/src/mailman/interfaces/usermanager.py b/src/mailman/interfaces/usermanager.py index 16c45ebcc..5893d74f3 100644 --- a/src/mailman/interfaces/usermanager.py +++ b/src/mailman/interfaces/usermanager.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Interface describing a user manager service.""" +"""Interface describing the user management service.""" from __future__ import absolute_import, unicode_literals @@ -30,67 +30,74 @@ from zope.interface import Interface, Attribute class IUserManager(Interface): - """The interface of a global user manager service. + """The global user management service.""" - Different user managers have different concepts of what a user is, and the - users managed by different IUserManagers are completely independent. This - is how you can separate the user contexts for different domains, on a - multiple domain system. + def create_user(email=None, real_name=None): + """Create and return an `IUser`. - There is one special roster, the null roster ('') which contains all - IUsers in all IRosters. - """ - - def create_user(address=None, real_name=None): - """Create and return an IUser. - - When address is given, an IAddress is also created and linked to the - new IUser object. If the address already exists, an - `ExistingAddressError` is raised. If the address exists but is - already linked to another user, an AddressAlreadyLinkedError is - raised. - - When real_name is given, the IUser's real_name is set to this string. - If an IAddress is also created and linked, its real_name is set to the - same string. + :param email: The text email address for the user being created. + :type email: str + :param real_name: The real name of the user. + :type real_name: str + :return: The newly created user, with the given email address and real + name, if given. + :rtype: `IUser` + :raises ExistingAddressError: when the email address is already + registered. """ def delete_user(user): - """Delete the given IUser.""" + """Delete the given user. - def get_user(address): + :param user: The user to delete. + :type user: `IUser`. + """ + + def get_user(email): """Get the user that controls the given email address, or None. - 'address' is a text email address. + :param email: The email address to look up. + :type email: str + :return: The user found or None. + :rtype: `IUser`. """ users = Attribute( - """An iterator over all the IUsers managed by this user manager.""") + """An iterator over all the `IUsers` managed by this user manager.""") - def create_address(address, real_name=None): - """Create and return an unlinked IAddress object. + def create_address(email, real_name=None): + """Create and return an address unlinked to any user. - address is the text email address. If real_name is not given, it - defaults to the empty string. If the IAddress already exists an - ExistingAddressError is raised. + :param email: The text email address for the address being created. + :type email: str + :param real_name: The real name associated with the address. + :type real_name: str + :return: The newly created address object, with the given email + address and real name, if given. + :rtype: `IAddress` + :raises ExistingAddressError: when the email address is already + registered. """ def delete_address(address): - """Delete the given IAddress object. + """Delete the given `IAddress` object. + + If the `IAddress` is linked to a user, it is first unlinked before it + is deleted. - If this IAddress linked to a user, it is first unlinked before it is - deleted. + :param address: The address to delete. + :type address: `IAddress`. """ - def get_address(address): - """Find and return the `IAddress` matching a text address. + def get_address(email): + """Find and return the `IAddress` matching an email address. - :param address: the text email address - :type address: string + :param email: The text email address. + :type email: str :return: The matching `IAddress` object, or None if no registered - `IAddress` matches the text address + `IAddress` matches the text address. :rtype: `IAddress` or None """ addresses = Attribute( - """An iterator over all the IAddresses managed by this manager.""") + """An iterator over all the `IAddresses` managed by this manager.""") diff --git a/src/mailman/model/address.py b/src/mailman/model/address.py index 6209858a8..d2b87e5b6 100644 --- a/src/mailman/model/address.py +++ b/src/mailman/model/address.py @@ -41,7 +41,7 @@ class Address(Model): implements(IAddress) id = Int(primary=True) - address = Unicode() + email = Unicode() _original = Unicode() real_name = Unicode() verified_on = DateTime() @@ -52,15 +52,15 @@ class Address(Model): preferences_id = Int() preferences = Reference(preferences_id, 'Preferences.id') - def __init__(self, address, real_name): + def __init__(self, email, real_name): super(Address, self).__init__() - lower_case = address.lower() - self.address = lower_case + lower_case = email.lower() + self.email = lower_case self.real_name = real_name - self._original = (None if lower_case == address else address) + self._original = (None if lower_case == email else email) def __str__(self): - addr = (self.address if self._original is None else self._original) + addr = (self.email if self._original is None else self._original) return formataddr((self.real_name, addr)) def __repr__(self): @@ -71,7 +71,7 @@ class Address(Model): address_str, verified, id(self)) else: return '<Address: {0} [{1}] key: {2} at {3:#x}>'.format( - address_str, verified, self.address, id(self)) + address_str, verified, self.email, id(self)) def subscribe(self, mailing_list, role): # This member has no preferences by default. @@ -83,7 +83,7 @@ class Address(Model): Member.address == self).one() if member: raise AlreadySubscribedError( - mailing_list.fqdn_listname, self.address, role) + mailing_list.fqdn_listname, self.email, role) member = Member(role=role, mailing_list=mailing_list.fqdn_listname, address=self) @@ -92,5 +92,5 @@ class Address(Model): return member @property - def original_address(self): - return (self.address if self._original is None else self._original) + def original_email(self): + return (self.email if self._original is None else self._original) diff --git a/src/mailman/model/docs/addresses.txt b/src/mailman/model/docs/addresses.txt index aeddb5e31..0ddacb321 100644 --- a/src/mailman/model/docs/addresses.txt +++ b/src/mailman/model/docs/addresses.txt @@ -18,42 +18,45 @@ Creating addresses Addresses are created directly through the user manager, which starts out with no addresses. - >>> sorted(address.address for address in user_manager.addresses) - [] + >>> dump_list(address.email for address in user_manager.addresses) + *Empty* Creating an unlinked email address is straightforward. >>> address_1 = user_manager.create_address('aperson@example.com') - >>> sorted(address.address for address in user_manager.addresses) - [u'aperson@example.com'] + >>> dump_list(address.email for address in user_manager.addresses) + aperson@example.com However, such addresses have no real name. - >>> address_1.real_name - u'' + >>> print address_1.real_name + <BLANKLINE> You can also create an email address object with a real name. >>> address_2 = user_manager.create_address( ... 'bperson@example.com', 'Ben Person') - >>> sorted(address.address for address in user_manager.addresses) - [u'aperson@example.com', u'bperson@example.com'] - >>> sorted(address.real_name for address in user_manager.addresses) - [u'', u'Ben Person'] + >>> dump_list(address.email for address in user_manager.addresses) + aperson@example.com + bperson@example.com + >>> dump_list(address.real_name for address in user_manager.addresses) + <BLANKLINE> + Ben Person The ``str()`` of the address is the RFC 2822 preferred originator format, while the ``repr()`` carries more information. - >>> str(address_2) - 'Ben Person <bperson@example.com>' - >>> repr(address_2) - '<Address: Ben Person <bperson@example.com> [not verified] at 0x...>' + >>> print str(address_2) + Ben Person <bperson@example.com> + >>> print repr(address_2) + <Address: Ben Person <bperson@example.com> [not verified] at 0x...> You can assign real names to existing addresses. >>> address_1.real_name = 'Anne Person' - >>> sorted(address.real_name for address in user_manager.addresses) - [u'Anne Person', u'Ben Person'] + >>> dump_list(address.real_name for address in user_manager.addresses) + Anne Person + Ben Person These addresses are not linked to users, and can be seen by searching the user manager for an associated user. @@ -68,12 +71,16 @@ interface. >>> user_1 = user_manager.create_user( ... 'cperson@example.com', u'Claire Person') - >>> sorted(address.address for address in user_1.addresses) - [u'cperson@example.com'] - >>> sorted(address.address for address in user_manager.addresses) - [u'aperson@example.com', u'bperson@example.com', u'cperson@example.com'] - >>> sorted(address.real_name for address in user_manager.addresses) - [u'Anne Person', u'Ben Person', u'Claire Person'] + >>> dump_list(address.email for address in user_1.addresses) + cperson@example.com + >>> dump_list(address.email for address in user_manager.addresses) + aperson@example.com + bperson@example.com + cperson@example.com + >>> dump_list(address.real_name for address in user_manager.addresses) + Anne Person + Ben Person + Claire Person And now you can find the associated user. @@ -91,26 +98,28 @@ Deleting addresses You can remove an unlinked address from the user manager. >>> user_manager.delete_address(address_1) - >>> sorted(address.address for address in user_manager.addresses) - [u'bperson@example.com', u'cperson@example.com'] - >>> sorted(address.real_name for address in user_manager.addresses) - [u'Ben Person', u'Claire Person'] + >>> dump_list(address.email for address in user_manager.addresses) + bperson@example.com + cperson@example.com + >>> dump_list(address.real_name for address in user_manager.addresses) + Ben Person + Claire Person Deleting a linked address does not delete the user, but it does unlink the address from the user. - >>> sorted(address.address for address in user_1.addresses) - [u'cperson@example.com'] + >>> dump_list(address.email for address in user_1.addresses) + cperson@example.com >>> user_1.controls('cperson@example.com') True >>> address_3 = list(user_1.addresses)[0] >>> user_manager.delete_address(address_3) - >>> sorted(address.address for address in user_1.addresses) - [] + >>> dump_list(address.email for address in user_1.addresses) + *Empty* >>> user_1.controls('cperson@example.com') False - >>> sorted(address.address for address in user_manager.addresses) - [u'bperson@example.com'] + >>> dump_list(address.email for address in user_manager.addresses) + bperson@example.com Registration and validation @@ -153,34 +162,36 @@ subscribed, a role is specified. >>> address_5 = user_manager.create_address( ... 'eperson@example.com', 'Elly Person') - >>> mlist = create_list('_xtext@example.com') + >>> mlist = create_list('test@example.com') >>> from mailman.interfaces.member import MemberRole >>> address_5.subscribe(mlist, MemberRole.owner) <Member: Elly Person <eperson@example.com> on - _xtext@example.com as MemberRole.owner> + test@example.com as MemberRole.owner> >>> address_5.subscribe(mlist, MemberRole.member) <Member: Elly Person <eperson@example.com> on - _xtext@example.com as MemberRole.member> + test@example.com as MemberRole.member> Now Elly is both an owner and a member of the mailing list. - >>> sorted(mlist.owners.members) - [<Member: Elly Person <eperson@example.com> on - _xtext@example.com as MemberRole.owner>] - >>> sorted(mlist.moderators.members) - [] - >>> sorted(mlist.administrators.members) - [<Member: Elly Person <eperson@example.com> on - _xtext@example.com as MemberRole.owner>] - >>> sorted(mlist.members.members) - [<Member: Elly Person <eperson@example.com> on - _xtext@example.com as MemberRole.member>] - >>> sorted(mlist.regular_members.members) - [<Member: Elly Person <eperson@example.com> on - _xtext@example.com as MemberRole.member>] - >>> sorted(mlist.digest_members.members) - [] + >>> def memberkey(member): + ... return member.mailing_list, member.address.email, int(member.role) + >>> dump_list(mlist.owners.members, key=memberkey) + <Member: Elly Person <eperson@example.com> on + test@example.com as MemberRole.owner> + >>> dump_list(mlist.moderators.members, key=memberkey) + *Empty* + >>> dump_list(mlist.administrators.members, key=memberkey) + <Member: Elly Person <eperson@example.com> on + test@example.com as MemberRole.owner> + >>> dump_list(mlist.members.members, key=memberkey) + <Member: Elly Person <eperson@example.com> on + test@example.com as MemberRole.member> + >>> dump_list(mlist.regular_members.members, key=memberkey) + <Member: Elly Person <eperson@example.com> on + test@example.com as MemberRole.member> + >>> dump_list(mlist.digest_members.members, key=memberkey) + *Empty* Case-preserved addresses @@ -198,18 +209,18 @@ The str() of such an address prints the RFC 2822 preferred originator format with the original case-preserved address. The repr() contains all the gory details. - >>> str(address_6) - 'Frank Person <FPERSON@example.com>' - >>> repr(address_6) - '<Address: Frank Person <FPERSON@example.com> [not verified] - key: fperson@example.com at 0x...>' + >>> print str(address_6) + Frank Person <FPERSON@example.com> + >>> print repr(address_6) + <Address: Frank Person <FPERSON@example.com> [not verified] + key: fperson@example.com at 0x...> Both the case-insensitive version of the address and the original -case-preserved version are available on attributes of the IAddress object. +case-preserved version are available on attributes of the `IAddress` object. - >>> print address_6.address + >>> print address_6.email fperson@example.com - >>> print address_6.original_address + >>> print address_6.original_email FPERSON@example.com Because addresses are case-insensitive for all other purposes, you cannot @@ -231,7 +242,7 @@ create an address that differs only in case. You can get the address using either the lower cased version or case-preserved version. In fact, searching for an address is case insensitive. - >>> print user_manager.get_address('fperson@example.com').address + >>> print user_manager.get_address('fperson@example.com').email fperson@example.com - >>> print user_manager.get_address('FPERSON@example.com').address + >>> print user_manager.get_address('FPERSON@example.com').email fperson@example.com diff --git a/src/mailman/model/docs/membership.txt b/src/mailman/model/docs/membership.txt index ebf1b6ebf..00e79d733 100644 --- a/src/mailman/model/docs/membership.txt +++ b/src/mailman/model/docs/membership.txt @@ -85,7 +85,7 @@ Now, both Anne and Bart are list administrators. >>> from operator import attrgetter >>> def dump_members(roster): ... all_addresses = list(member.address for member in roster) - ... sorted_addresses = sorted(all_addresses, key=attrgetter('address')) + ... sorted_addresses = sorted(all_addresses, key=attrgetter('email')) ... dump_list(sorted_addresses) >>> dump_members(mlist.administrators.members) @@ -121,7 +121,7 @@ It's easy to make the list administrators members of the mailing list too. >>> for address in mlist.administrators.addresses: ... member = address.subscribe(mlist, MemberRole.member) ... members.append(member) - >>> dump_list(members, key=attrgetter('address.address')) + >>> dump_list(members, key=attrgetter('address.email')) <Member: Anne Person <aperson@example.com> on test@example.com as MemberRole.member> <Member: Bart Person <bperson@example.com> on @@ -211,9 +211,9 @@ There is also a roster containing all the subscribers of a mailing list, regardless of their role. >>> def sortkey(member): - ... return (member.address.address, int(member.role)) + ... return (member.address.email, int(member.role)) >>> for member in sorted(mlist.subscribers.members, key=sortkey): - ... print member.address.address, member.role + ... print member.address.email, member.role aperson@example.com MemberRole.member aperson@example.com MemberRole.owner bperson@example.com MemberRole.member @@ -242,8 +242,8 @@ postings from that member are handled. By default, owners and moderators are automatically accepted for posting to the mailing list. >>> for member in sorted(mlist.administrators.members, - ... key=attrgetter('address.address')): - ... print member.address.address, member.role, member.moderation_action + ... key=attrgetter('address.email')): + ... print member.address.email, member.role, member.moderation_action aperson@example.com MemberRole.owner Action.accept bperson@example.com MemberRole.moderator Action.accept @@ -251,8 +251,8 @@ By default, members have a *deferred* action which specifies that the posting should go through the normal moderation checks. >>> for member in sorted(mlist.members.members, - ... key=attrgetter('address.address')): - ... print member.address.address, member.role, member.moderation_action + ... key=attrgetter('address.email')): + ... print member.address.email, member.role, member.moderation_action aperson@example.com MemberRole.member Action.defer bperson@example.com MemberRole.member Action.defer cperson@example.com MemberRole.member Action.defer @@ -260,5 +260,5 @@ should go through the normal moderation checks. Postings by nonmembers are held for moderator approval by default. >>> for member in mlist.nonmembers.members: - ... print member.address.address, member.role, member.moderation_action + ... print member.address.email, member.role, member.moderation_action fperson@example.com MemberRole.nonmember Action.hold diff --git a/src/mailman/model/docs/registration.txt b/src/mailman/model/docs/registration.txt index 050a4d0f3..e92c63f52 100644 --- a/src/mailman/model/docs/registration.txt +++ b/src/mailman/model/docs/registration.txt @@ -100,7 +100,7 @@ But this address is waiting for confirmation. >>> pendingdb = getUtility(IPendings) >>> dump_msgdata(pendingdb.confirm(token, expunge=False)) - address : aperson@example.com + email : aperson@example.com list_name: alpha@example.com real_name: Anne Person type : registration @@ -178,8 +178,8 @@ token and uses that to confirm the pending registration. >>> registrar.confirm(token) True -Now, there is an ``IAddress`` in the database matching the address, as well as -an ``IUser`` linked to this address. The ``IAddress`` is verified. +Now, there is an `IAddress` in the database matching the address, as well as +an `IUser` linked to this address. The `IAddress` is verified. >>> found_address = user_manager.get_address('aperson@example.com') >>> found_address @@ -187,7 +187,7 @@ an ``IUser`` linked to this address. The ``IAddress`` is verified. >>> found_user = user_manager.get_user('aperson@example.com') >>> found_user <User "Anne Person" at ...> - >>> found_user.controls(found_address.address) + >>> found_user.controls(found_address.email) True >>> from datetime import datetime >>> isinstance(found_address.verified_on, datetime) @@ -247,8 +247,7 @@ Discarding ========== A confirmation token can also be discarded, say if the user changes his or her -mind about registering. When discarded, no ``IAddress`` or ``IUser`` is -created. +mind about registering. When discarded, no `IAddress` or `IUser` is created. :: >>> token = registrar.register(mlist, 'eperson@example.com', 'Elly Person') @@ -282,8 +281,8 @@ can be used. >>> address.verified_on = datetime.now() >>> from operator import attrgetter - >>> sorted((addr for addr in dperson.addresses), key=attrgetter('address')) - [<Address: Dave Person <dperson@example.com> [verified] at ...>] + >>> dump_list(repr(address) for address in dperson.addresses) + <Address: Dave Person <dperson@example.com> [verified] at ...> >>> dperson.register('david.person@example.com', 'David Person') <Address: David Person <david.person@example.com> [not verified] at ...> >>> token = registrar.register(mlist, 'david.person@example.com') @@ -299,9 +298,9 @@ can be used. True >>> user <User "Dave Person" at ...> - >>> sorted((addr for addr in user.addresses), key=attrgetter('address')) - [<Address: David Person <david.person@example.com> [verified] at ...>, - <Address: Dave Person <dperson@example.com> [verified] at ...>] + >>> dump_list(repr(address) for address in user.addresses) + <Address: Dave Person <dperson@example.com> [verified] at ...> + <Address: David Person <david.person@example.com> [verified] at ...> Corner cases @@ -313,7 +312,7 @@ confirm method will just return False. >>> registrar.confirm(bytes('no token')) False -Likewise, if you try to confirm, through the ``IUserRegistrar`` interface, a +Likewise, if you try to confirm, through the `IUserRegistrar` interface, a token that doesn't match a registration event, you will get ``None``. However, the pending event matched with that token will still be removed. diff --git a/src/mailman/model/docs/requests.txt b/src/mailman/model/docs/requests.txt index 6cca8f602..94c81e1dc 100644 --- a/src/mailman/model/docs/requests.txt +++ b/src/mailman/model/docs/requests.txt @@ -56,8 +56,8 @@ The list's requests database starts out empty. >>> requests.count 0 - >>> list(requests.held_requests) - [] + >>> dump_list(requests.held_requests) + *Empty* At the lowest level, the requests database is very simple. Holding a request requires a request type (as an enum value), a key, and an optional dictionary @@ -697,7 +697,7 @@ Frank Person is now a member of the mailing list. >>> from zope.component import getUtility >>> user_manager = getUtility(IUserManager) - >>> user = user_manager.get_user(member.address.address) + >>> user = user_manager.get_user(member.address.email) >>> print user.real_name Frank Person >>> print user.password diff --git a/src/mailman/model/docs/usermanager.txt b/src/mailman/model/docs/usermanager.txt index a6bd4fed2..7b333248c 100644 --- a/src/mailman/model/docs/usermanager.txt +++ b/src/mailman/model/docs/usermanager.txt @@ -24,10 +24,10 @@ have a password. >>> verifyObject(IUser, user) True - >>> sorted(address.address for address in user.addresses) - [] - >>> user.real_name - u'' + >>> dump_list(address.email for address in user.addresses) + *Empty* + >>> print user.real_name + <BLANKLINE> >>> print user.password None @@ -39,52 +39,59 @@ The user has preferences, but none of them will be specified. A user can be assigned a real name. >>> user.real_name = 'Anne Person' - >>> sorted(user.real_name for user in user_manager.users) - [u'Anne Person'] + >>> dump_list(user.real_name for user in user_manager.users) + Anne Person A user can be assigned a password. >>> user.password = 'secret' - >>> sorted(user.password for user in user_manager.users) - [u'secret'] + >>> dump_list(user.password for user in user_manager.users) + secret You can also create a user with an address to start out with. >>> user_2 = user_manager.create_user('bperson@example.com') >>> verifyObject(IUser, user_2) True - >>> sorted(address.address for address in user_2.addresses) - [u'bperson@example.com'] - >>> sorted(user.real_name for user in user_manager.users) - [u'', u'Anne Person'] + >>> dump_list(address.email for address in user_2.addresses) + bperson@example.com + >>> dump_list(user.real_name for user in user_manager.users) + <BLANKLINE> + Anne Person As above, you can assign a real name to such users. >>> user_2.real_name = 'Ben Person' - >>> sorted(user.real_name for user in user_manager.users) - [u'Anne Person', u'Ben Person'] + >>> dump_list(user.real_name for user in user_manager.users) + Anne Person + Ben Person You can also create a user with just a real name. >>> user_3 = user_manager.create_user(real_name='Claire Person') >>> verifyObject(IUser, user_3) True - >>> sorted(address.address for address in user.addresses) - [] - >>> sorted(user.real_name for user in user_manager.users) - [u'Anne Person', u'Ben Person', u'Claire Person'] + >>> dump_list(address.email for address in user.addresses) + *Empty* + >>> dump_list(user.real_name for user in user_manager.users) + Anne Person + Ben Person + Claire Person Finally, you can create a user with both an address and a real name. >>> user_4 = user_manager.create_user('dperson@example.com', 'Dan Person') >>> verifyObject(IUser, user_3) True - >>> sorted(address.address for address in user_4.addresses) - [u'dperson@example.com'] - >>> sorted(address.real_name for address in user_4.addresses) - [u'Dan Person'] - >>> sorted(user.real_name for user in user_manager.users) - [u'Anne Person', u'Ben Person', u'Claire Person', u'Dan Person'] + >>> dump_list(address.email for address in user_4.addresses) + dperson@example.com + >>> dump_list(address.real_name for address in user_4.addresses) + Dan Person + >>> dump_list(user.real_name for user in user_manager.users) + Anne Person + Ben Person + Claire Person + Dan Person Deleting users @@ -94,8 +101,10 @@ You delete users by going through the user manager. The deleted user is no longer available through the user manager iterator. >>> user_manager.delete_user(user) - >>> sorted(user.real_name for user in user_manager.users) - [u'Ben Person', u'Claire Person', u'Dan Person'] + >>> dump_list(user.real_name for user in user_manager.users) + Ben Person + Claire Person + Dan Person Finding users @@ -107,7 +116,7 @@ that the ``.get_user()`` method takes a string email address, not an ``IAddress`` object. >>> address = list(user_4.addresses)[0] - >>> found_user = user_manager.get_user(address.address) + >>> found_user = user_manager.get_user(address.email) >>> found_user <User "Dan Person" at ...> >>> found_user is user_4 @@ -119,5 +128,5 @@ with it, you will get ``None`` back. >>> print user_manager.get_user('zperson@example.com') None >>> user_4.unlink(address) - >>> print user_manager.get_user(address.address) + >>> print user_manager.get_user(address.email) None diff --git a/src/mailman/model/docs/users.txt b/src/mailman/model/docs/users.txt index b36c250f9..bbfef8391 100644 --- a/src/mailman/model/docs/users.txt +++ b/src/mailman/model/docs/users.txt @@ -21,19 +21,19 @@ Users may have a real name and a password. >>> user_1 = user_manager.create_user() >>> user_1.password = 'my password' >>> user_1.real_name = 'Zoe Person' - >>> sorted(user.real_name for user in user_manager.users) - [u'Zoe Person'] - >>> sorted(user.password for user in user_manager.users) - [u'my password'] + >>> 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 = 'another password' - >>> sorted(user.real_name for user in user_manager.users) - [u'Zoe X. Person'] - >>> sorted(user.password for user in user_manager.users) - [u'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 Users addresses @@ -50,19 +50,25 @@ address on a user object. <Address: Zoe Person <zperson@example.com> [not verified] at 0x...> >>> user_1.register('zperson@example.org') <Address: zperson@example.org [not verified] at 0x...> - >>> sorted(address.address for address in user_1.addresses) - [u'zperson@example.com', u'zperson@example.org'] - >>> sorted(address.real_name for address in user_1.addresses) - [u'', u'Zoe Person'] + >>> 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) - >>> sorted(address.address for address in user_1.addresses) - [u'zperson@example.com', u'zperson@example.net', u'zperson@example.org'] - >>> sorted(address.real_name for address in user_1.addresses) - [u'', u'', u'Zoe Person'] + >>> 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. @@ -74,7 +80,7 @@ But don't try to link an address to more than one user. You can also ask whether a given user controls a given address. - >>> user_1.controls(address_1.address) + >>> user_1.controls(address_1.email) True >>> user_1.controls('bperson@example.com') False @@ -163,8 +169,10 @@ membership role. :: >>> user_1.link(address_1) - >>> sorted(address.address for address in user_1.addresses) - [u'zperson@example.com', u'zperson@example.net', u'zperson@example.org'] + >>> 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') @@ -194,10 +202,10 @@ membership role. >>> len(members) 4 >>> def sortkey(member): - ... return (member.address.address, member.mailing_list, + ... return (member.address.email, member.mailing_list, ... int(member.role)) >>> for member in sorted(members, key=sortkey): - ... print member.address.address, member.mailing_list, member.role + ... 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 diff --git a/src/mailman/model/member.py b/src/mailman/model/member.py index 5dde5c629..8723a3ca4 100644 --- a/src/mailman/model/member.py +++ b/src/mailman/model/member.py @@ -110,7 +110,7 @@ class Member(Model): @property def options_url(self): # XXX Um, this is definitely wrong - return 'http://example.com/' + self.address.address + return 'http://example.com/' + self.address.email def unsubscribe(self): config.db.store.remove(self.preferences) diff --git a/src/mailman/model/roster.py b/src/mailman/model/roster.py index 89ef98531..2b098ff54 100644 --- a/src/mailman/model/roster.py +++ b/src/mailman/model/roster.py @@ -99,7 +99,7 @@ class AbstractRoster: Member, Member.mailing_list == self._mlist.fqdn_listname, Member.role == self.role, - Address.address == address, + Address.email == address, Member.address_id == Address.id) if results.count() == 0: return None @@ -169,7 +169,7 @@ class AdministratorRoster(AbstractRoster): Member.mailing_list == self._mlist.fqdn_listname, Or(Member.role == MemberRole.moderator, Member.role == MemberRole.owner), - Address.address == address, + Address.email == address, Member.address_id == Address.id) if results.count() == 0: return None diff --git a/src/mailman/model/user.py b/src/mailman/model/user.py index 327a3b184..9aa751ea1 100644 --- a/src/mailman/model/user.py +++ b/src/mailman/model/user.py @@ -66,28 +66,28 @@ class User(Model): raise AddressNotLinkedError(address) address.user = None - def controls(self, address): + def controls(self, email): """See `IUser`.""" - found = config.db.store.find(Address, address=address) + found = config.db.store.find(Address, email=email) if found.count() == 0: return False assert found.count() == 1, 'Unexpected count' return found[0].user is self - def register(self, address, real_name=None): + def register(self, email, real_name=None): """See `IUser`.""" # First, see if the address already exists - addrobj = config.db.store.find(Address, address=address).one() - if addrobj is None: + address = config.db.store.find(Address, email=email).one() + if address is None: if real_name is None: real_name = '' - addrobj = Address(address=address, real_name=real_name) - addrobj.preferences = Preferences() + address = Address(email=email, real_name=real_name) + address.preferences = Preferences() # Link the address to the user if it is not already linked. - if addrobj.user is not None: - raise AddressAlreadyLinkedError(addrobj) - addrobj.user = self - return addrobj + if address.user is not None: + raise AddressAlreadyLinkedError(address) + address.user = self + return address @property def memberships(self): diff --git a/src/mailman/model/usermanager.py b/src/mailman/model/usermanager.py index da12ba33c..11e9ad24e 100644 --- a/src/mailman/model/usermanager.py +++ b/src/mailman/model/usermanager.py @@ -39,13 +39,12 @@ from mailman.model.user import User class UserManager: implements(IUserManager) - def create_user(self, address=None, real_name=None): + def create_user(self, email=None, real_name=None): user = User() user.real_name = ('' if real_name is None else real_name) - if address: - addrobj = Address(address, user.real_name) - addrobj.preferences = Preferences() - user.link(addrobj) + if email: + address = self.create_address(email, real_name) + user.link(address) user.preferences = Preferences() config.db.store.add(user) return user @@ -53,13 +52,8 @@ class UserManager: def delete_user(self, user): config.db.store.remove(user) - @property - def users(self): - for user in config.db.store.find(User): - yield user - - def get_user(self, address): - addresses = config.db.store.find(Address, address=address.lower()) + def get_user(self, email): + addresses = config.db.store.find(Address, email=email.lower()) if addresses.count() == 0: return None elif addresses.count() == 1: @@ -67,17 +61,22 @@ class UserManager: else: raise AssertionError('Unexpected query count') - def create_address(self, address, real_name=None): - addresses = config.db.store.find(Address, address=address.lower()) + @property + def users(self): + for user in config.db.store.find(User): + yield user + + def create_address(self, email, real_name=None): + addresses = config.db.store.find(Address, email=email.lower()) if addresses.count() == 1: found = addresses[0] - raise ExistingAddressError(found.original_address) + raise ExistingAddressError(found.original_email) assert addresses.count() == 0, 'Unexpected results' if real_name is None: real_name = '' - # It's okay not to lower case the 'address' argument because the + # It's okay not to lower case the 'email' argument because the # constructor will do the right thing. - address = Address(address, real_name) + address = Address(email, real_name) address.preferences = Preferences() config.db.store.add(address) return address @@ -89,8 +88,8 @@ class UserManager: address.user.unlink(address) config.db.store.remove(address) - def get_address(self, address): - addresses = config.db.store.find(Address, address=address.lower()) + def get_address(self, email): + addresses = config.db.store.find(Address, email=email.lower()) if addresses.count() == 0: return None elif addresses.count() == 1: diff --git a/src/mailman/pipeline/calculate_recipients.py b/src/mailman/pipeline/calculate_recipients.py index 8ff799f85..3892b3085 100644 --- a/src/mailman/pipeline/calculate_recipients.py +++ b/src/mailman/pipeline/calculate_recipients.py @@ -91,12 +91,12 @@ delivery. The original message as received by Mailman is attached. """) raise errors.RejectMessage(Utils.wrap(text)) # Calculate the regular recipients of the message - recipients = set(member.address.address + recipients = set(member.address.email for member in mlist.regular_members.members if member.delivery_status == DeliveryStatus.enabled) # Remove the sender if they don't want to receive their own posts - if not include_sender and member.address.address in recipients: - recipients.remove(member.address.address) + if not include_sender and member.address.email in recipients: + recipients.remove(member.address.email) # Handle topic classifications do_topic_filters(mlist, msg, msgdata, recipients) # Bookkeeping diff --git a/src/mailman/pipeline/decorate.py b/src/mailman/pipeline/decorate.py index 66ec83e90..4648b17f3 100644 --- a/src/mailman/pipeline/decorate.py +++ b/src/mailman/pipeline/decorate.py @@ -57,12 +57,12 @@ def process(mlist, msg, msgdata): member = mlist.members.get_member(recipient) d['user_address'] = recipient if user is not None and member is not None: - d['user_delivered_to'] = member.address.original_address + d['user_delivered_to'] = member.address.original_email # BAW: Hmm, should we allow this? d['user_password'] = user.password d['user_language'] = member.preferred_language.description d['user_name'] = (user.real_name if user.real_name - else member.address.original_address) + else member.address.original_email) d['user_optionsurl'] = member.options_url # These strings are descriptive for the log file and shouldn't be i18n'd d.update(msgdata.get('decoration-data', {})) diff --git a/src/mailman/pipeline/docs/calc-recips.txt b/src/mailman/pipeline/docs/calc-recips.txt index 27c2ba806..efa1bc9c7 100644 --- a/src/mailman/pipeline/docs/calc-recips.txt +++ b/src/mailman/pipeline/docs/calc-recips.txt @@ -59,8 +59,9 @@ but not all of the recipients. >>> handler = config.handlers['calculate-recipients'] >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recipients']) - [u'qperson@example.com', u'zperson@example.com'] + >>> dump_list(msgdata['recipients']) + qperson@example.com + zperson@example.com Regular delivery recipients @@ -71,8 +72,10 @@ soon as they are posted. In other words, these folks are not digest members. >>> msgdata = {} >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recipients']) - [u'aperson@example.com', u'bperson@example.com', u'cperson@example.com'] + >>> dump_list(msgdata['recipients']) + aperson@example.com + bperson@example.com + cperson@example.com Members can elect not to receive a list copy of their own postings. @@ -84,8 +87,9 @@ Members can elect not to receive a list copy of their own postings. ... """) >>> msgdata = {} >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recipients']) - [u'aperson@example.com', u'bperson@example.com'] + >>> dump_list(msgdata['recipients']) + aperson@example.com + bperson@example.com Members can also elect not to receive a list copy of any message on which they are explicitly named as a recipient. However, see the `avoid duplicates`_ diff --git a/src/mailman/pipeline/docs/file-recips.txt b/src/mailman/pipeline/docs/file-recips.txt index c7eeb9ce8..c994f820e 100644 --- a/src/mailman/pipeline/docs/file-recips.txt +++ b/src/mailman/pipeline/docs/file-recips.txt @@ -30,8 +30,8 @@ returns. <BLANKLINE> A message. <BLANKLINE> - >>> msgdata - {u'recipients': 7} + >>> dump_msgdata(msgdata) + recipients: 7 Missing file @@ -50,8 +50,8 @@ empty. No such file or directory: u'.../_xtest@example.com/members.txt' >>> msgdata = {} >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recipients']) - [] + >>> dump_list(msgdata['recipients']) + *Empty* Existing file @@ -74,9 +74,13 @@ addresses are returned as the set of recipients. >>> msgdata = {} >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recipients']) - ['bperson@example.com', 'cperson@example.com', 'dperson@example.com', - 'eperson@example.com', 'fperson@example.com', 'gperson@example.com'] + >>> dump_list(msgdata['recipients']) + bperson@example.com + cperson@example.com + dperson@example.com + eperson@example.com + fperson@example.com + gperson@example.com However, if the sender of the original message is a member of the list and their address is in the include file, the sender's address is *not* included @@ -99,6 +103,9 @@ in the recipients list. ... """) >>> msgdata = {} >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recipients']) - ['bperson@example.com', 'dperson@example.com', - 'eperson@example.com', 'fperson@example.com', 'gperson@example.com'] + >>> dump_list(msgdata['recipients']) + bperson@example.com + dperson@example.com + eperson@example.com + fperson@example.com + gperson@example.com diff --git a/src/mailman/pipeline/file_recipients.py b/src/mailman/pipeline/file_recipients.py index bd163a283..f4cf7a4c6 100644 --- a/src/mailman/pipeline/file_recipients.py +++ b/src/mailman/pipeline/file_recipients.py @@ -61,5 +61,5 @@ class FileRecipients: # recipients. member = mlist.members.get_member(msg.sender) if member is not None: - addrs.discard(member.address.address) + addrs.discard(member.address.email) msgdata['recipients'] = addrs diff --git a/src/mailman/queue/digest.py b/src/mailman/queue/digest.py index 46228580d..3b2aff11c 100644 --- a/src/mailman/queue/digest.py +++ b/src/mailman/queue/digest.py @@ -337,7 +337,7 @@ class DigestRunner(Runner): continue # Send the digest to the case-preserved address of the digest # members. - email_address = member.address.original_address + email_address = member.address.original_email if member.delivery_mode == DeliveryMode.plaintext_digests: rfc1153_recipients.add(email_address) elif member.delivery_mode == DeliveryMode.mime_digests: @@ -349,9 +349,9 @@ class DigestRunner(Runner): # Add also the folks who are receiving one last digest. for address, delivery_mode in mlist.last_digest_recipients: if delivery_mode == DeliveryMode.plaintext_digests: - rfc1153_recipients.add(address.original_address) + rfc1153_recipients.add(address.original_email) elif delivery_mode == DeliveryMode.mime_digests: - mime_recipients.add(address.original_address) + mime_recipients.add(address.original_email) else: raise AssertionError( 'OLD recipient "{0}" unexpected delivery mode: {1}'.format( diff --git a/src/mailman/queue/docs/incoming.txt b/src/mailman/queue/docs/incoming.txt index b0f644098..75130c4cc 100644 --- a/src/mailman/queue/docs/incoming.txt +++ b/src/mailman/queue/docs/incoming.txt @@ -71,7 +71,7 @@ not linked to a user and are unverified. ... email = '{0}@example.com'.format(localpart) ... address = user_manager.get_address(email) ... print '{0}; verified? {1}; user? {2}'.format( - ... address.address, + ... address.email, ... ('No' if address.verified_on is None else 'Yes'), ... user_manager.get_user(email)) xperson@example.com; verified? No; user? None diff --git a/src/mailman/rest/adapters.py b/src/mailman/rest/adapters.py index 817032ccf..4a3ec9ebd 100644 --- a/src/mailman/rest/adapters.py +++ b/src/mailman/rest/adapters.py @@ -51,7 +51,7 @@ class SubscriptionService: # XXX 2010-02-24 barry Clean this up. # lazr.restful requires the return value to be a concrete list. members = [] - address_of_member = attrgetter('address.address') + address_of_member = attrgetter('address.email') list_manager = getUtility(IListManager) for fqdn_listname in sorted(list_manager.names): mailing_list = list_manager.get(fqdn_listname) diff --git a/src/mailman/rest/members.py b/src/mailman/rest/members.py index 1b3023865..243836fbe 100644 --- a/src/mailman/rest/members.py +++ b/src/mailman/rest/members.py @@ -51,9 +51,9 @@ class _MemberBase(resource.Resource, CollectionMixin): enum, dot, role = str(member.role).partition('.') return dict( fqdn_listname=member.mailing_list, - address=member.address.address, + address=member.address.email, self_link=path_to('lists/{0}/{1}/{2}'.format( - member.mailing_list, role, member.address.address)), + member.mailing_list, role, member.address.email)), ) def _get_collection(self, request): @@ -114,7 +114,7 @@ class AllMembers(_MemberBase): # wsgiref wants headers to be bytes, not unicodes. Also, we have to # quote any unsafe characters in the address. Specifically, we need # to quote forward slashes, but not @-signs. - quoted_address = quote(member.address.address, safe=b'@') + quoted_address = quote(member.address.email, safe=b'@') location = path_to('lists/{0}/member/{1}'.format( member.mailing_list, quoted_address)) # Include no extra headers or body. @@ -140,7 +140,7 @@ class MembersOfList(_MemberBase): # Overrides _MemberBase._get_collection() because we only want to # return the members from the requested roster. roster = self._mlist.get_roster(self._role) - address_of_member = attrgetter('address.address') + address_of_member = attrgetter('address.email') return list(sorted(roster.members, key=address_of_member)) @resource.GET() diff --git a/src/mailman/tests/test_documentation.py b/src/mailman/tests/test_documentation.py index f89979ba2..a8157a18f 100644 --- a/src/mailman/tests/test_documentation.py +++ b/src/mailman/tests/test_documentation.py @@ -110,7 +110,7 @@ def dump_msgdata(msgdata, *additional_skips): print '{0:{2}}: {1}'.format(key, msgdata[key], longest) -def dump_list(list_of_things, key=None): +def dump_list(list_of_things, key=str): """Print items in a string to get rid of stupid u'' prefixes.""" # List of things may be a generator. list_of_things = list(list_of_things) |
