Revision: 8051
          http://svn.sourceforge.net/mailman/?rev=8051&view=rev
Author:   bwarsaw
Date:     2006-10-08 11:23:22 -0700 (Sun, 08 Oct 2006)

Log Message:
-----------
First crack at an XML exporter of a mailing list's configuration and
membership.  The next step is to write an XML importer that reads this file.
I'm not 100% sure that all the import data is included yet, but the intent is
that this will be the official way to move mailing lists.

A few notes: member passwords are not included by default, the idea being that
if we enable XML dumping from the web, we don't want the clear text user
passwords to be leaked.  A command line option includes the member passwords.

Also, the various substitutable texts (i.e. those that include %-strings) will
be autoconverted to $-strings.  In Mailman 2.2, we'll only have $-strings,
although this is not yet enforced in other parts of the code yet.

Convert config_list.py to mmshell, $-strings, and optparse.

Modified Paths:
--------------
    trunk/mailman/Mailman/bin/list_lists.py
    trunk/mailman/bin/Makefile.in
    trunk/mailman/configure
    trunk/mailman/configure.in

Added Paths:
-----------
    trunk/mailman/Mailman/bin/config_list.py
    trunk/mailman/Mailman/bin/export.py

Removed Paths:
-------------
    trunk/mailman/bin/config_list

