Commit 3d655909 authored by Barry Warsaw's avatar Barry Warsaw

* Make IDomainManager a utility, since the config object is global.

* Give IMailingList a .domain attribute which looks up the IDomain for its
  .host_name.  This cleans up a lot of code.
* Add a test for the 'confirm' email command.
* Suppress blank lines in email command responses.
* Make the IDomainCollection a utility.
parent 24f92b6c
......@@ -30,6 +30,7 @@ import mailbox
from cStringIO import StringIO
from string import Template
from zope.component import getUtility
from mailman import Utils
from mailman.config import config
......@@ -129,7 +130,7 @@ class Archiver:
if self.archive_private:
url = self.GetScriptURL('private') + '/index.html'
else:
domain = IDomainManager(config).get(self.host_name)
domain = getUtility(IDomainManager).get(self.host_name)
web_host = (self.host_name if domain is None else domain.url_host)
url = Template(config.PUBLIC_ARCHIVE_URL).safe_substitute(
listname=self.fqdn_listname,
......
......@@ -53,7 +53,7 @@ def create_list(fqdn_listname, owners=None):
validate(fqdn_listname)
# pylint: disable-msg=W0612
listname, domain = fqdn_listname.split('@', 1)
if domain not in IDomainManager(config):
if domain not in getUtility(IDomainManager):
raise BadDomainSpecificationError(domain)
mlist = getUtility(IListManager).create(fqdn_listname)
for style in config.style_manager.lookup(mlist):
......
......@@ -35,7 +35,6 @@ from zope.interface import implements
from mailman.config import config
from mailman.interfaces.archiver import IArchiver
from mailman.interfaces.domain import IDomainManager
from mailman.utilities.string import expand
......@@ -54,10 +53,9 @@ class MHonArc:
def list_url(mlist):
"""See `IArchiver`."""
# XXX What about private MHonArc archives?
web_host = IDomainManager(config)[mlist.host_name].url_host
return expand(config.archiver.mhonarc.base_url,
dict(listname=mlist.fqdn_listname,
hostname=web_host,
hostname=mlist.domain.url_host,
fqdn_listname=mlist.fqdn_listname,
))
......
......@@ -35,7 +35,6 @@ from zope.interface.interface import adapter_hooks
from mailman.config import config
from mailman.interfaces.archiver import IArchiver, IPipermailMailingList
from mailman.interfaces.domain import IDomainManager
from mailman.interfaces.mailinglist import IMailingList
from mailman.utilities.filesystem import makedirs
from mailman.utilities.string import expand
......@@ -98,10 +97,9 @@ class Pipermail:
if mlist.archive_private:
url = mlist.script_url('private') + '/index.html'
else:
web_host = IDomainManager(config)[mlist.host_name].url_host
return expand(config.archiver.pipermail.base_url,
dict(listname=mlist.fqdn_listname,
hostname=web_host,
hostname=mlist.domain.url_host,
fqdn_listname=mlist.fqdn_listname,
))
......
......@@ -33,7 +33,6 @@ from zope.interface import implements
from mailman.config import config
from mailman.interfaces.archiver import IArchiver
from mailman.interfaces.domain import IDomainManager
......@@ -51,7 +50,7 @@ class Prototype:
@staticmethod
def list_url(mlist):
"""See `IArchiver`."""
return IDomainManager(config)[mlist.host_name].base_url
return mlist.domain.base_url
@staticmethod
def permalink(mlist, msg):
......
......@@ -182,9 +182,9 @@ class Create:
# Check to see if the domain exists or not.
fqdn_listname = args.listname[0]
listname, at, domain = fqdn_listname.partition('@')
domain_mgr = IDomainManager(config)
if domain_mgr.get(domain) is None and args.domain:
domain_mgr.add(domain)
domain_manager = getUtility(IDomainManager)
if domain_manager.get(domain) is None and args.domain:
domain_manager.add(domain)
try:
mlist = create_list(fqdn_listname, args.owners)
except InvalidEmailAddress:
......
......@@ -43,7 +43,7 @@ Now both the domain and the mailing list exist in the database.
<mailing list "[email protected]" at ...>
>>> from mailman.interfaces.domain import IDomainManager
>>> IDomainManager(config).get('example.xx')
>>> getUtility(IDomainManager).get('example.xx')
<Domain example.xx, base_url: http://example.xx,
contact_address: [email protected]>
......
......@@ -20,10 +20,9 @@ line. When there are no mailing lists, a helpful message is displayed.
When there are a few mailing lists, they are shown in alphabetical order by
their fully qualified list names, with a description.
>>> from mailman.config import config
>>> from mailman.interfaces.domain import IDomainManager
>>> domain_mgr = IDomainManager(config)
>>> domain_mgr.add('example.net')
>>> from zope.component import getUtility
>>> getUtility(IDomainManager).add('example.net')
<Domain example.net...>
>>> mlist_1 = create_list('[email protected]')
......
......@@ -80,7 +80,8 @@ first.
>>> from mailman.interfaces.usermanager import IUserManager
>>> from zope.component import getUtility
>>> print getUtility(IUserManager).get_user('[email protected]')
>>> user_manager = getUtility(IUserManager)
>>> print user_manager.get_user('[email protected]')
None
Mailman has sent her the confirmation message.
......@@ -123,11 +124,11 @@ list.
>>> token = str(qmsg['subject']).split()[1].strip()
>>> from mailman.interfaces.domain import IDomainManager
>>> from mailman.interfaces.registrar import IRegistrar
>>> registrar = IRegistrar(IDomainManager(config)['example.com'])
>>> registrar = IRegistrar(getUtility(IDomainManager)['example.com'])
>>> registrar.confirm(token)
True
>>> user = getUtility(IUserManager).get_user('[email protected]')
>>> user = user_manager.get_user('[email protected]')
>>> print user.real_name
Anne Person
>>> list(user.addresses)
......@@ -153,7 +154,7 @@ Joining a second list
Anne of course, is still registered.
>>> print getUtility(IUserManager).get_user('[email protected]')
>>> print user_manager.get_user('[email protected]')
<User "Anne Person" at ...>
But she is not a member of the mailing list.
......@@ -207,7 +208,7 @@ Anne does not need to leave a mailing list with the same email address she's
subscribe with. Any of her registered, linked, and validated email addresses
will do.
>>> anne = getUtility(IUserManager).get_user('[email protected]')
>>> anne = user_manager.get_user('[email protected]')
>>> address = anne.register('[email protected]')
>>> results = Results()
......@@ -260,3 +261,63 @@ unsubscribe her from the list.
Confirmations
=============
Bart wants to join the alpha list, so he sends his subscription request.
>>> msg = message_from_string("""\
... From: Bart Person <[email protected]>
...
... """)
>>> command = config.commands['join']
>>> print command.process(mlist, msg, {}, (), Results())
ContinueProcessing.yes
There are two messages in the virgin queue, one of which is the confirmation
message.
>>> from mailman.testing.helpers import get_queue_messages
>>> for item in get_queue_messages('virgin'):
... subject = str(item.msg['subject'])
... if subject.startswith('confirm'):
... break
... else:
... raise AssertionError('No confirmation message')
>>> token = subject.split()[1].strip()
Bart is still not a user.
>>> print user_manager.get_user('[email protected]')
None
Bart replies to the original message, specifically keeping the Subject header
intact except for any prefix. Mailman matches the token and confirms Bart as
a user of the system.
>>> msg = message_from_string("""\
... From: Bart Person <[email protected]>
... To: [email protected]
... Subject: Re: confirm {token}
...
... """.format(token=token))
>>> command = config.commands['confirm']
>>> results = Results()
>>> print command.process(mlist, msg, {}, (token,), results)
ContinueProcessing.yes
>>> print unicode(results)
The results of your email command are provided below.
<BLANKLINE>
Confirmed
<BLANKLINE>
Now Bart is a user...
>>> print user_manager.get_user('[email protected]')
<User "Bart Person" at ...>
...and a member of the mailing list.
>>> print mlist.members.get_member('[email protected]')
<Member: Bart Person <bar[email protected]>
on [email protected] as MemberRole.member>
......@@ -29,6 +29,7 @@ from zope.interface import implements
from mailman.core.i18n import _
from mailman.interfaces.command import ContinueProcessing, IEmailCommand
from mailman.interfaces.registrar import IRegistrar
......@@ -43,5 +44,13 @@ class Confirm:
def process(self, mlist, msg, msgdata, arguments, results):
"""See `IEmailCommand`."""
print >> results, _('Confirmed')
return ContinueProcessing.yes
# The token must be in the arguments.
if len(arguments) == 0:
print >> results, _('No confirmation token found')
return ContinueProcessing.no
succeeded = IRegistrar(mlist.domain).confirm(arguments[0])
if succeeded:
print >> results, _('Confirmed')
return ContinueProcessing.yes
print >> results, _('Confirmation token did not match')
return ContinueProcessing.no
......@@ -35,7 +35,6 @@ from zope.interface import implements
from mailman.config import config
from mailman.core.i18n import _
from mailman.interfaces.command import ContinueProcessing, IEmailCommand
from mailman.interfaces.domain import IDomainManager
from mailman.interfaces.member import DeliveryMode
from mailman.interfaces.registrar import IRegistrar
from mailman.interfaces.usermanager import IUserManager
......@@ -75,8 +74,7 @@ example:
print >> results, _(
'$self.name: No valid address found to subscribe')
return ContinueProcessing.no
domain = IDomainManager(config)[mlist.host_name]
registrar = IRegistrar(domain)
registrar = IRegistrar(mlist.domain)
registrar.register(address, real_name, mlist)
person = formataddr((real_name, address))
print >> results, _('Confirmation email sent to $person')
......
......@@ -4,8 +4,6 @@
<include package="mailman.rest" file="configure.zcml"/>
<!-- adapters -->
<adapter
for="mailman.interfaces.domain.IDomain"
provides="mailman.interfaces.registrar.IRegistrar"
......@@ -24,13 +22,11 @@
factory="mailman.model.mailinglist.AcceptableAliasSet"
/>
<adapter
for="mailman.config.config.IConfiguration"
provides="mailman.interfaces.domain.IDomainManager"
<utility
factory="mailman.model.domain.DomainManager"
provides="mailman.interfaces.domain.IDomainManager"
/>
<!-- utilities -->
<utility
factory="mailman.model.listmanager.ListManager"
provides="mailman.interfaces.listmanager.IListManager"
......
......@@ -5,7 +5,8 @@ Domains
# The test framework starts out with an example domain, so let's delete
# that first.
>>> from mailman.interfaces.domain import IDomainManager
>>> manager = IDomainManager(config)
>>> from zope.component import getUtility
>>> manager = getUtility(IDomainManager)
>>> manager.remove('example.com')
<Domain example.com...>
......
......@@ -16,8 +16,8 @@ checks, etc. The IRegistrar is the interface to the object handling all this
stuff.
>>> from mailman.interfaces.domain import IDomainManager
>>> manager = IDomainManager(config)
>>> domain = manager['example.com']
>>> from zope.component import getUtility
>>> domain = getUtility(IDomainManager)['example.com']
Get a registrar by adapting a domain.
......
......@@ -88,7 +88,6 @@ class IMailingList(Interface):
part of the posting email address. For example, if messages are
posted to m[email protected], then the list_name is 'mylist'.
""")))
host_name = exported(TextLine(
title=_('Host name'),
description=_("""\
......@@ -108,6 +107,9 @@ class IMailingList(Interface):
always comprised of the list_name + '@' + host_name.
""")))
domain = Attribute(
"""The `IDomain` that this mailing list is defined in.""")
real_name = exported(TextLine(
title=_('Real name'),
description=_("""\
......
......@@ -29,6 +29,7 @@ from urlparse import urljoin, urlparse
from storm.locals import Int, Unicode
from zope.interface import implements
from mailman.config import config
from mailman.database.model import Model
from mailman.interfaces.domain import (
BadDomainSpecificationError, IDomain, IDomainManager)
......@@ -105,13 +106,8 @@ class DomainManager:
implements(IDomainManager)
def __init__(self, config):
"""Create a domain manager.
:param config: The configuration object.
:type config: `IConfiguration`
"""
self.config = config
def __init__(self):
"""Create a domain manager."""
self.store = config.db.store
def add(self, email_host,
......
......@@ -32,6 +32,7 @@ from storm.locals import (
And, Bool, DateTime, Float, Int, Pickle, Reference, Store, TimeDelta,
Unicode)
from urlparse import urljoin
from zope.component import getUtility
from zope.interface import implements
from mailman.config import config
......@@ -209,18 +210,21 @@ class MailingList(Model):
"""See `IMailingList`."""
return '{0}@{1}'.format(self.list_name, self.host_name)
@property
def domain(self):
"""See `IMailingList`."""
return getUtility(IDomainManager)[self.host_name]
@property
def web_host(self):
"""See `IMailingList`."""
return IDomainManager(config)[self.host_name]
return self.domain.url_host
def script_url(self, target, context=None):
"""See `IMailingList`."""
# Find the domain for this mailing list.
domain = IDomainManager(config)[self.host_name]
# XXX Handle the case for when context is not None; those would be
# relative URLs.
return urljoin(domain.base_url, target + '/' + self.fqdn_listname)
return urljoin(self.domain.base_url, target + '/' + self.fqdn_listname)
@property
def data_path(self):
......
......@@ -134,7 +134,9 @@ class Pendings:
def confirm(self, token, expunge=True):
store = config.db.store
pendings = store.find(Pended, token=token)
# Token can come in as a unicode, but it's stored in the database as
# bytes. They must be ascii.
pendings = store.find(Pended, token=str(token))
if pendings.count() == 0:
return None
assert pendings.count() == 1, (
......
......@@ -225,8 +225,8 @@ There are some circumstances when the list administrator wants to explicitly
set the List-ID header. Start by creating a new domain.
>>> from mailman.interfaces.domain import IDomainManager
>>> manager = IDomainManager(config)
>>> domain = manager.add('mail.example.net')
>>> from zope.component import getUtility
>>> domain = getUtility(IDomainManager).add('mail.example.net')
>>> mlist.host_name = 'mail.example.net'
>>> process(mlist, msg, {})
......
......@@ -183,14 +183,16 @@ class CommandRunner(Runner):
'Invalid status: %s' % status)
if status == ContinueProcessing.no:
break
# All done, send the response.
if len(finder.command_lines) > 0:
# All done. Strip blank lines and send the response.
lines = filter(None, (line.strip() for line in finder.command_lines))
if len(lines) > 0:
print >> results, _('\n- Unprocessed:')
for line in finder.command_lines:
for line in lines:
print >> results, line
if len(finder.ignored_lines) > 0:
lines = filter(None, (line.strip() for line in finder.ignored_lines))
if len(lines) > 0:
print >> results, _('\n- Ignored:')
for line in finder.ignored_lines:
for line in lines:
print >> results, line
print >> results, _('\n- Done.')
# Send a reply, but do not attach the original message. This is a
......
......@@ -137,7 +137,8 @@ address, and the other is the results of his email command.
>>> from mailman.interfaces.domain import IDomainManager
>>> from mailman.interfaces.registrar import IRegistrar
>>> registrar = IRegistrar(IDomainManager(config)['example.com'])
>>> from zope.component import getUtility
>>> registrar = IRegistrar(getUtility(IDomainManager)['example.com'])
>>> for item in messages:
... subject = item.msg['subject']
... print 'Subject:', subject
......@@ -210,7 +211,7 @@ The -confirm address is also available as an implicit command.
Message-ID: ...
<BLANKLINE>
- Results:
Confirmed
Confirmation token did not match
<BLANKLINE>
- Done.
<BLANKLINE>
......
......@@ -27,10 +27,11 @@ __all__ = [
from operator import attrgetter
from zope.component import getUtility
from zope.interface import implements
from zope.publisher.interfaces import NotFound
from mailman.interfaces.domain import IDomainCollection
from mailman.interfaces.domain import IDomainCollection, IDomainManager
from mailman.interfaces.rest import IResolvePathNames
......@@ -42,22 +43,14 @@ class DomainCollection:
__name__ = 'domains'
def __init__(self, manager):
"""Initialize the adapter from an `IDomainManager`.
:param manager: The domain manager.
:type manager: `IDomainManager`.
"""
self._manager = manager
def get_domains(self):
"""See `IDomainCollection`."""
# lazr.restful requires the return value to be a concrete list.
return sorted(self._manager, key=attrgetter('email_host'))
return sorted(getUtility(IDomainManager), key=attrgetter('email_host'))
def get(self, name):
"""See `IResolvePathNames`."""
domain = self._manager.get(name)
domain = getUtility(IDomainManager).get(name)
if domain is None:
raise NotFound(self, name)
return domain
......@@ -65,6 +58,6 @@ class DomainCollection:
def new(self, email_host, description=None, base_url=None,
contact_address=None):
"""See `IDomainCollection`."""
value = self._manager.add(
value = getUtility(IDomainManager).add(
email_host, description, base_url, contact_address)
return value
......@@ -14,12 +14,6 @@
<adapter factory="zope.publisher.http.HTTPCharsets" />
<adapter
for="mailman.interfaces.domain.IDomainManager"
provides="mailman.interfaces.domain.IDomainCollection"
factory="mailman.rest.adapters.DomainCollection"
/>
<adapter
for="mailman.interfaces.domain.IDomain
lazr.restful.simple.Request"
......@@ -51,7 +45,12 @@
<utility
factory="mailman.rest.configuration.AdminWebServiceConfiguration"
provides="lazr.restful.interfaces.IWebServiceConfiguration">
</utility>
provides="lazr.restful.interfaces.IWebServiceConfiguration"
/>
<utility
factory="mailman.rest.adapters.DomainCollection"
provides="mailman.interfaces.domain.IDomainCollection"
/>
</configure>
......@@ -5,8 +5,10 @@ Domains
# The test framework starts out with an example domain, so let's delete
# that first.
>>> from mailman.interfaces.domain import IDomainManager
>>> manager = IDomainManager(config)
>>> manager.remove('example.com')
>>> from zope.component import getUtility
>>> domain_manager = getUtility(IDomainManager)
>>> domain_manager.remove('example.com')
<Domain example.com...>
>>> transaction.commit()
......@@ -20,8 +22,8 @@ initially none.
Once a domain is added though, it is accessible through the API.
>>> manager.add('example.com', 'An example domain',
... 'http://lists.example.com')
>>> 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]>
......@@ -43,15 +45,17 @@ Once a domain is added though, it is accessible through the API.
At the top level, all domains are returned as separate entries.
>>> manager.add('example.org',
... base_url='http://mail.example.org',
... contact_address='[email protected]')
>>> 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]>
>>> manager.add('lists.example.net',
... 'Porkmasters',
... 'http://example.net',
... '[email protected]')
>>> domain_manager.add(
... 'lists.example.net',
... 'Porkmasters',
... 'http://example.net',
... '[email protected]')
<Domain lists.example.net, Porkmasters,
base_url: http://example.net,
contact_address: [email protected]>
......@@ -148,7 +152,7 @@ Now the web service knows about our new domain.
And the new domain is in our database.
>>> manager['lists.example.com']
>>> domain_manager['lists.example.com']
<Domain lists.example.com,
base_url: http://lists.example.com,
contact_address: [email protected]>
......@@ -185,7 +189,7 @@ address.
self_link: http://localhost:8001/3.0/domains/my.example.com
url_host: allmy.example.com
>>> manager['my.example.com']
>>> domain_manager['my.example.com']
<Domain my.example.com, My new domain,
base_url: http://allmy.example.com,
contact_address: [email protected]>
......
......@@ -80,7 +80,7 @@ class AdminWebServiceApplication:
"""Maps root names to resources."""
top_level = dict(
system=system,
domains=IDomainCollection(IDomainManager(config)),
domains=getUtility(IDomainCollection),
lists=getUtility(IListManager),
)
next_step = top_level.get(name)
......
......@@ -166,7 +166,7 @@ class ConfigLayer(MockAndMonkeyLayer):
@classmethod
def testSetUp(cls):
# Add an example domain.
IDomainManager(config).add(
getUtility(IDomainManager).add(
'example.com', 'An example domain.',
'http://lists.example.com', '[email protected]')
config.db.commit()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment