diff options
| author | Barry Warsaw | 2008-02-27 01:26:18 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2008-02-27 01:26:18 -0500 |
| commit | a1c73f6c305c7f74987d99855ba59d8fa823c253 (patch) | |
| tree | 65696889450862357c9e05c8e9a589f1bdc074ac /Mailman/Cgi/admin.py | |
| parent | 3f31f8cce369529d177cfb5a7c66346ec1e12130 (diff) | |
| download | mailman-a1c73f6c305c7f74987d99855ba59d8fa823c253.tar.gz mailman-a1c73f6c305c7f74987d99855ba59d8fa823c253.tar.zst mailman-a1c73f6c305c7f74987d99855ba59d8fa823c253.zip | |
Diffstat (limited to 'Mailman/Cgi/admin.py')
| -rw-r--r-- | Mailman/Cgi/admin.py | 1433 |
1 files changed, 0 insertions, 1433 deletions
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py deleted file mode 100644 index 9516564a2..000000000 --- a/Mailman/Cgi/admin.py +++ /dev/null @@ -1,1433 +0,0 @@ -# Copyright (C) 1998-2008 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. - -"""Process and produce the list-administration options forms.""" - -import os -import re -import cgi -import sha -import sys -import urllib -import logging - -from email.Utils import unquote, parseaddr, formataddr -from string import lowercase, digits - -from Mailman import Errors -from Mailman import MailList -from Mailman import MemberAdaptor -from Mailman import Utils -from Mailman import i18n -from Mailman import passwords -from Mailman.Cgi import Auth -from Mailman.UserDesc import UserDesc -from Mailman.configuration import config -from Mailman.htmlformat import * - -# Set up i18n -_ = i18n._ -i18n.set_language(config.DEFAULT_SERVER_LANGUAGE) - -NL = '\n' -OPTCOLUMNS = 11 - -log = logging.getLogger('mailman.error') - - - -def main(): - # Try to find out which list is being administered - parts = Utils.GetPathPieces() - if not parts: - # None, so just do the admin overview and be done with it - admin_overview() - return - # Get the list object - listname = parts[0].lower() - try: - mlist = MailList.MailList(listname, lock=False) - except Errors.MMListError, e: - # Avoid cross-site scripting attacks - safelistname = Utils.websafe(listname) - admin_overview(_('No such list <em>%(safelistname)s</em>')) - log.error('admin.py access for non-existent list: %s', listname) - return - # Now that we know what list has been requested, all subsequent admin - # pages are shown in that list's preferred language. - i18n.set_language(mlist.preferred_language) - # If the user is not authenticated, we're done. - cgidata = cgi.FieldStorage(keep_blank_values=1) - - if not mlist.WebAuthenticate((config.AuthListAdmin, - config.AuthSiteAdmin), - cgidata.getvalue('adminpw', '')): - if cgidata.has_key('adminpw'): - # This is a re-authorization attempt - msg = Bold(FontSize('+1', _('Authorization failed.'))).Format() - else: - msg = '' - Auth.loginpage(mlist, 'admin', msg=msg) - return - - # Which subcategory was requested? Default is `general' - if len(parts) == 1: - category = 'general' - subcat = None - elif len(parts) == 2: - category = parts[1] - subcat = None - else: - category = parts[1] - subcat = parts[2] - - # Is this a log-out request? - if category == 'logout': - print mlist.ZapCookie(config.AuthListAdmin) - Auth.loginpage(mlist, 'admin', frontpage=True) - return - - # Sanity check - if category not in mlist.GetConfigCategories().keys(): - category = 'general' - - # Is the request for variable details? - varhelp = None - qsenviron = os.environ.get('QUERY_STRING') - parsedqs = None - if qsenviron: - parsedqs = cgi.parse_qs(qsenviron) - if cgidata.has_key('VARHELP'): - varhelp = cgidata.getvalue('VARHELP') - elif parsedqs: - # POST methods, even if their actions have a query string, don't get - # put into FieldStorage's keys :-( - qs = parsedqs.get('VARHELP') - if qs and isinstance(qs, list): - varhelp = qs[0] - if varhelp: - option_help(mlist, varhelp) - return - - # The html page document - doc = Document() - doc.set_language(mlist.preferred_language) - mlist.Lock() - try: - if cgidata.keys(): - # There are options to change - change_options(mlist, category, subcat, cgidata, doc) - # Let the list sanity check the changed values - mlist.CheckValues() - # Additional sanity checks - if not mlist.digestable and not mlist.nondigestable: - doc.addError( - _('''You have turned off delivery of both digest and - non-digest messages. This is an incompatible state of - affairs. You must turn on either digest delivery or - non-digest delivery or your mailing list will basically be - unusable.'''), tag=_('Warning: ')) - - if not mlist.digestable and mlist.getDigestMemberKeys(): - doc.addError( - _('''You have digest members, but digests are turned - off. Those people will not receive mail.'''), - tag=_('Warning: ')) - if not mlist.nondigestable and mlist.getRegularMemberKeys(): - doc.addError( - _('''You have regular list members but non-digestified mail is - turned off. They will receive mail until you fix this - problem.'''), tag=_('Warning: ')) - # Glom up the results page and print it out - show_results(mlist, doc, category, subcat, cgidata) - print doc.Format() - mlist.Save() - finally: - mlist.Unlock() - - - -def admin_overview(msg=''): - # Show the administrative overview page, with the list of all the lists on - # this host. msg is an optional error message to display at the top of - # the page. - # - # This page should be displayed in the server's default language, which - # should have already been set. - hostname = Utils.get_request_domain() - legend = _('%(hostname)s mailing lists - Admin Links') - # The html `document' - doc = Document() - doc.set_language(config.DEFAULT_SERVER_LANGUAGE) - doc.SetTitle(legend) - # The table that will hold everything - table = Table(border=0, width="100%") - table.AddRow([Center(Header(2, legend))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, - bgcolor=config.WEB_HEADER_COLOR) - # Skip any mailing list that isn't advertised. - advertised = [] - for name in sorted(config.list_manager.names): - mlist = MailList.MailList(name, lock=False) - if mlist.advertised: - if hostname not in mlist.web_page_url: - # This list is situated in a different virtual domain - continue - else: - advertised.append((mlist.GetScriptURL('admin'), - mlist.real_name, - mlist.description)) - # Greeting depends on whether there was an error or not - if msg: - greeting = FontAttr(msg, color="ff5060", size="+1") - else: - greeting = _("Welcome!") - - welcome = [] - mailmanlink = Link(config.MAILMAN_URL, _('Mailman')).Format() - if not advertised: - welcome.extend([ - greeting, - _('''<p>There currently are no publicly-advertised %(mailmanlink)s - mailing lists on %(hostname)s.'''), - ]) - else: - welcome.extend([ - greeting, - _('''<p>Below is the collection of publicly-advertised - %(mailmanlink)s mailing lists on %(hostname)s. Click on a list - name to visit the configuration pages for that list.'''), - ]) - - creatorurl = Utils.ScriptURL('create') - mailman_owner = Utils.get_site_noreply() - extra = msg and _('right ') or '' - welcome.extend([ - _('''To visit the administrators configuration page for an - unadvertised list, open a URL similar to this one, but with a '/' and - the %(extra)slist name appended. If you have the proper authority, - you can also <a href="%(creatorurl)s">create a new mailing list</a>. - - <p>General list information can be found at '''), - Link(Utils.ScriptURL('listinfo'), - _('the mailing list overview page')), - '.', - _('<p>(Send questions and comments to '), - Link('mailto:%s' % mailman_owner, mailman_owner), - '.)<p>', - ]) - - table.AddRow([Container(*welcome)]) - table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, colspan=2) - - if advertised: - table.AddRow([' ', ' ']) - table.AddRow([Bold(FontAttr(_('List'), size='+2')), - Bold(FontAttr(_('Description'), size='+2')) - ]) - highlight = 1 - for url, real_name, description in advertised: - table.AddRow( - [Link(url, Bold(real_name)), - description or Italic(_('[no description available]'))]) - if highlight and config.WEB_HIGHLIGHT_COLOR: - table.AddRowInfo(table.GetCurrentRowIndex(), - bgcolor=config.WEB_HIGHLIGHT_COLOR) - highlight = not highlight - - doc.AddItem(table) - doc.AddItem('<hr>') - doc.AddItem(MailmanLogo()) - print doc.Format() - - - -def option_help(mlist, varhelp): - # The html page document - doc = Document() - doc.set_language(mlist.preferred_language) - # Find out which category and variable help is being requested for. - item = None - reflist = varhelp.split('/') - if len(reflist) >= 2: - category = subcat = None - if len(reflist) == 2: - category, varname = reflist - elif len(reflist) == 3: - category, subcat, varname = reflist - options = mlist.GetConfigInfo(category, subcat) - if options: - for i in options: - if i and i[0] == varname: - item = i - break - # Print an error message if we couldn't find a valid one - if not item: - bad = _('No valid variable name found.') - doc.addError(bad) - doc.AddItem(mlist.GetMailmanFooter()) - print doc.Format() - return - # Get the details about the variable - varname, kind, params, dependancies, description, elaboration = \ - get_item_characteristics(item) - # Set up the document - realname = mlist.real_name - legend = _("""%(realname)s Mailing list Configuration Help - <br><em>%(varname)s</em> Option""") - - header = Table(width='100%') - header.AddRow([Center(Header(3, legend))]) - header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2, - bgcolor=config.WEB_HEADER_COLOR) - doc.SetTitle(_("Mailman %(varname)s List Option Help")) - doc.AddItem(header) - doc.AddItem("<b>%s</b> (%s): %s<p>" % (varname, category, description)) - if elaboration: - doc.AddItem("%s<p>" % elaboration) - - if subcat: - url = '%s/%s/%s' % (mlist.GetScriptURL('admin'), category, subcat) - else: - url = '%s/%s' % (mlist.GetScriptURL('admin'), category) - form = Form(url) - valtab = Table(cellspacing=3, cellpadding=4, width='100%') - add_options_table_item(mlist, category, subcat, valtab, item, detailsp=0) - form.AddItem(valtab) - form.AddItem('<p>') - form.AddItem(Center(submit_button())) - doc.AddItem(Center(form)) - - doc.AddItem(_("""<em><strong>Warning:</strong> changing this option here - could cause other screens to be out-of-sync. Be sure to reload any other - pages that are displaying this option for this mailing list. You can also - """)) - - adminurl = mlist.GetScriptURL('admin') - if subcat: - url = '%s/%s/%s' % (adminurl, category, subcat) - else: - url = '%s/%s' % (adminurl, category) - categoryname = mlist.GetConfigCategories()[category][0] - doc.AddItem(Link(url, _('return to the %(categoryname)s options page.'))) - doc.AddItem('</em>') - doc.AddItem(mlist.GetMailmanFooter()) - print doc.Format() - - - -def show_results(mlist, doc, category, subcat, cgidata): - # Produce the results page - adminurl = mlist.GetScriptURL('admin') - categories = mlist.GetConfigCategories() - label = _(categories[category][0]) - - # Set up the document's headers - realname = mlist.real_name - doc.SetTitle(_('%(realname)s Administration (%(label)s)')) - doc.AddItem(Center(Header(2, _( - '%(realname)s mailing list administration<br>%(label)s Section')))) - doc.AddItem('<hr>') - # Now we need to craft the form that will be submitted, which will contain - # all the variable settings, etc. This is a bit of a kludge because we - # know that the autoreply and members categories supports file uploads. - encoding = None - if category in ('autoreply', 'members'): - encoding = 'multipart/form-data' - if subcat: - form = Form('%s/%s/%s' % (adminurl, category, subcat), - encoding=encoding) - else: - form = Form('%s/%s' % (adminurl, category), encoding=encoding) - # This holds the two columns of links - linktable = Table(valign='top', width='100%') - linktable.AddRow([Center(Bold(_("Configuration Categories"))), - Center(Bold(_("Other Administrative Activities")))]) - # The `other links' are stuff in the right column. - otherlinks = UnorderedList() - otherlinks.AddItem(Link(mlist.GetScriptURL('admindb'), - _('Tend to pending moderator requests'))) - otherlinks.AddItem(Link(mlist.GetScriptURL('listinfo'), - _('Go to the general list information page'))) - otherlinks.AddItem(Link(mlist.GetScriptURL('edithtml'), - _('Edit the public HTML pages and text files'))) - otherlinks.AddItem(Link(mlist.GetBaseArchiveURL(), - _('Go to list archives')).Format() + - '<br> <br>') - if config.OWNERS_CAN_DELETE_THEIR_OWN_LISTS: - otherlinks.AddItem(Link(mlist.GetScriptURL('rmlist'), - _('Delete this mailing list')).Format() + - _(' (requires confirmation)<br> <br>')) - otherlinks.AddItem(Link('%s/logout' % adminurl, - # BAW: What I really want is a blank line, but - # adding an won't do it because of the - # bullet added to the list item. - '<FONT SIZE="+2"><b>%s</b></FONT>' % - _('Logout'))) - # These are links to other categories and live in the left column - categorylinks_1 = categorylinks = UnorderedList() - categorylinks_2 = '' - categorykeys = categories.keys() - half = len(categorykeys) / 2 - counter = 0 - subcat = None - for k in categorykeys: - label = _(categories[k][0]) - url = '%s/%s' % (adminurl, k) - if k == category: - # Handle subcategories - subcats = mlist.GetConfigSubCategories(k) - if subcats: - subcat = Utils.GetPathPieces()[-1] - for k, v in subcats: - if k == subcat: - break - else: - # The first subcategory in the list is the default - subcat = subcats[0][0] - subcat_items = [] - for sub, text in subcats: - if sub == subcat: - text = Bold('[%s]' % text).Format() - subcat_items.append(Link(url + '/' + sub, text)) - categorylinks.AddItem( - Bold(label).Format() + - UnorderedList(*subcat_items).Format()) - else: - categorylinks.AddItem(Link(url, Bold('[%s]' % label))) - else: - categorylinks.AddItem(Link(url, label)) - counter += 1 - if counter >= half: - categorylinks_2 = categorylinks = UnorderedList() - counter = -len(categorykeys) - # Make the emergency stop switch a rude solo light - etable = Table() - # Add all the links to the links table... - etable.AddRow([categorylinks_1, categorylinks_2]) - etable.AddRowInfo(etable.GetCurrentRowIndex(), valign='top') - if mlist.emergency: - label = _('Emergency moderation of all list traffic is enabled') - etable.AddRow([Center( - Link('?VARHELP=general/emergency', Bold(label)))]) - color = config.WEB_ERROR_COLOR - etable.AddCellInfo(etable.GetCurrentRowIndex(), 0, - colspan=2, bgcolor=color) - linktable.AddRow([etable, otherlinks]) - # ...and add the links table to the document. - form.AddItem(linktable) - form.AddItem('<hr>') - form.AddItem( - _('''Make your changes in the following section, then submit them - using the <em>Submit Your Changes</em> button below.''') - + '<p>') - - # The members and passwords categories are special in that they aren't - # defined in terms of gui elements. Create those pages here. - if category == 'members': - # Figure out which subcategory we should display - subcat = Utils.GetPathPieces()[-1] - if subcat not in ('list', 'add', 'remove'): - subcat = 'list' - # Add member category specific tables - form.AddItem(membership_options(mlist, subcat, cgidata, doc, form)) - form.AddItem(Center(submit_button('setmemberopts_btn'))) - # In "list" subcategory, we can also search for members - if subcat == 'list': - form.AddItem('<hr>\n') - table = Table(width='100%') - table.AddRow([Center(Header(2, _('Additional Member Tasks')))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, - bgcolor=config.WEB_HEADER_COLOR) - # Add a blank separator row - table.AddRow([' ', ' ']) - # Add a section to set the moderation bit for all members - table.AddRow([_("""<li>Set everyone's moderation bit, including - those members not currently visible""")]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - table.AddRow([RadioButtonArray('allmodbit_val', - (_('Off'), _('On')), - mlist.default_member_moderation), - SubmitButton('allmodbit_btn', _('Set'))]) - form.AddItem(table) - elif category == 'passwords': - form.AddItem(Center(password_inputs(mlist))) - form.AddItem(Center(submit_button())) - else: - form.AddItem(show_variables(mlist, category, subcat, cgidata, doc)) - form.AddItem(Center(submit_button())) - # And add the form - doc.AddItem(form) - doc.AddItem(mlist.GetMailmanFooter()) - - - -def show_variables(mlist, category, subcat, cgidata, doc): - options = mlist.GetConfigInfo(category, subcat) - - # The table containing the results - table = Table(cellspacing=3, cellpadding=4, width='100%') - - # Get and portray the text label for the category. - categories = mlist.GetConfigCategories() - label = _(categories[category][0]) - - table.AddRow([Center(Header(2, label))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, - bgcolor=config.WEB_HEADER_COLOR) - - # The very first item in the config info will be treated as a general - # description if it is a string - description = options[0] - if isinstance(description, basestring): - table.AddRow([description]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - options = options[1:] - - if not options: - return table - - # Add the global column headers - table.AddRow([Center(Bold(_('Description'))), - Center(Bold(_('Value')))]) - table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, - width='15%') - table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 1, - width='85%') - - for item in options: - if isinstance(item, basestring): - # The very first banner option (string in an options list) is - # treated as a general description, while any others are - # treated as section headers - centered and italicized... - table.AddRow([Center(Italic(item))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - else: - add_options_table_item(mlist, category, subcat, table, item) - table.AddRow(['<br>']) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - return table - - - -def add_options_table_item(mlist, category, subcat, table, item, detailsp=1): - # Add a row to an options table with the item description and value. - varname, kind, params, extra, descr, elaboration = \ - get_item_characteristics(item) - if elaboration is None: - elaboration = descr - descr = get_item_gui_description(mlist, category, subcat, - varname, descr, elaboration, detailsp) - val = get_item_gui_value(mlist, category, kind, varname, params, extra) - table.AddRow([descr, val]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, - bgcolor=config.WEB_ADMINITEM_COLOR) - table.AddCellInfo(table.GetCurrentRowIndex(), 1, - bgcolor=config.WEB_ADMINITEM_COLOR) - - - -def get_item_characteristics(record): - # Break out the components of an item description from its description - # record: - # - # 0 -- option-var name - # 1 -- type - # 2 -- entry size - # 3 -- ?dependancies? - # 4 -- Brief description - # 5 -- Optional description elaboration - if len(record) == 5: - elaboration = None - varname, kind, params, dependancies, descr = record - elif len(record) == 6: - varname, kind, params, dependancies, descr, elaboration = record - else: - raise ValueError, _('Badly formed options entry:\n %(record)s') - return varname, kind, params, dependancies, descr, elaboration - - - -def get_item_gui_value(mlist, category, kind, varname, params, extra): - """Return a representation of an item's settings.""" - # Give the category a chance to return the value for the variable - value = None - label, gui = mlist.GetConfigCategories()[category] - if hasattr(gui, 'getValue'): - value = gui.getValue(mlist, kind, varname, params) - # Filter out None, and volatile attributes - if value is None and not varname.startswith('_'): - value = getattr(mlist, varname) - # Now create the widget for this value - if kind == config.Radio or kind == config.Toggle: - # If we are returning the option for subscribe policy and this site - # doesn't allow open subscribes, then we have to alter the value of - # mlist.subscribe_policy as passed to RadioButtonArray in order to - # compensate for the fact that there is one fewer option. - # Correspondingly, we alter the value back in the change options - # function -scott - # - # TBD: this is an ugly ugly hack. - if varname.startswith('_'): - checked = 0 - else: - checked = value - if varname == 'subscribe_policy' and not config.ALLOW_OPEN_SUBSCRIBE: - checked = checked - 1 - # For Radio buttons, we're going to interpret the extra stuff as a - # horizontal/vertical flag. For backwards compatibility, the value 0 - # means horizontal, so we use "not extra" to get the parity right. - return RadioButtonArray(varname, params, checked, not extra) - elif (kind == config.String or kind == config.Email or - kind == config.Host or kind == config.Number): - return TextBox(varname, value, params) - elif kind == config.Text: - if params: - r, c = params - else: - r, c = None, None - return TextArea(varname, value or '', r, c) - elif kind in (config.EmailList, config.EmailListEx): - if params: - r, c = params - else: - r, c = None, None - res = NL.join(value) - return TextArea(varname, res, r, c, wrap='off') - elif kind == config.FileUpload: - # like a text area, but also with uploading - if params: - r, c = params - else: - r, c = None, None - container = Container() - container.AddItem(_('<em>Enter the text below, or...</em><br>')) - container.AddItem(TextArea(varname, value or '', r, c)) - container.AddItem(_('<br><em>...specify a file to upload</em><br>')) - container.AddItem(FileUpload(varname+'_upload', r, c)) - return container - elif kind == config.Select: - if params: - values, legend, selected = params - else: - codes = mlist.language_codes - legend = [config.languages.get_description(code) for code in codes] - selected = codes.index(mlist.preferred_language) - return SelectOptions(varname, values, legend, selected) - elif kind == config.Topics: - # A complex and specialized widget type that allows for setting of a - # topic name, a mark button, a regexp text box, an "add after mark", - # and a delete button. Yeesh! params are ignored. - table = Table(border=0) - # This adds the html for the entry widget - def makebox(i, name, pattern, desc, empty=False, table=table): - deltag = 'topic_delete_%02d' % i - boxtag = 'topic_box_%02d' % i - reboxtag = 'topic_rebox_%02d' % i - desctag = 'topic_desc_%02d' % i - wheretag = 'topic_where_%02d' % i - addtag = 'topic_add_%02d' % i - newtag = 'topic_new_%02d' % i - if empty: - table.AddRow([Center(Bold(_('Topic %(i)d'))), - Hidden(newtag)]) - else: - table.AddRow([Center(Bold(_('Topic %(i)d'))), - SubmitButton(deltag, _('Delete'))]) - table.AddRow([Label(_('Topic name:')), - TextBox(boxtag, value=name, size=30)]) - table.AddRow([Label(_('Regexp:')), - TextArea(reboxtag, text=pattern, - rows=4, cols=30, wrap='off')]) - table.AddRow([Label(_('Description:')), - TextArea(desctag, text=desc, - rows=4, cols=30, wrap='soft')]) - if not empty: - table.AddRow([SubmitButton(addtag, _('Add new item...')), - SelectOptions(wheretag, ('before', 'after'), - (_('...before this one.'), - _('...after this one.')), - selected=1), - ]) - table.AddRow(['<hr>']) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - # Now for each element in the existing data, create a widget - i = 1 - data = getattr(mlist, varname) - for name, pattern, desc, empty in data: - makebox(i, name, pattern, desc, empty) - i += 1 - # Add one more non-deleteable widget as the first blank entry, but - # only if there are no real entries. - if i == 1: - makebox(i, '', '', '', empty=True) - return table - elif kind == config.HeaderFilter: - # A complex and specialized widget type that allows for setting of a - # spam filter rule including, a mark button, a regexp text box, an - # "add after mark", up and down buttons, and a delete button. Yeesh! - # params are ignored. - table = Table(border=0) - # This adds the html for the entry widget - def makebox(i, pattern, action, empty=False, table=table): - deltag = 'hdrfilter_delete_%02d' % i - reboxtag = 'hdrfilter_rebox_%02d' % i - actiontag = 'hdrfilter_action_%02d' % i - wheretag = 'hdrfilter_where_%02d' % i - addtag = 'hdrfilter_add_%02d' % i - newtag = 'hdrfilter_new_%02d' % i - uptag = 'hdrfilter_up_%02d' % i - downtag = 'hdrfilter_down_%02d' % i - if empty: - table.AddRow([Center(Bold(_('Spam Filter Rule %(i)d'))), - Hidden(newtag)]) - else: - table.AddRow([Center(Bold(_('Spam Filter Rule %(i)d'))), - SubmitButton(deltag, _('Delete'))]) - table.AddRow([Label(_('Spam Filter Regexp:')), - TextArea(reboxtag, text=pattern, - rows=4, cols=30, wrap='off')]) - values = [config.DEFER, config.HOLD, config.REJECT, - config.DISCARD, config.ACCEPT] - try: - checked = values.index(action) - except ValueError: - checked = 0 - radio = RadioButtonArray( - actiontag, - (_('Defer'), _('Hold'), _('Reject'), - _('Discard'), _('Accept')), - values=values, - checked=checked).Format() - table.AddRow([Label(_('Action:')), radio]) - if not empty: - table.AddRow([SubmitButton(addtag, _('Add new item...')), - SelectOptions(wheretag, ('before', 'after'), - (_('...before this one.'), - _('...after this one.')), - selected=1), - ]) - # BAW: IWBNI we could disable the up and down buttons for the - # first and last item respectively, but it's not easy to know - # which is the last item, so let's not worry about that for - # now. - table.AddRow([SubmitButton(uptag, _('Move rule up')), - SubmitButton(downtag, _('Move rule down'))]) - table.AddRow(['<hr>']) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - # Now for each element in the existing data, create a widget - i = 1 - data = getattr(mlist, varname) - for pattern, action, empty in data: - makebox(i, pattern, action, empty) - i += 1 - # Add one more non-deleteable widget as the first blank entry, but - # only if there are no real entries. - if i == 1: - makebox(i, '', config.DEFER, empty=True) - return table - elif kind == config.Checkbox: - return CheckBoxArray(varname, *params) - else: - assert 0, 'Bad gui widget type: %s' % kind - - - -def get_item_gui_description(mlist, category, subcat, - varname, descr, elaboration, detailsp): - # Return the item's description, with link to details. - # - # Details are not included if this is a VARHELP page, because that /is/ - # the details page! - if detailsp: - if subcat: - varhelp = '?VARHELP=%s/%s/%s' % (category, subcat, varname) - else: - varhelp = '?VARHELP=%s/%s' % (category, varname) - if descr == elaboration: - linktext = _('<br>(Edit <b>%(varname)s</b>)') - else: - linktext = _('<br>(Details for <b>%(varname)s</b>)') - link = Link(mlist.GetScriptURL('admin') + varhelp, - linktext).Format() - text = Label('%s %s' % (descr, link)).Format() - else: - text = Label(descr).Format() - if varname[0] == '_': - text += Label(_('''<br><em><strong>Note:</strong> - setting this value performs an immediate action but does not modify - permanent state.</em>''')).Format() - return text - - - -def membership_options(mlist, subcat, cgidata, doc, form): - # Show the main stuff - adminurl = mlist.GetScriptURL('admin') - container = Container() - header = Table(width="100%") - # If we're in the list subcategory, show the membership list - if subcat == 'add': - header.AddRow([Center(Header(2, _('Mass Subscriptions')))]) - header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2, - bgcolor=config.WEB_HEADER_COLOR) - container.AddItem(header) - mass_subscribe(mlist, container) - return container - if subcat == 'remove': - header.AddRow([Center(Header(2, _('Mass Removals')))]) - header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2, - bgcolor=config.WEB_HEADER_COLOR) - container.AddItem(header) - mass_remove(mlist, container) - return container - # Otherwise... - header.AddRow([Center(Header(2, _('Membership List')))]) - header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2, - bgcolor=config.WEB_HEADER_COLOR) - container.AddItem(header) - # Add a "search for member" button - table = Table(width='100%') - link = Link('http://www.python.org/doc/current/lib/re-syntax.html', - _('(help)')).Format() - table.AddRow([Label(_('Find member %(link)s:')), - TextBox('findmember', - value=cgidata.getvalue('findmember', '')), - SubmitButton('findmember_btn', _('Search...'))]) - container.AddItem(table) - container.AddItem('<hr><p>') - usertable = Table(width="90%", border='2') - # If there are more members than allowed by chunksize, then we split the - # membership up alphabetically. Otherwise just display them all. - chunksz = mlist.admin_member_chunksize - # The email addresses had /better/ be ASCII, but might be encoded in the - # database as Unicodes. - all = [_m.encode() for _m in mlist.getMembers()] - all.sort(lambda x, y: cmp(x.lower(), y.lower())) - # See if the query has a regular expression - regexp = cgidata.getvalue('findmember', '').strip() - if regexp: - try: - cre = re.compile(regexp, re.IGNORECASE) - except re.error: - doc.addError(_('Bad regular expression: ') + regexp) - else: - # BAW: There's got to be a more efficient way of doing this! - names = [mlist.getMemberName(s) or '' for s in all] - all = [a for n, a in zip(names, all) - if cre.search(n) or cre.search(a)] - chunkindex = None - bucket = None - actionurl = None - if len(all) < chunksz: - members = all - else: - # Split them up alphabetically, and then split the alphabetical - # listing by chunks - buckets = {} - for addr in all: - members = buckets.setdefault(addr[0].lower(), []) - members.append(addr) - # Now figure out which bucket we want - bucket = None - qs = {} - # POST methods, even if their actions have a query string, don't get - # put into FieldStorage's keys :-( - qsenviron = os.environ.get('QUERY_STRING') - if qsenviron: - qs = cgi.parse_qs(qsenviron) - bucket = qs.get('letter', 'a')[0].lower() - if bucket not in digits + lowercase: - bucket = None - if not bucket or not buckets.has_key(bucket): - keys = buckets.keys() - keys.sort() - bucket = keys[0] - members = buckets[bucket] - action = adminurl + '/members?letter=%s' % bucket - if len(members) <= chunksz: - form.set_action(action) - else: - i, r = divmod(len(members), chunksz) - numchunks = i + (not not r * 1) - # Now chunk them up - chunkindex = 0 - if qs.has_key('chunk'): - try: - chunkindex = int(qs['chunk'][0]) - except ValueError: - chunkindex = 0 - if chunkindex < 0 or chunkindex > numchunks: - chunkindex = 0 - members = members[chunkindex*chunksz:(chunkindex+1)*chunksz] - # And set the action URL - form.set_action(action + '&chunk=%s' % chunkindex) - # So now members holds all the addresses we're going to display - allcnt = len(all) - if bucket: - membercnt = len(members) - usertable.AddRow([Center(Italic(_( - '%(allcnt)s members total, %(membercnt)s shown')))]) - else: - usertable.AddRow([Center(Italic(_('%(allcnt)s members total')))]) - usertable.AddCellInfo(usertable.GetCurrentRowIndex(), - usertable.GetCurrentCellIndex(), - colspan=OPTCOLUMNS, - bgcolor=config.WEB_ADMINITEM_COLOR) - # Add the alphabetical links - if bucket: - cells = [] - for letter in digits + lowercase: - if not buckets.get(letter): - continue - url = adminurl + '/members?letter=%s' % letter - if letter == bucket: - show = Bold('[%s]' % letter.upper()).Format() - else: - show = letter.upper() - cells.append(Link(url, show).Format()) - joiner = ' '*2 + '\n' - usertable.AddRow([Center(joiner.join(cells))]) - usertable.AddCellInfo(usertable.GetCurrentRowIndex(), - usertable.GetCurrentCellIndex(), - colspan=OPTCOLUMNS, - bgcolor=config.WEB_ADMINITEM_COLOR) - usertable.AddRow([Center(h) for h in (_('unsub'), - _('member address<br>member name'), - _('mod'), _('hide'), - _('nomail<br>[reason]'), - _('ack'), _('not metoo'), - _('nodupes'), - _('digest'), _('plain'), - _('language'))]) - rowindex = usertable.GetCurrentRowIndex() - for i in range(OPTCOLUMNS): - usertable.AddCellInfo(rowindex, i, bgcolor=config.WEB_ADMINITEM_COLOR) - # Find the longest name in the list - longest = 0 - if members: - names = filter(None, [mlist.getMemberName(s) for s in members]) - # Make the name field at least as long as the longest email address - longest = max([len(s) for s in names + members]) - # Abbreviations for delivery status details - ds_abbrevs = {MemberAdaptor.UNKNOWN : _('?'), - MemberAdaptor.BYUSER : _('U'), - MemberAdaptor.BYADMIN : _('A'), - MemberAdaptor.BYBOUNCE: _('B'), - } - # Now populate the rows - for addr in members: - link = Link(mlist.GetOptionsURL(addr, obscure=1), - mlist.getMemberCPAddress(addr)) - fullname = mlist.getMemberName(addr) - name = TextBox(addr + '_realname', fullname, size=longest).Format() - cells = [Center(CheckBox(addr + '_unsub', 'off', 0).Format()), - link.Format() + '<br>' + - name + - Hidden('user', urllib.quote(addr)).Format(), - ] - # Do the `mod' option - if mlist.getMemberOption(addr, config.Moderate): - value = 'on' - checked = 1 - else: - value = 'off' - checked = 0 - box = CheckBox('%s_mod' % addr, value, checked) - cells.append(Center(box).Format()) - for opt in ('hide', 'nomail', 'ack', 'notmetoo', 'nodupes'): - extra = '' - if opt == 'nomail': - status = mlist.getDeliveryStatus(addr) - if status == MemberAdaptor.ENABLED: - value = 'off' - checked = 0 - else: - value = 'on' - checked = 1 - extra = '[%s]' % ds_abbrevs[status] - elif mlist.getMemberOption(addr, config.OPTINFO[opt]): - value = 'on' - checked = 1 - else: - value = 'off' - checked = 0 - box = CheckBox('%s_%s' % (addr, opt), value, checked) - cells.append(Center(box.Format() + extra)) - # This code is less efficient than the original which did a has_key on - # the underlying dictionary attribute. This version is slower and - # less memory efficient. It points to a new MemberAdaptor interface - # method. - if addr in mlist.getRegularMemberKeys(): - cells.append(Center(CheckBox(addr + '_digest', 'off', 0).Format())) - else: - cells.append(Center(CheckBox(addr + '_digest', 'on', 1).Format())) - if mlist.getMemberOption(addr, config.OPTINFO['plain']): - value = 'on' - checked = 1 - else: - value = 'off' - checked = 0 - cells.append(Center(CheckBox('%s_plain' % addr, value, checked))) - # User's preferred language - langpref = mlist.getMemberLanguage(addr) - langs = mlist.language_codes - langdescs = [_(config.languges.get_description(code)) - for code in langs] - try: - selected = langs.index(langpref) - except ValueError: - selected = 0 - cells.append(Center(SelectOptions(addr + '_language', langs, - langdescs, selected)).Format()) - usertable.AddRow(cells) - # Add the usertable and a legend - legend = UnorderedList() - legend.AddItem( - _('<b>unsub</b> -- Click on this to unsubscribe the member.')) - legend.AddItem( - _("""<b>mod</b> -- The user's personal moderation flag. If this is - set, postings from them will be moderated, otherwise they will be - approved.""")) - legend.AddItem( - _("""<b>hide</b> -- Is the member's address concealed on - the list of subscribers?""")) - legend.AddItem(_( - """<b>nomail</b> -- Is delivery to the member disabled? If so, an - abbreviation will be given describing the reason for the disabled - delivery: - <ul><li><b>U</b> -- Delivery was disabled by the user via their - personal options page. - <li><b>A</b> -- Delivery was disabled by the list - administrators. - <li><b>B</b> -- Delivery was disabled by the system due to - excessive bouncing from the member's address. - <li><b>?</b> -- The reason for disabled delivery isn't known. - This is the case for all memberships which were disabled - in older versions of Mailman. - </ul>""")) - legend.AddItem( - _('''<b>ack</b> -- Does the member get acknowledgements of their - posts?''')) - legend.AddItem( - _('''<b>not metoo</b> -- Does the member want to avoid copies of their - own postings?''')) - legend.AddItem( - _('''<b>nodupes</b> -- Does the member want to avoid duplicates of the - same message?''')) - legend.AddItem( - _('''<b>digest</b> -- Does the member get messages in digests? - (otherwise, individual messages)''')) - legend.AddItem( - _('''<b>plain</b> -- If getting digests, does the member get plain - text digests? (otherwise, MIME)''')) - legend.AddItem(_("<b>language</b> -- Language preferred by the user")) - addlegend = '' - parsedqs = 0 - qsenviron = os.environ.get('QUERY_STRING') - if qsenviron: - qs = cgi.parse_qs(qsenviron).get('legend') - if qs and isinstance(qs, list): - qs = qs[0] - if qs == 'yes': - addlegend = 'legend=yes&' - if addlegend: - container.AddItem(legend.Format() + '<p>') - container.AddItem( - Link(adminurl + '/members/list', - _('Click here to hide the legend for this table.'))) - else: - container.AddItem( - Link(adminurl + '/members/list?legend=yes', - _('Click here to include the legend for this table.'))) - container.AddItem(Center(usertable)) - - # There may be additional chunks - if chunkindex is not None: - buttons = [] - url = adminurl + '/members?%sletter=%s&' % (addlegend, bucket) - footer = _('''<p><em>To view more members, click on the appropriate - range listed below:</em>''') - chunkmembers = buckets[bucket] - last = len(chunkmembers) - for i in range(numchunks): - if i == chunkindex: - continue - start = chunkmembers[i*chunksz] - end = chunkmembers[min((i+1)*chunksz, last)-1] - link = Link(url + 'chunk=%d' % i, _('from %(start)s to %(end)s')) - buttons.append(link) - buttons = UnorderedList(*buttons) - container.AddItem(footer + buttons.Format() + '<p>') - return container - - - -def mass_subscribe(mlist, container): - # MASS SUBSCRIBE - GREY = config.WEB_ADMINITEM_COLOR - table = Table(width='90%') - table.AddRow([ - Label(_('Subscribe these users now or invite them?')), - RadioButtonArray('subscribe_or_invite', - (_('Subscribe'), _('Invite')), - 0, values=(0, 1)) - ]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY) - table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY) - table.AddRow([ - Label(_('Send welcome messages to new subscribees?')), - RadioButtonArray('send_welcome_msg_to_this_batch', - (_('No'), _('Yes')), - mlist.send_welcome_msg, - values=(0, 1)) - ]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY) - table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY) - table.AddRow([ - Label(_('Send notifications of new subscriptions to the list owner?')), - RadioButtonArray('send_notifications_to_list_owner', - (_('No'), _('Yes')), - mlist.admin_notify_mchanges, - values=(0,1)) - ]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY) - table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY) - table.AddRow([Italic(_('Enter one address per line below...'))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - table.AddRow([Center(TextArea(name='subscribees', - rows=10, cols='70%', wrap=None))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - table.AddRow([Italic(Label(_('...or specify a file to upload:'))), - FileUpload('subscribees_upload', cols='50')]) - container.AddItem(Center(table)) - # Invitation text - table.AddRow([' ', ' ']) - table.AddRow([Italic(_("""Below, enter additional text to be added to the - top of your invitation or the subscription notification. Include at least - one blank line at the end..."""))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - table.AddRow([Center(TextArea(name='invitation', - rows=10, cols='70%', wrap=None))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - - - -def mass_remove(mlist, container): - # MASS UNSUBSCRIBE - GREY = config.WEB_ADMINITEM_COLOR - table = Table(width='90%') - table.AddRow([ - Label(_('Send unsubscription acknowledgement to the user?')), - RadioButtonArray('send_unsub_ack_to_this_batch', - (_('No'), _('Yes')), - 0, values=(0, 1)) - ]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY) - table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY) - table.AddRow([ - Label(_('Send notifications to the list owner?')), - RadioButtonArray('send_unsub_notifications_to_list_owner', - (_('No'), _('Yes')), - mlist.admin_notify_mchanges, - values=(0, 1)) - ]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY) - table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY) - table.AddRow([Italic(_('Enter one address per line below...'))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - table.AddRow([Center(TextArea(name='unsubscribees', - rows=10, cols='70%', wrap=None))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - table.AddRow([Italic(Label(_('...or specify a file to upload:'))), - FileUpload('unsubscribees_upload', cols='50')]) - container.AddItem(Center(table)) - - - -def password_inputs(mlist): - adminurl = mlist.GetScriptURL('admin') - table = Table(cellspacing=3, cellpadding=4) - table.AddRow([Center(Header(2, _('Change list ownership passwords')))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, - bgcolor=config.WEB_HEADER_COLOR) - table.AddRow([_("""\ -The <em>list administrators</em> are the people who have ultimate control over -all parameters of this mailing list. They are able to change any list -configuration variable available through these administration web pages. - -<p>The <em>list moderators</em> have more limited permissions; they are not -able to change any list configuration variable, but they are allowed to tend -to pending administration requests, including approving or rejecting held -subscription requests, and disposing of held postings. Of course, the -<em>list administrators</em> can also tend to pending requests. - -<p>In order to split the list ownership duties into administrators and -moderators, you must set a separate moderator password in the fields below, -and also provide the email addresses of the list moderators in the -<a href="%(adminurl)s/general">general options section</a>.""")]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) - # Set up the admin password table on the left - atable = Table(border=0, cellspacing=3, cellpadding=4, - bgcolor=config.WEB_ADMINPW_COLOR) - atable.AddRow([Label(_('Enter new administrator password:')), - PasswordBox('newpw', size=20)]) - atable.AddRow([Label(_('Confirm administrator password:')), - PasswordBox('confirmpw', size=20)]) - # Set up the moderator password table on the right - mtable = Table(border=0, cellspacing=3, cellpadding=4, - bgcolor=config.WEB_ADMINPW_COLOR) - mtable.AddRow([Label(_('Enter new moderator password:')), - PasswordBox('newmodpw', size=20)]) - mtable.AddRow([Label(_('Confirm moderator password:')), - PasswordBox('confirmmodpw', size=20)]) - # Add these tables to the overall password table - table.AddRow([atable, mtable]) - return table - - - -def submit_button(name='submit'): - table = Table(border=0, cellspacing=0, cellpadding=2) - table.AddRow([Bold(SubmitButton(name, _('Submit Your Changes')))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, align='middle') - return table - - - -def change_options(mlist, category, subcat, cgidata, doc): - def safeint(formvar, defaultval=None): - try: - return int(cgidata.getvalue(formvar)) - except (ValueError, TypeError): - return defaultval - confirmed = 0 - # Handle changes to the list moderator password. Do this before checking - # the new admin password, since the latter will force a reauthentication. - new = cgidata.getvalue('newmodpw', '').strip() - confirm = cgidata.getvalue('confirmmodpw', '').strip() - if new or confirm: - if new == confirm: - mlist.mod_password = passwords.make_secret( - new, config.PASSWORD_SCHEME) - # No re-authentication necessary because the moderator's - # password doesn't get you into these pages. - else: - doc.addError(_('Moderator passwords did not match')) - # Handle changes to the list administrator password - new = cgidata.getvalue('newpw', '').strip() - confirm = cgidata.getvalue('confirmpw', '').strip() - if new or confirm: - if new == confirm: - mlist.password = passwords.make_secret(new, config.PASSWORD_SCHEME) - # Set new cookie - print mlist.MakeCookie(config.AuthListAdmin) - else: - doc.addError(_('Administrator passwords did not match')) - # Give the individual gui item a chance to process the form data - categories = mlist.GetConfigCategories() - label, gui = categories[category] - # BAW: We handle the membership page special... for now. - if category <> 'members': - gui.handleForm(mlist, category, subcat, cgidata, doc) - # mass subscription, removal processing for members category - subscribers = '' - subscribers += cgidata.getvalue('subscribees', '') - subscribers += cgidata.getvalue('subscribees_upload', '') - if subscribers: - entries = filter(None, [n.strip() for n in subscribers.splitlines()]) - send_welcome_msg = safeint('send_welcome_msg_to_this_batch', - mlist.send_welcome_msg) - send_admin_notif = safeint('send_notifications_to_list_owner', - mlist.admin_notify_mchanges) - # Default is to subscribe - subscribe_or_invite = safeint('subscribe_or_invite', 0) - invitation = cgidata.getvalue('invitation', '') - digest = mlist.digest_is_default - if not mlist.digestable: - digest = 0 - if not mlist.nondigestable: - digest = 1 - subscribe_errors = [] - subscribe_success = [] - # Now cruise through all the subscribees and do the deed. BAW: we - # should limit the number of "Successfully subscribed" status messages - # we display. Try uploading a file with 10k names -- it takes a while - # to render the status page. - for entry in entries: - fullname, address = parseaddr(entry) - # Canonicalize the full name - fullname = Utils.canonstr(fullname, mlist.preferred_language) - userdesc = UserDesc(address, fullname, - Utils.MakeRandomPassword(), - digest, mlist.preferred_language) - try: - if subscribe_or_invite: - if mlist.isMember(address): - raise Errors.MMAlreadyAMember - else: - mlist.InviteNewMember(userdesc, invitation) - else: - mlist.ApprovedAddMember(userdesc, send_welcome_msg, - send_admin_notif, invitation, - whence='admin mass sub') - except Errors.MMAlreadyAMember: - subscribe_errors.append((entry, _('Already a member'))) - except Errors.InvalidEmailAddress: - if userdesc.address == '': - subscribe_errors.append((_('<blank line>'), - _('Bad/Invalid email address'))) - else: - subscribe_errors.append((entry, - _('Bad/Invalid email address'))) - except Errors.MembershipIsBanned, pattern: - subscribe_errors.append( - (entry, _('Banned address (matched %(pattern)s)'))) - else: - member = Utils.uncanonstr(formataddr((fullname, address))) - subscribe_success.append(Utils.websafe(member)) - if subscribe_success: - if subscribe_or_invite: - doc.AddItem(Header(5, _('Successfully invited:'))) - else: - doc.AddItem(Header(5, _('Successfully subscribed:'))) - doc.AddItem(UnorderedList(*subscribe_success)) - doc.AddItem('<p>') - if subscribe_errors: - if subscribe_or_invite: - doc.AddItem(Header(5, _('Error inviting:'))) - else: - doc.AddItem(Header(5, _('Error subscribing:'))) - items = ['%s -- %s' % (x0, x1) for x0, x1 in subscribe_errors] - doc.AddItem(UnorderedList(*items)) - doc.AddItem('<p>') - # Unsubscriptions - removals = '' - if cgidata.has_key('unsubscribees'): - removals += cgidata['unsubscribees'].value - if cgidata.has_key('unsubscribees_upload') and \ - cgidata['unsubscribees_upload'].value: - removals += cgidata['unsubscribees_upload'].value - if removals: - names = filter(None, [n.strip() for n in removals.splitlines()]) - send_unsub_notifications = int( - cgidata['send_unsub_notifications_to_list_owner'].value) - userack = int( - cgidata['send_unsub_ack_to_this_batch'].value) - unsubscribe_errors = [] - unsubscribe_success = [] - for addr in names: - try: - mlist.ApprovedDeleteMember( - addr, whence='admin mass unsub', - admin_notif=send_unsub_notifications, - userack=userack) - unsubscribe_success.append(addr) - except Errors.NotAMemberError: - unsubscribe_errors.append(addr) - if unsubscribe_success: - doc.AddItem(Header(5, _('Successfully Unsubscribed:'))) - doc.AddItem(UnorderedList(*unsubscribe_success)) - doc.AddItem('<p>') - if unsubscribe_errors: - doc.AddItem(Header(3, Bold(FontAttr( - _('Cannot unsubscribe non-members:'), - color='#ff0000', size='+2')).Format())) - doc.AddItem(UnorderedList(*unsubscribe_errors)) - doc.AddItem('<p>') - # See if this was a moderation bit operation - if cgidata.has_key('allmodbit_btn'): - val = cgidata.getvalue('allmodbit_val') - try: - val = int(val) - except VallueError: - val = None - if val not in (0, 1): - doc.addError(_('Bad moderation flag value')) - else: - for member in mlist.getMembers(): - mlist.setMemberOption(member, config.Moderate, val) - # do the user options for members category - if cgidata.has_key('setmemberopts_btn') and cgidata.has_key('user'): - user = cgidata['user'] - if isinstance(user, list): - users = [] - for ui in range(len(user)): - users.append(urllib.unquote(user[ui].value)) - else: - users = [urllib.unquote(user.value)] - errors = [] - removes = [] - for user in users: - if cgidata.has_key('%s_unsub' % user): - try: - mlist.ApprovedDeleteMember(user, whence='member mgt page') - removes.append(user) - except Errors.NotAMemberError: - errors.append((user, _('Not subscribed'))) - continue - if not mlist.isMember(user): - doc.addError(_('Ignoring changes to deleted member: %(user)s'), - tag=_('Warning: ')) - continue - value = cgidata.has_key('%s_digest' % user) - try: - mlist.setMemberOption(user, config.Digests, value) - except (Errors.AlreadyReceivingDigests, - Errors.AlreadyReceivingRegularDeliveries, - Errors.CantDigestError, - Errors.MustDigestError): - # BAW: Hmm... - pass - - newname = cgidata.getvalue(user+'_realname', '') - newname = Utils.canonstr(newname, mlist.preferred_language) - mlist.setMemberName(user, newname) - - newlang = cgidata.getvalue(user+'_language') - oldlang = mlist.getMemberLanguage(user) - if (newlang not in config.languages.enabled_codes - and newlang <> oldlang): - # Then - mlist.setMemberLanguage(user, newlang) - - moderate = not not cgidata.getvalue(user+'_mod') - mlist.setMemberOption(user, config.Moderate, moderate) - - # Set the `nomail' flag, but only if the user isn't already - # disabled (otherwise we might change BYUSER into BYADMIN). - if cgidata.has_key('%s_nomail' % user): - if mlist.getDeliveryStatus(user) == MemberAdaptor.ENABLED: - mlist.setDeliveryStatus(user, MemberAdaptor.BYADMIN) - else: - mlist.setDeliveryStatus(user, MemberAdaptor.ENABLED) - for opt in ('hide', 'ack', 'notmetoo', 'nodupes', 'plain'): - opt_code = config.OPTINFO[opt] - if cgidata.has_key('%s_%s' % (user, opt)): - mlist.setMemberOption(user, opt_code, 1) - else: - mlist.setMemberOption(user, opt_code, 0) - # Give some feedback on who's been removed - if removes: - doc.AddItem(Header(5, _('Successfully Removed:'))) - doc.AddItem(UnorderedList(*removes)) - doc.AddItem('<p>') - if errors: - doc.AddItem(Header(5, _("Error Unsubscribing:"))) - items = ['%s -- %s' % (x[0], x[1]) for x in errors] - doc.AddItem(apply(UnorderedList, tuple((items)))) - doc.AddItem("<p>") |
