summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorbwarsaw2006-10-08 18:23:22 +0000
committerbwarsaw2006-10-08 18:23:22 +0000
commite22c46d9d4f440bf23d053f80954ab91928f33f7 (patch)
tree300c6636534097e6011cb7f88843d42a7298fe51 /bin
parent838e3e65cf36777f063b8fa3c5ee4ff057ef80a6 (diff)
downloadmailman-e22c46d9d4f440bf23d053f80954ab91928f33f7.tar.gz
mailman-e22c46d9d4f440bf23d053f80954ab91928f33f7.tar.zst
mailman-e22c46d9d4f440bf23d053f80954ab91928f33f7.zip
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.
Diffstat (limited to 'bin')
-rw-r--r--bin/Makefile.in11
-rw-r--r--bin/config_list362
2 files changed, 6 insertions, 367 deletions
diff --git a/bin/Makefile.in b/bin/Makefile.in
index 1aeb68923..2256cfd60 100644
--- a/bin/Makefile.in
+++ b/bin/Makefile.in
@@ -47,16 +47,17 @@ SHELL= /bin/sh
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
diff --git a/bin/config_list b/bin/config_list
deleted file mode 100644
index 25d4fb62a..000000000
--- a/bin/config_list
+++ /dev/null
@@ -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()