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 | |
Because it was just to damn confusing, rename IAddress.address to
IAddress.email and IAddress.original_address to IAddress.original_email. From
now on we'll use "address" to talk about the IAddress object and "email" to
talk about the textual email address.
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) |
