Abhilash Raj has proposed merging lp:~raj-abhilash1/mailman/bug_1423756 into
lp:mailman.
Requested reviews:
Mailman Coders (mailman-coders)
For more details, see:
https://code.launchpad.net/~raj-abhilash1/mailman/bug_1423756/+merge/254479
Ability to define a server owner and domain owner.
@barry: I couldn't find where are variables filled in the confirm.txt template
for verification of addresses so two tests related to the same are failing.
--
Your team Mailman Coders is requested to review the proposed merge of
lp:~raj-abhilash1/mailman/bug_1423756 into lp:mailman.
=== modified file 'src/mailman/app/registrar.py'
--- src/mailman/app/registrar.py 2015-01-05 01:22:39 +0000
+++ src/mailman/app/registrar.py 2015-03-28 13:55:44 +0000
@@ -161,8 +161,6 @@
# For i18n interpolation.
confirm_url = mlist.domain.confirm_url(event.token)
email_address = event.pendable['email']
- domain_name = mlist.domain.mail_host
- contact_address = mlist.domain.contact_address
# Send a verification email to the address.
template = getUtility(ITemplateLoader).get(
'mailman:///{0}/{1}/confirm.txt'.format(
=== modified file 'src/mailman/commands/docs/create.rst'
--- src/mailman/commands/docs/create.rst 2014-04-28 15:23:35 +0000
+++ src/mailman/commands/docs/create.rst 2015-03-28 13:55:44 +0000
@@ -44,8 +44,7 @@
>>> from mailman.interfaces.domain import IDomainManager
>>> getUtility(IDomainManager).get('example.xx')
- <Domain example.xx, base_url: http://example.xx,
- contact_address: [email protected]>
+ <Domain example.xx, base_url: http://example.xx>
You can also create mailing lists in existing domains without the
auto-creation flag.
=== modified file 'src/mailman/commands/tests/test_lists.py'
--- src/mailman/commands/tests/test_lists.py 2015-03-14 01:16:51 +0000
+++ src/mailman/commands/tests/test_lists.py 2015-03-28 13:55:44 +0000
@@ -48,7 +48,7 @@
# LP: #1166911 - non-matching lists were returned.
getUtility(IDomainManager).add(
'example.net', 'An example domain.',
- 'http://lists.example.net', '[email protected]')
+ 'http://lists.example.net')
create_list('[email protected]')
create_list('[email protected]')
# Only this one should show up.
=== added file 'src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py'
--- src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py 1970-01-01 00:00:00 +0000
+++ src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py 2015-03-28 13:55:44 +0000
@@ -0,0 +1,45 @@
+"""add_serverowner_domainowner
+
+Revision ID: 46e92facee7
+Revises: 33e1f5f6fa8
+Create Date: 2015-03-20 16:01:25.007242
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '46e92facee7'
+down_revision = '33e1f5f6fa8'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('owner',
+ sa.Column('user_id', sa.Integer(), nullable=False),
+ sa.Column('domain_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['domain_id'], ['domain.id'], ),
+ sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
+ sa.PrimaryKeyConstraint('user_id', 'domain_id')
+ )
+ op.add_column('user', sa.Column('is_server_owner', sa.Boolean(),
+ nullable=True))
+ if op.get_bind().dialect.name != 'sqlite':
+ op.drop_column('domain', 'contact_address')
+ # The migration below may be because the first migration was created
+ # before we changed this attribute to a primary_key
+ op.create_foreign_key('_preferred_address', 'user', 'address',
+ ['_preferred_address_id'], ['id'])
+ ### end Alembic commands ###
+
+
+def downgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('user', 'is_server_owner')
+ if op.get_bind().dialect.name != 'sqlite':
+ op.add_column('domain', sa.Column('contact_address', sa.VARCHAR(),
+ nullable=True))
+ op.drop_constraint('_preferred_address', 'user', type_='foreignkey')
+ op.drop_table('owner')
+ ### end Alembic commands ###
=== modified file 'src/mailman/interfaces/domain.py'
--- src/mailman/interfaces/domain.py 2015-01-05 01:22:39 +0000
+++ src/mailman/interfaces/domain.py 2015-03-28 13:55:44 +0000
@@ -88,9 +88,8 @@
description = Attribute(
'The human readable description of the domain name.')
- contact_address = Attribute("""\
- The contact address for the human at this domain.
- E.g. [email protected]""")
+ owners = Attribute("""\
+ The relationship with the user database representing domain owners""")
mailing_lists = Attribute(
"""All mailing lists for this domain.
@@ -112,7 +111,7 @@
class IDomainManager(Interface):
"""The manager of domains."""
- def add(mail_host, description=None, base_url=None, contact_address=None):
+ def add(mail_host, description=None, base_url=None, owner_id=None):
"""Add a new domain.
:param mail_host: The email host name for the domain.
@@ -123,10 +122,8 @@
interface of the domain. If not given, it defaults to
http://`mail_host`/
:type base_url: string
- :param contact_address: The email contact address for the human
- managing the domain. If not given, defaults to
- postmaster@`mail_host`
- :type contact_address: string
+ :param owner_id: Owner of the domain, defaults to None
+ :type owner_id: integer
:return: The new domain object
:rtype: `IDomain`
:raises `BadDomainSpecificationError`: when the `mail_host` is
=== modified file 'src/mailman/model/docs/domains.rst'
--- src/mailman/model/docs/domains.rst 2014-12-13 18:26:05 +0000
+++ src/mailman/model/docs/domains.rst 2015-03-28 13:55:44 +0000
@@ -9,6 +9,10 @@
>>> 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('[email protected]')
+ >>> config.db.commit()
Domains are how Mailman interacts with email host names and web host names.
::
@@ -28,17 +32,14 @@
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: [email protected]>
+ <Domain example.org, base_url: http://example.org>
>>> show_domains()
- <Domain example.org, base_url: http://example.org,
- contact_address: [email protected]>
+ <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: [email protected]>
+ <Domain example.org, base_url: http://example.org>
>>> show_domains()
no domains
@@ -46,30 +47,34 @@
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: [email protected]>
+ <Domain example.com, base_url: https://mail.example.com>
>>> show_domains()
- <Domain example.com, base_url: https://mail.example.com,
- contact_address: [email protected]>
+ <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='[email protected]',
- ... description='The example domain')
+ ... description='The example domain',
+ ... owner_id=1)
<Domain example.net, The example domain,
- base_url: http://lists.example.net,
- contact_address: [email protected]>
+ base_url: http://lists.example.net>
>>> show_domains()
- <Domain example.com, base_url: https://mail.example.com,
- contact_address: [email protected]>
+ <Domain example.com, base_url: https://mail.example.com>
<Domain example.net, The example domain,
- base_url: http://lists.example.net,
- contact_address: [email protected]>
+ base_url: http://lists.example.net>
+
+Domains can have multiple number of owners, ideally one of the owners
+should have a verified preferred address. However this is not checked
+right now and contact_address from config is used as a fallback.
+::
+
+ >>> net_domain = manager['example.net']
+ >>> net_domain.add_owner(user_manager.get_user('[email protected]'))
+
Domains can list all associated mailing lists with the mailing_lists property.
::
@@ -105,8 +110,7 @@
>>> print(manager['example.net'])
<Domain example.net, The example domain,
- base_url: http://lists.example.net,
- contact_address: [email protected]>
+ 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 @@
>>> print(manager.get('example.net'))
<Domain example.net, The example domain,
- base_url: http://lists.example.net,
- contact_address: [email protected]>
+ base_url: http://lists.example.net>
>>> print(manager.get('doesnotexist.com'))
None
=== modified file 'src/mailman/model/domain.py'
--- src/mailman/model/domain.py 2015-01-05 01:40:47 +0000
+++ src/mailman/model/domain.py 2015-03-28 13:55:44 +0000
@@ -29,8 +29,10 @@
BadDomainSpecificationError, DomainCreatedEvent, DomainCreatingEvent,
DomainDeletedEvent, DomainDeletingEvent, IDomain, IDomainManager)
from mailman.model.mailinglist import MailingList
+from mailman.model.user import User, Owner
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
@@ -47,12 +49,14 @@
mail_host = Column(Unicode) # TODO: add index?
base_url = Column(Unicode)
description = Column(Unicode)
- contact_address = Column(Unicode)
+ owners = relationship("User",
+ secondary="owner",
+ backref="domains")
def __init__(self, mail_host,
description=None,
base_url=None,
- contact_address=None):
+ owner=None):
"""Create and register a domain.
:param mail_host: The host name for the email interface.
@@ -63,18 +67,16 @@
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 owner: The `User` who is the owner of this domain
+ :type owner: mailman.models.user.User
"""
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 owner is not None:
+ self.owners.append(owner)
@property
def url_host(self):
@@ -103,13 +105,19 @@
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"""
+ self.owners.append(owner)
+
+ @dbconnection
+ def remove_owner(self, store, owner):
+ """ Remove a domain owner"""
+ self.owners.remove(owner)
@implementer(IDomainManager)
@@ -121,15 +129,23 @@
mail_host,
description=None,
base_url=None,
- contact_address=None):
+ owner_id=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)
+ # Be sure that the owner exists
+ owner = None
+ if owner_id is not None:
+ owner = store.query(User).get(owner_id)
+ if owner is None:
+ raise BadDomainSpecificationError(
+ 'Owner of this domain does not exist')
+
notify(DomainCreatingEvent(mail_host))
- domain = Domain(mail_host, description, base_url, contact_address)
+ domain = Domain(mail_host, description, base_url, owner)
store.add(domain)
notify(DomainCreatedEvent(domain))
return domain
=== modified file 'src/mailman/model/tests/test_domain.py'
--- src/mailman/model/tests/test_domain.py 2015-01-05 01:22:39 +0000
+++ src/mailman/model/tests/test_domain.py 2015-03-28 13:55:44 +0000
@@ -26,9 +26,11 @@
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,32 @@
# 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):
+ user = getUtility(IUserManager).create_user('[email protected]')
+ config.db.commit()
+ domain = self._manager.add('example.org', owner_id=user.id)
+ self.assertEqual(len(domain.owners), 1)
+ self.assertEqual(domain.owners[0].id, user.id)
+
+ def test_domain_create_with_non_existent_owner(self):
+ with self.assertRaises(BadDomainSpecificationError):
+ self._manager.add('testdomain.org', owner_id=100)
+
+ def test_add_domain_owner(self):
+ user = getUtility(IUserManager).create_user('[email protected]')
+ config.db.commit()
+ domain = self._manager.add('example.org')
+ domain.add_owner(user)
+ self.assertEqual(len(domain.owners), 1)
+ self.assertEqual(domain.owners[0].id, user.id)
+
+ def test_remove_domain_owner(self):
+ user = getUtility(IUserManager).create_user('[email protected]')
+ config.db.commit()
+ domain = self._manager.add('example.org', owner_id=user.id)
+ domain.remove_owner(user)
+ self.assertEqual(len(domain.owners), 0)
+
class TestDomainLifecycleEvents(unittest.TestCase):
@@ -110,3 +138,7 @@
self.assertEqual(listmanager.get('[email protected]'), None)
self.assertEqual(listmanager.get('[email protected]'), ewe)
self.assertEqual(listmanager.get('[email protected]'), fly)
+
+ def test_owners_are_deleted_when_domain_is(self):
+ #TODO: Complete this
+ pass
=== modified file 'src/mailman/model/user.py'
--- src/mailman/model/user.py 2015-03-20 16:38:00 +0000
+++ src/mailman/model/user.py 2015-03-28 13:55:44 +0000
@@ -34,7 +34,7 @@
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 +55,7 @@
_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 +177,11 @@
@property
def memberships(self):
return Memberships(self)
+
+
+class Owner(Model):
+ """Doomain to owners(user) association class"""
+
+ __tablename__ = 'owner'
+ user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
+ domain_id = Column(Integer, ForeignKey('domain.id'), primary_key=True)
=== modified file 'src/mailman/rest/docs/addresses.rst'
--- src/mailman/rest/docs/addresses.rst 2015-02-14 01:35:35 +0000
+++ src/mailman/rest/docs/addresses.rst 2015-03-28 13:55:44 +0000
@@ -190,6 +190,7 @@
created_on: 2005-08-01T07:49:23
display_name: Cris X. Person
http_etag: "..."
+ is_server_owner: False
password: ...
self_link: http://localhost:9001/3.0/users/1
user_id: 1
=== modified file 'src/mailman/rest/docs/domains.rst'
--- src/mailman/rest/docs/domains.rst 2014-12-16 01:01:53 +0000
+++ src/mailman/rest/docs/domains.rst 2015-03-28 13:55:44 +0000
@@ -29,14 +29,12 @@
>>> domain_manager.add(
... 'example.com', 'An example domain', 'http://lists.example.com')
<Domain example.com, An example domain,
- base_url: http://lists.example.com,
- contact_address: [email protected]>
+ base_url: http://lists.example.com>
>>> transaction.commit()
>>> dump_json('http://localhost:9001/3.0/domains')
entry 0:
base_url: http://lists.example.com
- contact_address: [email protected]
description: An example domain
http_etag: "..."
mail_host: example.com
@@ -51,24 +49,19 @@
>>> domain_manager.add(
... 'example.org',
- ... base_url='http://mail.example.org',
- ... contact_address='[email protected]')
- <Domain example.org, base_url: http://mail.example.org,
- contact_address: [email protected]>
+ ... base_url='http://mail.example.org')
+ <Domain example.org, base_url: http://mail.example.org>
>>> domain_manager.add(
... 'lists.example.net',
... 'Porkmasters',
- ... 'http://example.net',
- ... '[email protected]')
+ ... 'http://example.net')
<Domain lists.example.net, Porkmasters,
- base_url: http://example.net,
- contact_address: [email protected]>
+ base_url: http://example.net>
>>> transaction.commit()
>>> dump_json('http://localhost:9001/3.0/domains')
entry 0:
base_url: http://lists.example.com
- contact_address: [email protected]
description: An example domain
http_etag: "..."
mail_host: example.com
@@ -76,7 +69,6 @@
url_host: lists.example.com
entry 1:
base_url: http://mail.example.org
- contact_address: [email protected]
description: None
http_etag: "..."
mail_host: example.org
@@ -84,7 +76,6 @@
url_host: mail.example.org
entry 2:
base_url: http://example.net
- contact_address: [email protected]
description: Porkmasters
http_etag: "..."
mail_host: lists.example.net
@@ -103,7 +94,6 @@
>>> dump_json('http://localhost:9001/3.0/domains/lists.example.net')
base_url: http://example.net
- contact_address: [email protected]
description: Porkmasters
http_etag: "..."
mail_host: lists.example.net
@@ -165,7 +155,6 @@
>>> dump_json('http://localhost:9001/3.0/domains/lists.example.com')
base_url: http://lists.example.com
- contact_address: [email protected]
description: None
http_etag: "..."
mail_host: lists.example.com
@@ -176,9 +165,7 @@
::
>>> domain_manager['lists.example.com']
- <Domain lists.example.com,
- base_url: http://lists.example.com,
- contact_address: [email protected]>
+ <Domain lists.example.com, base_url: http://lists.example.com>
# Unlock the database.
>>> transaction.abort()
@@ -190,8 +177,7 @@
>>> dump_json('http://localhost:9001/3.0/domains', {
... 'mail_host': 'my.example.com',
... 'description': 'My new domain',
- ... 'base_url': 'http://allmy.example.com',
- ... 'contact_address': '[email protected]'
+ ... 'base_url': 'http://allmy.example.com'
... })
content-length: 0
date: ...
@@ -200,7 +186,6 @@
>>> dump_json('http://localhost:9001/3.0/domains/my.example.com')
base_url: http://allmy.example.com
- contact_address: [email protected]
description: My new domain
http_etag: "..."
mail_host: my.example.com
@@ -208,9 +193,7 @@
url_host: allmy.example.com
>>> domain_manager['my.example.com']
- <Domain my.example.com, My new domain,
- base_url: http://allmy.example.com,
- contact_address: [email protected]>
+ <Domain my.example.com, My new domain, base_url: http://allmy.example.com>
# Unlock the database.
>>> transaction.abort()
=== modified file 'src/mailman/rest/docs/users.rst'
--- src/mailman/rest/docs/users.rst 2014-12-22 18:40:30 +0000
+++ src/mailman/rest/docs/users.rst 2015-03-28 13:55:44 +0000
@@ -34,6 +34,7 @@
created_on: 2005-08-01T07:49:23
display_name: Anne Person
http_etag: "..."
+ is_server_owner: False
self_link: http://localhost:9001/3.0/users/1
user_id: 1
http_etag: "..."
@@ -50,11 +51,13 @@
created_on: 2005-08-01T07:49:23
display_name: Anne Person
http_etag: "..."
+ is_server_owner: False
self_link: http://localhost:9001/3.0/users/1
user_id: 1
entry 1:
created_on: 2005-08-01T07:49:23
http_etag: "..."
+ is_server_owner: False
self_link: http://localhost:9001/3.0/users/2
user_id: 2
http_etag: "..."
@@ -76,6 +79,7 @@
created_on: 2005-08-01T07:49:23
display_name: Anne Person
http_etag: "..."
+ is_server_owner: False
self_link: http://localhost:9001/3.0/users/1
user_id: 1
http_etag: "..."
@@ -86,6 +90,7 @@
entry 0:
created_on: 2005-08-01T07:49:23
http_etag: "..."
+ is_server_owner: False
self_link: http://localhost:9001/3.0/users/2
user_id: 2
http_etag: "..."
@@ -120,6 +125,7 @@
>>> dump_json('http://localhost:9001/3.0/users/3')
created_on: 2005-08-01T07:49:23
http_etag: "..."
+ is_server_owner: False
password: {plaintext}...
self_link: http://localhost:9001/3.0/users/3
user_id: 3
@@ -131,6 +137,7 @@
>>> dump_json('http://localhost:9001/3.0/users/[email protected]')
created_on: 2005-08-01T07:49:23
http_etag: "..."
+ is_server_owner: False
password: {plaintext}...
self_link: http://localhost:9001/3.0/users/3
user_id: 3
@@ -158,6 +165,7 @@
created_on: 2005-08-01T07:49:23
display_name: Dave Person
http_etag: "..."
+ is_server_owner: False
password: {plaintext}...
self_link: http://localhost:9001/3.0/users/4
user_id: 4
@@ -190,6 +198,7 @@
created_on: 2005-08-01T07:49:23
display_name: Elly Person
http_etag: "..."
+ is_server_owner: False
password: {plaintext}supersekrit
self_link: http://localhost:9001/3.0/users/5
user_id: 5
@@ -214,6 +223,7 @@
created_on: 2005-08-01T07:49:23
display_name: David Person
http_etag: "..."
+ is_server_owner: False
password: {plaintext}...
self_link: http://localhost:9001/3.0/users/4
user_id: 4
@@ -238,6 +248,7 @@
created_on: 2005-08-01T07:49:23
display_name: David Person
http_etag: "..."
+ is_server_owner: False
password: {plaintext}clockwork angels
self_link: http://localhost:9001/3.0/users/4
user_id: 4
@@ -260,6 +271,7 @@
created_on: 2005-08-01T07:49:23
display_name: David Personhood
http_etag: "..."
+ is_server_owner: False
password: {plaintext}the garden
self_link: http://localhost:9001/3.0/users/4
user_id: 4
@@ -343,6 +355,7 @@
created_on: 2005-08-01T07:49:23
display_name: Fred Person
http_etag: "..."
+ is_server_owner: False
self_link: http://localhost:9001/3.0/users/6
user_id: 6
@@ -350,6 +363,7 @@
created_on: 2005-08-01T07:49:23
display_name: Fred Person
http_etag: "..."
+ is_server_owner: False
self_link: http://localhost:9001/3.0/users/6
user_id: 6
@@ -357,6 +371,7 @@
created_on: 2005-08-01T07:49:23
display_name: Fred Person
http_etag: "..."
+ is_server_owner: False
self_link: http://localhost:9001/3.0/users/6
user_id: 6
@@ -364,6 +379,7 @@
created_on: 2005-08-01T07:49:23
display_name: Fred Person
http_etag: "..."
+ is_server_owner: False
self_link: http://localhost:9001/3.0/users/6
user_id: 6
@@ -382,6 +398,7 @@
created_on: 2005-08-01T07:49:23
display_name: Elly Person
http_etag: "..."
+ is_server_owner: False
password: {plaintext}supersekrit
self_link: http://localhost:9001/3.0/users/5
user_id: 5
=== modified file 'src/mailman/rest/domains.py'
--- src/mailman/rest/domains.py 2015-01-05 01:40:47 +0000
+++ src/mailman/rest/domains.py 2015-03-28 13:55:44 +0000
@@ -29,6 +29,7 @@
BadRequest, CollectionMixin, NotFound, bad_request, child, created, etag,
no_content, not_found, okay, path_to)
from mailman.rest.lists import ListsForDomain
+from mailman.rest.users import OwnersForDomain
from mailman.rest.validator import Validator
from zope.component import getUtility
@@ -41,7 +42,6 @@
"""See `CollectionMixin`."""
return dict(
base_url=domain.base_url,
- contact_address=domain.contact_address,
description=domain.description,
mail_host=domain.mail_host,
self_link=path_to('domains/{0}'.format(domain.mail_host)),
@@ -88,6 +88,17 @@
else:
return BadRequest(), []
+ @child()
+ def owners(self, request, segments):
+ """/domains/<domain>/owners"""
+ if len(segments) == 0:
+ domain = getUtility(IDomainManager).get(self._domain)
+ if domain is None:
+ return NotFound()
+ return OwnersForDomain(domain)
+ else:
+ return BadRequest(), []
+
class AllDomains(_DomainBase):
"""The domains."""
@@ -99,12 +110,12 @@
validator = Validator(mail_host=str,
description=str,
base_url=str,
- contact_address=str,
+ owner_id=int,
_optional=('description', 'base_url',
- 'contact_address'))
+ 'owner_id'))
domain = domain_manager.add(**validator(request))
- except BadDomainSpecificationError:
- bad_request(response, b'Domain exists')
+ except BadDomainSpecificationError as error:
+ bad_request(response, str(error))
except ValueError as error:
bad_request(response, str(error))
else:
=== modified file 'src/mailman/rest/users.py'
--- src/mailman/rest/users.py 2015-03-20 16:38:00 +0000
+++ src/mailman/rest/users.py 2015-03-28 13:55:44 +0000
@@ -67,8 +67,9 @@
email=str,
display_name=str,
password=str,
- _optional=('display_name', 'password'),
- )
+ is_server_owner=bool,
+ _optional=('display_name', 'password', 'is_server_owner'),
+)
@@ -108,7 +109,8 @@
user_id=user_id,
created_on=user.created_on,
self_link=path_to('users/{}'.format(user_id)),
- )
+ is_server_owner=user.is_server_owner,
+ )
# Add the password attribute, only if the user has a password. Same
# with the real name. These could be None or the empty string.
if user.password:
@@ -293,7 +295,8 @@
del fields['email']
fields['user_id'] = int
fields['auto_create'] = as_boolean
- fields['_optional'] = fields['_optional'] + ('user_id', 'auto_create')
+ fields['_optional'] = fields['_optional'] + ('user_id', 'auto_create',
+ 'is_server_owner')
try:
validator = Validator(**fields)
arguments = validator(request)
@@ -328,7 +331,8 @@
# Process post data and check for an existing user.
fields = CREATION_FIELDS.copy()
fields['user_id'] = int
- fields['_optional'] = fields['_optional'] + ('user_id', 'email')
+ fields['_optional'] = fields['_optional'] + ('user_id', 'email',
+ 'is_server_owner')
try:
validator = Validator(**fields)
arguments = validator(request)
@@ -377,3 +381,43 @@
no_content(response)
else:
forbidden(response)
+
+class OwnersForDomain(_UserBase):
+ """Owners for a particular domain."""
+
+ def __init__(self, domain):
+ self._domain = domain
+
+ def on_get(self, request, response):
+ """/domains/<domain>/owners"""
+ resource = self._make_collection(request)
+ okay(response, etag(resource))
+
+ def on_post(self, request, response):
+ """POST to /domains/<domain>/owners """
+ validator = Validator(owner_id=GetterSetter(int))
+ try:
+ values = validator(request)
+ except ValueError as error:
+ bad_request(response, str(error))
+ return
+ owner = getUtility(IUserManager).get_user_by_id(values['owner_id'])
+ self._domain.add_owner(owner)
+ return no_content(response)
+
+ def on_patch(self, request, response):
+ # TODO: complete this
+ pass
+
+ def on_put(self, request, response):
+ # TODO: complete this
+ pass
+
+ def on_delete(self, request, response):
+ # TODO: complete this
+ pass
+
+ @paginate
+ def _get_collection(self, request):
+ """See `CollectionMixin`."""
+ return list(self._domain.owners)
=== modified file 'src/mailman/testing/layers.py'
--- src/mailman/testing/layers.py 2015-01-05 01:22:39 +0000
+++ src/mailman/testing/layers.py 2015-03-28 13:55:44 +0000
@@ -200,7 +200,7 @@
with transaction():
getUtility(IDomainManager).add(
'example.com', 'An example domain.',
- 'http://lists.example.com', '[email protected]')
+ 'http://lists.example.com')
@classmethod
def testTearDown(cls):
_______________________________________________
Mailman-coders mailing list
[email protected]
https://mail.python.org/mailman/listinfo/mailman-coders