diff options
| author | bwarsaw | 2006-10-08 18:23:22 +0000 |
|---|---|---|
| committer | bwarsaw | 2006-10-08 18:23:22 +0000 |
| commit | e22c46d9d4f440bf23d053f80954ab91928f33f7 (patch) | |
| tree | 300c6636534097e6011cb7f88843d42a7298fe51 /bin | |
| parent | 838e3e65cf36777f063b8fa3c5ee4ff057ef80a6 (diff) | |
| download | mailman-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.in | 11 | ||||
| -rw-r--r-- | bin/config_list | 362 |
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 </> to <> - desc = re.sub('<', '<', desc) - desc = re.sub('>', '>', 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() |
