Merge branch 'alias' into 'master'
[mailman.git] / src / mailman / model / domain.py
blobaf470b7a202fcadb07aa5efe19f37a0824b60378
1 # Copyright (C) 2008-2019 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
8 # any later version.
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 # more details.
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18 """Domains."""
20 from mailman.database.model import Model
21 from mailman.database.transaction import dbconnection
22 from mailman.database.types import SAUnicode
23 from mailman.interfaces.domain import (
24 BadDomainSpecificationError, DomainCreatedEvent, DomainCreatingEvent,
25 DomainDeletedEvent, DomainDeletingEvent, IDomain, IDomainManager)
26 from mailman.interfaces.user import IUser
27 from mailman.interfaces.usermanager import IUserManager
28 from mailman.model.mailinglist import MailingList
29 from public import public
30 from sqlalchemy import Column, Integer
31 from sqlalchemy.orm import relationship
32 from zope.component import getUtility
33 from zope.event import notify
34 from zope.interface import implementer
37 @public
38 @implementer(IDomain)
39 class Domain(Model):
40 """Domains."""
42 __tablename__ = 'domain'
44 id = Column(Integer, primary_key=True)
46 mail_host = Column(SAUnicode)
47 description = Column(SAUnicode)
48 owners = relationship('User',
49 secondary='domain_owner',
50 backref='domains')
51 alias_domain = Column(SAUnicode)
53 def __init__(self, mail_host,
54 description=None,
55 owners=None,
56 alias_domain=None):
57 """Create and register a domain.
59 :param mail_host: The host name for the email interface.
60 :type mail_host: string
61 :param description: An optional description of the domain.
62 :type description: string
63 :param owners: Optional owners of this domain.
64 :type owners: sequence of `IUser` or string emails.
65 :param alias_domain: Alternate domain for Postfix
66 :type alias_domain: string
67 """
68 self.mail_host = mail_host
69 self.description = description
70 if owners is not None:
71 self.add_owners(owners)
72 self.alias_domain = alias_domain
74 @property
75 @dbconnection
76 def mailing_lists(self, store):
77 """See `IDomain`."""
78 yield from store.query(MailingList).filter(
79 MailingList.mail_host == self.mail_host
80 ).order_by(MailingList._list_id)
82 def __repr__(self):
83 """repr(a_domain)"""
84 if self.description is None and self.alias_domain is None:
85 return ('<Domain {0.mail_host}>').format(self)
86 elif self.alias_domain is None:
87 return ('<Domain {0.mail_host}, {0.description}>').format(self)
88 elif self.description is None:
89 return ('<Domain {0.mail_host}, '
90 'alias: {0.alias_domain}>').format(self)
91 else:
92 return ('<Domain {0.mail_host}, '
93 '{0.description}, '
94 'alias: {0.alias_domain}>').format(self)
96 def add_owner(self, owner):
97 """See `IDomain`."""
98 user_manager = getUtility(IUserManager)
99 if IUser.providedBy(owner):
100 user = owner
101 else:
102 user = user_manager.get_user(owner)
103 # BAW 2015-04-06: Make sure this path is tested.
104 if user is None:
105 user = user_manager.create_user(owner)
106 self.owners.append(user)
108 def add_owners(self, owners):
109 """See `IDomain`."""
110 # BAW 2015-04-06: This should probably be more efficient by inlining
111 # add_owner().
112 for owner in owners:
113 self.add_owner(owner)
115 def remove_owner(self, owner):
116 """See `IDomain`."""
117 user_manager = getUtility(IUserManager)
118 self.owners.remove(user_manager.get_user(owner))
121 @public
122 @implementer(IDomainManager)
123 class DomainManager:
124 """Domain manager."""
126 @dbconnection
127 def add(self, store,
128 mail_host,
129 description=None,
130 owners=None,
131 alias_domain=None):
132 """See `IDomainManager`."""
133 # Be sure the mail_host is not already registered. This is probably
134 # a constraint that should (also) be maintained in the database.
135 if self.get(mail_host) is not None:
136 raise BadDomainSpecificationError(
137 'Duplicate email host: {}'.format(mail_host))
138 notify(DomainCreatingEvent(mail_host))
139 domain = Domain(mail_host, description, owners, alias_domain)
140 store.add(domain)
141 notify(DomainCreatedEvent(domain))
142 return domain
144 @dbconnection
145 def remove(self, store, mail_host):
146 domain = self[mail_host]
147 notify(DomainDeletingEvent(domain))
148 store.delete(domain)
149 notify(DomainDeletedEvent(mail_host))
150 return domain
152 @dbconnection
153 def get(self, store, mail_host, default=None):
154 """See `IDomainManager`."""
155 domains = store.query(Domain).filter_by(mail_host=mail_host)
156 if domains.count() < 1:
157 return default
158 assert domains.count() == 1, (
159 'Too many matching domains: %s' % mail_host)
160 return domains.one()
162 def __getitem__(self, mail_host):
163 """See `IDomainManager`."""
164 missing = object()
165 domain = self.get(mail_host, missing)
166 if domain is missing:
167 raise KeyError(mail_host)
168 return domain
170 @dbconnection
171 def __len__(self, store):
172 return store.query(Domain).count()
174 @dbconnection
175 def __iter__(self, store):
176 """See `IDomainManager`."""
177 yield from store.query(Domain).order_by(Domain.mail_host).all()
179 @dbconnection
180 def __contains__(self, store, mail_host):
181 """See `IDomainManager`."""
182 return store.query(Domain).filter_by(mail_host=mail_host).count() > 0