diff options
Diffstat (limited to 'src/mailman/model')
| -rw-r--r-- | src/mailman/model/docs/domains.rst | 49 | ||||
| -rw-r--r-- | src/mailman/model/docs/registration.rst | 2 | ||||
| -rw-r--r-- | src/mailman/model/domain.py | 51 | ||||
| -rw-r--r-- | src/mailman/model/tests/test_domain.py | 24 | ||||
| -rw-r--r-- | src/mailman/model/user.py | 12 |
5 files changed, 97 insertions, 41 deletions
diff --git a/src/mailman/model/docs/domains.rst b/src/mailman/model/docs/domains.rst index abb594a62..ded52f817 100644 --- a/src/mailman/model/docs/domains.rst +++ b/src/mailman/model/docs/domains.rst @@ -9,6 +9,10 @@ Domains >>> manager = getUtility(IDomainManager) >>> manager.remove('example.com') <Domain example.com...> + >>> from mailman.interfaces.usermanager import IUserManager + >>> user_manager = getUtility(IUserManager) + >>> user = user_manager.create_user('test@example.org') + >>> config.db.commit() Domains are how Mailman interacts with email host names and web host names. :: @@ -28,17 +32,14 @@ Adding a domain requires some basic information, of which the email host name is the only required piece. The other parts are inferred from that. >>> manager.add('example.org') - <Domain example.org, base_url: http://example.org, - contact_address: postmaster@example.org> + <Domain example.org, base_url: http://example.org> >>> show_domains() - <Domain example.org, base_url: http://example.org, - contact_address: postmaster@example.org> + <Domain example.org, base_url: http://example.org> We can remove domains too. >>> manager.remove('example.org') - <Domain example.org, base_url: http://example.org, - contact_address: postmaster@example.org> + <Domain example.org, base_url: http://example.org> >>> show_domains() no domains @@ -46,30 +47,34 @@ Sometimes the email host name is different than the base url for hitting the web interface for the domain. >>> manager.add('example.com', base_url='https://mail.example.com') - <Domain example.com, base_url: https://mail.example.com, - contact_address: postmaster@example.com> + <Domain example.com, base_url: https://mail.example.com> >>> show_domains() - <Domain example.com, base_url: https://mail.example.com, - contact_address: postmaster@example.com> + <Domain example.com, base_url: https://mail.example.com> -Domains can have explicit descriptions and contact addresses. +Domains can have explicit descriptions. :: >>> manager.add( ... 'example.net', ... base_url='http://lists.example.net', - ... contact_address='postmaster@example.com', - ... description='The example domain') + ... description='The example domain', + ... owners=['user@domain.com']) <Domain example.net, The example domain, - base_url: http://lists.example.net, - contact_address: postmaster@example.com> + base_url: http://lists.example.net> >>> show_domains() - <Domain example.com, base_url: https://mail.example.com, - contact_address: postmaster@example.com> + <Domain example.com, base_url: https://mail.example.com> <Domain example.net, The example domain, - base_url: http://lists.example.net, - contact_address: postmaster@example.com> + base_url: http://lists.example.net> + +Domains can have multiple owners, ideally one of the owners should have a +verified preferred address. However this is not checked right now and +contact_address from config can be used as a fallback. +:: + + >>> net_domain = manager['example.net'] + >>> net_domain.add_owner('test@example.org') + Domains can list all associated mailing lists with the mailing_lists property. :: @@ -105,8 +110,7 @@ In the global domain manager, domains are indexed by their email host name. >>> print(manager['example.net']) <Domain example.net, The example domain, - base_url: http://lists.example.net, - contact_address: postmaster@example.com> + base_url: http://lists.example.net> As with dictionaries, you can also get the domain. If the domain does not exist, ``None`` or a default is returned. @@ -114,8 +118,7 @@ exist, ``None`` or a default is returned. >>> print(manager.get('example.net')) <Domain example.net, The example domain, - base_url: http://lists.example.net, - contact_address: postmaster@example.com> + base_url: http://lists.example.net> >>> print(manager.get('doesnotexist.com')) None diff --git a/src/mailman/model/docs/registration.rst b/src/mailman/model/docs/registration.rst index 47f9f951d..2d2aa8ec7 100644 --- a/src/mailman/model/docs/registration.rst +++ b/src/mailman/model/docs/registration.rst @@ -120,7 +120,7 @@ message is sent to the user in order to verify the registered address. message. If you think you are being maliciously subscribed to the list, or have any other questions, you may contact <BLANKLINE> - postmaster@example.com + alpha-owner@example.com <BLANKLINE> >>> dump_msgdata(items[0].msgdata) _parsemsg : False diff --git a/src/mailman/model/domain.py b/src/mailman/model/domain.py index 9e627c119..32ea7db9b 100644 --- a/src/mailman/model/domain.py +++ b/src/mailman/model/domain.py @@ -28,11 +28,15 @@ from mailman.database.transaction import dbconnection from mailman.interfaces.domain import ( BadDomainSpecificationError, DomainCreatedEvent, DomainCreatingEvent, DomainDeletedEvent, DomainDeletingEvent, IDomain, IDomainManager) +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 zope.event import notify from zope.interface import implementer +from zope.component import getUtility @@ -44,15 +48,17 @@ class Domain(Model): id = Column(Integer, primary_key=True) - mail_host = Column(Unicode) # TODO: add index? + mail_host = Column(Unicode) base_url = Column(Unicode) description = Column(Unicode) - contact_address = Column(Unicode) + owners = relationship("User", + secondary="domain_owner", + backref="domains") def __init__(self, mail_host, description=None, base_url=None, - contact_address=None): + owners=[]): """Create and register a domain. :param mail_host: The host name for the email interface. @@ -63,18 +69,16 @@ class Domain(Model): scheme. If not given, it will be constructed from the `mail_host` using the http protocol. :type base_url: string - :param contact_address: The email address to contact a human for this - domain. If not given, postmaster@`mail_host` will be used. - :type contact_address: string + :param owners: List of `User` who are the owners of this domain + :type owners: list """ self.mail_host = mail_host self.base_url = (base_url if base_url is not None else 'http://' + mail_host) self.description = description - self.contact_address = (contact_address - if contact_address is not None - else 'postmaster@' + mail_host) + if len(owners): + self.add_owners(owners) @property def url_host(self): @@ -103,13 +107,29 @@ class Domain(Model): def __repr__(self): """repr(a_domain)""" if self.description is None: - return ('<Domain {0.mail_host}, base_url: {0.base_url}, ' - 'contact_address: {0.contact_address}>').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}, ' - 'contact_address: {0.contact_address}>').format(self) + 'base_url: {0.base_url}>').format(self) + def add_owner(self, owner): + """Add a domain owner""" + user_manager = getUtility(IUserManager) + user = user_manager.get_user(owner) + 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)) + for owner in owners: + self.add_owner(owner) + + def remove_owner(self, owner): + """ Remove a domain owner""" + user_manager = getUtility(IUserManager) + self.owners.remove(user_manager.get_user(owner)) @implementer(IDomainManager) @@ -121,15 +141,16 @@ class DomainManager: mail_host, description=None, base_url=None, - contact_address=None): + owners=[]): """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, contact_address) + domain = Domain(mail_host, description, base_url, owners) store.add(domain) notify(DomainCreatedEvent(domain)) return domain diff --git a/src/mailman/model/tests/test_domain.py b/src/mailman/model/tests/test_domain.py index b4a6dd75c..8223aa00b 100644 --- a/src/mailman/model/tests/test_domain.py +++ b/src/mailman/model/tests/test_domain.py @@ -26,9 +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) + DomainDeletingEvent, IDomainManager, BadDomainSpecificationError) +from mailman.interfaces.usermanager import IUserManager from mailman.interfaces.listmanager import IListManager from mailman.testing.helpers import event_subscribers from mailman.testing.layers import ConfigLayer @@ -78,6 +80,26 @@ 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']) + self.assertEqual(len(domain.owners), 1) + self.assertEqual(domain.owners[0].addresses[0].email, + 'someuser@example.org') + + def test_add_domain_owner(self): + domain = self._manager.add('example.org') + domain.add_owner('someuser@example.org') + self.assertEqual(len(domain.owners), 1) + self.assertEqual(domain.owners[0].addresses[0].email, + 'someuser@example.org') + + def test_remove_domain_owner(self): + domain = self._manager.add('example.org', + owners=['someuser@example.org']) + domain.remove_owner('someuser@example.org') + self.assertEqual(len(domain.owners), 0) + class TestDomainLifecycleEvents(unittest.TestCase): diff --git a/src/mailman/model/user.py b/src/mailman/model/user.py index 66197d72e..5fecc1836 100644 --- a/src/mailman/model/user.py +++ b/src/mailman/model/user.py @@ -19,6 +19,7 @@ __all__ = [ 'User', + 'DomainOwner' ] @@ -34,7 +35,7 @@ from mailman.model.preferences import Preferences from mailman.model.roster import Memberships from mailman.utilities.datetime import factory as date_factory from mailman.utilities.uid import UniqueIDFactory -from sqlalchemy import Column, DateTime, ForeignKey, Integer, Unicode +from sqlalchemy import Column, DateTime, ForeignKey, Integer, Unicode, Boolean from sqlalchemy.orm import relationship, backref from zope.event import notify from zope.interface import implementer @@ -55,6 +56,7 @@ class User(Model): _password = Column('password', Unicode) _user_id = Column(UUID, index=True) _created_on = Column(DateTime) + is_server_owner = Column(Boolean, default=False) addresses = relationship( 'Address', backref='user', @@ -176,3 +178,11 @@ class User(Model): @property def memberships(self): return Memberships(self) + + +class DomainOwner(Model): + """Domain to owners(user) association class""" + + __tablename__ = 'domain_owner' + user_id = Column(Integer, ForeignKey('user.id'), primary_key=True) + domain_id = Column(Integer, ForeignKey('domain.id'), primary_key=True) |
