diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/interfaces/domain.py | 9 | ||||
| -rw-r--r-- | src/mailman/model/domain.py | 40 | ||||
| -rw-r--r-- | src/mailman/model/tests/test_domain.py | 97 |
3 files changed, 112 insertions, 34 deletions
diff --git a/src/mailman/interfaces/domain.py b/src/mailman/interfaces/domain.py index ee5eafc78..33d835325 100644 --- a/src/mailman/interfaces/domain.py +++ b/src/mailman/interfaces/domain.py @@ -111,7 +111,7 @@ class IDomain(Interface): class IDomainManager(Interface): """The manager of domains.""" - def add(mail_host, description=None, base_url=None, owner_id=None): + def add(mail_host, description=None, base_url=None, owners=None): """Add a new domain. :param mail_host: The email host name for the domain. @@ -122,9 +122,10 @@ class IDomainManager(Interface): interface of the domain. If not given, it defaults to http://`mail_host`/ :type base_url: string - :param owners: List of owners of the domain, defaults to None - :type owners: list - :return: The new domain object + :param owners: Sequence of owners of the domain, defaults to None, + meaning the domain does not have owners. + :type owners: sequence of `IUser` or string emails. + :return: The new domain object. :rtype: `IDomain` :raises `BadDomainSpecificationError`: when the `mail_host` is already registered. diff --git a/src/mailman/model/domain.py b/src/mailman/model/domain.py index 32ea7db9b..40298c719 100644 --- a/src/mailman/model/domain.py +++ b/src/mailman/model/domain.py @@ -28,12 +28,12 @@ from mailman.database.transaction import dbconnection from mailman.interfaces.domain import ( BadDomainSpecificationError, DomainCreatedEvent, DomainCreatingEvent, DomainDeletedEvent, DomainDeletingEvent, IDomain, IDomainManager) +from mailman.interfaces.user import IUser from mailman.interfaces.usermanager import IUserManager from mailman.model.mailinglist import MailingList -from mailman.model.user import User, DomainOwner from urllib.parse import urljoin, urlparse from sqlalchemy import Column, Integer, Unicode -from sqlalchemy.orm import relationship, backref +from sqlalchemy.orm import relationship from zope.event import notify from zope.interface import implementer from zope.component import getUtility @@ -51,14 +51,14 @@ class Domain(Model): mail_host = Column(Unicode) base_url = Column(Unicode) description = Column(Unicode) - owners = relationship("User", - secondary="domain_owner", - backref="domains") + owners = relationship('User', + secondary='domain_owner', + backref='domains') def __init__(self, mail_host, description=None, base_url=None, - owners=[]): + owners=None): """Create and register a domain. :param mail_host: The host name for the email interface. @@ -69,15 +69,15 @@ class Domain(Model): scheme. If not given, it will be constructed from the `mail_host` using the http protocol. :type base_url: string - :param owners: List of `User` who are the owners of this domain - :type owners: list + :param owners: Optional owners of this domain. + :type owners: sequence of `IUser` or string emails. """ self.mail_host = mail_host self.base_url = (base_url if base_url is not None else 'http://' + mail_host) self.description = description - if len(owners): + if owners is not None: self.add_owners(owners) @property @@ -107,30 +107,37 @@ class Domain(Model): def __repr__(self): """repr(a_domain)""" if self.description is None: - return ('<Domain {0.mail_host}, base_url: {0.base_url}>').format(self) + return ('<Domain {0.mail_host}, base_url: {0.base_url}>').format( + self) else: return ('<Domain {0.mail_host}, {0.description}, ' 'base_url: {0.base_url}>').format(self) def add_owner(self, owner): - """Add a domain owner""" + """See `IDomain`.""" user_manager = getUtility(IUserManager) - user = user_manager.get_user(owner) + if IUser.providedBy(owner): + user = owner + else: + user = user_manager.get_user(owner) + # BAW 2015-04-06: Make sure this path is tested. if user is None: user = user_manager.create_user(owner) self.owners.append(user) def add_owners(self, owners): - """Add multiple owners""" - assert(isinstance(owners, list)) + """See `IDomain`.""" + # BAW 2015-04-06: This should probably be more efficient by inlining + # add_owner(). for owner in owners: self.add_owner(owner) def remove_owner(self, owner): - """ Remove a domain owner""" + """See `IDomain`.""" user_manager = getUtility(IUserManager) self.owners.remove(user_manager.get_user(owner)) + @implementer(IDomainManager) class DomainManager: @@ -141,14 +148,13 @@ class DomainManager: mail_host, description=None, base_url=None, - owners=[]): + owners=None): """See `IDomainManager`.""" # Be sure the mail_host is not already registered. This is probably # a constraint that should (also) be maintained in the database. if self.get(mail_host) is not None: raise BadDomainSpecificationError( 'Duplicate email host: %s' % mail_host) - notify(DomainCreatingEvent(mail_host)) domain = Domain(mail_host, description, base_url, owners) store.add(domain) diff --git a/src/mailman/model/tests/test_domain.py b/src/mailman/model/tests/test_domain.py index 8223aa00b..afde6cd53 100644 --- a/src/mailman/model/tests/test_domain.py +++ b/src/mailman/model/tests/test_domain.py @@ -26,12 +26,11 @@ __all__ = [ import unittest from mailman.app.lifecycle import create_list -from mailman.config import config from mailman.interfaces.domain import ( DomainCreatedEvent, DomainCreatingEvent, DomainDeletedEvent, - DomainDeletingEvent, IDomainManager, BadDomainSpecificationError) -from mailman.interfaces.usermanager import IUserManager + DomainDeletingEvent, IDomainManager) from mailman.interfaces.listmanager import IListManager +from mailman.interfaces.usermanager import IUserManager from mailman.testing.helpers import event_subscribers from mailman.testing.layers import ConfigLayer from zope.component import getUtility @@ -80,25 +79,97 @@ class TestDomainManager(unittest.TestCase): # Trying to delete a missing domain gives you a KeyError. self.assertRaises(KeyError, self._manager.remove, 'doesnotexist.com') - def test_domain_create_with_owner(self): - domain = self._manager.add('example.org', - owners=['someuser@example.org']) + def test_domain_creation_no_default_owners(self): + # If a domain is created without owners, then it has none. + domain = self._manager.add('example.org') + self.assertEqual(len(domain.owners), 0) + + def test_domain_creation_with_owner(self): + # You can create a new domain with a single owner. + domain = self._manager.add('example.org', owners=['anne@example.org']) self.assertEqual(len(domain.owners), 1) self.assertEqual(domain.owners[0].addresses[0].email, - 'someuser@example.org') + 'anne@example.org') + + def test_domain_creation_with_owners(self): + # You can create a new domain with multiple owners. + domain = self._manager.add( + 'example.org', owners=['anne@example.org', + 'bart@example.net']) + self.assertEqual(len(domain.owners), 2) + self.assertEqual( + sorted(owner.addresses[0].email for owner in domain.owners), + ['anne@example.org', 'bart@example.net']) + + def test_domain_creation_creates_new_users(self): + # Domain creation with existing users does not create new users, but + # any user which doesn't yet exist (and is linked to the given + # address), gets created. + user_manager = getUtility(IUserManager) + user_manager.make_user('anne@example.com') + user_manager.make_user('bart@example.com') + domain = self._manager.add( + 'example.org', owners=['anne@example.com', + 'bart@example.com', + 'cris@example.com']) + self.assertEqual(len(domain.owners), 3) + self.assertEqual( + sorted(owner.addresses[0].email for owner in domain.owners), + ['anne@example.com', 'bart@example.com', 'cris@example.com']) + # Now cris exists as a user. + self.assertIsNotNone(user_manager.get_user('cris@example.com')) + + def test_domain_creation_with_users(self): + # Domains can be created with IUser objects. + user_manager = getUtility(IUserManager) + anne = user_manager.make_user('anne@example.com') + bart = user_manager.make_user('bart@example.com') + domain = self._manager.add('example.org', owners=[anne, bart]) + self.assertEqual(len(domain.owners), 2) + self.assertEqual( + sorted(owner.addresses[0].email for owner in domain.owners), + ['anne@example.com', 'bart@example.com']) + def sort_key(owner): + return owner.addresses[0].email + self.assertEqual(sorted(domain.owners, key=sort_key), [anne, bart]) def test_add_domain_owner(self): + # Domain owners can be added after the domain is created. domain = self._manager.add('example.org') - domain.add_owner('someuser@example.org') + self.assertEqual(len(domain.owners), 0) + domain.add_owner('anne@example.org') self.assertEqual(len(domain.owners), 1) self.assertEqual(domain.owners[0].addresses[0].email, - 'someuser@example.org') + 'anne@example.org') - def test_remove_domain_owner(self): - domain = self._manager.add('example.org', - owners=['someuser@example.org']) - domain.remove_owner('someuser@example.org') + def test_add_multiple_domain_owners(self): + # Multiple domain owners can be added after the domain is created. + domain = self._manager.add('example.org') self.assertEqual(len(domain.owners), 0) + domain.add_owners(['anne@example.org', 'bart@example.net']) + self.assertEqual(len(domain.owners), 2) + self.assertEqual([owner.addresses[0].email for owner in domain.owners], + ['anne@example.org', 'bart@example.net']) + + def test_remove_domain_owner(self): + # Domain onwers can be removed. + domain = self._manager.add( + 'example.org', owners=['anne@example.org', + 'bart@example.net']) + domain.remove_owner('anne@example.org') + self.assertEqual(len(domain.owners), 1) + self.assertEqual([owner.addresses[0].email for owner in domain.owners], + ['bart@example.net']) + + def test_remove_missing_owner(self): + # Users which aren't owners can't be removed. + domain = self._manager.add( + 'example.org', owners=['anne@example.org', + 'bart@example.net']) + self.assertRaises(ValueError, domain.remove_owner, 'cris@example.org') + self.assertEqual(len(domain.owners), 2) + self.assertEqual([owner.addresses[0].email for owner in domain.owners], + ['anne@example.org', 'bart@example.net']) |