Copied: trunk/mailman/Mailman/bin/config_list.py (from rev 8049, 
trunk/mailman/bin/config_list)
===================================================================
--- trunk/mailman/Mailman/bin/config_list.py                            (rev 0)
+++ trunk/mailman/Mailman/bin/config_list.py    2006-10-08 18:23:22 UTC (rev 
8051)
@@ -0,0 +1,333 @@
+# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+#
+# This program 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 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+import re
+import sys
+import time
+import optparse
+
+from Mailman import Errors
+from Mailman import MailList
+from Mailman import Utils
+from Mailman import Version
+from Mailman import i18n
+from Mailman.configuration import config
+
+_ = i18n._
+__i18n_templates__ = True
+
+NL = '\n'
+nonasciipat = re.compile(r'[\x80-\xff]')
+
+
+
+def parseargs():
+    parser = optparse.OptionParser(version=Version.MAILMAN_VERSION,
+                                   usage=_("""\
+%prog [options] listname
+
+Configure a list from a text file description, or dump a list's configuration
+settings."""))
+    parser.add_option('-i', '--inputfile',
+                      metavar='FILENAME', default=None, type='string',
+                      help=_("""\
+Configure the list by assigning each module-global variable in the file to an
+attribute on the mailing list object, then save the list.  The named file is
+loaded with execfile() and must be legal Python code.  Any variable that isn't
+already an attribute of the list object is ignored (a warning message is
+printed).  See also the -c option.
+
+A special variable named 'mlist' is put into the globals during the execfile,
+which is bound to the actual MailList object.  This lets you do all manner of
+bizarre thing to the list object, but BEWARE!  Using this can severely (and
+possibly irreparably) damage your mailing list!
+
+The may not be used with the -o option."""))
+    parser.add_option('-o', '--outputfile',
+                      metavar='FILENAME', default=None, type='string',
+                      help=_("""\
+Instead of configuring the list, print out a mailing list's configuration
+variables in a format suitable for input using this script.  In this way, you
+can easily capture the configuration settings for a particular list and
+imprint those settings on another list.  FILENAME is the file to output the
+settings to.  If FILENAME is `-', standard out is used.
+
+This may not be used with the -i option."""))
+    parser.add_option('-c', '--checkonly',
+                      default=False, action='store_true', help=_("""\
+With this option, the modified list is not actually changed.  This is only
+useful with the -i option."""))
+    parser.add_option('-v', '--verbose',
+                      default=False, action='store_true', help=_("""\
+Print the name of each attribute as it is being changed.  This is only useful
+with the -i option."""))
+    parser.add_option('-C', '--config',
+                      help=_('Alternative configuration file to use'))
+    opts, args = parser.parse_args()
+    if len(args) > 1:
+        parser.print_help()
+        parser.error(_('Unexpected arguments'))
+    if not args:
+        parser.error(_('List name is required'))
+    return parser, opts, args
+
+
+
+def do_output(listname, outfile, parser):
+    closep = False
+    try:
+        if outfile == '-':
+            outfp = sys.stdout
+        else:
+            outfp = open(outfile, 'w')
+            closep = True
+        # Open the specified list unlocked, since we're only reading it.
+        try:
+            mlist = MailList.MailList(listname, lock=False)
+        except Errors.MMListError:
+            parser.error(_('No such list: $listname'))
+        # Preamble for the config info. PEP 263 charset and capture time.
+        language = mlist.preferred_language
+        charset = Utils.GetCharSet(language)
+        i18n.set_language(language)
+        if not charset:
+            charset = 'us-ascii'
+        when = time.ctime(time.time())
+        print >> outfp, _('''\
+# -*- python -*-
+# -*- coding: $charset -*-
+## "$listname" mailing list configuration settings
+## captured on $when
+''')
+        # Get all the list config info.  All this stuff is accessible via the
+        # web interface.
+        for k in config.ADMIN_CATEGORIES:
+            subcats = mlist.GetConfigSubCategories(k)
+            if subcats is None:
+                do_list_categories(mlist, k, None, outfp)
+            else:
+                for subcat in [t[0] for t in subcats]:
+                    do_list_categories(mlist, k, subcat, outfp)
+    finally:
+        if closep:
+            outfp.close()
+
+
+
+def do_list_categories(mlist, k, subcat, outfp):
+    info = mlist.GetConfigInfo(k, subcat)
+    label, gui = mlist.GetConfigCategories()[k]
+    if info is None:
+        return
+    charset = Utils.GetCharSet(mlist.preferred_language)
+    print >> outfp, '##', k.capitalize(), _('options')
+    print >> outfp, '#'
+    # First, massage the descripton text, which could have obnoxious
+    # leading whitespace on second and subsequent lines due to
+    # triple-quoted string nonsense in the source code.
+    desc = NL.join([s.lstrip() for s in info[0].splitlines()])
+    # Print out the category description
+    desc = Utils.wrap(desc)
+    for line in desc.splitlines():
+        print >> outfp, '#', line
+    print >> outfp
+    for data in info[1:]:
+        if not isinstance(data, tuple):
+            continue
+        varname = data[0]
+        # Variable could be volatile
+        if varname[0] == '_':
+            continue
+        vtype = data[1]
+        # First, massage the descripton text, which could have
+        # obnoxious leading whitespace on second and subsequent lines
+        # due to triple-quoted string nonsense in the source code.
+        desc = NL.join([s.lstrip() for s in data[-1].splitlines()])
+        # Now strip out all HTML tags
+        desc = re.sub('<.*?>', '', desc)
+        # And convert &lt;/&gt; to <>
+        desc = re.sub('&lt;', '<', desc)
+        desc = re.sub('&gt;', '>', desc)
+        # Print out the variable description.
+        desc = Utils.wrap(desc)
+        for line in desc.split('\n'):
+            print >> outfp, '#', line
+        # munge the value based on its type
+        value = None
+        if hasattr(gui, 'getValue'):
+            value = gui.getValue(mlist, vtype, varname, data[2])
+        if value is None and not varname.startswith('_'):
+            value = getattr(mlist, varname)
+        if vtype in (config.String, config.Text, config.FileUpload):
+            print >> outfp, varname, '=',
+            lines = value.splitlines()
+            if not lines:
+                print >> outfp, "''"
+            elif len(lines) == 1:
+                if charset <> 'us-ascii' and nonasciipat.search(lines[0]):
+                    # This is more readable for non-english list.
+                    print >> outfp, '"' + lines[0].replace('"', '\\"') + '"'
+                else:
+                    print >> outfp, repr(lines[0])
+            else:
+                if charset == 'us-ascii' and nonasciipat.search(value):
+                    # Normally, an english list should not have non-ascii char.
+                    print >> outfp, repr(NL.join(lines))
+                else:
+                    outfp.write(' """')
+                    outfp.write(NL.join(lines).replace('"', '\\"'))
+                    outfp.write('"""\n')
+        elif vtype in (config.Radio, config.Toggle):
+            print >> outfp, '#'
+            print >> outfp, '#', _('legal values are:')
+            # TBD: This is disgusting, but it's special cased
+            # everywhere else anyway...
+            if varname == 'subscribe_policy' and \
+                   not config.ALLOW_OPEN_SUBSCRIBE:
+                i = 1
+            else:
+                i = 0
+            for choice in data[2]:
+                print >> outfp, '#   ', i, '= "%s"' % choice
+                i += 1
+            print >> outfp, varname, '=', repr(value)
+        else:
+            print >> outfp, varname, '=', repr(value)
+        print >> outfp
+
+
+
+def getPropertyMap(mlist):
+    guibyprop = {}
+    categories = mlist.GetConfigCategories()
+    for category, (label, gui) in categories.items():
+        if not hasattr(gui, 'GetConfigInfo'):
+            continue
+        subcats = mlist.GetConfigSubCategories(category)
+        if subcats is None:
+            subcats = [(None, None)]
+        for subcat, sclabel in subcats:
+            for element in gui.GetConfigInfo(mlist, category, subcat):
+                if not isinstance(element, tuple):
+                    continue
+                propname = element[0]
+                wtype = element[1]
+                guibyprop[propname] = (gui, wtype)
+    return guibyprop
+
+
+class FakeDoc:
+    # Fake the error reporting API for the htmlformat.Document class
+    def addError(self, s, tag=None, *args):
+        if tag:
+            print >> sys.stderr, tag
+        print >> sys.stderr, s % args
+
+    def set_language(self, val):
+        pass
+
+
+
+def do_input(listname, infile, checkonly, verbose, parser):
+    fakedoc = FakeDoc()
+    # Open the specified list locked, unless checkonly is set
+    try:
+        mlist = MailList.MailList(listname, lock=not checkonly)
+    except Errors.MMListError, e:
+        parser.error(_('No such list "$listname"\n$e'))
+    savelist = False
+    guibyprop = getPropertyMap(mlist)
+    try:
+        globals = {'mlist': mlist}
+        # Any exception that occurs in execfile() will cause the list to not
+        # be saved, but any other problems are not save-fatal.
+        execfile(infile, globals)
+        savelist = True
+        for k, v in globals.items():
+            if k in ('mlist', '__builtins__'):
+                continue
+            if not hasattr(mlist, k):
+                print >> sys.stderr, _('attribute "$k" ignored')
+                continue
+            if verbose:
+                print >> sys.stderr, _('attribute "$k" changed')
+            missing = []
+            gui, wtype = guibyprop.get(k, (missing, missing))
+            if gui is missing:
+                # This isn't an official property of the list, but that's
+                # okay, we'll just restore it the old fashioned way
+                print >> sys.stderr, _('Non-standard property restored: $k')
+                setattr(mlist, k, v)
+            else:
+                # BAW: This uses non-public methods.  This logic taken from
+                # the guts of GUIBase.handleForm().
+                try:
+                    validval = gui._getValidValue(mlist, k, wtype, v)
+                except ValueError:
+                    print >> sys.stderr, _('Invalid value for property: $k')
+                except Errors.EmailAddressError:
+                    print >> sys.stderr, _(
+                        'Bad email address for option $k: $v')
+                else:
+                    # BAW: Horrible hack, but then this is special cased
+                    # everywhere anyway. :(  Privacy._setValue() knows that
+                    # when ALLOW_OPEN_SUBSCRIBE is false, the web values are
+                    # 0, 1, 2 but these really should be 1, 2, 3, so it adds
+                    # one.  But we really do provide [0..3] so we need to undo
+                    # the hack that _setValue adds. :( :(
+                    if k == 'subscribe_policy' and \
+                           not mm_cfg.ALLOW_OPEN_SUBSCRIBE:
+                        validval -= 1
+                    # BAW: Another horrible hack.  This one is just too hard
+                    # to fix in a principled way in Mailman 2.1
+                    elif k == 'new_member_options':
+                        # Because this is a Checkbox, _getValidValue()
+                        # transforms the value into a list of one item.
+                        validval = validval[0]
+                        validval = [bitfield for bitfield, bitval
+                                    in mm_cfg.OPTINFO.items()
+                                    if validval & bitval]
+                    gui._setValue(mlist, k, validval, fakedoc)
+            # BAW: when to do gui._postValidate()???
+    finally:
+        if savelist and not checkonly:
+            mlist.Save()
+        mlist.Unlock()
+
+
+
+def main():
+    parser, opts, args = parseargs()
+    config.load(opts.config)
+    listname = args[0]
+
+    # Sanity check
+    if opts.inputfile and opts.outputfile:
+        parser.error(_('Only one of -i or -o is allowed'))
+    if not opts.inputfile and not opts.outputfile:
+        parser.error(_('One of -i or -o is required'))
+
+    if opts.outputfile:
+        do_output(listname, opts.outputfile, parser)
+    else:
+        do_input(listname, opts.inputfile, opts.checkonly,
+                 opts.verbose, parser)
+
+
+
+if __name__ == '__main__':
+    main()

