From da566eb047608ed2e65c71dd25979f01c825c843 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 21 Aug 2009 16:11:50 -0400 Subject: * Updated NEWS.txt for 3.0a3. * Use the IListManager() adapter instead of hanging the list manager off of config.db. This makes the code somewhat cleaner. --- docs/NEWS.txt | 485 ++++++++++++++++++------------------ src/attic/add_members.py | 186 ++++++++++++++ src/mailman/Archiver/HyperArch.py | 3 +- src/mailman/app/lifecycle.py | 5 +- src/mailman/app/registrar.py | 3 +- src/mailman/bin/add_members.py | 186 -------------- src/mailman/bin/inject.py | 3 +- src/mailman/commands/cli_lists.py | 6 +- src/mailman/commands/cli_members.py | 3 +- src/mailman/config/configure.zcml | 6 + src/mailman/database/__init__.py | 2 - src/mailman/database/listmanager.py | 20 +- 12 files changed, 465 insertions(+), 443 deletions(-) create mode 100644 src/attic/add_members.py delete mode 100644 src/mailman/bin/add_members.py diff --git a/docs/NEWS.txt b/docs/NEWS.txt index 16e10dd8b..d2226687a 100644 --- a/docs/NEWS.txt +++ b/docs/NEWS.txt @@ -1,257 +1,262 @@ +================================================ Mailman - The GNU Mailing List Management System +================================================ + Copyright (C) 1998-2009 by the Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Here is a history of user visible changes to Mailman. 3.0 alpha 3 -- "Working Man" -(XX-XXX-200X) - - Architecture - - - Back port of Mailman 2.1's limit on .bak file restoration. After 3 - restores, the file is moved to the bad queue, with a .psv extension. +============================ + +Configuration +------------- + * Configuration is now done through lazr.config. Defaults.py is + dead. lazr.config files are essentially hierarchical ini files. + * Domains are now stored in the database instead of in the configuration file. + * pre- and post- initialization hooks are now available to plugins. Specify + additional hooks to run in the configuration file. + * Add the environment variable $MAILMAN_CONFIG_FILE which overrides the -C + command line option. + * Make LMTP more compliant with Postfix docs (Patrick Koetter) + * Added a NullMTA for mail servers like Exim which just work automatically. + +Architecture +------------ + + * 'bin/mailman' is a new super-command for managing Mailman from the command + line. Some older bin scripts have been converted, with more to come. + * Mailman now has an administrative REST interface which can be used to get + information from and manage Mailman remotely. + * Back port of Mailman 2.1's limit on .bak file restoration. After 3 + restores, the file is moved to the bad queue, with a .psv extension. (Mark + Sapiro) + * Digest creation is moved into a new queue runner so it doesn't block main + message processing. + +Other changes +------------- + * bin/make_instance is no longer necessary, and removed + * The debug log is turned up to info by default to reduce log file spam. + +Building and installation +------------------------- + * All doc tests can now be turned into documentation, via Sphinx. Just run + bin/docs after bin/buildout. 3.0 alpha 2 -- "Grand Designs" +============================== (03-Jan-2009) - Licensing - - - Mailman 3 is now licensed under the GPLv3. - - Bug fixes - - - Changed bin/arch to attempt to open the mbox before wiping the old - archive. Launchpad bug #280418. - - - Added digest.mbox and pending.pck to the 'list' files checked by - check_perms. Launchpad bug #284802. - - Architecture - - - Converted to using zope.testing as the test infrastructure. Use bin/test - now to run the full test suite. - - - - Partially converted to using lazr.config as the new configuration - regime. Not everything has been converted yet, so some manual editing - of mailman/Defaults.py is required. This will be rectified in future - versions. - - - All web-related stuff is moved to its own directory, effectively moving - it out of the way for now. - - - The email command infrastructure has been reworked to play more nicely - with the plug-in architecture. Not all commands have yet been - converted. - - Other changes - - - The LMTP server now properly calculates the message's original size. - - - For command line scripts, -C names the configuration file to use. For - convenient testing, if -C is not given, then the environment variable - MAILMAN_CONFIG_FILE is consulted. - - - Support added for a local MHonArc archiver, as well as archiving - automatically in the remote Mail-Archive.com service. - - - The permalink proposal for supporting RFC 5064 has been adopted. - - - Mailing lists no longer have a .web_page_url attribute; this is taken - from the mailing list's domain's base_url attribute. - - - Incoming MTA selection is now taken from the config file instead of - plugins. An MTA for Postfix+LMTP is added. bin/genaliases works again. - - - If a message has no Message-ID, the stock archivers will return None for - the permalink now instead of raising an assertion. - - - IArchiver no longer has an is_enabled property; this is taken from the - configuration file now. - - Installation - - - Python 2.6 is the minimal requirement. - - - Converted to using zc.buildout as the build infrastructure. See - docs/ALPHA.txt for details. - +Licensing +--------- + + * Mailman 3 is now licensed under the GPLv3. + +Bug fixes +--------- + + * Changed bin/arch to attempt to open the mbox before wiping the old + archive. Launchpad bug #280418. + + * Added digest.mbox and pending.pck to the 'list' files checked by + check_perms. Launchpad bug #284802. + +Architecture +------------ + + * Converted to using zope.testing as the test infrastructure. Use bin/test + now to run the full test suite. + + * Partially converted to using lazr.config as the new configuration + regime. Not everything has been converted yet, so some manual editing + of mailman/Defaults.py is required. This will be rectified in future + versions. + * All web-related stuff is moved to its own directory, effectively moving + it out of the way for now. + * The email command infrastructure has been reworked to play more nicely + with the plug-in architecture. Not all commands have yet been + converted. + +Other changes +------------- + + * The LMTP server now properly calculates the message's original size. + * For command line scripts, -C names the configuration file to use. For + convenient testing, if -C is not given, then the environment variable + MAILMAN_CONFIG_FILE is consulted. + * Support added for a local MHonArc archiver, as well as archiving + automatically in the remote Mail-Archive.com service. + * The permalink proposal for supporting RFC 5064 has been adopted. + * Mailing lists no longer have a .web_page_url attribute; this is taken from + the mailing list's domain's base_url attribute. + * Incoming MTA selection is now taken from the config file instead of + plugins. An MTA for Postfix+LMTP is added. bin/genaliases works again. + * If a message has no Message-ID, the stock archivers will return None for + the permalink now instead of raising an assertion. + * IArchiver no longer has an is_enabled property; this is taken from the + configuration file now. + +Installation +------------ + + * Python 2.6 is the minimal requirement. + * Converted to using zc.buildout as the build infrastructure. See + docs/ALPHA.txt for details. + 3.0 alpha 1 -- "Leave That Thing Alone" +======================================= (08-Apr-2008) - User visible changes - - - So called 'new style' subject prefixing is the default now, and the only - option. When a list's subject prefix is added, it's always done so - before any Re: tag, not after. E.g. '[My List] Re: The subject'. - - - RFC 2369 headers List-Subscribe and List-Unsubscribe now use the - preferred -join and -leave addresses instead of the -request address - with a subject value. - - Configuration - - - There is no more separate configure; make; make install step. Mailman - 3.0 is a setuptools package. - - - Mailman can now be configured via a 'mailman.cfg' file which lives in - $VAR_PREFIX/etc. This is used to separate the configuration from the - source directory. Alternative configuration files can be specified via - -C/--config for most command line scripts. mailman.cfg contains Python - code. mm_cfg.py is no more. You do not need to import Defaults.py in - etc/mailman.cfg. You should still consult Defaults.py for the list of - site configuration variables available to you. - - See the etc/mailman.cfg.sample file. - - - PUBLIC_ARCHIVE_URL and DEFAULT_SUBJECT_PREFIX now takes $-string - substitutions instead of %-string substitutions. See documentation in - Defaults.py.in for details. - - - Message headers and footers now only accept $-string substitutions; - %-strings are no longer supported. The substitution variable - '_internal_name' has been removed; use $list_name or $real_name - instead. The substitution variable $fqdn_listname has been added. - DEFAULT_MSG_FOOTER in Defaults.py.in has been updated accordingly. - - - The KNOWN_SPAMMERS global variable is replaced with HEADER_MATCHES. The - mailing list's header_filter_rules variable is replaced with - header_matches which has the same semantics as HEADER_MATCHES, but is - list-specific. - - - DEFAULT_MAIL_COMMANDS_MAX_LINES -> EMAIL_COMMANDS_MAX_LINES - - - All SMTP_LOG_* templates use $-strings and all consistently write the - Message-ID as the first item in the log entry. - - - DELIVERY_MODULE now names a handler, not a module (yes, this is a - misnomer, but it will likely change again before the final release). - - Architecture - - - Internally, all strings are Unicodes. - - - Implementation of a chain-of-rules based approach for deciding whether a - message should initially be accepted, held for approval, - rejected/bounced, or discarded. This replaces most of the disposition - handlers in the pipeline. The IncomingRunner now only processes message - through the rule chains, and once accepted, places the message in a new - queue processed by the PipelineRunner. - - - Substantially reworked the entire queue runner process management, - including mailmanctl, a new master script, and the qrunners. This - should be much more robust and reliable now. - - - The Storm ORM is used for data storage, with the SQLite backend as the - default relational database. - - - Zope interfaces are used to describe the major components. - - - Users are now stored in a unified database, and shared across all - mailing lists. - - - Mailman's web interface is now WSGI compliant. WSGI is a Python - standard (PEP 333) allowing web applications to be (more) easily - integrated with any number of existing Python web application - frameworks. For more information see: - - http://www.wsgi.org/wsgi - http://www.python.org/dev/peps/pep-0333/ - - Mailman can still be run as a traditional CGI program of course. - - - Mailman now provides an LMTP server for more efficient integration with - supporting mail servers (e.g. Postfix, Sendmail). The Local Mail - Transport Protocol is defined in RFC 2033: - - http://www.faqs.org/rfcs/rfc2033.html - - - Virtual domains are now fully supported in that mailing lists of the - same name can exist in more than one domain. This is accomplished by - renaming the lists/ and archives/ subdirectories after the list's - posting address. For example, data for list foo in example.com and list - foo in example.org will be stored in lists/foo@example.com and - lists/foo@example.org. - - For Postfix or manual MTA users, you will need to regenerate your mail - aliases. Use bin/genaliases. - - VIRTUAL_HOST_OVERVIEW has been removed, effectively Mailman now operates - as if it were always enabled. If your site has more than one domain, - you must configure all domains by using add_domain() in your - etc/mailman.cfg flie (see below -- add_virtual() has been removed). - - - If you had customizations based on Site.py, you will need to - re-implement them. Site.py has been removed. - - - The site list is no more. You can remove your 'mailman' site list - unless you want to retain it for other purposes, but it is no longer - used (or required) by Mailman. You should set NO_REPLY_ADDRESS to an - address that throws away replies, and you should set SITE_OWNER_ADDRESS - to an email address that reaches the person ultimately responsible for - the Mailman installation. The MAILMAN_SITE_LIST variable has been - removed. - - - qrunners no longer restart on SIGINT; SIGUSR1 is used for that now. - - Internationalization Big Changes - - - Translators should work only on messages//LC_MESSAGES/mailman.po. - Templates files are generated from mailman.po during the build process. - - New Features - - - Confirmed member change of address is logged in the 'subscribe' log, - and if admin_notify_mchanges is true, a notice is sent to the list - owner using a new adminaddrchgack.txt template. - - - There is a new list attribute 'subscribe_auto_approval' which is a list - of email addresses and regular expressions matching email addresses - whose subscriptions are exempt from admin approval. RFE 403066. - - Command line scripts - - - Most scripts have grown a -C/--config flag to allow you to specify a - different configuration file. Without this, the default etc/mailman.cfg - file will be used. - - - the -V/--virtual-host-overview switch in list_lists has been removed, - while -d/--domain and -f/--full have been added. - - - bin/newlist is renamed bin/create_list and bin/rmlist is renamed - bin/remove_list. Both take fully-qualified list names now (i.e. the - list's posting address), but also accept short names, in which case the - default domain is used. newlist's -u/--urlhost and -e/--emailhost - switches have been removed. The domain that the list is being added to - must already exist. - - - Backport the ability to specify additional footer interpolation - variables by the message metadata 'decoration-data' key. - - Bug fixes and other patches - - - Removal of DomainKey/DKIM signatures is now controlled by Defaults.py - mm_cfg.py variable REMOVE_DKIM_HEADERS (default = No). - - - Queue runner processing is improved to log and preserve for analysis in - the shunt queue certain bad queue entries that were previously logged - but lost. Also, entries are preserved when an attempt to shunt throws - an exception (1656289). - - - The processing of Topics regular expressions has changed. Previously the - Topics regexp was compiled in verbose mode but not documented as such - which caused some confusion. Also, the documentation indicated that - topic keywords could be entered one per line, but these entries were not - handled properly. Topics regexps are now compiled in non-verbose mode - and multi- line entries are 'ored'. Existing Topics regexps will be - converted when the list is updated so they will continue to work. - - - The List-Help, List-Subscribe, and List-Unsubscribe headers were - incorrectly suppressed in messages that Mailman sends directly to - users. - - - The 'adminapproved' metadata key is renamed 'moderator_approved'. +User visible changes +-------------------- + + * So called 'new style' subject prefixing is the default now, and the only + option. When a list's subject prefix is added, it's always done so before + any Re: tag, not after. E.g. '[My List] Re: The subject'. + * RFC 2369 headers List-Subscribe and List-Unsubscribe now use the preferred + -join and -leave addresses instead of the -request address with a subject + value. + +Configuration +------------- + + * There is no more separate configure; make; make install step. Mailman 3.0 + is a setuptools package. + * Mailman can now be configured via a 'mailman.cfg' file which lives in + $VAR_PREFIX/etc. This is used to separate the configuration from the + source directory. Alternative configuration files can be specified via + -C/--config for most command line scripts. mailman.cfg contains Python + code. mm_cfg.py is no more. You do not need to import Defaults.py in + etc/mailman.cfg. You should still consult Defaults.py for the list of site + configuration variables available to you. + + See the etc/mailman.cfg.sample file. + * PUBLIC_ARCHIVE_URL and DEFAULT_SUBJECT_PREFIX now takes $-string + substitutions instead of %-string substitutions. See documentation in + Defaults.py.in for details. + * Message headers and footers now only accept $-string substitutions; + %-strings are no longer supported. The substitution variable + '_internal_name' has been removed; use $list_name or $real_name + instead. The substitution variable $fqdn_listname has been added. + DEFAULT_MSG_FOOTER in Defaults.py.in has been updated accordingly. + * The KNOWN_SPAMMERS global variable is replaced with HEADER_MATCHES. The + mailing list's header_filter_rules variable is replaced with header_matches + which has the same semantics as HEADER_MATCHES, but is list-specific. + * DEFAULT_MAIL_COMMANDS_MAX_LINES -> EMAIL_COMMANDS_MAX_LINES + * All SMTP_LOG_* templates use $-strings and all consistently write the + Message-ID as the first item in the log entry. + * DELIVERY_MODULE now names a handler, not a module (yes, this is a + misnomer, but it will likely change again before the final release). + +Architecture +------------ + + * Internally, all strings are Unicodes. + * Implementation of a chain-of-rules based approach for deciding whether a + message should initially be accepted, held for approval, rejected/bounced, + or discarded. This replaces most of the disposition handlers in the + pipeline. The IncomingRunner now only processes message through the rule + chains, and once accepted, places the message in a new queue processed by + the PipelineRunner. + * Substantially reworked the entire queue runner process management, + including mailmanctl, a new master script, and the qrunners. This should + be much more robust and reliable now. + * The Storm ORM is used for data storage, with the SQLite backend as the + default relational database. + * Zope interfaces are used to describe the major components. + * Users are now stored in a unified database, and shared across all mailing + lists. + * Mailman's web interface is now WSGI compliant. WSGI is a Python standard + (PEP 333) allowing web applications to be (more) easily integrated with any + number of existing Python web application frameworks. For more information + see: + + http://www.wsgi.org/wsgi + http://www.python.org/dev/peps/pep-0333/ + + Mailman can still be run as a traditional CGI program of course. + * Mailman now provides an LMTP server for more efficient integration with + supporting mail servers (e.g. Postfix, Sendmail). The Local Mail Transport + Protocol is defined in RFC 2033: + + http://www.faqs.org/rfcs/rfc2033.html + * Virtual domains are now fully supported in that mailing lists of the same + name can exist in more than one domain. This is accomplished by renaming + the lists/ and archives/ subdirectories after the list's posting address. + For example, data for list foo in example.com and list foo in example.org + will be stored in lists/foo@example.com and lists/foo@example.org. + + For Postfix or manual MTA users, you will need to regenerate your mail + aliases. Use bin/genaliases. + + VIRTUAL_HOST_OVERVIEW has been removed, effectively Mailman now operates + as if it were always enabled. If your site has more than one domain, + you must configure all domains by using add_domain() in your + etc/mailman.cfg flie (see below -- add_virtual() has been removed). + * If you had customizations based on Site.py, you will need to re-implement + them. Site.py has been removed. + * The site list is no more. You can remove your 'mailman' site list unless + you want to retain it for other purposes, but it is no longer used (or + required) by Mailman. You should set NO_REPLY_ADDRESS to an address that + throws away replies, and you should set SITE_OWNER_ADDRESS to an email + address that reaches the person ultimately responsible for the Mailman + installation. The MAILMAN_SITE_LIST variable has been removed. + * qrunners no longer restart on SIGINT; SIGUSR1 is used for that now. + +Internationalization Big Changes +-------------------------------- + + * Translators should work only on messages//LC_MESSAGES/mailman.po. + Templates files are generated from mailman.po during the build process. + +New Features +------------ + + * Confirmed member change of address is logged in the 'subscribe' log, and if + admin_notify_mchanges is true, a notice is sent to the list owner using a + new adminaddrchgack.txt template. + * There is a new list attribute 'subscribe_auto_approval' which is a list of + email addresses and regular expressions matching email addresses whose + subscriptions are exempt from admin approval. RFE 403066. + +Command line scripts +-------------------- + + * Most scripts have grown a -C/--config flag to allow you to specify a + different configuration file. Without this, the default etc/mailman.cfg + file will be used. + * the -V/--virtual-host-overview switch in list_lists has been removed, while + -d/--domain and -f/--full have been added. + * bin/newlist is renamed bin/create_list and bin/rmlist is renamed + bin/remove_list. Both take fully-qualified list names now (i.e. the list's + posting address), but also accept short names, in which case the default + domain is used. newlist's -u/--urlhost and -e/--emailhost switches have + been removed. The domain that the list is being added to must already + exist. + * Backport the ability to specify additional footer interpolation variables + by the message metadata 'decoration-data' key. + +Bug fixes and other patches +--------------------------- + + * Removal of DomainKey/DKIM signatures is now controlled by Defaults.py + mm_cfg.py variable REMOVE_DKIM_HEADERS (default = No). + * Queue runner processing is improved to log and preserve for analysis in the + shunt queue certain bad queue entries that were previously logged but lost. + Also, entries are preserved when an attempt to shunt throws an exception + (1656289). + * The processing of Topics regular expressions has changed. Previously the + Topics regexp was compiled in verbose mode but not documented as such which + caused some confusion. Also, the documentation indicated that topic + keywords could be entered one per line, but these entries were not handled + properly. Topics regexps are now compiled in non-verbose mode and multi- + line entries are 'ored'. Existing Topics regexps will be converted when + the list is updated so they will continue to work. + * The List-Help, List-Subscribe, and List-Unsubscribe headers were + incorrectly suppressed in messages that Mailman sends directly to users. + * The 'adminapproved' metadata key is renamed 'moderator_approved'. diff --git a/src/attic/add_members.py b/src/attic/add_members.py new file mode 100644 index 000000000..540c0facb --- /dev/null +++ b/src/attic/add_members.py @@ -0,0 +1,186 @@ +# Copyright (C) 1998-2009 by the Free Software Foundation, Inc. +# +# This file is part of GNU Mailman. +# +# GNU Mailman is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# GNU Mailman. If not, see . + +import os +import sys +import codecs + +from cStringIO import StringIO +from email.utils import parseaddr + +from mailman import Utils +from mailman import i18n +from mailman.app.membership import add_member +from mailman.config import config +from mailman.core import errors +from mailman.email.message import UserNotification +from mailman.interfaces.member import AlreadySubscribedError, DeliveryMode +from mailman.options import SingleMailingListOptions + +_ = i18n._ + + + +class ScriptOptions(SingleMailingListOptions): + usage=_("""\ +%prog [options] + +Add members to a list. 'listname' is the name of the Mailman list you are +adding members to; the list must already exist. + +You must supply at least one of -r and -d options. At most one of the +files can be '-'. +""") + + def add_options(self): + super(ScriptOptions, self).add_options() + self.parser.add_option( + '-r', '--regular-members-file', + type='string', dest='regular', help=_("""\ +A file containing addresses of the members to be added, one address per line. +This list of people become non-digest members. If file is '-', read addresses +from stdin.""")) + self.parser.add_option( + '-d', '--digest-members-file', + type='string', dest='digest', help=_("""\ +Similar to -r, but these people become digest members.""")) + self.parser.add_option( + '-w', '--welcome-msg', + type='yesno', metavar='', help=_("""\ +Set whether or not to send the list members a welcome message, overriding +whatever the list's 'send_welcome_msg' setting is.""")) + self.parser.add_option( + '-a', '--admin-notify', + type='yesno', metavar='', help=_("""\ +Set whether or not to send the list administrators a notification on the +success/failure of these subscriptions, overriding whatever the list's +'admin_notify_mchanges' setting is.""")) + + def sanity_check(self): + if not self.options.listname: + self.parser.error(_('Missing listname')) + if len(self.arguments) > 0: + self.parser.print_error(_('Unexpected arguments')) + if self.options.regular is None and self.options.digest is None: + parser.error(_('At least one of -r or -d is required')) + if self.options.regular == '-' and self.options.digest == '-': + parser.error(_("-r and -d cannot both be '-'")) + + + +def readfile(filename): + if filename == '-': + fp = sys.stdin + else: + # XXX Need to specify other encodings. + fp = codecs.open(filename, encoding='utf-8') + # Strip all the lines of whitespace and discard blank lines + try: + return set(line.strip() for line in fp if line) + finally: + if fp is not sys.stdin: + fp.close() + + + +class Tee: + def __init__(self, outfp): + self._outfp = outfp + + def write(self, msg): + sys.stdout.write(msg) + self._outfp.write(msg) + + + +def addall(mlist, subscribers, delivery_mode, ack, admin_notify, outfp): + tee = Tee(outfp) + for subscriber in subscribers: + try: + fullname, address = parseaddr(subscriber) + # Watch out for the empty 8-bit string. + if not fullname: + fullname = u'' + password = Utils.MakeRandomPassword() + add_member(mlist, address, fullname, password, delivery_mode, + unicode(config.mailman.default_language)) + # XXX Support ack and admin_notify + except AlreadySubscribedError: + print >> tee, _('Already a member: $subscriber') + except errors.InvalidEmailAddress: + if not address: + print >> tee, _('Bad/Invalid email address: blank line') + else: + print >> tee, _('Bad/Invalid email address: $subscriber') + else: + print >> tee, _('Subscribing: $subscriber') + + + +def main(): + options = ScriptOptions() + options.initialize() + + fqdn_listname = options.options.listname + mlist = config.db.list_manager.get(fqdn_listname) + if mlist is None: + parser.error(_('No such list: $fqdn_listname')) + + # Set up defaults. + send_welcome_msg = (options.options.welcome_msg + if options.options.welcome_msg is not None + else mlist.send_welcome_msg) + admin_notify = (options.options.admin_notify + if options.options.admin_notify is not None + else mlist.admin_notify) + + with i18n.using_language(mlist.preferred_language): + if options.options.digest: + dmembers = readfile(options.options.digest) + else: + dmembers = set() + if options.options.regular: + nmembers = readfile(options.options.regular) + else: + nmembers = set() + + if not dmembers and not nmembers: + print _('Nothing to do.') + sys.exit(0) + + outfp = StringIO() + if nmembers: + addall(mlist, nmembers, DeliveryMode.regular, + send_welcome_msg, admin_notify, outfp) + + if dmembers: + addall(mlist, dmembers, DeliveryMode.mime_digests, + send_welcome_msg, admin_notify, outfp) + + config.db.commit() + + if admin_notify: + subject = _('$mlist.real_name subscription notification') + msg = UserNotification( + mlist.owner, mlist.no_reply_address, subject, + outfp.getvalue(), mlist.preferred_language) + msg.send(mlist) + + + +if __name__ == '__main__': + main() diff --git a/src/mailman/Archiver/HyperArch.py b/src/mailman/Archiver/HyperArch.py index d78831e8e..b3601f5fa 100644 --- a/src/mailman/Archiver/HyperArch.py +++ b/src/mailman/Archiver/HyperArch.py @@ -49,6 +49,7 @@ from mailman import i18n from mailman.Archiver import HyperDatabase from mailman.Archiver import pipermail from mailman.config import config +from mailman.interfaces.listmanager import IListManager log = logging.getLogger('mailman.error') @@ -319,7 +320,7 @@ class Article(pipermail.Article): listname = d.get('__listname') if listname: del d['__listname'] - d['_mlist'] = config.db.list_manager.get(listname) + d['_mlist'] = IListManager(config).get(listname) if not d.has_key('_lang'): if hasattr(self, '_mlist'): self._lang = self._mlist.preferred_language diff --git a/src/mailman/app/lifecycle.py b/src/mailman/app/lifecycle.py index d7a8ec1a7..b7f30afd8 100644 --- a/src/mailman/app/lifecycle.py +++ b/src/mailman/app/lifecycle.py @@ -34,6 +34,7 @@ from mailman.config import config from mailman.email.validate import validate from mailman.interfaces.domain import ( BadDomainSpecificationError, IDomainManager) +from mailman.interfaces.listmanager import IListManager from mailman.interfaces.member import MemberRole from mailman.utilities.modules import call_name @@ -51,7 +52,7 @@ def create_list(fqdn_listname, owners=None): listname, domain = fqdn_listname.split('@', 1) if domain not in IDomainManager(config): raise BadDomainSpecificationError(domain) - mlist = config.db.list_manager.create(fqdn_listname) + mlist = IListManager(config).create(fqdn_listname) for style in config.style_manager.lookup(mlist): style.apply(mlist) # Coordinate with the MTA, as defined in the configuration file. @@ -80,7 +81,7 @@ def remove_list(fqdn_listname, mailing_list=None, archives=True): for member in mailing_list.subscribers.members: member.unsubscribe() # Delete the mailing list from the database. - config.db.list_manager.delete(mailing_list) + IListManager(config).delete(mailing_list) # Do the MTA-specific list deletion tasks call_name(config.mta.incoming).create(mailing_list) # Remove the list directory. diff --git a/src/mailman/app/registrar.py b/src/mailman/app/registrar.py index 4ec7991d6..3161ed149 100644 --- a/src/mailman/app/registrar.py +++ b/src/mailman/app/registrar.py @@ -35,6 +35,7 @@ from mailman.email.message import UserNotification from mailman.email.validate import validate from mailman.i18n import _ from mailman.interfaces.domain import IDomain +from mailman.interfaces.listmanager import IListManager from mailman.interfaces.member import MemberRole from mailman.interfaces.pending import IPendable from mailman.interfaces.registrar import IRegistrar @@ -136,7 +137,7 @@ class Registrar: # to the list right now. list_name = pendable.get('list_name') if list_name is not None: - mlist = config.db.list_manager.get(list_name) + mlist = IListManager(config).get(list_name) if mlist: addr.subscribe(mlist, MemberRole.member) return True diff --git a/src/mailman/bin/add_members.py b/src/mailman/bin/add_members.py deleted file mode 100644 index 540c0facb..000000000 --- a/src/mailman/bin/add_members.py +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright (C) 1998-2009 by the Free Software Foundation, Inc. -# -# This file is part of GNU Mailman. -# -# GNU Mailman is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along with -# GNU Mailman. If not, see . - -import os -import sys -import codecs - -from cStringIO import StringIO -from email.utils import parseaddr - -from mailman import Utils -from mailman import i18n -from mailman.app.membership import add_member -from mailman.config import config -from mailman.core import errors -from mailman.email.message import UserNotification -from mailman.interfaces.member import AlreadySubscribedError, DeliveryMode -from mailman.options import SingleMailingListOptions - -_ = i18n._ - - - -class ScriptOptions(SingleMailingListOptions): - usage=_("""\ -%prog [options] - -Add members to a list. 'listname' is the name of the Mailman list you are -adding members to; the list must already exist. - -You must supply at least one of -r and -d options. At most one of the -files can be '-'. -""") - - def add_options(self): - super(ScriptOptions, self).add_options() - self.parser.add_option( - '-r', '--regular-members-file', - type='string', dest='regular', help=_("""\ -A file containing addresses of the members to be added, one address per line. -This list of people become non-digest members. If file is '-', read addresses -from stdin.""")) - self.parser.add_option( - '-d', '--digest-members-file', - type='string', dest='digest', help=_("""\ -Similar to -r, but these people become digest members.""")) - self.parser.add_option( - '-w', '--welcome-msg', - type='yesno', metavar='', help=_("""\ -Set whether or not to send the list members a welcome message, overriding -whatever the list's 'send_welcome_msg' setting is.""")) - self.parser.add_option( - '-a', '--admin-notify', - type='yesno', metavar='', help=_("""\ -Set whether or not to send the list administrators a notification on the -success/failure of these subscriptions, overriding whatever the list's -'admin_notify_mchanges' setting is.""")) - - def sanity_check(self): - if not self.options.listname: - self.parser.error(_('Missing listname')) - if len(self.arguments) > 0: - self.parser.print_error(_('Unexpected arguments')) - if self.options.regular is None and self.options.digest is None: - parser.error(_('At least one of -r or -d is required')) - if self.options.regular == '-' and self.options.digest == '-': - parser.error(_("-r and -d cannot both be '-'")) - - - -def readfile(filename): - if filename == '-': - fp = sys.stdin - else: - # XXX Need to specify other encodings. - fp = codecs.open(filename, encoding='utf-8') - # Strip all the lines of whitespace and discard blank lines - try: - return set(line.strip() for line in fp if line) - finally: - if fp is not sys.stdin: - fp.close() - - - -class Tee: - def __init__(self, outfp): - self._outfp = outfp - - def write(self, msg): - sys.stdout.write(msg) - self._outfp.write(msg) - - - -def addall(mlist, subscribers, delivery_mode, ack, admin_notify, outfp): - tee = Tee(outfp) - for subscriber in subscribers: - try: - fullname, address = parseaddr(subscriber) - # Watch out for the empty 8-bit string. - if not fullname: - fullname = u'' - password = Utils.MakeRandomPassword() - add_member(mlist, address, fullname, password, delivery_mode, - unicode(config.mailman.default_language)) - # XXX Support ack and admin_notify - except AlreadySubscribedError: - print >> tee, _('Already a member: $subscriber') - except errors.InvalidEmailAddress: - if not address: - print >> tee, _('Bad/Invalid email address: blank line') - else: - print >> tee, _('Bad/Invalid email address: $subscriber') - else: - print >> tee, _('Subscribing: $subscriber') - - - -def main(): - options = ScriptOptions() - options.initialize() - - fqdn_listname = options.options.listname - mlist = config.db.list_manager.get(fqdn_listname) - if mlist is None: - parser.error(_('No such list: $fqdn_listname')) - - # Set up defaults. - send_welcome_msg = (options.options.welcome_msg - if options.options.welcome_msg is not None - else mlist.send_welcome_msg) - admin_notify = (options.options.admin_notify - if options.options.admin_notify is not None - else mlist.admin_notify) - - with i18n.using_language(mlist.preferred_language): - if options.options.digest: - dmembers = readfile(options.options.digest) - else: - dmembers = set() - if options.options.regular: - nmembers = readfile(options.options.regular) - else: - nmembers = set() - - if not dmembers and not nmembers: - print _('Nothing to do.') - sys.exit(0) - - outfp = StringIO() - if nmembers: - addall(mlist, nmembers, DeliveryMode.regular, - send_welcome_msg, admin_notify, outfp) - - if dmembers: - addall(mlist, dmembers, DeliveryMode.mime_digests, - send_welcome_msg, admin_notify, outfp) - - config.db.commit() - - if admin_notify: - subject = _('$mlist.real_name subscription notification') - msg = UserNotification( - mlist.owner, mlist.no_reply_address, subject, - outfp.getvalue(), mlist.preferred_language) - msg.send(mlist) - - - -if __name__ == '__main__': - main() diff --git a/src/mailman/bin/inject.py b/src/mailman/bin/inject.py index 73f1b9015..591d6697b 100644 --- a/src/mailman/bin/inject.py +++ b/src/mailman/bin/inject.py @@ -24,6 +24,7 @@ from mailman import Utils from mailman.configuration import config from mailman.i18n import _ from mailman.inject import inject_text +from mailman.interfaces.listmanager import IListManager from mailman.message import Message from mailman.options import SingleMailingListOptions @@ -71,7 +72,7 @@ def main(): options.parser.error(_('Bad queue directory: $qdir')) fqdn_listname = options.options.listname - mlist = config.db.list_manager.get(fqdn_listname) + mlist = IListManager(config).get(fqdn_listname) if mlist is None: options.parser.error(_('No such list: $fqdn_listname')) diff --git a/src/mailman/commands/cli_lists.py b/src/mailman/commands/cli_lists.py index b95d1a779..95c7b650c 100644 --- a/src/mailman/commands/cli_lists.py +++ b/src/mailman/commands/cli_lists.py @@ -39,7 +39,7 @@ from mailman.i18n import _, using_language from mailman.interfaces.command import ICLISubCommand from mailman.interfaces.domain import ( BadDomainSpecificationError, IDomainManager) -from mailman.interfaces.listmanager import ListAlreadyExistsError +from mailman.interfaces.listmanager import IListManager, ListAlreadyExistsError @@ -76,7 +76,7 @@ class Lists: def process(self, args): """See `ICLISubCommand`.""" mailing_lists = [] - list_manager = config.db.list_manager + list_manager = IListManager(config) # Gather the matching mailing lists. for fqdn_name in sorted(list_manager.names): mlist = list_manager.get(fqdn_name) @@ -251,7 +251,7 @@ remove any residual archives.""")) assert len(args.listname) == 1, ( 'Unexpected positional arguments: %s' % args.listname) fqdn_listname = args.listname[0] - mlist = config.db.list_manager.get(fqdn_listname) + mlist = IListManager(config).get(fqdn_listname) if mlist is None: if args.archives: log(_('No such list: $fqdn_listname; ' diff --git a/src/mailman/commands/cli_members.py b/src/mailman/commands/cli_members.py index 7e22166ed..5a2a419f7 100644 --- a/src/mailman/commands/cli_members.py +++ b/src/mailman/commands/cli_members.py @@ -35,6 +35,7 @@ from mailman.app.membership import add_member from mailman.config import config from mailman.i18n import _ from mailman.interfaces.command import ICLISubCommand +from mailman.interfaces.listmanager import IListManager from mailman.interfaces.member import DeliveryMode @@ -67,7 +68,7 @@ class Members: assert len(args.listname) == 1, ( 'Unexpected positional arguments: %s' % args.listname) fqdn_listname = args.listname[0] - mlist = config.db.list_manager.get(fqdn_listname) + mlist = IListManager(config).get(fqdn_listname) if mlist is None: self.parser.error(_('No such list: $fqdn_listname')) if args.filename == '-': diff --git a/src/mailman/config/configure.zcml b/src/mailman/config/configure.zcml index cc1a9face..7e139980c 100644 --- a/src/mailman/config/configure.zcml +++ b/src/mailman/config/configure.zcml @@ -6,6 +6,12 @@ + +