Commit 8938d653 authored by Barry Warsaw's avatar Barry Warsaw

* Refactor the language manager off of the config object and into a utility.

* Fix a few small typos in exception handlers.
* Move the initialization of the Zope Component Architecture into the first
  initialization step.  The only reason we couldn't do that previously was
  because the domain object referenced the config, causing a circularity
  problem.  Refactor the Domain implementation to avoid that.
parent fbc81f61
......@@ -44,6 +44,7 @@ from email.errors import HeaderParseError
from email.header import decode_header, make_header
from lazr.config import as_boolean
from string import ascii_letters, digits, whitespace
from zope.component import getUtility
import mailman.templates
......@@ -51,6 +52,7 @@ from mailman import passwords
from mailman.config import config
from mailman.core import errors
from mailman.core.i18n import _
from mailman.interfaces.languages import ILanguageManager
from mailman.utilities.string import expand
......@@ -406,7 +408,7 @@ def findtext(templatefile, raw_dict=None, raw=False, lang=None, mlist=None):
else:
template = fp.read()
fp.close()
charset = config.languages[lang].charset
charset = getUtility(ILanguageManager)[lang].charset
template = unicode(template, charset, 'replace')
text = template
if raw_dict is not None:
......@@ -431,7 +433,7 @@ def uncanonstr(s, lang=None):
if lang is None:
charset = 'us-ascii'
else:
charset = config.languages[lang].charset
charset = getUtility(ILanguageManager)[lang].charset
# See if the string contains characters only in the desired character
# set. If so, return it unchanged, except for coercing it to a byte
# string.
......
......@@ -44,6 +44,7 @@ from mailman.core import errors
from mailman.core.i18n import _
from mailman.email.message import UserNotification
from mailman.interfaces.action import Action
from mailman.interfaces.languages import ILanguageManager
from mailman.interfaces.member import AlreadySubscribedError, DeliveryMode
from mailman.interfaces.messages import IMessageStore
from mailman.interfaces.requests import IRequests, RequestType
......@@ -237,14 +238,14 @@ def handle_subscription(mlist, id, action, comment=None):
_refuse(mlist, _('Subscription request'),
data['address'],
comment or _('[No reason given]'),
lang=config.languages[data['language']])
lang=getUtility(ILanguageManager)[data['language']])
elif action is Action.accept:
key, data = requestdb.get_request(id)
enum_value = data['delivery_mode'].split('.')[-1]
delivery_mode = DeliveryMode(enum_value)
address = data['address']
realname = data['realname']
language = config.languages[data['language']]
language = getUtility(ILanguageManager)[data['language']]
password = data['password']
try:
add_member(mlist, address, realname, password,
......
......@@ -83,7 +83,7 @@ class Registrar:
# Send a verification email to the address.
text = _(resource_string('mailman.templates.en', 'verify.txt'))
msg = UserNotification(address, confirm_address, subject, text)
msg.send(mlist=None)
msg.send(mlist=mlist)
return token
def confirm(self, token):
......
......@@ -194,14 +194,15 @@ def acquire_lock(force):
if status == WatcherState.conflict:
# Hostname matches and process exists.
message = _("""\
The master queue runner lock could not be acquired because it appears as
though another master is already running.""")
The master queue runner lock could not be acquired because it
appears as though another master is already running.""")
elif status == WatcherState.stale_lock:
# Hostname matches but the process does not exist.
program = sys.argv[0]
message = _("""\
The master queue runner lock could not be acquired. It appears as though
there is a stale master lock. Try re-running $program with the -s flag.""")
The master queue runner lock could not be acquired. It appears
as though there is a stale master lock. Try re-running $program
with the -s flag.""")
else:
# Hostname doesn't even match.
assert status == WatcherState.host_mismatch, (
......@@ -209,9 +210,10 @@ there is a stale master lock. Try re-running $program with the -s flag.""")
# pylint: disable-msg=W0612
hostname, pid, tempfile = get_lock_data()
message = _("""\
The master qrunner lock could not be acquired, because it appears as if some
process on some other host may have acquired it. We can't test for stale
locks across host boundaries, so you'll have to clean this up manually.
The master qrunner lock could not be acquired, because it appears
as if some process on some other host may have acquired it. We
can't test for stale locks across host boundaries, so you'll have
to clean this up manually.
Lock file: $config.LOCK_FILE
Lock host: $hostname
......
......@@ -41,6 +41,7 @@ from mailman.config import config
from mailman.core.i18n import _
from mailman.email.message import UserNotification
from mailman.interfaces.autorespond import IAutoResponseSet, Response
from mailman.interfaces.languages import ILanguageManager
from mailman.interfaces.pending import IPendable, IPendings
from mailman.interfaces.usermanager import IUserManager
......@@ -189,8 +190,9 @@ class HoldChain(TerminalChainBase):
text = maketext('postheld.txt', substitutions,
lang=send_language_code, mlist=mlist)
adminaddr = mlist.bounces_address
nmsg = UserNotification(msg.sender, adminaddr, subject, text,
config.languages[send_language_code])
nmsg = UserNotification(
msg.sender, adminaddr, subject, text,
getUtility(ILanguageManager)[send_language_code])
nmsg.send(mlist)
# Now the message for the list moderators. This one should appear to
# come from <list>-owner since we really don't need to do bounce
......
......@@ -30,6 +30,7 @@ __all__ = [
import os
import sys
import errno
import signal
import logging
......@@ -137,10 +138,10 @@ def kill_watcher(sig):
try:
os.kill(pid, sig)
except OSError as error:
if e.errno != errno.ESRCH:
if error.errno != errno.ESRCH:
raise
print >> sys.stderr, _('No child with pid: $pid')
print >> sys.stderr, e
print >> sys.stderr, error
print >> sys.stderr, _('Stale pid file removed.')
os.unlink(config.PIDFILE)
......
......@@ -40,6 +40,7 @@ from mailman.email.message import UserNotification
from mailman.interfaces.command import ICLISubCommand
from mailman.interfaces.domain import (
BadDomainSpecificationError, IDomainManager)
from mailman.interfaces.languages import ILanguageManager
from mailman.interfaces.listmanager import IListManager, ListAlreadyExistsError
......@@ -174,7 +175,7 @@ class Create:
if args.language is not None
else system_preferences.preferred_language.code)
# Make sure that the selected language code is known.
if language_code not in config.languages.codes:
if language_code not in getUtility(ILanguageManager).codes:
self.parser.error(_('Invalid language code: $language_code'))
return
assert len(args.listname) == 1, (
......@@ -199,7 +200,7 @@ class Create:
# Find the language associated with the code, then set the mailing
# list's preferred language to that. The changes then must be
# committed to the database.
mlist.preferred_language = config.languages[language_code]
mlist.preferred_language = getUtility(ILanguageManager)[language_code]
config.db.commit()
# Do the notification.
if not args.quiet:
......
......@@ -116,7 +116,8 @@ The language must be known to Mailman.
>>> command.process(args)
Invalid language code: ee
>>> config.languages.add('ee', 'iso-8859-1', 'Freedonian')
>>> from mailman.interfaces.languages import ILanguageManager
>>> getUtility(ILanguageManager).add('ee', 'iso-8859-1', 'Freedonian')
>>> args.quiet = False
>>> args.listname = ['[email protected]']
......
......@@ -32,11 +32,12 @@ import logging
from lazr.config import ConfigSchema, as_boolean
from pkg_resources import resource_stream
from zope.component import getUtility
from zope.interface import Interface, implements
from mailman import version
from mailman.core import errors
from mailman.languages.manager import LanguageManager
from mailman.interfaces.languages import ILanguageManager
from mailman.styles.manager import StyleManager
from mailman.utilities.filesystem import makedirs
from mailman.utilities.modules import call_name
......@@ -58,7 +59,6 @@ class Configuration:
def __init__(self):
self.switchboards = {}
self.languages = LanguageManager()
self.style_manager = StyleManager()
self.QFILE_SCHEMA_VERSION = version.QFILE_SCHEMA_VERSION
self._config = None
......@@ -73,7 +73,7 @@ class Configuration:
def _clear(self):
"""Clear the cached configuration variables."""
self.switchboards.clear()
self.languages = LanguageManager()
getUtility(ILanguageManager).clear()
def __getattr__(self, name):
"""Delegate to the configuration object."""
......@@ -143,13 +143,16 @@ class Configuration:
Switchboard.initialize()
# Set up all the languages.
languages = self._config.getByCategory('language', [])
language_manager = getUtility(ILanguageManager)
for language in languages:
if language.enabled:
code = language.name.split('.')[1]
self.languages.add(
language_manager.add(
code, language.charset, language.description)
# The default language must always be available.
assert self._config.mailman.default_language in self.languages
assert self._config.mailman.default_language in language_manager, (
'System default language code not defined: %s' %
self._config.mailman.default_language)
self.ensure_directories_exist()
self.style_manager.populate()
# Set the default system language.
......
......@@ -27,6 +27,11 @@
provides="mailman.interfaces.domain.IDomainManager"
/>
<utility
factory="mailman.languages.manager.LanguageManager"
provides="mailman.interfaces.languages.ILanguageManager"
/>
<utility
factory="mailman.model.listmanager.ListManager"
provides="mailman.interfaces.listmanager.IListManager"
......
......@@ -25,9 +25,11 @@ __all__ = [
]
from zope.component import getUtility
from zope.interface import implements
from mailman.config import config
from mailman.interfaces.languages import ILanguageManager
from mailman.interfaces.member import DeliveryMode, DeliveryStatus
from mailman.interfaces.preferences import IPreferences
......@@ -52,7 +54,7 @@ class SystemDefaultPreferences:
@property
def preferred_language(self):
"""Return the system preferred language."""
return config.languages['en']
return getUtility(ILanguageManager)[config.mailman.default_language]
......
......@@ -58,6 +58,7 @@ from mailman.utilities.modules import call_name
def initialize_1(config_path=None, propagate_logs=None):
"""First initialization step.
* Zope component architecture
* The configuration system
* Run-time directories
* The logging subsystem
......@@ -68,6 +69,8 @@ def initialize_1(config_path=None, propagate_logs=None):
:param propagate_logs: Should the log output propagate to stderr?
:type propagate_logs: boolean or None
"""
zcml = resource_string('mailman.config', 'configure.zcml')
xmlconfig.string(zcml)
# By default, set the umask so that only owner and group can read and
# write our files. Specifically we must have g+rw and we probably want
# o-rwx although I think in most cases it doesn't hurt if other can read
......@@ -120,11 +123,8 @@ def initialize_2(debug=False):
def initialize_3():
"""Third initialization step.
* Zope component architecture
* Post-hook
"""
zcml = resource_string('mailman.config', 'configure.zcml')
xmlconfig.string(zcml)
# Run the post-hook if there is one.
config = mailman.config.config
if config.mailman.post_hook:
......
=========
Languages
=========
......@@ -6,12 +7,16 @@ languages at run time, as well as enabling those languages for use in a
running Mailman instance.
>>> from mailman.interfaces.languages import ILanguageManager
>>> from mailman.languages.manager import LanguageManager
>>> from zope.component import getUtility
>>> from zope.interface.verify import verifyObject
>>> mgr = LanguageManager()
>>> mgr = getUtility(ILanguageManager)
>>> verifyObject(ILanguageManager, mgr)
True
# The language manager component comes pre-populated; clear it out.
>>> mgr.clear()
A language manager keeps track of the languages it knows about.
>>> list(mgr.codes)
......@@ -21,7 +26,7 @@ A language manager keeps track of the languages it knows about.
Adding languages
----------------
================
Adding a new language requires three pieces of information, the 2-character
language code, the English description of the language, and the character set
......@@ -43,7 +48,7 @@ And you can get information for all known languages.
Other iterations
----------------
================
You can iterate over all the known language codes.
......@@ -83,3 +88,23 @@ You can get a particular language by its code.
None
>>> print mgr.get('xx', 'missing')
missing
Clearing the known languages
============================
The language manager can forget about all the language codes it knows about.
>>> 'en' in mgr
True
# Make a copy of the language manager's dictionary, so we can restore it
# after the test. Currently the test layer doesn't manage this.
>>> saved = mgr._languages.copy()
>>> mgr.clear()
>>> 'en' in mgr
False
# Restore the data.
>>> mgr._languages = saved
========
Messages
========
......@@ -6,7 +7,7 @@ email.message.Message class, but providing additional useful methods.
User notifications
------------------
==================
When Mailman needs to send a message to a user, it creates a UserNotification
instance, and then calls the .send() method on this object. This method
......
......@@ -135,7 +135,8 @@ Users have preferences, but these preferences have no default settings.
Some of these preferences are booleans and they can be set to True or False.
>>> config.languages.add('it', 'iso-8859-1', 'Italian')
>>> from mailman.interfaces.languages import ILanguageManager
>>> getUtility(ILanguageManager).add('it', 'iso-8859-1', 'Italian')
>>> from mailman.core.constants import DeliveryMode
>>> prefs = user_1.preferences
......
......@@ -93,3 +93,6 @@ class ILanguageManager(Interface):
:return: A flag indicating whether the language code is known or not.
:rtype: bool
"""
def clear():
"""Remove all language code mappings."""
......@@ -68,3 +68,7 @@ class LanguageManager:
def __contains__(self, code):
"""See `ILanguageManager`."""
return code in self._languages
def clear(self):
"""See `ILanguageManager`."""
self._languages.clear()
......@@ -106,10 +106,6 @@ class DomainManager:
implements(IDomainManager)
def __init__(self):
"""Create a domain manager."""
self.store = config.db.store
def add(self, email_host,
description=None,
base_url=None,
......@@ -121,17 +117,17 @@ class DomainManager:
raise BadDomainSpecificationError(
'Duplicate email host: %s' % email_host)
domain = Domain(email_host, description, base_url, contact_address)
self.store.add(domain)
config.db.store.add(domain)
return domain
def remove(self, email_host):
domain = self[email_host]
self.store.remove(domain)
config.db.store.remove(domain)
return domain
def get(self, email_host, default=None):
"""See `IDomainManager`."""
domains = self.store.find(Domain, email_host=email_host)
domains = config.db.store.find(Domain, email_host=email_host)
if domains.count() < 1:
return default
assert domains.count() == 1, (
......@@ -147,13 +143,13 @@ class DomainManager:
return domain
def __len__(self):
return self.store.find(Domain).count()
return config.db.store.find(Domain).count()
def __iter__(self):
"""See `IDomainManager`."""
for domain in self.store.find(Domain):
for domain in config.db.store.find(Domain):
yield domain
def __contains__(self, email_host):
"""See `IDomainManager`."""
return self.store.find(Domain, email_host=email_host).count() > 0
return config.db.store.find(Domain, email_host=email_host).count() > 0
......@@ -39,6 +39,7 @@ from mailman.config import config
from mailman.database.model import Model
from mailman.database.types import Enum
from mailman.interfaces.domain import IDomainManager
from mailman.interfaces.languages import ILanguageManager
from mailman.interfaces.mailinglist import (
IAcceptableAlias, IAcceptableAliasSet, IMailingList, Personalization)
from mailman.interfaces.mime import FilterType
......@@ -288,7 +289,7 @@ class MailingList(Model):
@property
def preferred_language(self):
"""See `IMailingList`."""
return config.languages[self._preferred_language]
return getUtility(ILanguageManager)[self._preferred_language]
@preferred_language.setter
def preferred_language(self, language):
......
......@@ -26,11 +26,13 @@ __all__ = [
from storm.locals import *
from zope.component import getUtility
from zope.interface import implements
from mailman.config import config
from mailman.database.model import Model
from mailman.database.types import Enum
from mailman.interfaces.languages import ILanguageManager
from mailman.interfaces.preferences import IPreferences
......@@ -54,7 +56,7 @@ class Preferences(Model):
def preferred_language(self):
if self._preferred_language is None:
return None
return config.languages[self._preferred_language]
return getUtility(ILanguageManager)[self._preferred_language]
@preferred_language.setter
def preferred_language(self, language):
......
......@@ -28,6 +28,7 @@ __all__ = [
]
from zope.component import getUtility
from zope.interface import implements
from mailman import Utils
......@@ -35,6 +36,7 @@ from mailman.config import config
from mailman.core.i18n import _
from mailman.email.message import Message, UserNotification
from mailman.interfaces.handler import IHandler
from mailman.interfaces.languages import ILanguageManager
......@@ -61,10 +63,11 @@ class Acknowledge:
original_subject = msgdata.get(
'origsubj', msg.get('subject', _('(no subject)')))
# Get the user's preferred language.
language = (config.languages[msgdata['lang']]
language_manager = getUtility(ILanguageManager)
language = (language_manager[msgdata['lang']]
if 'lang' in msgdata
else member.preferred_language)
charset = config.languages[language.code].charset
charset = language_manager[language.code].charset
# Now get the acknowledgement template.
realname = mlist.real_name
text = Utils.maketext(
......
......@@ -53,6 +53,7 @@ from zope.interface import implements
from mailman.config import config
from mailman.core.i18n import _
from mailman.email.message import Message
from mailman.interfaces.languages import ILanguageManager
from mailman.interfaces.listmanager import IListManager
from mailman.interfaces.runner import IRunner
from mailman.interfaces.switchboard import ISwitchboard
......@@ -421,11 +422,16 @@ class Runner:
# them out of our sight.
#
# Find out which mailing list this message is destined for.
listname = unicode(msgdata.get('listname'))
mlist = getUtility(IListManager).get(listname)
missing = object()
listname = msgdata.get('listname', missing)
mlist = (None
if listname is missing
else getUtility(IListManager).get(unicode(listname)))
if mlist is None:
elog.error('Dequeuing message destined for missing list: %s',
listname)
elog.error(
'%s runner "%s" shunting message for missing list: %s',
msg['message-id'], self.name,
('n/a' if listname is missing else listname))
config.switchboards['shunt'].enqueue(msg, msgdata)
return
# Now process this message. We also want to set up the language
......@@ -434,7 +440,10 @@ class Runner:
# will be the list's preferred language. However, we must take
# special care to reset the defaults, otherwise subsequent messages
# may be translated incorrectly.
if msg.sender:
if mlist is None:
language_manager = getUtility(ILanguageManager)
language = language_manager[config.mailman.default_language]
elif msg.sender:
member = mlist.members.get_member(msg.sender)
language = (member.preferred_language
if member is not None
......
......@@ -32,15 +32,17 @@ import re
import logging
from StringIO import StringIO
from email.Errors import HeaderParseError
from email.Header import decode_header, make_header
from email.Iterators import typed_subpart_iterator
from email.errors import HeaderParseError
from email.header import decode_header, make_header
from email.iterators import typed_subpart_iterator
from zope.component import getUtility
from zope.interface import implements
from mailman.config import config
from mailman.core.i18n import _
from mailman.email.message import Message, UserNotification
from mailman.interfaces.command import ContinueProcessing, IEmailResults
from mailman.interfaces.languages import ILanguageManager
from mailman.queue import Runner
......@@ -198,7 +200,7 @@ class CommandRunner(Runner):
# Send a reply, but do not attach the original message. This is a
# compromise because the original message is often helpful in tracking
# down problems, but it's also a vector for backscatter spam.
language = config.languages[msgdata['lang']]
language = getUtility(ILanguageManager)[msgdata['lang']]
reply = UserNotification(msg.sender, mlist.bounces_address,
_('The results of your email commands'),
lang=language)
......
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