Added: trunk/mailman/Mailman/bin/export.py
===================================================================
--- trunk/mailman/Mailman/bin/export.py                         (rev 0)
+++ trunk/mailman/Mailman/bin/export.py 2006-10-08 18:23:22 UTC (rev 8051)
@@ -0,0 +1,308 @@
+# Copyright (C) 2006 by the Free Software Foundation, Inc.
+#
+# This program 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 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Export an XML representation of a mailing list."""
+
+import sys
+import datetime
+import optparse
+
+from xml.sax.saxutils import escape
+
+from Mailman import Defaults
+from Mailman import Errors
+from Mailman import MemberAdaptor
+from Mailman import Utils
+from Mailman import Version
+from Mailman.MailList import MailList
+from Mailman.configuration import config
+from Mailman.i18n import _
+
+__i18n_templates__ = True
+
+SPACE           = ' '
+DOLLAR_STRINGS  = ('msg_header', 'msg_footer',
+                   'digest_header', 'digest_footer',
+                   'autoresponse_postings_text',
+                   'autoresponse_admin_text',
+                   'autoresponse_request_text')
+
+
+
+class Indenter:
+    def __init__(self, fp, indentwidth=4):
+        self._fp     = fp
+        self._indent = 0
+        self._width  = indentwidth
+
+    def indent(self):
+        self._indent += 1
+
+    def dedent(self):
+        self._indent -= 1
+        assert self._indent >= 0
+
+    def write(self, s):
+        self._fp.write(self._indent * self._width * ' ')
+        self._fp.write(s)
+
+
+
+class XMLDumper(object):
+    def __init__(self, fp):
+        self._fp        = Indenter(fp)
+        self._tagbuffer = None
+        self._stack     = []
+
+    def _makeattrs(self, tagattrs):
+        # The attribute values might contain angle brackets.  They might also
+        # be None.
+        attrs = []
+        for k, v in tagattrs.items():
+            if v is None:
+                v = ''
+            else:
+                v = escape(str(v))
+            attrs.append('%s="%s"' % (k, v))
+        return SPACE.join(attrs)
+
+    def _flush(self, more=True):
+        if not self._tagbuffer:
+            return
+        name, attributes = self._tagbuffer
+        self._tagbuffer = None
+        if attributes:
+            attrstr = ' ' + self._makeattrs(attributes)
+        else:
+            attrstr = ''
+        if more:
+            print >> self._fp, '<%s%s>' % (name, attrstr)
+            self._fp.indent()
+            self._stack.append(name)
+        else:
+            print >> self._fp, '<%s%s/>' % (name, attrstr)
+
+    # Use this method when you know you have sub-elements.
+    def _push_element(self, _name, **_tagattrs):
+        self._flush()
+        self._tagbuffer = (_name, _tagattrs)
+
+    def _pop_element(self, _name):
+        buffered = bool(self._tagbuffer)
+        self._flush(more=False)
+        if not buffered:
+            name = self._stack.pop()
+            assert name == _name, 'got: %s, expected: %s' % (_name, name)
+            self._fp.dedent()
+            print >> self._fp, '</%s>' % name
+
+    # Use this method when you do not have sub-elements
+    def _element(self, _name, _value=None, **_attributes):
+        self._flush()
+        if _attributes:
+            attrs = ' ' + self._makeattrs(_attributes)
+        else:
+            attrs = ''
+        if _value is None:
+            print >> self._fp, '<%s%s/>' % (_name, attrs)
+        else:
+            # The value might contain angle brackets.
+            value = escape(str(_value))
+            print >> self._fp, '<%s%s>%s</%s>' % (_name, attrs, value, _name)
+
+    def _do_list_categories(self, mlist, k, subcat=None):
+        is_converted = bool(getattr(mlist, 'use_dollar_strings', False))
+        info = mlist.GetConfigInfo(k, subcat)
+        label, gui = mlist.GetConfigCategories()[k]
+        if info is None:
+            return
+        for data in info[1:]:
+            if not isinstance(data, tuple):
+                continue
+            varname = data[0]
+            # Variable could be volatile
+            if varname.startswith('_'):
+                continue
+            vtype = data[1]
+            # Munge the value based on its type
+            value = None
+            if hasattr(gui, 'getValue'):
+                value = gui.getValue(mlist, vtype, varname, data[2])
+            if value is None:
+                value = getattr(mlist, varname)
+            # Do %-string to $-string conversions if the list hasn't already
+            # been converted.
+            if varname == 'use_dollar_strings':
+                continue
+            if not is_converted and varname in DOLLAR_STRINGS:
+                value = Utils.to_dollar(value)
+            if isinstance(value, list):
+                self._push_element('option', name=varname)
+                for v in value:
+                    self._element('value', v)
+                self._pop_element('option')
+            else:
+                self._element('option', name=varname, value=value)
+
+    def _dump_list(self, mlist, with_passwords):
+        # Write list configuration values
+        self._push_element('list', name=mlist.fqdn_listname)
+        self._element('language', mlist.preferred_language)
+        for k in config.ADMIN_CATEGORIES:
+            subcats = mlist.GetConfigSubCategories(k)
+            if subcats is None:
+                self._do_list_categories(mlist, k)
+            else:
+                for subcat in [t[0] for t in subcats]:
+                    self._do_list_categories(mlist, k, subcat)
+        # Write membership
+        self._push_element('roster')
+        digesters = set(mlist.getDigestMemberKeys())
+        for member in sorted(mlist.getMembers()):
+            attrs = dict(id=member)
+            cased = mlist.getMemberCPAddress(member)
+            if cased <> member:
+                dict['original'] = cased
+            self._push_element('member', **attrs)
+            self._element('realname', mlist.getMemberName(member))
+            if with_passwords:
+                self._element('password', mlist.getMemberPassword(member))
+            self._element('language', mlist.getMemberLanguage(member))
+            # Delivery status, combined with the type of delivery
+            attrs = {}
+            status = mlist.getDeliveryStatus(member)
+            if status == MemberAdaptor.ENABLED:
+                attrs['status'] = 'enabled'
+            else:
+                attrs['status'] = 'disabled'
+                attrs['reason'] = {MemberAdaptor.BYUSER    : 'byuser',
+                                   MemberAdaptor.BYADMIN   : 'byadmin',
+                                   MemberAdaptor.BYBOUNCE  : 'bybounce',
+                                   }.get(mlist.getDeliveryStatus(member),
+                                         'unknown')
+            if member in digesters:
+                if mlist.getMemberOption('plain'):
+                    attrs['delivery'] = 'plain'
+                else:
+                    attrs['delivery'] = 'mime'
+            else:
+                attrs['delivery'] = 'regular'
+            changed = mlist.getDeliveryStatusChangeTime(member)
+            if changed:
+                when = datetime.datetime.fromtimestamp(changed)
+                attrs['changed'] = when.isoformat()
+            self._element('delivery', **attrs)
+            self._push_element('options')
+            for option, flag in Defaults.OPTINFO.items():
+                # Digest/Regular delivery flag must be handled separately
+                if option in ('digest', 'plain'):
+                    continue
+                value = mlist.getMemberOption(member, flag)
+                self._element(option, value)
+            self._pop_element('options')
+            topics = mlist.getMemberTopics(member)
+            if not topics:
+                self._element('topics')
+            else:
+                self._push_element('topics')
+                for topic in topics:
+                    self._element('topic', topic)
+                self._pop_element('topics')
+            self._pop_element('member')
+        self._pop_element('roster')
+        self._pop_element('list')
+
+    def dump(self, listnames, with_passwords=False):
+        print >> self._fp, '<?xml version="1.0" encoding="UTF-8"?>'
+        self._push_element('mailman', **{
+            'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
+            'xsi:noNamespaceSchemaLocation': 'ssi-1.0.xsd',
+            })
+        for listname in sorted(listnames):
+            try:
+                mlist = MailList(listname, lock=False)
+            except Errors.MMUnknownListError:
+                print >> sys.stderr, _('No such list: $listname')
+                continue
+            self._dump_list(mlist, with_passwords)
+        self._pop_element('mailman')
+
+    def close(self):
+        while self._stack:
+            self._pop_element()
+
+
+
+def parseargs():
+    parser = optparse.OptionParser(version=Version.MAILMAN_VERSION,
+                                   usage=_("""\
+%prog [options]
+
+Export the configuration and members of a mailing list in XML format."""))
+    parser.add_option('-o', '--outputfile',
+                      metavar='FILENAME', default=None, type='string',
+                      help=_("""\
+Output XML to FILENAME.  If not given, or if FILENAME is '-', standard out is
+used."""))
+    parser.add_option('-p', '--with-passwords',
+                      default=False, action='store_true', help=_("""\
+With this option, user passwords are included in cleartext.  For this reason,
+the default is to not include passwords."""))
+    parser.add_option('-l', '--listname',
+                      default=[], action='append', type='string',
+                      dest='listnames', help=_("""\
+The list to include in the output.  If not given, then all mailing lists are
+included in the XML output.  Multiple -l flags may be given."""))
+    parser.add_option('-C', '--config',
+                      help=_('Alternative configuration file to use'))
+    opts, args = parser.parse_args()
+    if args:
+        parser.print_help()
+        parser.error(_('Unexpected arguments'))
+    return parser, opts, args
+
+
+
+def main():
+    parser, opts, args = parseargs()
+    config.load(opts.config)
+
+    if opts.outputfile in (None, '-'):
+        fp = sys.stdout
+    else:
+        fp = open(opts.outputfile, 'w')
+
+    try:
+        dumper = XMLDumper(fp)
+        if opts.listnames:
+            listnames = []
+            for listname in opts.listnames:
+                if '@' not in listname:
+                    listname = '[EMAIL PROTECTED]' % (listname, 
config.DEFAULT_EMAIL_HOST)
+                listnames.append(listname)
+        else:
+            listnames = Utils.list_names()
+        dumper.dump(listnames, opts.with_passwords)
+        dumper.close()
+    finally:
+        if fp is not sys.stdout:
+            fp.close()
+
+
+
+if __name__ == '__main__':
+    main()

Modified: trunk/mailman/Mailman/bin/list_lists.py
===================================================================
--- trunk/mailman/Mailman/bin/list_lists.py     2006-10-05 04:20:32 UTC (rev 
8050)
+++ trunk/mailman/Mailman/bin/list_lists.py     2006-10-08 18:23:22 UTC (rev 
8051)
@@ -15,7 +15,6 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 # USA.
 
-import sys
 import optparse
 
 from Mailman import Defaults
@@ -59,8 +58,7 @@
     opts, args = parser.parse_args()
     if args:
         parser.print_help()
-        print >> sys.stderr, _('Unexpected arguments')
-        sys.exit(1)
+        parser.error(_('Unexpected arguments'))
     return parser, opts, args
 
 

Modified: trunk/mailman/bin/Makefile.in
===================================================================
--- trunk/mailman/bin/Makefile.in       2006-10-05 04:20:32 UTC (rev 8050)
+++ trunk/mailman/bin/Makefile.in       2006-10-08 18:23:22 UTC (rev 8051)
@@ -47,16 +47,17 @@
 SCRIPTS=       mmshell \
                remove_members clone_member \
                sync_members check_db withlist \
-               config_list dumpdb cleanarch \
+               dumpdb cleanarch \
                list_admins \
                fix_url.py convert.py transcheck \
                msgfmt.py discard \
                reset_pw.py templ2pot.py po2templ.py
 
-LN_SCRIPTS=    add_members arch change_pw check_perms find_member \
-               genaliases inject list_lists list_members list_owners \
-               mailmanctl mmsitepass newlist qrunner rmlist show_qfiles \
-               show_mm_cfg testall unshunt update version
+LN_SCRIPTS=    add_members arch change_pw check_perms config_list \
+               export find_member genaliases inject list_lists \
+               list_members list_owners mailmanctl mmsitepass newlist \
+               qrunner rmlist show_qfiles show_mm_cfg testall unshunt \
+               update version
 
 BUILDDIR=      ../build/bin
 

Deleted: trunk/mailman/bin/config_list
===================================================================
--- trunk/mailman/bin/config_list       2006-10-05 04:20:32 UTC (rev 8050)
+++ trunk/mailman/bin/config_list       2006-10-08 18:23:22 UTC (rev 8051)
@@ -1,362 +0,0 @@
-#! @PYTHON@
-#
-# Copyright (C) 1998-2005 by the Free Software Foundation, Inc.
-#
-# This program 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 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-"""Configure a list from a text file description.
-
-Usage: config_list [options] listname
-
-Options:
-    --inputfile filename
-    -i filename
-        Configure the list by assigning each module-global variable in the
-        file to an attribute on the list object, then saving the list.  The
-        named file is loaded with execfile() and must be legal Python code.
-        Any variable that isn't already an attribute of the list object is
-        ignored (a warning message is printed).  See also the -c option.
-
-        A special variable named `mlist' is put into the globals during the
-        execfile, which is bound to the actual MailList object.  This lets you
-        do all manner of bizarre thing to the list object, but BEWARE!  Using
-        this can severely (and possibly irreparably) damage your mailing list!
-
-    --outputfile filename
-    -o filename
-        Instead of configuring the list, print out a list's configuration
-        variables in a format suitable for input using this script.  In this
-        way, you can easily capture the configuration settings for a
-        particular list and imprint those settings on another list.  filename
-        is the file to output the settings to.  If filename is `-', standard
-        out is used.
-
-    --checkonly
-    -c
-        With this option, the modified list is not actually changed.  Only
-        useful with -i.
-
-    --verbose
-    -v
-        Print the name of each attribute as it is being changed.  Only useful
-        with -i.
-
-    --help
-    -h
-        Print this help message and exit.
-
-The options -o and -i are mutually exclusive.
-
-"""
-
-import sys
-import re
-import time
-import getopt
-from types import TupleType
-
-import paths
-from Mailman import mm_cfg
-from Mailman import MailList
-from Mailman import Utils
-from Mailman import Errors
-from Mailman import i18n
-
-_ = i18n._
-
-NL = '\n'
-nonasciipat = re.compile(r'[\x80-\xff]')
-
-
-
-def usage(code, msg=''):
-    if code:
-        fd = sys.stderr
-    else:
-        fd = sys.stdout
-    print >> fd, _(__doc__)
-    if msg:
-        print >> fd, msg
-    sys.exit(code)
-
-
-
-def do_output(listname, outfile):
-    closep = 0
-    try:
-        if outfile == '-':
-            outfp = sys.stdout
-        else:
-            outfp = open(outfile, 'w')
-            closep = 1
-        # Open the specified list unlocked, since we're only reading it.
-        try:
-            mlist = MailList.MailList(listname, lock=0)
-        except Errors.MMListError:
-            usage(1, _('No such list: %(listname)s'))
-        # Preamble for the config info. PEP263 charset and capture time.
-        language = mlist.preferred_language
-        charset = Utils.GetCharSet(language)
-        i18n.set_language(language)
-        if not charset:
-            charset = 'us-ascii'
-        when = time.ctime(time.time())
-        print >> outfp, _('''\
-# -*- python -*-
-# -*- coding: %(charset)s -*-
-## "%(listname)s" mailing list configuration settings
-## captured on %(when)s
-''')
-        # get all the list config info.  all this stuff is accessible via the
-        # web interface
-        for k in mm_cfg.ADMIN_CATEGORIES:
-            subcats = mlist.GetConfigSubCategories(k)
-            if subcats is None:
-                do_list_categories(mlist, k, None, outfp)
-            else:
-                for subcat in [t[0] for t in subcats]:
-                    do_list_categories(mlist, k, subcat, outfp)
-    finally:
-        if closep:
-            outfp.close()
-
-
-def do_list_categories(mlist, k, subcat, outfp):
-    info = mlist.GetConfigInfo(k, subcat)
-    label, gui = mlist.GetConfigCategories()[k]
-    if info is None:
-        return
-    charset = Utils.GetCharSet(mlist.preferred_language)
-    print >> outfp, '##', k.capitalize(), _('options')
-    print >> outfp, '#'
-    # First, massage the descripton text, which could have obnoxious
-    # leading whitespace on second and subsequent lines due to
-    # triple-quoted string nonsense in the source code.
-    desc = NL.join([s.lstrip() for s in info[0].split('\n')])
-    # Print out the category description
-    desc = Utils.wrap(desc)
-    for line in desc.split('\n'):
-        print >> outfp, '#', line
-    print >> outfp
-    for data in info[1:]:
-        if not isinstance(data, TupleType):
-            continue
-        varname = data[0]
-        # Variable could be volatile
-        if varname[0] == '_':
-            continue
-        vtype = data[1]
-        # First, massage the descripton text, which could have
-        # obnoxious leading whitespace on second and subsequent lines
-        # due to triple-quoted string nonsense in the source code.
-        desc = NL.join([s.lstrip() for s in data[-1].split('\n')])
-        # Now strip out all HTML tags
-        desc = re.sub('<.*?>', '', desc)
-        # And convert &lt;/&gt; to <>
-        desc = re.sub('&lt;', '<', desc)
-        desc = re.sub('&gt;', '>', desc)
-        # Print out the variable description.
-        desc = Utils.wrap(desc)
-        for line in desc.split('\n'):
-            print >> outfp, '#', line
-        # munge the value based on its type
-        value = None
-        if hasattr(gui, 'getValue'):
-            value = gui.getValue(mlist, vtype, varname, data[2])
-        if value is None and not varname.startswith('_'):
-            value = getattr(mlist, varname)
-        if vtype in (mm_cfg.String, mm_cfg.Text, mm_cfg.FileUpload):
-            print >> outfp, varname, '=',
-            lines = value.splitlines()
-            if not lines:
-                print >> outfp, "''"
-            elif len(lines) == 1:
-                if charset <> 'us-ascii' and nonasciipat.search(lines[0]):
-                    # This is more readable for non-english list.
-                    print >> outfp, '"' + lines[0].replace('"', '\\"') + '"'
-                else:
-                    print >> outfp, repr(lines[0])
-            else:
-                if charset == 'us-ascii' and nonasciipat.search(value):
-                    # Normally, an english list should not have non-ascii char.
-                    print >> outfp, repr(NL.join(lines))
-                else:
-                    outfp.write(' """')
-                    outfp.write(NL.join(lines).replace('"', '\\"'))
-                    outfp.write('"""\n')
-        elif vtype in (mm_cfg.Radio, mm_cfg.Toggle):
-            print >> outfp, '#'
-            print >> outfp, '#', _('legal values are:')
-            # TBD: This is disgusting, but it's special cased
-            # everywhere else anyway...
-            if varname == 'subscribe_policy' and \
-                   not mm_cfg.ALLOW_OPEN_SUBSCRIBE:
-                i = 1
-            else:
-                i = 0
-            for choice in data[2]:
-                print >> outfp, '#   ', i, '= "%s"' % choice
-                i += 1
-            print >> outfp, varname, '=', repr(value)
-        else:
-            print >> outfp, varname, '=', repr(value)
-        print >> outfp
-
-
-
-def getPropertyMap(mlist):
-    guibyprop = {}
-    categories = mlist.GetConfigCategories()
-    for category, (label, gui) in categories.items():
-        if not hasattr(gui, 'GetConfigInfo'):
-            continue
-        subcats = mlist.GetConfigSubCategories(category)
-        if subcats is None:
-            subcats = [(None, None)]
-        for subcat, sclabel in subcats:
-            for element in gui.GetConfigInfo(mlist, category, subcat):
-                if not isinstance(element, TupleType):
-                    continue
-                propname = element[0]
-                wtype = element[1]
-                guibyprop[propname] = (gui, wtype)
-    return guibyprop
-
-
-class FakeDoc:
-    # Fake the error reporting API for the htmlformat.Document class
-    def addError(self, s, tag=None, *args):
-        if tag:
-            print >> sys.stderr, tag
-        print >> sys.stderr, s % args
-
-    def set_language(self, val):
-        pass
-
-
-def do_input(listname, infile, checkonly, verbose):
-    fakedoc = FakeDoc()
-    # open the specified list locked, unless checkonly is set
-    try:
-        mlist = MailList.MailList(listname, lock=not checkonly)
-    except Errors.MMListError, e:
-        usage(1, _('No such list "%(listname)s"\n%(e)s'))
-    savelist = 0
-    guibyprop = getPropertyMap(mlist)
-    try:
-        globals = {'mlist': mlist}
-        # Any exception that occurs in execfile() will cause the list to not
-        # be saved, but any other problems are not save-fatal.
-        execfile(infile, globals)
-        savelist = 1
-        for k, v in globals.items():
-            if k in ('mlist', '__builtins__'):
-                continue
-            if not hasattr(mlist, k):
-                print >> sys.stderr, _('attribute "%(k)s" ignored')
-                continue
-            if verbose:
-                print >> sys.stderr, _('attribute "%(k)s" changed')
-            missing = []
-            gui, wtype = guibyprop.get(k, (missing, missing))
-            if gui is missing:
-                # This isn't an official property of the list, but that's
-                # okay, we'll just restore it the old fashioned way
-                print >> sys.stderr, _('Non-standard property restored: %(k)s')
-                setattr(mlist, k, v)
-            else:
-                # BAW: This uses non-public methods.  This logic taken from
-                # the guts of GUIBase.handleForm().
-                try:
-                    validval = gui._getValidValue(mlist, k, wtype, v)
-                except ValueError:
-                    print >> sys.stderr, _('Invalid value for property: %(k)s')
-                except Errors.EmailAddressError:
-                    print >> sys.stderr, _(
-                        'Bad email address for option %(k)s: %(v)s')
-                else:
-                    # BAW: Horrible hack, but then this is special cased
-                    # everywhere anyway. :(  Privacy._setValue() knows that
-                    # when ALLOW_OPEN_SUBSCRIBE is false, the web values are
-                    # 0, 1, 2 but these really should be 1, 2, 3, so it adds
-                    # one.  But we really do provide [0..3] so we need to undo
-                    # the hack that _setValue adds. :( :(
-                    if k == 'subscribe_policy' and \
-                           not mm_cfg.ALLOW_OPEN_SUBSCRIBE:
-                        validval -= 1
-                    # BAW: Another horrible hack.  This one is just too hard
-                    # to fix in a principled way in Mailman 2.1
-                    elif k == 'new_member_options':
-                        # Because this is a Checkbox, _getValidValue()
-                        # transforms the value into a list of one item.
-                        validval = validval[0]
-                        validval = [bitfield for bitfield, bitval
-                                    in mm_cfg.OPTINFO.items()
-                                    if validval & bitval]
-                    gui._setValue(mlist, k, validval, fakedoc)
-            # BAW: when to do gui._postValidate()???
-    finally:
-        if savelist and not checkonly:
-            mlist.Save()
-        mlist.Unlock()
-
-
-
-def main():
-    try:
-        opts, args = getopt.getopt(
-            sys.argv[1:], 'ci:o:vh',
-            ['checkonly', 'inputfile=', 'outputfile=', 'verbose', 'help'])
-    except getopt.error, msg:
-        usage(1, msg)
-
-    # defaults
-    infile = None
-    outfile = None
-    checkonly = 0
-    verbose = 0
-    for opt, arg in opts:
-        if opt in ('-h', '--help'):
-            usage(0)
-        elif opt in ('-o', '--outputfile'):
-            outfile = arg
-        elif opt in ('-i', '--inputfile'):
-            infile = arg
-        elif opt in ('-c', '--checkonly'):
-            checkonly = 1
-        elif opt in ('-v', '--verbose'):
-            verbose = 1
-
-    # sanity check
-    if infile is not None and outfile is not None:
-        usage(1, _('Only one of -i or -o is allowed'))
-    if infile is None and outfile is None:
-        usage(1, _('One of -i or -o is required'))
-
-    # get the list name
-    if len(args) <> 1:
-        usage(1, _('List name is required'))
-    listname = args[0].lower().strip()
-
-    if outfile:
-        do_output(listname, outfile)
-    else:
-        do_input(listname, infile, checkonly, verbose)
-
-
-
-if __name__ == '__main__':
-    main()

Modified: trunk/mailman/configure
===================================================================
--- trunk/mailman/configure     2006-10-05 04:20:32 UTC (rev 8050)
+++ trunk/mailman/configure     2006-10-08 18:23:22 UTC (rev 8051)
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.in Revision: 8038 .
+# From configure.in Revision: 8040 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.59 for GNU Mailman 2.2.0a0.
 #
@@ -4287,7 +4287,6 @@
 SCRIPTS="build/bin/check_db:bin/check_db \
 build/bin/cleanarch:bin/cleanarch \
 build/bin/clone_member:bin/clone_member \
-build/bin/config_list:bin/config_list \
 build/bin/convert.py:bin/convert.py \
 build/bin/discard:bin/discard \
 build/bin/dumpdb:bin/dumpdb \

Modified: trunk/mailman/configure.in
===================================================================
--- trunk/mailman/configure.in  2006-10-05 04:20:32 UTC (rev 8050)
+++ trunk/mailman/configure.in  2006-10-08 18:23:22 UTC (rev 8051)
@@ -595,7 +595,6 @@
 bin/check_db \
 bin/cleanarch \
 bin/clone_member \
-bin/config_list \
 bin/convert.py \
 bin/discard \
 bin/dumpdb \


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to